diff --git a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt deleted file mode 100644 index 1ec3081e..00000000 --- a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt +++ /dev/null @@ -1,34 +0,0 @@ -MSM Bus Scaling Driver - -The msm bus scaling driver provides the ability to configure -bus performance parameters across the entire chip-set. -Various clients use MSM scaling APIs to request bandwidth -between multiple master-slave pairs. The bus driver then finds -the optimal path between the master and the slave, and aggregates -the bandwidth and clock requests for all master-slave pairs on -that path, and programs hardware accordingly. - -The device-tree data required for bus-scaling can be embedded within -the clients' device nodes. The clients can register with the bus driver -using the following properties: - -- qcom,msm_bus,name: String representing the client-name -- qcom,msm_bus,num_cases: Total number of usecases -- qcom,msm_bus,active_only: Context flag for requests in active or - dual (active & sleep) contex -- qcom,msm_bus,num_paths: Total number of master-slave pairs -- qcom,msm_bus,vectors: Arrays of unsigned integers representing: - master-id, slave-id, arbitrated bandwidth, - instantaneous bandwidth - -Example: - - qcom,msm_bus,name = "client-name"; - qcom,msm_bus,num_cases = <3>; - qcom,msm_bus,active_only = <0>; - qcom,msm_bus,num_paths = <2>; - qcom,msm_bus,vectors = - <22 512 0 0>, <26 512 0 0>, - <22 512 320000 320000000>, <26 512 3200000 320000000>, - <22 512 160000 160000000>, <26 512 1600000 160000000>; - diff --git a/Makefile b/Makefile index de570d00..09a1f8e2 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 4 SUBLEVEL = 110 -EXTRAVERSION = --ThE-ShAdOw-v1.1-- +EXTRAVERSION = NAME = Saber-toothed Squirrel # *DOCUMENTATION* @@ -162,7 +162,7 @@ export srctree objtree VPATH # SUBARCH tells the usermode build what the underlying arch is. That is set # first, and if a usermode build is happening, the "ARCH=um" on the command # line overrides the setting of ARCH below. If a native build is happening, -# then ARCH is assigned, getting whatever value it gets normally, and +# then ARCH is assigned, getting whatever value it gets normally, and # SUBARCH is subsequently ignored. SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ @@ -171,6 +171,8 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ -e s/sh[234].*/sh/ ) +SUBARCH := arm + # Cross compiling and selecting different set of gcc/bin-utils # --------------------------------------------------------------------------- # @@ -192,7 +194,7 @@ 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 ?= arm +ARCH ?=arm CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%) # Architecture as present in compile.h @@ -243,10 +245,10 @@ CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ else if [ -x /bin/bash ]; then echo /bin/bash; \ else echo sh; fi ; fi) -HOSTCC = ccache gcc -HOSTCXX = ccache g++ -HOSTCFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -O3 -fomit-frame-pointer -HOSTCXXFLAGS = -O3 +HOSTCC = gcc +HOSTCXX = g++ +HOSTCFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer +HOSTCXXFLAGS = -O2 # Decide whether to build built-in, modular, or both. # Normally, just do built-in. @@ -289,7 +291,7 @@ export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD # cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< # # If $(quiet) is empty, the whole command will be printed. -# If it is set to "quiet_", only the short version will be printed. +# If it is set to "quiet_", only the short version will be printed. # If it is set to "silent_", nothing will be printed at all, since # the variable $(silent_cmd_cc_o_c) doesn't exist. # @@ -330,7 +332,7 @@ include $(srctree)/scripts/Kbuild.include AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld -REAL_CC = ccache $(CROSS_COMPILE)gcc +REAL_CC = $(CROSS_COMPILE)gcc CPP = $(CC) -E AR = $(CROSS_COMPILE)ar NM = $(CROSS_COMPILE)nm @@ -347,24 +349,17 @@ CHECK = sparse # Use the wrapper for the compiler. This wrapper scans for new # warnings and causes the build to stop upon encountering them. -CC = $(srctree)/scripts/gcc-wrapper.py $(REAL_CC) - -CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ - -Wbitwise -Wno-return-void $(CF) +CC = $(srctree)/scripts/gcc-wrapper.py $(REAL_CC) -LIN_FLAG = -mfloat-abi=soft -munaligned-access -marm -march=armv7-a -mtune=cortex-a5 -mfpu=neon-vfpv4 -mvectorize-with-neon-quad \ - -ffloat-store -ffast-math -foptimize-sibling-calls -fcrossjumping -fgcse-lm -fgcse-sm -fgcse-las -fsched-spec-load \ - -fforce-addr -fsingle-precision-constant -funsafe-math-optimizations -ftree-vectorize -funswitch-loops -fno-toplevel-reorder \ - -fuse-linker-plugin -fpredictive-commoning -fgraphite -fgraphite-identity -ftree-loop-linear -floop-interchange -floop-strip-mine \ - -floop-block -fsanitize=leak -fopenmp -lgomp -lgcc -pipe +CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ + -Wbitwise -Wno-return-void $(CF) +CFLAGS_MODULE = +AFLAGS_MODULE = +LDFLAGS_MODULE = +CFLAGS_KERNEL = +AFLAGS_KERNEL = +CFLAGS_GCOV = -fprofile-arcs -ftest-coverage -MODFLAGS = -DMODULE $(LIN_FLAG) -CFLAGS_MODULE = $(MODFLAGS) -fno-pic -AFLAGS_MODULE = $(MODFLAGS) -fno-pic -LDFLAGS_MODULE = -CFLAGS_KERNEL = $(LIN_FLAG) -AFLAGS_KERNEL = $(LIN_FLAG) -CFLAGS_GCOV = -fprofile-arcs -ftest-coverage # Use LINUXINCLUDE when you must reference the include/ directory. # Needed to be compatible with the O= option @@ -375,22 +370,16 @@ LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include \ KBUILD_CPPFLAGS := -D__KERNEL__ -# -# Optimizations -# -CFLAGS_A5 = -mtune=cortex-a5 -marm -march=armv7-a -mfpu=neon-vfpv4 -funsafe-math-optimizations -ftree-vectorize -CFLAGS_MODULO = -fmodulo-sched -fmodulo-sched-allow-regmoves -KERNEL_MODS = $(CFLAGS_A5) $(CFLAGS_MODULO) - KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ + -Wno-array-bounds \ + -Wno-maybe-uninitialized \ + -Wno-unused-value \ + -Wno-sequence-point \ + -Wno-address \ -fno-strict-aliasing -fno-common \ -Werror-implicit-function-declaration \ -Wno-format-security \ - -fno-delete-null-pointer-checks \ - -mno-unaligned-access \ - -Wno-sizeof-pointer-memaccess \ - $(KERNEL_MODS) - + -fno-delete-null-pointer-checks KBUILD_AFLAGS_KERNEL := KBUILD_CFLAGS_KERNEL := KBUILD_AFLAGS := -D__ASSEMBLY__ @@ -583,7 +572,7 @@ all: vmlinux ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE KBUILD_CFLAGS += -Os else -KBUILD_CFLAGS += -O3 +KBUILD_CFLAGS += -O2 endif include $(srctree)/arch/$(SRCARCH)/Makefile @@ -592,9 +581,6 @@ ifneq ($(CONFIG_FRAME_WARN),0) KBUILD_CFLAGS += $(call cc-option,-Wframe-larger-than=${CONFIG_FRAME_WARN}) endif -# Tell gcc to never replace conditional load with a non-conditional one -KBUILD_CFLAGS += $(call cc-option,--param=allow-store-data-races=0) - # Force gcc to behave correct even for buggy distributions ifndef CONFIG_CC_STACKPROTECTOR KBUILD_CFLAGS += $(call cc-option, -fno-stack-protector) @@ -617,8 +603,6 @@ KBUILD_CFLAGS += -fomit-frame-pointer endif endif -KBUILD_CFLAGS += $(call cc-option, -fno-var-tracking-assignments) - ifdef CONFIG_DEBUG_INFO KBUILD_CFLAGS += -g KBUILD_AFLAGS += -gdwarf-2 @@ -878,7 +862,7 @@ endef # First command is ':' to allow us to use + in front of this rule cmd_ksym_ld = $(cmd_vmlinux__) define rule_ksym_ld - : + : +$(call cmd,vmlinux_version) $(call cmd,vmlinux__) $(Q)echo 'cmd_$@ := $(cmd_vmlinux__)' > $(@D)/.$(@F).cmd @@ -957,7 +941,7 @@ modpost-init := $(filter-out init/built-in.o, $(vmlinux-init)) vmlinux.o: $(modpost-init) $(vmlinux-main) FORCE $(call if_changed_rule,vmlinux-modpost) -# The actual objects are generated when descending, +# The actual objects are generated when descending, # make sure no implicit rule kicks in $(sort $(vmlinux-init) $(vmlinux-main)) $(vmlinux-lds): $(vmlinux-dirs) ; @@ -1547,7 +1531,7 @@ endif $(build)=$(build-dir) $(@:.ko=.o) $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost -# FIXME Should go into a make.lib or something +# FIXME Should go into a make.lib or something # =========================================================================== quiet_cmd_rmdirs = $(if $(wildcard $(rm-dirs)),CLEAN $(wildcard $(rm-dirs))) diff --git a/arch/Kconfig b/arch/Kconfig index 0a3ffe46..0d887601 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -169,6 +169,13 @@ config HAVE_HW_BREAKPOINT bool depends on PERF_EVENTS +config HAVE_HW_BRKPT_RESERVED_RW_ACCESS + bool + depends on HAVE_HW_BREAKPOINT + help + Some of the hardware might not have r/w access beyond a certain number + of breakpoint register access. + config HAVE_MIXED_BREAKPOINTS_REGS bool depends on HAVE_HW_BREAKPOINT diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 71494c87..e1d7bb1c 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -28,7 +28,7 @@ config ARM select HAVE_PERF_EVENTS select PERF_USE_VMALLOC select HAVE_REGS_AND_STACK_ACCESS_API - select HAVE_HW_BREAKPOINT if (PERF_EVENTS && (CPU_V6 || CPU_V6K || CPU_V7)) + #select HAVE_HW_BREAKPOINT if (PERF_EVENTS && (CPU_V6 || CPU_V6K || CPU_V7)) select HAVE_C_RECORDMCOUNT select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_SHOW diff --git a/arch/arm/configs/jsr_i6_defconfig b/arch/arm/configs/jsr_i6_defconfig new file mode 100644 index 00000000..ada8750c --- /dev/null +++ b/arch/arm/configs/jsr_i6_defconfig @@ -0,0 +1,3524 @@ +CONFIG_ARM=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +## CONFIG_GENERIC_LOCKBREAK=y +## CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARM_TICKET_LOCKS=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=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_FIQ=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_NEED_MACH_IO_H=y +CONFIG_NEED_MACH_MEMORY_H=y +CONFIG_PHYS_OFFSET=0x00200000 +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=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="-Chil360-MM" +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_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +CONFIG_AUDIT=y +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_IRQ_DOMAIN=y +# CONFIG_IRQ_DOMAIN_DEBUG is not set + +# +# RCU Subsystem +# +CONFIG_TREE_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_RCU_FAST_NO_HZ is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_RCU_BOOST is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=21 +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_TIMER_SLACK=y +CONFIG_CGROUP_DYNAMIC_TIMER_SLACK=y +CONFIG_CGROUP_DEVICE=y +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_CGROUP_MEM_RES_CTLR is not set +# CONFIG_CGROUP_PERF is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_CFS_BANDWIDTH is not set +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP 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=y +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=3 +CONFIG_EXPERT=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +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_ASHMEM=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=y +CONFIG_TRACEPOINTS=y +CONFIG_OPROFILE=y +CONFIG_HAVE_OPROFILE=y +CONFIG_KPROBES=y +# CONFIG_JUMP_LABEL is not set +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_KRETPROBES=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_USE_GENERIC_SMP_HELPERS=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BREAKPOINT=y +## CONFIG_HAVE_HW_BRKPT_RESERVED_RW_ACCESS=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 is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_STOP_MACHINE=y +CONFIG_BLOCK=y +## CONFIG_LBDAF=y +# 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=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_BFQ=y +CONFIG_CGROUP_BFQIO=y +CONFIG_IOSCHED_CFQ=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_FIOPS=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_ROW=y +CONFIG_IOSCHED_SIO=y +CONFIG_IOSCHED_SIOPLUS=y +# CONFIG_IOSCHED_TEST is not set +CONFIG_IOSCHED_VR=y +CONFIG_IOSCHED_ZEN=y +# CONFIG_DEFAULT_BFQ is not set +# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_DEADLINE=y +# CONFIG_DEFAULT_FIOPS is not set +# CONFIG_DEFAULT_NOOP is not set +# CONFIG_DEFAULT_ROW is not set +# CONFIG_DEFAULT_SIO is not set +# CONFIG_DEFAULT_SIOPLUS is not set +# CONFIG_DEFAULT_VR is not set +# CONFIG_DEFAULT_ZEN is not set +CONFIG_DEFAULT_IOSCHED="deadline" +# 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_UNINLINE_SPIN_UNLOCK=y +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# 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 is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# 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 is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +CONFIG_MUTEX_SPIN_ON_OWNER=y +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=y +# 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_GPIO_PCA953X is not set + +# +# MSM SoC Type +# +# CONFIG_ARCH_MSM7X01A is not set +# CONFIG_ARCH_MSM7X25 is not set +CONFIG_ARCH_MSM7X27=y +# CONFIG_ARCH_MSM7X30 is not set +# CONFIG_ARCH_QSD8X50 is not set +# CONFIG_ARCH_MSM8X60 is not set +# CONFIG_ARCH_MSM8960 is not set +# CONFIG_ARCH_MSM8930 is not set +# CONFIG_ARCH_APQ8064 is not set +# CONFIG_ARCH_MSM8974 is not set +# CONFIG_ARCH_FSM9XXX is not set +# CONFIG_ARCH_MSM9615 is not set +CONFIG_ARCH_MSM8625=y +# CONFIG_ARCH_MSM9625 is not set +# CONFIG_MSM_SOC_REV_NONE is not set +CONFIG_MSM_SOC_REV_A=y +CONFIG_MSM_SMP=y +CONFIG_ARCH_MSM_CORTEXMP=y +CONFIG_ARCH_MSM_CORTEX_A5=y +CONFIG_ARCH_MSM7X27A=y +CONFIG_MSM_VIC=y +# CONFIG_MSM_RPM is not set +# CONFIG_MSM_RPM_SMD is not set +# CONFIG_MSM_MPM is not set + +# +# MSM Board Selection +# +# CONFIG_MACH_MSM7X27_SURF is not set +# CONFIG_MACH_MSM7X27_FFA is not set +CONFIG_MACH_MSM7X27A_RUMI3=y +CONFIG_MACH_MSM7X27A_SURF=y +CONFIG_MACH_MSM7X27A_FFA=y +# CONFIG_JSR_D9 is not set +# CONFIG_JSR_D9C is not set +CONFIG_MACH_MSM7X27A_I6=y +CONFIG_JSR_KERNEL=y +CONFIG_JSR_I6=y +# CONFIG_JSR_I6C is not set +# CONFIG_JSR_I6C_SMARTFREN is not set +# CONFIG_JSR_I3DC_SMARTFREN is not set +# CONFIG_JSR_I3DC is not set +# CONFIG_JSR_I3D is not set +CONFIG_MACH_MSM7625A_SURF=y +CONFIG_MACH_MSM7625A_FFA=y +CONFIG_MACH_MSM7627A_QRD1=y +CONFIG_MACH_MSM7627A_QRD3=y +CONFIG_MACH_MSM7627A_EVB=y +CONFIG_MACH_MSM8625_RUMI3=y +CONFIG_MACH_MSM8625_SURF=y +CONFIG_MACH_MSM8625_FFA=y +CONFIG_MACH_MSM8625_EVB=y +CONFIG_MACH_MSM8625_EVT=y +CONFIG_MACH_MSM7X27A_QRD5A=y +CONFIG_MACH_MSM8625_QRD7=y +CONFIG_MACH_MSM8625_QRD5=y +CONFIG_MACH_MSM8625Q_SKUD=y +CONFIG_MACH_MSM8625Q_SKUE=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_MSM_AMSS_VERSION=6225 +# CONFIG_MSM_AMSS_VERSION_6210 is not set +# CONFIG_MSM_AMSS_VERSION_6220 is not set +CONFIG_MSM_AMSS_VERSION_6225=y +# CONFIG_MSM7X00A_USE_GP_TIMER is not set +CONFIG_MSM7X00A_USE_DG_TIMER=y +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=20000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET=1000000000 +# CONFIG_CPU_HAS_L2_PMU is not set +# CONFIG_HTC_HEADSET is not set +# CONFIG_HTC_PWRSINK is not set +# CONFIG_MSM_FIQ_SUPPORT is not set +# CONFIG_MSM_SERIAL_DEBUGGER is not set +CONFIG_MSM_PROC_COMM=y +CONFIG_MSM_SMD=y +## CONFIG_MSM_AMSS_ENHANCE_DEBUG=y +# CONFIG_MSM_SMD_PKG3 is not set +CONFIG_MSM_SMD_PKG4=y +## CONFIG_MSM_FUSE_INFO_DEBUG=y +# CONFIG_MSM_SMD_DEBUG is not set +CONFIG_MSM_FAST_BOOT=y +CONFIG_MSM_N_WAY_SMD=y +CONFIG_MSM_N_WAY_SMSM=y +# CONFIG_MSM_RESET_MODEM is not set +CONFIG_MSM_SMD_LOGGING=y +# CONFIG_MSM_IPC_LOGGING is not set +# CONFIG_MSM_SMD_NMEA is not set +CONFIG_MSM_SMD_TTY=y +# CONFIG_MSM_SMD_QMI is not set +CONFIG_MSM_SMD_PKT=y +CONFIG_MSM_ONCRPCROUTER=y +# CONFIG_MSM_IPC_ROUTER is not set +## CONFIG_MSM_ONCRPCROUTER_DEBUG=y +# CONFIG_MSM_RPC_LOOPBACK_XPRT is not set +# CONFIG_MSM_RPCSERVER_TIME_REMOTE is not set +CONFIG_MSM_RPCSERVER_WATCHDOG=y +# CONFIG_MSM_RPC_WATCHDOG is not set +CONFIG_MSM_RPC_PING=y +CONFIG_MSM_RPC_PROC_COMM_TEST=y +CONFIG_MSM_RPC_OEM_RAPI=y +CONFIG_MSM_RPCSERVER_HANDSET=y +CONFIG_MSM_RMT_STORAGE_CLIENT=y +# CONFIG_MSM_RMT_STORAGE_CLIENT_STATS is not set +CONFIG_MSM_DALRPC=y +CONFIG_MSM_DALRPC_TEST=m +# CONFIG_MSM_CPU_FREQ_SET_MIN_MAX is not set +# CONFIG_MSM_AVS_HW is not set +# CONFIG_MSM_HW3D is not set +CONFIG_MSM_ADSP=y +CONFIG_ADSP_RPC_VER=0x30002 +CONFIG_AMSS_7X25_VERSION_2009=y +# CONFIG_AMSS_7X25_VERSION_2008 is not set +CONFIG_MSM_ADSP_REPORT_EVENTS=y +CONFIG_RTAC=y +CONFIG_MSM7X27A_AUDIO=y +CONFIG_MSM_PROC_COMM_REGULATOR=y +# CONFIG_MSM_VREG_SWITCH_INVERTED is not set +CONFIG_MSM_DMA_TEST=y +# CONFIG_WIFI_CONTROL_FUNC is not set +CONFIG_AUDIO_AAC_PLUS=y +CONFIG_AUDIO_ENHANCED_AAC_PLUS=y +CONFIG_SURF_FFA_GPIO_KEYPAD=y +CONFIG_MSM_SLEEP_TIME_OVERRIDE=y +# CONFIG_MSM_MEMORY_LOW_POWER_MODE is not set +CONFIG_MSM_PM_TIMEOUT_HALT=y +# CONFIG_MSM_PM_TIMEOUT_RESET_MODEM is not set +# CONFIG_MSM_PM_TIMEOUT_RESET_CHIP is not set +CONFIG_MSM_IDLE_WAIT_ON_MODEM=0 +# CONFIG_MSM_PIL is not set +# CONFIG_MSM_SCM is not set +# CONFIG_MSM_TZ_LOG is not set +# CONFIG_MSM_DIRECT_SCLK_ACCESS is not set +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_MODEM_RESTART=y +CONFIG_MSM_PM2=y +CONFIG_MSM_EVENT_TIMER=y +# CONFIG_MSM_BUS_SCALING is not set +# CONFIG_MSM_WATCHDOG_V2 is not set +# CONFIG_MSM_JTAG is not set +CONFIG_MSM_SLEEP_STATS_DEVICE=y +## CONFIG_MSM_RUN_QUEUE_STATS=y +## CONFIG_MSM_SM_EVENT=y +## CONFIG_MSM_SM_EVENT_LOG=m +## CONFIG_UNCACHED_BUF=y +## CONFIG_MSM_SLEEP_MONITOR=m +# CONFIG_MSM_STANDALONE_POWER_COLLAPSE is not set +# CONFIG_MSM_GSBI9_UART is not set +CONFIG_MSM_SHOW_RESUME_IRQ=y +CONFIG_BT_MSM_PINTEST=y +# CONFIG_MSM_FAKE_BATTERY is not set +# CONFIG_MSM_QDSP6_APR is not set +# CONFIG_MSM_QDSP6_CODECS is not set +# CONFIG_MSM_QDSP6V2_CODECS is not set +# CONFIG_MSM_AUDIO_QDSP6 is not set +# CONFIG_MSM_AUDIO_QDSP6V2 is not set +CONFIG_MSM_RPC_VIBRATOR=y +CONFIG_PM8XXX_RPC_VIBRATOR=y +# CONFIG_MSM_SPM_V1 is not set +CONFIG_MSM_SPM_V2=y +# CONFIG_MSM_L2_SPM is not set +CONFIG_MSM_MULTIMEDIA_USE_ION=y +# CONFIG_MSM_OCMEM is not set +# CONFIG_MSM_RTB is not set +# CONFIG_MSM_EBI_ERP is not set +## CONFIG_MSM_CPR=y +CONFIG_MSM_VP_REGULATOR=y +# CONFIG_MSM_CACHE_DUMP is not set +# CONFIG_MSM_HSIC_SYSMON is not set +# CONFIG_MSM_8X60_FUSION_GPIO_GLITCH is not set +## CONFIG_MSM_FIQ=y + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +CONFIG_SWP_EMULATE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_KUSER_HELPERS=y +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_CACHE_L2X0=y +CONFIG_CACHE_PL310=y +# CONFIG_CACHE_PL310_ERP is not set +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_ARCH_HAS_BARRIERS=y +# CONFIG_VCM is not set +# CONFIG_STRICT_MEMORY_RWX is not set +CONFIG_ARM_NR_BANKS=8 +# CONFIG_RESERVE_FIRST_PAGE is not set +CONFIG_CPU_HAS_PMU=y +CONFIG_MULTI_IRQ_HANDLER=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_742230 is not set +# CONFIG_ARM_ERRATA_742231 is not set +# CONFIG_PL310_ERRATA_588369 is not set +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_PL310_ERRATA_727915 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_ARM_ERRATA_751472 is not set +# CONFIG_PL310_ERRATA_753970 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_PL310_ERRATA_769419 is not set +CONFIG_ARM_GIC=y +## CONFIG_GIC_SECURE=y +# 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_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_HAVE_SMP=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_ARM_CPU_TOPOLOGY=y +# CONFIG_SCHED_MC is not set +# CONFIG_SCHED_SMT is not set +CONFIG_HAVE_ARM_SCU=y +# CONFIG_ARM_ARCH_TIMER is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_NR_CPUS=2 +CONFIG_HOTPLUG_CPU=y +CONFIG_LOCAL_TIMERS=y +CONFIG_ARCH_NR_GPIO=0 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +CONFIG_HZ=100 +CONFIG_SCHED_HRTICK=y +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +## CONFIG_OABI_COMPAT=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HIGHMEM=y +# CONFIG_HIGHPTE is not set +CONFIG_HW_PERF_EVENTS=y +CONFIG_VMALLOC_RESERVE=0xC800000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=2 +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +## CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_CLEANCACHE=y +CONFIG_FRONTSWAP=y +# CONFIG_USE_USER_ACCESSIBLE_TIMERS is not set +CONFIG_ZSMALLOC=y +# CONFIG_PGTABLE_MAPPING is not set +## CONFIG_DYNAMIC_PAGE_WRITEBACK=y +# CONFIG_ARCH_MEMORY_PROBE is not set +# CONFIG_ARCH_MEMORY_REMOVE is not set +# CONFIG_ARCH_POPULATES_NODE_MAP is not set +# CONFIG_ENABLE_DMM is not set +# CONFIG_FIX_MOVABLE_ZONE is not set +CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0=y +# CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG is not set +# CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set +CONFIG_CP_ACCESS=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="init=/sbin/init root=/dev/ram rw initrd=0x11000000,16M console=ttyDCC0 mem=88M" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ADAPTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ASSWAX is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_BADASS is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_DANCEDANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_HYPER is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_LIONHEART is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_LULZACTIVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMANDX is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASSH3 is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTMAX is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_WHEATLEY is not set +CONFIG_CPU_FREQ_GOV_ADAPTIVE=y +# CONFIG_CPU_FREQ_GOV_ASSWAX is not set +CONFIG_CPU_FREQ_GOV_BADASS=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_DANCEDANCE=y +CONFIG_CPU_FREQ_GOV_HYPER=y +CONFIG_CPU_FREQ_GOV_INTELLIDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_LIONHEART=y +CONFIG_CPU_FREQ_GOV_LULZACTIVE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_ONDEMANDX=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +# CONFIG_CPU_FREQ_GOV_SMARTASS2 is not set +# CONFIG_CPU_FREQ_GOV_SMARTASSH3 is not set +CONFIG_CPU_FREQ_GOV_SMARTMAX=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_WHEATLEY=y +CONFIG_CPU_FREQ_VDD_LEVELS=y + +# +# ARM CPU frequency scaling drivers +# +# CONFIG_ARM_EXYNOS4210_CPUFREQ is not set +# CONFIG_ARM_EXYNOS4X12_CPUFREQ is not set +# CONFIG_ARM_EXYNOS5250_CPUFREQ is not set +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_FPE_NWFPE is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +## 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_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +# CONFIG_CONSOLE_EARLYSUSPEND is not set +CONFIG_FB_EARLYSUSPEND=y +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_PM_RUNTIME=y +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_WQ_POWER_EFFICIENT_DEFAULT=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 is not set +CONFIG_XFRM=y +CONFIG_XFRM_USER=y +# CONFIG_XFRM_SUB_POLICY is not set +CONFIG_XFRM_MIGRATE=y +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +CONFIG_NET_KEY_MIGRATE=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_FIB_TRIE_STATS is not set +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +# CONFIG_IP_PNP is not set +CONFIG_NET_IPIP=y +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_XFRM_TUNNEL=y +CONFIG_INET_TUNNEL=y +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 is not set +CONFIG_TCP_CONG_ADVANCED=y +# CONFIG_TCP_CONG_BIC is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_TCP_CONG_WESTWOOD=y +# CONFIG_TCP_CONG_HTCP is not set +# CONFIG_TCP_CONG_HSTCP is not set +# CONFIG_TCP_CONG_HYBLA is not set +CONFIG_TCP_CONG_VEGAS=y +# CONFIG_TCP_CONG_SCALABLE is not set +# CONFIG_TCP_CONG_LP is not set +CONFIG_TCP_CONG_VENO=y +# CONFIG_TCP_CONG_YEAH is not set +# CONFIG_TCP_CONG_ILLINOIS is not set +# CONFIG_DEFAULT_CUBIC is not set +# CONFIG_DEFAULT_VEGAS is not set +# CONFIG_DEFAULT_VENO is not set +CONFIG_DEFAULT_WESTWOOD=y +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_TCP_CONG="westwood" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETLABEL is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +CONFIG_NETWORK_SECMARK=y +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +# CONFIG_NETFILTER_NETLINK_ACCT is not set +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_PROCFS=y +CONFIG_NF_CONNTRACK_EVENTS=y +# CONFIG_NF_CONNTRACK_TIMEOUT is not set +# CONFIG_NF_CONNTRACK_TIMESTAMP is not set +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_BROADCAST=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +# CONFIG_NF_CONNTRACK_SNMP is not set +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +# CONFIG_NF_CT_NETLINK_TIMEOUT is not set +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +# CONFIG_NETFILTER_XT_TARGET_HL is not set +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +CONFIG_NETFILTER_XT_MATCH_ECN=y +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_SET is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +# CONFIG_IP_NF_MATCH_RPFILTER is not set +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +# CONFIG_IP_NF_TARGET_REJECT_SKERR is not set +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +# CONFIG_NF_CONNTRACK_IPV6 is not set +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=y +# CONFIG_IP6_NF_MATCH_AH is not set +# CONFIG_IP6_NF_MATCH_EUI64 is not set +# CONFIG_IP6_NF_MATCH_FRAG is not set +# CONFIG_IP6_NF_MATCH_OPTS is not set +# CONFIG_IP6_NF_MATCH_HL is not set +# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set +# CONFIG_IP6_NF_MATCH_MH is not set +# CONFIG_IP6_NF_MATCH_RPFILTER is not set +# CONFIG_IP6_NF_MATCH_RT is not set +# CONFIG_IP6_NF_TARGET_HL is not set +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +# CONFIG_IP6_NF_TARGET_REJECT_SKERR is not set +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_IP6_NF_SECURITY=y +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +CONFIG_L2TP=y +CONFIG_L2TP_DEBUGFS=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA 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_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 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_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +# CONFIG_NETPRIO_CGROUP 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_NET_TCPPROBE is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCISMD is not set +# CONFIG_BT_HCIBTUSB is not set +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +CONFIG_BT_HCIUART_IBS=y +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBPA10X is not set +CONFIG_BT_MSM_SLEEP=y +# CONFIG_BT_HCIBFUSB is not set +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +CONFIG_MSM_BT_POWER=y +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +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 is not set +# 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 is not set +# CONFIG_WIRELESS_EXT_SYSFS is not set +# CONFIG_LIB80211 is not set +# CONFIG_CFG80211_ALLOW_RECONNECT is not set +# CONFIG_MAC80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_REGULATOR is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +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_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SW_SYNC_USER=y +CONFIG_CMA=y +# CONFIG_CMA_DEBUG is not set + +# +# Default contiguous memory area size: +# +CONFIG_CMA_SIZE_PERCENTAGE=5 +# CONFIG_CMA_SIZE_SEL_MBYTES is not set +CONFIG_CMA_SIZE_SEL_PERCENTAGE=y +# CONFIG_CMA_SIZE_SEL_MIN is not set +# CONFIG_CMA_SIZE_SEL_MAX is not set +CONFIG_CMA_ALIGNMENT=8 +CONFIG_CMA_AREAS=7 +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +## CONFIG_MTD_TESTS=m +# 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_SM_FTL is not set +# CONFIG_MTD_OOPS is not set +CONFIG_MTD_LAZYECCSTATS=y + +# +# 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_MSM_NAND=y +# 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=y +# 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_ZRAM=y +CONFIG_ZRAM_LZ4_COMPRESS=y +# CONFIG_ZRAM_DEBUG is not set +# 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 is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +## CONFIG_ANDROID_PMEM=y +# CONFIG_ATMEL_PWM is not set +# CONFIG_ICS932S401 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_DS1682 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +# CONFIG_APANIC is not set +# CONFIG_TSIF is not set +# CONFIG_TSPP is not set +# CONFIG_HAPTIC_ISA1200 is not set +# CONFIG_QSEECOM is not set +# CONFIG_QFP_FUSE is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set +# CONFIG_MPU_SENSORS_TIMERIRQ is not set +# CONFIG_INV_SENSORS is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST 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_TGT=y +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# 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=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +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=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_ISCSI_BOOT_SYSFS is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM_BUILTIN=y +CONFIG_BLK_DEV_DM=y +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_THIN_PROVISIONING is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +# CONFIG_DM_UEVENT is not set +# CONFIG_DM_FLAKEY is not set +# CONFIG_DM_VERITY 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=y +# CONFIG_NET_TEAM is not set +# CONFIG_MACVLAN is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_BROADCOM=y +# CONFIG_B44 is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +CONFIG_NET_VENDOR_CHELSIO=y +CONFIG_NET_VENDOR_CIRRUS=y +# CONFIG_CS89x0 is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +CONFIG_NET_VENDOR_FARADAY=y +# CONFIG_FTMAC100 is not set +# CONFIG_FTGMAC100 is not set +CONFIG_NET_VENDOR_INTEL=y +CONFIG_NET_VENDOR_I825XX=y +CONFIG_NET_VENDOR_MARVELL=y +CONFIG_NET_VENDOR_MICREL=y +# CONFIG_KS8851_MLL is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_QFEC is not set +CONFIG_NET_VENDOR_NATSEMI=y +CONFIG_NET_VENDOR_8390=y +# CONFIG_AX88796 is not set +# CONFIG_ETHOC is not set +CONFIG_NET_VENDOR_SEEQ=y +# CONFIG_SEEQ8005 is not set +CONFIG_NET_VENDOR_SMSC=y +CONFIG_SMC91X=y +# CONFIG_SMC911X is not set +CONFIG_SMSC911X=y +# CONFIG_SMSC911X_ARCH_HOOKS is not set +CONFIG_NET_VENDOR_STMICRO=y +# CONFIG_STMMAC_ETH is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_AMD_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPPOL2TP=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 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_USB_ZD1201 is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +CONFIG_LIBRA_SDIOIF=m +# CONFIG_ATH6K_LEGACY_EXT is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_IWM is not set +# CONFIG_LIBERTAS 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=m +# 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=y +CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH=y +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set +# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_EGALAX is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_ILI210X is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MAX11801 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +## CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C=y +CONFIG_SYNA_MULTI_TOUCH=y +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_PIXCIR is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC_SERIO is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_MSM_LEGACY is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +# CONFIG_TOUCHSCREEN_CY8C_TS is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC is not set +# CONFIG_TOUCHSCREEN_FT5X06 is not set +# CONFIG_TOUCHSCREEN_HIMAX is not set +# CONFIG_TOUCHSCREEN_WINPOWER is not set +CONFIG_TOUCHSCREEN_ELAN_KTF2K=y +CONFIG_TS_FT5306=y +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_BMA150 is not set +# CONFIG_INPUT_MMA8450 is not set +# CONFIG_INPUT_MPU3050 is not set +# CONFIG_INPUT_GP2A is not set +# CONFIG_INPUT_GPIO_TILT_POLLED is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYCHORD is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_KXTJ9 is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_ISA1200_FF_MEMLESS is not set +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_BOSCH_BMA150 is not set +# CONFIG_INPUT_LTR502 is not set +# CONFIG_BOSCH_BMA250 is not set +# CONFIG_AVAGO_APDS990X is not set +# CONFIG_INPUT_ISL29028 is not set +# CONFIG_INPUT_LIS3DH is not set +# CONFIG_INPUT_LTR558_D9 is not set +# CONFIG_INPUT_LTR558_D9C is not set +CONFIG_INPUT_LTR558_I6=y +# CONFIG_INPUT_LTR558_I6C is not set +# CONFIG_INPUT_LTR558_I6C_SMARTFREN is not set +CONFIG_INPUT_YAS_ACCELEROMETER=y +CONFIG_INPUT_YAS_ACCELEROMETER_POSITION=4 +CONFIG_INPUT_YAS_MAGNETOMETER=y +CONFIG_INPUT_YAS_MAGNETOMETER_POSITION=7 +CONFIG_INPUT_YAS_ORIENTATION=y +CONFIG_MXC_MMA8452=y +CONFIG_MXC_MMA_POSITION=7 + +# +# 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_N_GSM is not set +# CONFIG_N_SMUX 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_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_MSM_HS=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +# CONFIG_SERIAL_MSM_HSL is not set +# CONFIG_SERIAL_BCM_BT_LPM is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_MSM_SMD is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set + +# +# Diag Support +# +CONFIG_DIAG_CHAR=y + +# +# DIAG traffic over USB +# +CONFIG_DIAG_OVER_USB=y + +# +# SDIO support for DIAG +# + +# +# HSIC support for DIAG +# +# 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_MMC_GENERIC_CSDIO is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# 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_MSM=y +CONFIG_I2C_MSMv2=y +CONFIG_I2C_QUP=y +# CONFIG_I2C_OCORES 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_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SENSORS_BMA250 is not set +# CONFIG_SENSORS_BMM050 is not set +# CONFIG_SPI is not set +# CONFIG_SPMI is not set +# CONFIG_SLIMBUS is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# 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=y +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set +CONFIG_GPIO_MSM_V1=y +# CONFIG_GPIO_MSM_V2 is not set +# CONFIG_GPIO_MSM_V3 is not set +# CONFIG_GPIO_FSM9XXX 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=y +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MCP23S08 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_GPIO_PM8XXX_RPC is not set +CONFIG_GPIO_SLEEP=y +# 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_CHARGER_ISP1704 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_MANAGER is not set +CONFIG_BATTERY_MSM=y +CONFIG_MSM_BATTERY_CHG_LEGACY=y +# CONFIG_BATTERY_MSM8X60 is not set +# CONFIG_SMB137B_CHARGER is not set +# CONFIG_SMB349_CHARGER is not set +# CONFIG_BATTERY_EARLYSUSPEND is not set +# CONFIG_BATTERY_MSM_FAKE is not set +# CONFIG_BATTERY_BQ27520 is not set +# CONFIG_BATTERY_BQ27541 is not set +# CONFIG_LTC4088_CHARGER is not set +# CONFIG_CHARGER_SMB347 is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS620 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_GPIO_FAN is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_LINEAGE is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4151 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_LM95245 is not set +# CONFIG_SENSORS_MAX16065 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX1668 is not set +# CONFIG_SENSORS_MAX6639 is not set +# CONFIG_SENSORS_MAX6642 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MCP3021 is not set +# CONFIG_SENSORS_NTC_THERMISTOR is not set +CONFIG_SENSORS_MSM_ADC=y +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_PMBUS is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SHT21 is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_EMC6W201 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SCH56XX_COMMON is not set +# CONFIG_SENSORS_SCH5627 is not set +# CONFIG_SENSORS_SCH5636 is not set +# CONFIG_SENSORS_ADS1015 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF 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_MARIMBA_CORE=y +# CONFIG_MARIMBA_CODEC is not set +# CONFIG_TIMPANI_CODEC 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_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_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_PM8821_IRQ is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_WCD9304_CODEC is not set +# CONFIG_WCD9310_CODEC is not set +# CONFIG_WCD9320_CODEC is not set +# CONFIG_MFD_RC5T583 is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +CONFIG_REGULATOR_GPIO=y +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +CONFIG_REGULATOR_MSM_GPIO=y +# CONFIG_REGULATOR_STUB is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +CONFIG_VIDEO_V4L2_SUBDEV_API=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=y + +# +# Customize TV tuners +# +# CONFIG_MEDIA_TUNER_SIMPLE is not set +# CONFIG_MEDIA_TUNER_TDA8290 is not set +# CONFIG_MEDIA_TUNER_TDA827X is not set +# CONFIG_MEDIA_TUNER_TDA18271 is not set +# CONFIG_MEDIA_TUNER_TDA9887 is not set +# CONFIG_MEDIA_TUNER_TEA5761 is not set +# CONFIG_MEDIA_TUNER_TEA5767 is not set +# CONFIG_MEDIA_TUNER_MT20XX is not set +# CONFIG_MEDIA_TUNER_MT2060 is not set +# CONFIG_MEDIA_TUNER_MT2063 is not set +# CONFIG_MEDIA_TUNER_MT2266 is not set +# CONFIG_MEDIA_TUNER_MT2131 is not set +# CONFIG_MEDIA_TUNER_QT1010 is not set +# CONFIG_MEDIA_TUNER_XC2028 is not set +# CONFIG_MEDIA_TUNER_XC5000 is not set +# CONFIG_MEDIA_TUNER_XC4000 is not set +# CONFIG_MEDIA_TUNER_MXL5005S is not set +# CONFIG_MEDIA_TUNER_MXL5007T is not set +# CONFIG_MEDIA_TUNER_MC44S803 is not set +# CONFIG_MEDIA_TUNER_MAX2165 is not set +# CONFIG_MEDIA_TUNER_TDA18218 is not set +# CONFIG_MEDIA_TUNER_TDA18212 is not set +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEOBUF2_MEMOPS=y +CONFIG_VIDEOBUF2_DMA_CONTIG=y +CONFIG_VIDEOBUF2_VMALLOC=y +CONFIG_VIDEOBUF2_DMA_SG=y +CONFIG_VIDEOBUF2_MSM_MEM=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_TLV320AIC23B 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_MT9M032 is not set +# CONFIG_VIDEO_MT9P031 is not set +# CONFIG_VIDEO_MT9T001 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_MT9V032 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set +# CONFIG_VIDEO_NOON010PC30 is not set +# CONFIG_VIDEO_M5MOLS is not set +# CONFIG_VIDEO_S5K6AA is not set + +# +# Flash devices +# +# CONFIG_VIDEO_ADP1653 is not set +# CONFIG_VIDEO_AS3645A is not set + +# +# 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_MSM_VCAP=y +CONFIG_V4L_USB_DRIVERS=y +# CONFIG_USB_VIDEO_CLASS is not set +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +CONFIG_USB_GSPCA=m +# CONFIG_USB_M5602 is not set +# CONFIG_USB_STV06XX is not set +# CONFIG_USB_GL860 is not set +# CONFIG_USB_GSPCA_BENQ is not set +# CONFIG_USB_GSPCA_CONEX is not set +# CONFIG_USB_GSPCA_CPIA1 is not set +# CONFIG_USB_GSPCA_ETOMS is not set +# CONFIG_USB_GSPCA_FINEPIX is not set +# CONFIG_USB_GSPCA_JEILINJ is not set +# CONFIG_USB_GSPCA_JL2005BCD is not set +# CONFIG_USB_GSPCA_KINECT is not set +# CONFIG_USB_GSPCA_KONICA is not set +# CONFIG_USB_GSPCA_MARS is not set +# CONFIG_USB_GSPCA_MR97310A is not set +# CONFIG_USB_GSPCA_NW80X is not set +# CONFIG_USB_GSPCA_OV519 is not set +# CONFIG_USB_GSPCA_OV534 is not set +# CONFIG_USB_GSPCA_OV534_9 is not set +# CONFIG_USB_GSPCA_PAC207 is not set +# CONFIG_USB_GSPCA_PAC7302 is not set +# CONFIG_USB_GSPCA_PAC7311 is not set +# CONFIG_USB_GSPCA_SE401 is not set +# CONFIG_USB_GSPCA_SN9C2028 is not set +# CONFIG_USB_GSPCA_SN9C20X is not set +# CONFIG_USB_GSPCA_SONIXB is not set +# CONFIG_USB_GSPCA_SONIXJ is not set +# CONFIG_USB_GSPCA_SPCA500 is not set +# CONFIG_USB_GSPCA_SPCA501 is not set +# CONFIG_USB_GSPCA_SPCA505 is not set +# CONFIG_USB_GSPCA_SPCA506 is not set +# CONFIG_USB_GSPCA_SPCA508 is not set +# CONFIG_USB_GSPCA_SPCA561 is not set +# CONFIG_USB_GSPCA_SPCA1528 is not set +# CONFIG_USB_GSPCA_SQ905 is not set +# CONFIG_USB_GSPCA_SQ905C is not set +# CONFIG_USB_GSPCA_SQ930X is not set +# CONFIG_USB_GSPCA_STK014 is not set +# CONFIG_USB_GSPCA_STV0680 is not set +# CONFIG_USB_GSPCA_SUNPLUS is not set +# CONFIG_USB_GSPCA_T613 is not set +# CONFIG_USB_GSPCA_TOPRO is not set +# CONFIG_USB_GSPCA_TV8532 is not set +# CONFIG_USB_GSPCA_VC032X is not set +# CONFIG_USB_GSPCA_VICAM is not set +# CONFIG_USB_GSPCA_XIRLINK_CIT is not set +# CONFIG_USB_GSPCA_ZC3XX is not set +# CONFIG_VIDEO_PVRUSB2 is not set +# CONFIG_VIDEO_HDPVR is not set +# CONFIG_VIDEO_EM28XX is not set +# CONFIG_VIDEO_USBVISION is not set +# CONFIG_USB_ET61X251 is not set +# CONFIG_USB_SN9C102 is not set +# CONFIG_USB_PWC is not set +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_S2255 is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +# CONFIG_SOC_CAMERA is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set +CONFIG_MSM_CAMERA_V4L2=y + +# +# Camera Sensor Selection +# +# CONFIG_IMX074 is not set +# CONFIG_OV5647 is not set +# CONFIG_OV5647_TRULY_CM6868 is not set +# CONFIG_OV5648_TRULY_CM8352 is not set +# CONFIG_WEBCAM_OV7692_QRD is not set +CONFIG_GC0339=y +# CONFIG_MT9M114 is not set +CONFIG_OV2675=y +# CONFIG_WEBCAM_OV9726 is not set +# CONFIG_MT9E013 is not set +# CONFIG_IMX074_ACT is not set +# CONFIG_S5K4E1 is not set +# CONFIG_AR0543 is not set +# CONFIG_MSM_CAMERA_FLASH_SC628A is not set +# CONFIG_MSM_CAMERA_FLASH_TPS61310 is not set +# CONFIG_IMX072 is not set +# CONFIG_OV2720 is not set +# CONFIG_OV8825 is not set +CONFIG_OV5647_TRULY_CM8312=y +CONFIG_OV8825_TRULY_CM8313=y +CONFIG_MSM_CAMERA_FLASH=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_ACTUATOR=y +# CONFIG_MSM_EEPROM is not set +# CONFIG_IMX074_EEPROM is not set +# CONFIG_IMX091_EEPROM is not set +CONFIG_QUP_EXCLUSIVE_TO_CAMERA=y +# CONFIG_S5K3L1YX is not set +# CONFIG_IMX091 is not set +CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y +# CONFIG_OV7692 is not set +# CONFIG_OV7695 is not set +# CONFIG_OV7695_RAW is not set +# CONFIG_GC2235 is not set +# CONFIG_GC2235_GCDF130477 is not set +# CONFIG_OV5648_TRULY_CM7500 is not set +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +CONFIG_RADIO_ADAPTERS=y +# CONFIG_RADIO_SI470X is not set +# CONFIG_USB_MR800 is not set +# CONFIG_USB_DSBR is not set +# CONFIG_I2C_SI4713 is not set +# CONFIG_RADIO_SI4713 is not set +# CONFIG_USB_KEENE is not set +# CONFIG_RADIO_TEA5764 is not set +# CONFIG_RADIO_SAA7706H is not set +# CONFIG_RADIO_TEF6862 is not set +# CONFIG_RADIO_WL1273 is not set + +# +# Texas Instruments WL128x FM driver (ST based) +# +# CONFIG_RADIO_WL128X is not set +CONFIG_RADIO_TAVARUA=y +# CONFIG_RADIO_IRIS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y +CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_MSM_VIDC_CONTENT_PROTECTION is not set +CONFIG_FB_MSM=y +# CONFIG_FB_MSM_BACKLIGHT is not set +# CONFIG_FB_MSM_LOGO is not set +CONFIG_FB_MSM_LCDC_HW=y +CONFIG_FB_MSM_TRIPLE_BUFFER=y +CONFIG_FB_MSM_MDP_HW=y +# CONFIG_FB_MSM_MDP22 is not set +CONFIG_FB_MSM_MDP30=y +CONFIG_FB_MSM_MDP303=y +# CONFIG_FB_MSM_MDP31 is not set +# CONFIG_FB_MSM_MDP40 is not set +# CONFIG_FB_MSM_MDSS is not set +# CONFIG_FB_MSM_MDP_NONE is not set +# CONFIG_FB_MSM_EBI2 is not set +# CONFIG_FB_MSM_MDDI is not set +CONFIG_FB_MSM_MIPI_DSI=y +# CONFIG_FB_MSM_LCDC is not set +# CONFIG_FB_MSM_LVDS is not set +# CONFIG_FB_MSM_EXTMDDI is not set +# CONFIG_FB_MSM_TVOUT is not set + +# CONFIG_FB_MSM_MDDI_TOSHIBA_COMMON is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_COMMON_VGA is not set +# CONFIG_FB_MSM_MDDI_ORISE is not set +# CONFIG_FB_MSM_MDDI_QUICKVX is not set +# CONFIG_FB_MSM_MDDI_AUTO_DETECT is not set + +# CONFIG_FB_MSM_LCDC_AUTO_DETECT is not set +# CONFIG_FB_MSM_LCDC_PANEL is not set + +## CONFIG_FB_MSM_MIPI_DSI_TOSHIBA=y +## CONFIG_FB_MSM_MIPI_DSI_RENESAS=y +## CONFIG_FB_MSM_MIPI_DSI_TRULY=y +## CONFIG_FB_MSM_MIPI_DSI_SIMULATOR=y +## CONFIG_FB_MSM_MIPI_DSI_NOVATEK=y +## CONFIG_FB_MSM_MIPI_DSI_NT35510=y +## CONFIG_FB_MSM_MIPI_DSI_HX8389B=y +## CONFIG_FB_MSM_MIPI_DSI_ORISE=y +## CONFIG_FB_MSM_MIPI_DSI_NT35516=y +CONFIG_FB_MSM_MIPI_DSI_NT35516_SHARP=y +## CONFIG_FB_MSM_MIPI_DSI_TRULY_OTM8018B=y +## CONFIG_FB_MSM_MIPI_DSI_SUCCESS_NT35512=y +## CONFIG_FB_MSM_MIPI_DSI_RS69324_HITACHI=y +CONFIG_FB_MSM_MIPI_DSI_NT35516_NOVATEK=y +## CONFIG_FB_MSM_MIPI_DSI_NT35516_TRULY=y + +## CONFIG_FB_MSM_MIPI_DSI_TC358764_DSI2LVDS=y + +# CONFIG_FB_MSM_LCDC_ST15_WXGA is not set +# CONFIG_FB_MSM_LCDC_ST15_PANEL is not set +# CONFIG_FB_MSM_LCDC_PRISM_WVGA is not set +# CONFIG_FB_MSM_LCDC_SAMSUNG_WSVGA is not set +# CONFIG_FB_MSM_LCDC_CHIMEI_WXGA is not set +# CONFIG_FB_MSM_LCDC_GORDON_VGA is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_WVGA_PT is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_FWVGA_PT is not set +# CONFIG_FB_MSM_LCDC_SHARP_WVGA_PT is not set +# CONFIG_FB_MSM_LCDC_AUO_WVGA is not set +# CONFIG_FB_MSM_LCDC_TRULY_HVGA_IPS3P2335 is not set +# CONFIG_FB_MSM_LCDC_TRULY_HVGA_IPS3P2335_PT_PANEL is not set +# CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT is not set +# CONFIG_FB_MSM_LCDC_NT35582_WVGA is not set +# CONFIG_FB_MSM_LCDC_WXGA is not set + +# CONFIG_FB_MSM_LVDS_CHIMEI_WXGA is not set +# CONFIG_FB_MSM_LVDS_FRC_FHD is not set + +## CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT=y +## CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT=y +## CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA=y +## CONFIG_FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT=y +## CONFIG_FB_MSM_MIPI_NOVATEK_CMD_QHD_PT=y +## CONFIG_FB_MSM_MIPI_ORISE_VIDEO_720P_PT=y +## CONFIG_FB_MSM_MIPI_ORISE_CMD_720P_PT=y +## CONFIG_FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT=y +## CONFIG_FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT=y +## CONFIG_FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT=y +## CONFIG_FB_MSM_MIPI_NT35510_CMD_WVGA_PT=y +## CONFIG_FB_MSM_MIPI_HX8389B_VIDEO_QHD_PT=y +## CONFIG_FB_MSM_MIPI_NT35516_VIDEO_QHD_PT=y +## CONFIG_FB_MSM_MIPI_NT35516_CMD_QHD_PT=y +CONFIG_FB_MSM_MIPI_NT35516_VIDEO_SHARP_QHD_PT=y +CONFIG_FB_MSM_MIPI_NT35516_CMD_SHARP_QHD_PT=y +## CONFIG_FB_MSM_MIPI_TRULY_OTM8018B_VIDEO_WVGA_PT=y +## CONFIG_FB_MSM_MIPI_SUCCESS_NT35512_VIDEO_WVGA_PT=y +## CONFIG_FB_MSM_MIPI_RS69324_VIDEO_HITACHI_QHD_PT=y +CONFIG_FB_MSM_MIPI_NT35516_VIDEO_NOVATEK_QHD_PT=y +## CONFIG_FB_MSM_MIPI_NT35516_VIDEO_TRULY_QHD_PT=y +CONFIG_FB_MSM_MIPI_NT35516_CMD_NOVATEK_QHD_PT=y +## CONFIG_FB_MSM_MIPI_NT35516_CMD_TRULY_QHD_PT=y + +## CONFIG_FB_MSM_MIPI_CHIMEI_WXGA=y +## CONFIG_FB_MSM_MIPI_CHIMEI_WUXGA=y +## CONFIG_FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT=y +## CONFIG_FB_MSM_MIPI_SIMULATOR_VIDEO=y + +# CONFIG_FB_MSM_LCDC_PRISM_WVGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_SAMSUNG_WSVGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_CHIMEI_WXGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_GORDON_VGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_FWVGA_PT_PANEL is not set +# CONFIG_FB_MSM_LCDC_SHARP_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_LCDC_AUO_WVGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_NT35582_PANEL is not set +# CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT_PANEL is not set +# CONFIG_FB_MSM_LVDS_CHIMEI_WXGA_PANEL is not set +# CONFIG_FB_MSM_LVDS_FRC_FHD_PANEL is not set +# CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM is not set + +CONFIG_FB_MSM_MIPI_JSR_PANEL_DETECT=y +# CONFIG_FB_MSM_MIPI_PANEL_DETECT is not set +# CONFIG_FB_MSM_MDDI_PANEL_AUTO_DETECT is not set +# CONFIG_FB_MSM_LCDC_PANEL_AUTO_DETECT is not set +# CONFIG_FB_MSM_LCDC_MIPI_PANEL_AUTO_DETECT is not set +# CONFIG_FB_MSM_LVDS_MIPI_PANEL_DETECT is not set + +# CONFIG_FB_MSM_MDDI_PRISM_WVGA is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_WVGA_PORTRAIT is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_VGA is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_WVGA is not set +# CONFIG_FB_MSM_MDDI_SHARP_QVGA_128x128 is not set + +# CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA_PANEL is not set +# CONFIG_FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NOVATEK_CMD_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_ORISE_VIDEO_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_ORISE_CMD_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_CHIMEI_WXGA_PANEL is not set +# CONFIG_FB_MSM_MIPI_CHIMEI_WUXGA_PANEL is not set +# CONFIG_FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35510_CMD_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_HX8389B_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35516_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35516_CMD_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35516_VIDEO_SHARP_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35516_CMD_SHARP_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_TRULY_OTM8018B_VIDEO_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_SUCCESS_NT35512_VIDEO_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_RS69324_VIDEO_HITACHI_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35516_VIDEO_NOVATEK_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35516_CMD_NOVATEK_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35516_VIDEO_TRULY_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35516_CMD_TRULY_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_SIMULATOR_VIDEO_PANEL is not set + +# CONFIG_FB_MSM_EBI2_TMD_QVGA_EPSON_QCIF is not set +# CONFIG_FB_MSM_PANEL_NONE is not set +# CONFIG_FB_MSM_EXT_INTERFACE_COMMON is not set +# CONFIG_FB_MSM_HDMI_COMMON is not set +# CONFIG_FB_MSM_HDMI_3D is not set +# CONFIG_FB_MSM_DEFAULT_DEPTH_RGB565 is not set +# CONFIG_FB_MSM_DEFAULT_DEPTH_ARGB8888 is not set +CONFIG_FB_MSM_DEFAULT_DEPTH_RGBA8888=y +# CONFIG_FB_MSM_EBI2_EPSON_S1D_QVGA_PANEL is not set +# CONFIG_FB_MSM_EBI2_PANEL_DETECT is not set +## CONFIG_FB_DYNAMIC_GAMMA=y +## CONFIG_FB_AUTO_CABC=y +CONFIG_FB_MSM_ALIGN_BUFFER=y +# CONFIG_EXYNOS_VIDEO is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=m +# CONFIG_LCD_PLATFORM is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=y +# CONFIG_BACKLIGHT_ADP8860 is not set +# CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_LP855X is not set + +# +# Console display driver support +# +## CONFIG_DUMMY_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE is not set +# CONFIG_LOGO 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_JACK=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER 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_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_SOC=y + +# +# MSM SoC Audio support +# +CONFIG_SND_MSM_DAI_SOC=y +CONFIG_SND_MSM_SOC_MSM7K=y +CONFIG_SND_MSM_SOC=y +# CONFIG_SND_SOC_QDSP6 is not set +# CONFIG_SND_SOC_QDSP6V2 is not set +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HIDRAW is not set +CONFIG_UHID=y + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# Special HID drivers +# +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_ACRUX is not set +CONFIG_HID_APPLE=y +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_PRODIKEYS is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_EMS_FF is not set +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_EZKEY is not set +# CONFIG_HID_HOLTEK is not set +# CONFIG_HID_KEYTOUCH is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_UCLOGIC is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_KENSINGTON is not set +# CONFIG_HID_LCPOWER is not set +# CONFIG_HID_LOGITECH is not set +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +# CONFIG_HID_MONTEREY is not set +# CONFIG_HID_MULTITOUCH is not set +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_ROCCAT is not set +# CONFIG_HID_SAITEK is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SONY is not set +# CONFIG_HID_SPEEDLINK is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TIVO is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_WACOM is not set +# CONFIG_HID_WIIMOTE is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_SUPPORT_NON_STANDARD_WALL_CHARGER=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=y +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y +# CONFIG_USB_OTG 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_EHCI_HCD=y +CONFIG_USB_EHCI_EHSET=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +# CONFIG_USB_EHCI_MSM is not set +# CONFIG_USB_EHCI_MSM_HSIC is not set +# CONFIG_USB_OXU210HP_HCD is not set +CONFIG_USB_EHCI_MSM_72K=y +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_EHCI_HCD_PLATFORM is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_PEHCI_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=y +# 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=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_ALAUDA=y +CONFIG_USB_STORAGE_ONETOUCH=y +CONFIG_USB_STORAGE_KARMA=y +CONFIG_USB_STORAGE_CYPRESS_ATACB=y +# CONFIG_USB_STORAGE_ENE_UB6250 is not set +# CONFIG_USB_UAS 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_SISUSBVGA 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_EHSET_TEST_FIXTURE=y +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_QCOM_DIAG_BRIDGE is not set +# CONFIG_USB_QCOM_MDM_BRIDGE is not set +# CONFIG_USB_QCOM_KS_BRIDGE 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=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 + +# +# USB Peripheral Controller +# +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_CI13XXX_MSM is not set +# CONFIG_USB_CI13XXX_MSM_HSIC is not set +# CONFIG_USB_DWC3_MSM is not set +CONFIG_USB_MSM_72K=y +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_GADGET_SUPERSPEED is not set +# 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_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_G_ANDROID=y +# 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 +CONFIG_USB_CSW_HACK=y +# CONFIG_USB_MSC_PROFILING is not set +CONFIG_MODEM_SUPPORT=y +CONFIG_RMNET_SMD_CTL_CHANNEL="DATA40_CNTL" +CONFIG_RMNET_SMD_DATA_CHANNEL="DATA40" +# CONFIG_USB_ANDROID_RMNET_CTRL_SMD is not set + +# +# OTG and related infrastructure +# +CONFIG_USB_OTG_UTILS=y +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +CONFIG_USB_MSM_OTG_72K=y +# CONFIG_MSM_OTG_ENABLE_A_WAIT_BCON_TIMEOUT is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG is not set +# CONFIG_USB_MSM_ACA is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_PERF_PROFILING=y +CONFIG_JSR_WIFI_SDCC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +# CONFIG_MMC_EMBEDDED_SDIO is not set +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +CONFIG_MMC_TEST=m +CONFIG_MMC_BLOCK_TEST=m + +# +# 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_MSM=y +CONFIG_MMC_MSM_SDC1_SUPPORT=y +# CONFIG_MMC_MSM_SDC1_8_BIT_SUPPORT is not set +CONFIG_MMC_MSM_SDC2_SUPPORT=y +# CONFIG_MMC_MSM_SDC2_8_BIT_SUPPORT is not set +CONFIG_MMC_MSM_SDC3_SUPPORT=y +CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT=y +# CONFIG_MMC_MSM_SDC3_WP_SUPPORT is not set +# CONFIG_MMC_MSM_SDC4_SUPPORT is not set +# CONFIG_MMC_MSM_SDC5_SUPPORT is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_MSM_PDM=y +CONFIG_LEDS_PMIC_MPP=y +CONFIG_LEDS_MSM_TRICOLOR=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_CPLD 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_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_MSM_PMIC is not set +CONFIG_LEDS_TRICOLOR_FLAHSLIGHT=y +# CONFIG_LEDS_PMIC8029 is not set +# 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 is not set + +# +# LED Triggers +# +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# 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_ALARM=y +CONFIG_RTC_INTF_ALARM_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 +# + +# +# 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_MSM=y +CONFIG_RTC_SECURE_TIME_SUPPORT=y +# CONFIG_RTC_DRV_MSM7X00A 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 +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +CONFIG_STAGING=y +# CONFIG_USBIP_CORE is not set +# CONFIG_PRISM2_USB is not set +# CONFIG_ECHO is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_RTLLIB is not set +# CONFIG_R8712U is not set +# CONFIG_RTS5139 is not set +# CONFIG_TRANZPORT is not set +# CONFIG_LINE6_USB is not set +# CONFIG_VT6656 is not set +# CONFIG_IIO is not set +## CONFIG_QCACHE=y +# CONFIG_FB_SM7XX is not set +# CONFIG_USB_ENESTORAGE is not set +# CONFIG_BCM_WIMAX is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_SPEAKUP is not set +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set +# CONFIG_STAGING_MEDIA is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_PERSISTENT_RAM=y +CONFIG_ANDROID_RAM_CONSOLE=y +# CONFIG_PERSISTENT_TRACER is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +# CONFIG_ANDROID_TIMED_GPIO is not set +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_ANDROID_SWITCH is not set +# CONFIG_ANDROID_INTF_ALARM_DEV is not set +# CONFIG_PHONE is not set +# CONFIG_USB_WPAN_HCD is not set + +# +# Qualcomm MSM specific device drivers +# +# CONFIG_MSM_SSBI is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y + +# +# Hardware Spinlock drivers +# +CONFIG_IOMMU_SUPPORT=y + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_MOBICORE_SUPPORT is not set +# CONFIG_MSM_QDSS is not set + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +## CONFIG_EXT2_FS=y +## CONFIG_EXT2_FS_XATTR=y +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_SECURITY is not set +# CONFIG_EXT2_FS_XIP is not set +## CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +## CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +CONFIG_EXT4_FS_SECURITY=y +# CONFIG_EXT4_DEBUG is not set +## 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_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FS_POSIX_ACL=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=y +# CONFIG_CUSE 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 is not set +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 is not set +# CONFIG_TMPFS_XATTR is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS 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_F2FS_FS=y +CONFIG_F2FS_STAT_FS=y +CONFIG_F2FS_FS_XATTR=y +CONFIG_F2FS_FS_POSIX_ACL=y +CONFIG_F2FS_FS_SECURITY=y +# CONFIG_F2FS_CHECK_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_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=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 is not set +CONFIG_DYNAMIC_FSYNC=y + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +## CONFIG_PRINTK_RTC_TIME=y +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=9 +## CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=2048 +## CONFIG_MAGIC_SYSRQ=y +# 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=y +## CONFIG_LOCKUP_DETECTOR=y +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set +## CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=0 +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +## CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +## CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +## CONFIG_DEBUG_SPINLOCK=y +# 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=y +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +## CONFIG_DEBUG_STACK_USAGE=y +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_HIGHMEM is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED 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=y +# 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_CPU_STALL_TIMEOUT=60 +CONFIG_RCU_CPU_STALL_VERBOSE=y +# CONFIG_RCU_CPU_STALL_INFO is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_KPROBES_SANITY_TEST 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_DEBUG_PER_CPU_MAPS is not set +# CONFIG_LKDTM is not set +# CONFIG_CPU_NOTIFIER_ERROR_INJECT is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_NOP_TRACER=y +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_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_RING_BUFFER_ALLOW_SWAP=y +CONFIG_TRACING=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_SCHED_TRACER is not set +CONFIG_ENABLE_DEFAULT_TRACERS=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +CONFIG_KPROBE_EVENT=y +# CONFIG_RING_BUFFER_BENCHMARK is not set +CONFIG_DYNAMIC_DEBUG=y +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +CONFIG_SUPPORT_SYSTEMTAP=y +# CONFIG_SRECORDER_MSM is not set +# CONFIG_SRECORDER_POWERCOLLAPS is not set +# CONFIG_DEVICE_CHECK is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_ARM_KPROBES_TEST is not set +# CONFIG_PID_IN_CONTEXTIDR is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY=y +# CONFIG_SECURITYFS is not set +CONFIG_SECURITY_NETWORK=y +# CONFIG_SECURITY_NETWORK_XFRM is not set +# CONFIG_SECURITY_PATH is not set +CONFIG_LSM_MMAP_MIN_ADDR=32768 +CONFIG_SECURITY_SELINUX=y +# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set +# CONFIG_SECURITY_SELINUX_DISABLE is not set +CONFIG_SECURITY_SELINUX_DEVELOP=y +CONFIG_SECURITY_SELINUX_AVC_STATS=y +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_SECURITY_YAMA is not set +# CONFIG_IMA is not set +CONFIG_DEFAULT_SECURITY_SELINUX=y +# CONFIG_DEFAULT_SECURITY_DAC is not set +CONFIG_DEFAULT_SECURITY="selinux" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=m +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_PCRYPT is not set +CONFIG_CRYPTO_WORKQUEUE=y +CONFIG_CRYPTO_CRYPTD=y +CONFIG_CRYPTO_ABLK_HELPER=y +CONFIG_CRYPTO_AUTHENC=y +# 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=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# 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=y +CONFIG_CRYPTO_SHA1_ARM=y +CONFIG_CRYPTO_SHA1_ARM_NEON=y +CONFIG_CRYPTO_SHA256=y +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_SHA512_ARM_NEON is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_AES_ARM=y +CONFIG_CRYPTO_AES_ARM_BS=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=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=m +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_CRYPTO_DEV_QCRYPTO is not set +# CONFIG_CRYPTO_DEV_QCE is not set +# CONFIG_CRYPTO_DEV_QCEDEV is not set +# CONFIG_CRYPTO_DEV_OTA_CRYPTO is not set +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_CRC_CCITT=y +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=y +# CONFIG_CRC8 is not set +CONFIG_AUDIT_GENERIC=y +# CONFIG_RANDOM32_SELFTEST is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_LZ4_COMPRESS=y +CONFIG_LZ4_DECOMPRESS=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_NLATTR=y +# CONFIG_AVERAGE is not set +# CONFIG_CORDIC is not set + +# +# Misc +# +CONFIG_ENABLE_VMALLOC_SAVING=y +# CONFIG_NO_VM_RECLAIM is not set +CONFIG_INTELLI_PLUG=y +CONFIG_UID_CPUTIME=y diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig index 8c01b21e..6ab36c3a 100644 --- a/arch/arm/configs/msm8960-perf_defconfig +++ b/arch/arm/configs/msm8960-perf_defconfig @@ -59,7 +59,7 @@ CONFIG_MACH_MPQ8064_CDP=y CONFIG_MACH_MPQ8064_HRD=y CONFIG_MACH_MPQ8064_DTV=y # CONFIG_MSM_STACKED_MEMORY is not set -CONFIG_KERNEL_MSM_CONTIG_MEM_REGION=y +CONFIG_KERNEL_PMEM_EBI_REGION=y # CONFIG_MSM_FIQ_SUPPORT is not set # CONFIG_MSM_PROC_COMM is not set CONFIG_MSM_SMD=y diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig index 5bc5f225..482f9f44 100644 --- a/arch/arm/configs/msm8960_defconfig +++ b/arch/arm/configs/msm8960_defconfig @@ -58,7 +58,7 @@ CONFIG_MACH_MPQ8064_CDP=y CONFIG_MACH_MPQ8064_HRD=y CONFIG_MACH_MPQ8064_DTV=y # CONFIG_MSM_STACKED_MEMORY is not set -CONFIG_KERNEL_MSM_CONTIG_MEM_REGION=y +CONFIG_KERNEL_PMEM_EBI_REGION=y # CONFIG_MSM_FIQ_SUPPORT is not set # CONFIG_MSM_PROC_COMM is not set CONFIG_MSM_SMD=y diff --git a/arch/arm/configs/normandy_defconfig b/arch/arm/configs/normandy_defconfig index 36bfd385..c0be83e4 100644 --- a/arch/arm/configs/normandy_defconfig +++ b/arch/arm/configs/normandy_defconfig @@ -42,7 +42,7 @@ CONFIG_IRQ_WORK=y CONFIG_EXPERIMENTAL=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="$(KERNEL_LOCAL_VERSION)-perf" +CONFIG_LOCALVERSION="-WERITOS-MM" CONFIG_LOCALVERSION_AUTO=y CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y @@ -60,7 +60,7 @@ 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_AUDIT=y CONFIG_HAVE_GENERIC_HARDIRQS=y # @@ -460,6 +460,7 @@ CONFIG_MSM_IDLE_WAIT_ON_MODEM=0 CONFIG_MSM_GPIOMUX=y CONFIG_MSM_MODEM_RESTART=y CONFIG_MSM_PM2=y +CONFIG_MSM_EVENT_TIMER=y # CONFIG_MSM_BUS_SCALING is not set # CONFIG_MSM_WATCHDOG_V2 is not set # CONFIG_MSM_JTAG is not set @@ -806,7 +807,7 @@ CONFIG_IPV6_SUBTREES=y # CONFIG_IPV6_MROUTE is not set CONFIG_ANDROID_PARANOID_NETWORK=y CONFIG_NET_ACTIVITY_STATS=y -# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETWORK_SECMARK=y # CONFIG_NETWORK_PHY_TIMESTAMPING is not set CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set @@ -821,6 +822,7 @@ CONFIG_NETFILTER_NETLINK_QUEUE=y CONFIG_NETFILTER_NETLINK_LOG=y CONFIG_NF_CONNTRACK=y CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_SECMARK=y CONFIG_NF_CONNTRACK_PROCFS=y CONFIG_NF_CONNTRACK_EVENTS=y # CONFIG_NF_CONNTRACK_TIMEOUT is not set @@ -854,23 +856,26 @@ CONFIG_NETFILTER_XT_CONNMARK=y # # Xtables targets # +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set # CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y # CONFIG_NETFILTER_XT_TARGET_CT is not set # CONFIG_NETFILTER_XT_TARGET_DSCP is not set # CONFIG_NETFILTER_XT_TARGET_HL is not set # CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set CONFIG_NETFILTER_XT_TARGET_LOG=y CONFIG_NETFILTER_XT_TARGET_MARK=y -CONFIG_NETFILTER_XT_TARGET_NFLOG=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y # CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set -CONFIG_NETFILTER_XT_TARGET_TPROXY=y -CONFIG_NETFILTER_XT_TARGET_TRACE=y -# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y # CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set # @@ -935,7 +940,7 @@ CONFIG_IP_NF_MATCH_ECN=y CONFIG_IP_NF_MATCH_TTL=y CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_TARGET_REJECT=y -CONFIG_IP_NF_TARGET_REJECT_SKERR=y +# CONFIG_IP_NF_TARGET_REJECT_SKERR is not set # CONFIG_IP_NF_TARGET_ULOG is not set CONFIG_NF_NAT=y CONFIG_NF_NAT_NEEDED=y @@ -958,6 +963,7 @@ CONFIG_IP_NF_MANGLE=y # CONFIG_IP_NF_TARGET_ECN is not set # CONFIG_IP_NF_TARGET_TTL is not set CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y @@ -966,7 +972,7 @@ CONFIG_IP_NF_ARP_MANGLE=y # IPv6: Netfilter Configuration # CONFIG_NF_DEFRAG_IPV6=y -CONFIG_NF_CONNTRACK_IPV6=y +# CONFIG_NF_CONNTRACK_IPV6 is not set # CONFIG_IP6_NF_QUEUE is not set CONFIG_IP6_NF_IPTABLES=y # CONFIG_IP6_NF_MATCH_AH is not set @@ -981,16 +987,17 @@ CONFIG_IP6_NF_IPTABLES=y # CONFIG_IP6_NF_TARGET_HL is not set CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y -CONFIG_IP6_NF_TARGET_REJECT_SKERR=y +# CONFIG_IP6_NF_TARGET_REJECT_SKERR is not set CONFIG_IP6_NF_MANGLE=y CONFIG_IP6_NF_RAW=y +CONFIG_IP6_NF_SECURITY=y # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set # CONFIG_RDS is not set # CONFIG_TIPC is not set # CONFIG_ATM is not set CONFIG_L2TP=y -# CONFIG_L2TP_DEBUGFS is not set +CONFIG_L2TP_DEBUGFS=y CONFIG_L2TP_V3=y CONFIG_L2TP_IP=y CONFIG_L2TP_ETH=y @@ -998,10 +1005,8 @@ CONFIG_L2TP_ETH=y # CONFIG_NET_DSA is not set # CONFIG_VLAN_8021Q is not set # CONFIG_DECNET is not set -CONFIG_LLC=y # CONFIG_LLC2 is not set -CONFIG_IPX=y -CONFIG_IPX_INTERN=y +# CONFIG_IPX is not set # CONFIG_ATALK is not set # CONFIG_X25 is not set # CONFIG_LAPB is not set @@ -1170,8 +1175,10 @@ CONFIG_DMA_SHARED_BUFFER=y CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y CONFIG_SYNC=y -# CONFIG_SW_SYNC is not set -# CONFIG_CMA is not set +CONFIG_SW_SYNC=y +CONFIG_SW_SYNC_USER=y +CONFIG_CMA=y +# CONFIG_CMA_DEBUG is not set # CONFIG_CONNECTOR is not set # CONFIG_MTD is not set # CONFIG_PARPORT is not set @@ -1200,7 +1207,7 @@ CONFIG_BLK_DEV_RAM_SIZE=8192 # # CONFIG_SENSORS_LIS3LV02D is not set # CONFIG_AD525X_DPOT is not set -CONFIG_ANDROID_PMEM=y +## CONFIG_ANDROID_PMEM=y # CONFIG_ATMEL_PWM is not set # CONFIG_ICS932S401 is not set # CONFIG_ENCLOSURE_SERVICES is not set @@ -1979,19 +1986,33 @@ CONFIG_VIDEO_MEDIA=y # 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_TEA5761=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_MEDIA_TUNER_CUSTOMISE=y + +# +# Customize TV tuners +# +# CONFIG_MEDIA_TUNER_SIMPLE is not set +# CONFIG_MEDIA_TUNER_TDA8290 is not set +# CONFIG_MEDIA_TUNER_TDA827X is not set +# CONFIG_MEDIA_TUNER_TDA18271 is not set +# CONFIG_MEDIA_TUNER_TDA9887 is not set +# CONFIG_MEDIA_TUNER_TEA5761 is not set +# CONFIG_MEDIA_TUNER_TEA5767 is not set +# CONFIG_MEDIA_TUNER_MT20XX is not set +# CONFIG_MEDIA_TUNER_MT2060 is not set +# CONFIG_MEDIA_TUNER_MT2063 is not set +# CONFIG_MEDIA_TUNER_MT2266 is not set +# CONFIG_MEDIA_TUNER_MT2131 is not set +# CONFIG_MEDIA_TUNER_QT1010 is not set +# CONFIG_MEDIA_TUNER_XC2028 is not set +# CONFIG_MEDIA_TUNER_XC5000 is not set +# CONFIG_MEDIA_TUNER_XC4000 is not set +# CONFIG_MEDIA_TUNER_MXL5005S is not set +# CONFIG_MEDIA_TUNER_MXL5007T is not set +# CONFIG_MEDIA_TUNER_MC44S803 is not set +# CONFIG_MEDIA_TUNER_MAX2165 is not set +# CONFIG_MEDIA_TUNER_TDA18218 is not set +# CONFIG_MEDIA_TUNER_TDA18212 is not set CONFIG_VIDEO_V4L2=y CONFIG_VIDEOBUF2_CORE=y CONFIG_VIDEOBUF2_MEMOPS=y @@ -2245,9 +2266,8 @@ CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set -CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 -CONFIG_MSM_KGSL_PAGE_TABLE_COUNT=24 CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES=y # CONFIG_VGASTATE is not set @@ -3221,10 +3241,26 @@ CONFIG_DEBUG_USER=y # # CONFIG_KEYS is not set # CONFIG_SECURITY_DMESG_RESTRICT is not set -# CONFIG_SECURITY is not set +CONFIG_SECURITY=y # CONFIG_SECURITYFS is not set -CONFIG_DEFAULT_SECURITY_DAC=y -CONFIG_DEFAULT_SECURITY="" +CONFIG_SECURITY_NETWORK=y +# CONFIG_SECURITY_NETWORK_XFRM is not set +# CONFIG_SECURITY_PATH is not set +CONFIG_LSM_MMAP_MIN_ADDR=32768 +CONFIG_SECURITY_SELINUX=y +# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set +# CONFIG_SECURITY_SELINUX_DISABLE is not set +CONFIG_SECURITY_SELINUX_DEVELOP=y +CONFIG_SECURITY_SELINUX_AVC_STATS=y +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_SECURITY_YAMA is not set +# CONFIG_IMA is not set +CONFIG_DEFAULT_SECURITY_SELINUX=y +# CONFIG_DEFAULT_SECURITY_DAC is not set +CONFIG_DEFAULT_SECURITY="selinux" CONFIG_CRYPTO=y # @@ -3355,6 +3391,8 @@ CONFIG_CRC32_SLICEBY8=y # CONFIG_CRC7 is not set CONFIG_LIBCRC32C=y # CONFIG_CRC8 is not set +CONFIG_AUDIT_GENERIC=y +# CONFIG_RANDOM32_SELFTEST is not set CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y CONFIG_LZO_COMPRESS=y diff --git a/arch/arm/configs/weritos_defconfig b/arch/arm/configs/weritos_defconfig new file mode 100644 index 00000000..1c065c25 --- /dev/null +++ b/arch/arm/configs/weritos_defconfig @@ -0,0 +1,3484 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.110 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_LOCKBREAK=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=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_FIQ=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_NEED_MACH_IO_H=y +CONFIG_NEED_MACH_MEMORY_H=y +CONFIG_PHYS_OFFSET=0x00200000 +CONFIG_GENERIC_BUG=y +# CONFIG_ARCH_RANDOM is not set +# CONFIG_GENERIC_TIME_VSYSCALL is not set +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y +CONFIG_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="-WERITOS-KK-CAF" +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_HAVE_KERNEL_LZ4=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +# CONFIG_KERNEL_LZ4 is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_AUDIT_WATCH=y +CONFIG_AUDIT_TREE=y +# CONFIG_AUDIT_LOGINUID_IMMUTABLE is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_IRQ_DOMAIN=y +# CONFIG_IRQ_DOMAIN_DEBUG is not set + +# +# RCU Subsystem +# +CONFIG_TREE_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_RCU_FAST_NO_HZ is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_RCU_BOOST is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=20 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_MEM_RES_CTLR=y +# CONFIG_CGROUP_MEM_RES_CTLR_SWAP is not set +# CONFIG_CGROUP_MEM_RES_CTLR_KMEM is not set +# CONFIG_CGROUP_PERF is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_CFS_BANDWIDTH is not set +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +CONFIG_MM_OWNER=y +# 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_RD_LZ4 is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y +# CONFIG_OPTIMIZE_INLINING is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +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_ASHMEM=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=y +CONFIG_TRACEPOINTS=y +CONFIG_KPROBES=y +# CONFIG_JUMP_LABEL is not set +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_KRETPROBES=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_USE_GENERIC_SMP_HELPERS=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BRKPT_RESERVED_RW_ACCESS=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 is not set +# CONFIG_MODULE_FORCE_LOAD_ATH6KL is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_STOP_MACHINE=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# 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=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_BFQ=y +# CONFIG_CGROUP_BFQIO is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_FIOPS=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_ROW=y +CONFIG_IOSCHED_SIO=y +CONFIG_IOSCHED_SIOPLUS=y +CONFIG_IOSCHED_TEST=m +CONFIG_IOSCHED_VR=y +CONFIG_IOSCHED_ZEN=y +# CONFIG_DEFAULT_BFQ is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_FIOPS is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_ROW=y +# CONFIG_DEFAULT_SIO is not set +# CONFIG_DEFAULT_SIOPLUS is not set +# CONFIG_DEFAULT_VR is not set +# CONFIG_DEFAULT_ZEN is not set +CONFIG_DEFAULT_IOSCHED="row" +# 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_UNINLINE_SPIN_UNLOCK=y +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# 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 is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# 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 is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +CONFIG_MUTEX_SPIN_ON_OWNER=y +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=y +# 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_GPIO_PCA953X is not set + +# +# MSM SoC Type +# +# CONFIG_ARCH_MSM7X01A is not set +# CONFIG_ARCH_MSM7X25 is not set +CONFIG_ARCH_MSM7X27=y +# CONFIG_ARCH_MSM7X30 is not set +# CONFIG_ARCH_QSD8X50 is not set +# CONFIG_ARCH_MSM8X60 is not set +# CONFIG_ARCH_MSM8960 is not set +# CONFIG_ARCH_MSM8930 is not set +# CONFIG_ARCH_APQ8064 is not set +# CONFIG_ARCH_MSM8974 is not set +# CONFIG_ARCH_FSM9XXX is not set +# CONFIG_ARCH_MSM9615 is not set +CONFIG_ARCH_MSM8625=y +# CONFIG_ARCH_MSM9625 is not set +# CONFIG_ARCH_MSM8910 is not set +# CONFIG_MSM_SOC_REV_NONE is not set +CONFIG_MSM_SOC_REV_A=y +CONFIG_MSM_SMP=y +CONFIG_ARCH_MSM_CORTEXMP=y +CONFIG_ARCH_MSM_CORTEX_A5=y +CONFIG_ARCH_MSM7X27A=y +CONFIG_MSM_VIC=y +# CONFIG_MSM_RPM is not set +# CONFIG_MSM_RPM_SMD is not set +# CONFIG_MSM_MPM is not set + +# +# MSM Board Selection +# +# CONFIG_MACH_MSM7X27_SURF is not set +# CONFIG_MACH_MSM7X27_FFA is not set +CONFIG_MACH_MSM7X27A_RUMI3=y +CONFIG_MACH_MSM7X27A_SURF=y +CONFIG_MACH_MSM7X27A_FFA=y +CONFIG_WERITOS_KERNEL=y +CONFIG_MACH_MSM7625A_SURF=y +CONFIG_MACH_MSM7625A_FFA=y +CONFIG_MACH_MSM7627A_QRD1=y +CONFIG_MACH_MSM7627A_QRD3=y +CONFIG_MACH_MSM7627A_EVB=y +CONFIG_MACH_MSM8625_RUMI3=y +CONFIG_MACH_MSM8625_SURF=y +CONFIG_MACH_MSM8625_FFA=y +CONFIG_MACH_MSM8625_EVB=y +CONFIG_MACH_MSM7X27A_QRD5A=y +CONFIG_MACH_MSM8625_QRD7=y +CONFIG_MACH_MSM8625_QRD5=y +CONFIG_MACH_MSM8625Q_EVBD=y +CONFIG_MACH_MSM8625Q_SKUD=y +CONFIG_MACH_MSM8625Q_SKUE=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_MSM_AMSS_VERSION=6225 +# CONFIG_MSM_AMSS_VERSION_6210 is not set +# CONFIG_MSM_AMSS_VERSION_6220 is not set +CONFIG_MSM_AMSS_VERSION_6225=y +# CONFIG_MSM7X00A_USE_GP_TIMER is not set +CONFIG_MSM7X00A_USE_DG_TIMER=y +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=20000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET=1000000000 +CONFIG_CPU_HAS_L2_PMU=y +# CONFIG_HTC_HEADSET is not set +# CONFIG_HTC_PWRSINK is not set +CONFIG_MSM_FIQ_SUPPORT=y +# CONFIG_MSM_SERIAL_DEBUGGER is not set +CONFIG_MSM_PROC_COMM=y +CONFIG_MSM_SMD=y +CONFIG_MSM_AMSS_ENHANCE_DEBUG=y +# CONFIG_MSM_SMD_PKG3 is not set +CONFIG_MSM_SMD_PKG4=y +CONFIG_MSM_FUSE_INFO_DEBUG=y +# CONFIG_MSM_SMD_DEBUG is not set +CONFIG_MSM_FAST_BOOT=y +CONFIG_MSM_N_WAY_SMD=y +CONFIG_MSM_N_WAY_SMSM=y +# CONFIG_MSM_RESET_MODEM is not set +CONFIG_MSM_SMD_LOGGING=y +# CONFIG_MSM_IPC_LOGGING is not set +# CONFIG_MSM_SMD_NMEA is not set +CONFIG_MSM_SMD_TTY=y +# CONFIG_MSM_SMD_QMI is not set +CONFIG_MSM_SMD_PKT=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_IPC_ROUTER=y +# CONFIG_MSM_IPC_ROUTER_SMD_XPRT is not set +CONFIG_MSM_ONCRPCROUTER_DEBUG=y +# CONFIG_MSM_RPC_LOOPBACK_XPRT is not set +# CONFIG_MSM_RPCSERVER_TIME_REMOTE is not set +CONFIG_MSM_RPCSERVER_WATCHDOG=y +# CONFIG_MSM_RPC_WATCHDOG is not set +CONFIG_MSM_RPC_PING=y +CONFIG_MSM_RPC_PROC_COMM_TEST=y +CONFIG_MSM_RPC_OEM_RAPI=y +CONFIG_MSM_RPCSERVER_HANDSET=y +CONFIG_MSM_RMT_STORAGE_CLIENT=y +# CONFIG_MSM_RMT_STORAGE_CLIENT_STATS is not set +CONFIG_MSM_DALRPC=y +CONFIG_MSM_DALRPC_TEST=m +# CONFIG_MSM_CPU_FREQ_SET_MIN_MAX is not set +# CONFIG_MSM_AVS_HW is not set +# CONFIG_MSM_HW3D is not set +CONFIG_MSM_ADSP=y +CONFIG_ADSP_RPC_VER=0x30002 +CONFIG_AMSS_7X25_VERSION_2009=y +# CONFIG_AMSS_7X25_VERSION_2008 is not set +CONFIG_MSM_ADSP_REPORT_EVENTS=y +CONFIG_RTAC=y +CONFIG_MSM7X27A_AUDIO=y +CONFIG_MSM_PROC_COMM_REGULATOR=y +# CONFIG_MSM_VREG_SWITCH_INVERTED is not set +CONFIG_MSM_DMA_TEST=y +# CONFIG_WIFI_CONTROL_FUNC is not set +CONFIG_AUDIO_AAC_PLUS=y +CONFIG_AUDIO_ENHANCED_AAC_PLUS=y +CONFIG_SURF_FFA_GPIO_KEYPAD=y +CONFIG_MSM_SLEEP_TIME_OVERRIDE=y +# CONFIG_MSM_MEMORY_LOW_POWER_MODE is not set +CONFIG_MSM_PM_TIMEOUT_HALT=y +# CONFIG_MSM_PM_TIMEOUT_RESET_MODEM is not set +# CONFIG_MSM_PM_TIMEOUT_RESET_CHIP is not set +CONFIG_MSM_IDLE_WAIT_ON_MODEM=0 +# CONFIG_MSM_PIL is not set +# CONFIG_MSM_SCM is not set +# CONFIG_MSM_TZ_LOG is not set +# CONFIG_MSM_DIRECT_SCLK_ACCESS is not set +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_MODEM_RESTART=y +CONFIG_MSM_PM2=y +CONFIG_MSM_EVENT_TIMER=y +# CONFIG_MSM_BUS_SCALING is not set +# CONFIG_MSM_WATCHDOG_V2 is not set +# CONFIG_MSM_JTAG is not set +CONFIG_MSM_SLEEP_STATS_DEVICE=y +CONFIG_MSM_RUN_QUEUE_STATS=y +CONFIG_MSM_SM_EVENT=y +CONFIG_MSM_SM_EVENT_LOG=m +CONFIG_UNCACHED_BUF=y +CONFIG_MSM_SLEEP_MONITOR=m +# CONFIG_MSM_STANDALONE_POWER_COLLAPSE is not set +# CONFIG_MSM_GSBI9_UART is not set +CONFIG_MSM_SHOW_RESUME_IRQ=y +CONFIG_BT_MSM_PINTEST=y +# CONFIG_MSM_FAKE_BATTERY is not set +# CONFIG_MSM_QDSP6_APR is not set +# CONFIG_MSM_QDSP6_CODECS is not set +# CONFIG_MSM_QDSP6V2_CODECS is not set +# CONFIG_MSM_AUDIO_QDSP6 is not set +# CONFIG_MSM_AUDIO_QDSP6V2 is not set +CONFIG_MSM_RPC_VIBRATOR=y +CONFIG_PM8XXX_RPC_VIBRATOR=y +# CONFIG_MSM_SPM_V1 is not set +CONFIG_MSM_SPM_V2=y +# CONFIG_MSM_L2_SPM is not set +CONFIG_MSM_MULTIMEDIA_USE_ION=y +# CONFIG_MSM_OCMEM is not set +# CONFIG_MSM_RTB is not set +# CONFIG_MSM_EBI_ERP is not set +CONFIG_MSM_CPR=y +CONFIG_MSM_VP_REGULATOR=y +# CONFIG_MSM_CACHE_DUMP is not set +# CONFIG_MSM_HSIC_SYSMON is not set +# CONFIG_MSM_8X60_FUSION_GPIO_GLITCH is not set +CONFIG_MSM_FIQ=y +CONFIG_MSM_PBUS=y +CONFIG_HAS_MACH_MEMUTILS=y +# CONFIG_MSM_OVERCLOCK_GPU is not set +# CONFIG_INTELLI_PLUG is not set +# CONFIG_BRICKED_HOTPLUG is not set + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +CONFIG_SWP_EMULATE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_CACHE_L2X0=y +CONFIG_CACHE_PL310=y +# CONFIG_CACHE_PL310_ERP is not set +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_ARCH_HAS_BARRIERS=y +# CONFIG_VCM is not set +# CONFIG_STRICT_MEMORY_RWX is not set +CONFIG_ARM_NR_BANKS=8 +# CONFIG_RESERVE_FIRST_PAGE is not set +CONFIG_CPU_HAS_PMU=y +CONFIG_MULTI_IRQ_HANDLER=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_742230 is not set +# CONFIG_ARM_ERRATA_742231 is not set +# CONFIG_PL310_ERRATA_588369 is not set +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_PL310_ERRATA_727915 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_ARM_ERRATA_751472 is not set +# CONFIG_PL310_ERRATA_753970 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_PL310_ERRATA_769419 is not set +# CONFIG_ARM_ERRATA_775420 is not set +CONFIG_ARM_GIC=y +CONFIG_GIC_SECURE=y +# 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_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_HAVE_SMP=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_ARM_CPU_TOPOLOGY=y +# CONFIG_SCHED_MC is not set +# CONFIG_SCHED_SMT is not set +CONFIG_HAVE_ARM_SCU=y +# CONFIG_ARM_ARCH_TIMER is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_NR_CPUS=4 +CONFIG_HOTPLUG_CPU=y +CONFIG_LOCAL_TIMERS=y +CONFIG_ARCH_NR_GPIO=0 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +CONFIG_HZ=300 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HIGHMEM=y +# CONFIG_HIGHPTE is not set +CONFIG_HW_PERF_EVENTS=y +CONFIG_VMALLOC_RESERVE=0xC800000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_CLEANCACHE=y +# CONFIG_DYNAMIC_PAGE_WRITEBACK is not set +# CONFIG_FRONTSWAP is not set +# CONFIG_USE_USER_ACCESSIBLE_TIMERS is not set +CONFIG_ZSMALLOC=y +# CONFIG_PGTABLE_MAPPING is not set +# CONFIG_ARCH_MEMORY_PROBE is not set +# CONFIG_ARCH_MEMORY_REMOVE is not set +# CONFIG_ARCH_POPULATES_NODE_MAP is not set +# CONFIG_ENABLE_DMM is not set +# CONFIG_FIX_MOVABLE_ZONE is not set +CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0=y +# CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG is not set +# CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set +CONFIG_CP_ACCESS=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="init=/sbin/init root=/dev/ram rw initrd=0x11000000,16M console=ttyDCC0 mem=88M" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASSH3 is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIACTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ADAPTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIMM is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_STOCKDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_YANKDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_LIONHEART is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_HYPER is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMANDX is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVEX is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ALUCARD is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_SMARTASS2=y +# CONFIG_CPU_FREQ_GOV_SMARTASSH3 is not set +# CONFIG_CPU_FREQ_GOV_INTELLIACTIVE is not set +# CONFIG_CPU_FREQ_GOV_ADAPTIVE is not set +# CONFIG_CPU_FREQ_GOV_INTELLIMM is not set +# CONFIG_CPU_FREQ_GOV_STOCKDEMAND is not set +# CONFIG_CPU_FREQ_GOV_YANKDEMAND is not set +# CONFIG_CPU_FREQ_GOV_INTELLIDEMAND is not set +CONFIG_CPU_FREQ_GOV_LIONHEART=y +# CONFIG_CPU_FREQ_GOV_HYPER is not set +# CONFIG_CPU_FREQ_GOV_ONDEMANDX is not set +# CONFIG_CPU_FREQ_GOV_INTERACTIVEX is not set +CONFIG_CPU_FREQ_GOV_ALUCARD=y + +# +# ARM CPU frequency scaling drivers +# +# CONFIG_ARM_EXYNOS4210_CPUFREQ is not set +# CONFIG_ARM_EXYNOS4X12_CPUFREQ is not set +# CONFIG_ARM_EXYNOS5250_CPUFREQ is not set +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +# CONFIG_HAVE_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_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_PM_RUNTIME=y +CONFIG_PM=y +CONFIG_PM_DEBUG=y +# CONFIG_PM_ADVANCED_DEBUG is not set +# CONFIG_PM_TEST_SUSPEND is not set +CONFIG_CAN_PM_TRACE=y +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +CONFIG_SUSPEND_TIME=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +CONFIG_XFRM=y +CONFIG_XFRM_USER=y +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_FIB_TRIE_STATS is not set +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_XFRM_TUNNEL=y +CONFIG_INET_TUNNEL=y +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 is not set +CONFIG_TCP_CONG_ADVANCED=y +# CONFIG_TCP_CONG_BIC is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_TCP_CONG_WESTWOOD=y +# CONFIG_TCP_CONG_HTCP is not set +# CONFIG_TCP_CONG_HSTCP is not set +# CONFIG_TCP_CONG_HYBLA is not set +CONFIG_TCP_CONG_VEGAS=y +# CONFIG_TCP_CONG_SCALABLE is not set +# CONFIG_TCP_CONG_LP is not set +CONFIG_TCP_CONG_VENO=y +# CONFIG_TCP_CONG_YEAH is not set +# CONFIG_TCP_CONG_ILLINOIS is not set +# CONFIG_DEFAULT_CUBIC is not set +# CONFIG_DEFAULT_VEGAS is not set +# CONFIG_DEFAULT_VENO is not set +CONFIG_DEFAULT_WESTWOOD=y +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_TCP_CONG="westwood" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETLABEL is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +CONFIG_NETWORK_SECMARK=y +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +# CONFIG_NETFILTER_NETLINK_ACCT is not set +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_PROCFS=y +CONFIG_NF_CONNTRACK_EVENTS=y +# CONFIG_NF_CONNTRACK_TIMEOUT is not set +# CONFIG_NF_CONNTRACK_TIMESTAMP is not set +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_BROADCAST=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +# CONFIG_NF_CONNTRACK_SNMP is not set +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +# CONFIG_NF_CT_NETLINK_TIMEOUT is not set +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +# CONFIG_NETFILTER_XT_TARGET_HL is not set +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +CONFIG_NETFILTER_XT_MATCH_ECN=y +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_SET is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +# CONFIG_IP_NF_MATCH_RPFILTER is not set +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +# CONFIG_IP_NF_TARGET_REJECT_SKERR is not set +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +# CONFIG_NF_CONNTRACK_IPV6 is not set +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=y +# CONFIG_IP6_NF_MATCH_AH is not set +# CONFIG_IP6_NF_MATCH_EUI64 is not set +# CONFIG_IP6_NF_MATCH_FRAG is not set +# CONFIG_IP6_NF_MATCH_OPTS is not set +# CONFIG_IP6_NF_MATCH_HL is not set +# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set +# CONFIG_IP6_NF_MATCH_MH is not set +# CONFIG_IP6_NF_MATCH_RPFILTER is not set +# CONFIG_IP6_NF_MATCH_RT is not set +# CONFIG_IP6_NF_TARGET_HL is not set +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +# CONFIG_IP6_NF_TARGET_REJECT_SKERR is not set +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_IP6_NF_SECURITY=y +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +CONFIG_L2TP=y +CONFIG_L2TP_DEBUGFS=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA 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_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFB is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +# CONFIG_NET_SCH_MQPRIO is not set +# CONFIG_NET_SCH_CHOKE is not set +# CONFIG_NET_SCH_QFQ is not set +# CONFIG_NET_SCH_INGRESS is not set +# CONFIG_NET_SCH_PLUG is not set + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +# CONFIG_NET_ACT_POLICE is not set +# CONFIG_NET_ACT_GACT is not set +# CONFIG_NET_ACT_MIRRED is not set +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +# CONFIG_NETPRIO_CGROUP 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_NET_TCPPROBE is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCISMD is not set +# CONFIG_BT_HCIBTUSB is not set +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +CONFIG_BT_HCIUART_IBS=y +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBPA10X is not set +CONFIG_BT_MSM_SLEEP=y +# CONFIG_BT_HCIBFUSB is not set +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +CONFIG_MSM_BT_POWER=y +CONFIG_HUAWEI_BT_WCN2243=y +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +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=m +CONFIG_NL80211_TESTMODE=y +# 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 is not set +# CONFIG_LIB80211 is not set +# CONFIG_CFG80211_ALLOW_RECONNECT is not set +# CONFIG_MAC80211 is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_REGULATOR is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +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_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SW_SYNC_USER=y +# CONFIG_CMA is not set +# CONFIG_CONNECTOR is not set +# CONFIG_MTD is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +CONFIG_ZRAM=y +CONFIG_ZRAM_LZ4_COMPRESS=y +# CONFIG_ZRAM_DEBUG is not set +# 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=8192 +# 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 +# CONFIG_BLK_DEV_RBD is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ATMEL_PWM is not set +# CONFIG_ICS932S401 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_DS1682 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +# CONFIG_APANIC is not set +CONFIG_APANIC_MMC=y +CONFIG_APANIC_PLABEL="kpanic" +# CONFIG_TSIF is not set +CONFIG_GLANCEVIEW=y +# CONFIG_TSPP is not set +# CONFIG_HAPTIC_ISA1200 is not set +# CONFIG_QSEECOM is not set +# CONFIG_QFP_FUSE is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST 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_TGT=y +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# 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=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +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=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_ISCSI_BOOT_SYSFS is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM_BUILTIN=y +CONFIG_BLK_DEV_DM=y +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_THIN_PROVISIONING is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +# CONFIG_DM_FLAKEY is not set +# CONFIG_DM_VERITY 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=y +# CONFIG_IFB is not set +# CONFIG_NET_TEAM is not set +# CONFIG_MACVLAN is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_BROADCOM=y +# CONFIG_B44 is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +CONFIG_NET_VENDOR_CHELSIO=y +CONFIG_NET_VENDOR_CIRRUS=y +# CONFIG_CS89x0 is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +CONFIG_NET_VENDOR_FARADAY=y +# CONFIG_FTMAC100 is not set +# CONFIG_FTGMAC100 is not set +CONFIG_NET_VENDOR_INTEL=y +CONFIG_NET_VENDOR_I825XX=y +CONFIG_NET_VENDOR_MARVELL=y +CONFIG_NET_VENDOR_MICREL=y +# CONFIG_KS8851_MLL is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_QFEC is not set +CONFIG_NET_VENDOR_NATSEMI=y +CONFIG_NET_VENDOR_8390=y +# CONFIG_AX88796 is not set +# CONFIG_ETHOC is not set +CONFIG_NET_VENDOR_SEEQ=y +# CONFIG_SEEQ8005 is not set +CONFIG_NET_VENDOR_SMSC=y +CONFIG_SMC91X=y +# CONFIG_SMC911X is not set +CONFIG_SMSC911X=y +# CONFIG_SMSC911X_ARCH_HOOKS is not set +CONFIG_NET_VENDOR_STMICRO=y +# CONFIG_STMMAC_ETH is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_AMD_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPPOL2TP=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=y +CONFIG_USB_NET_CDCETHER=y +# CONFIG_USB_NET_CDC_EEM is not set +CONFIG_USB_NET_CDC_NCM=y +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_SMSC75XX is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_GL620A is not set +CONFIG_USB_NET_NET1080=y +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_MCS7830 is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +CONFIG_USB_NET_CDC_SUBSET=y +# CONFIG_USB_ALI_M5632 is not set +# CONFIG_USB_AN2720 is not set +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_KC2190 is not set +CONFIG_USB_NET_ZAURUS=y +# CONFIG_USB_NET_CX82310_ETH is not set +# CONFIG_USB_NET_KALMIA is not set +# CONFIG_USB_NET_QMI_WWAN is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_USB_SIERRA_NET is not set +# CONFIG_USB_VL600 is not set +# CONFIG_MSM_RMNET_USB is not set +CONFIG_WLAN=y +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +CONFIG_LIBRA_SDIOIF=m +# CONFIG_ATH6K_LEGACY_EXT is not set +CONFIG_ATH_COMMON=m +# CONFIG_ATH_DEBUG is not set +CONFIG_ATH6KL=m +CONFIG_ATH6KL_SDIO=m +# CONFIG_WIFI_MAC_FROM_NV is not set +# CONFIG_ATH6KL_USB is not set +# CONFIG_ATH6KL_DEBUG is not set +# CONFIG_ATH6KL_REGDOMAIN is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_IWM is not set +# CONFIG_LIBERTAS 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=y +# 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=m +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +# CONFIG_JOYSTICK_ANALOG is not set +# CONFIG_JOYSTICK_A3D is not set +# CONFIG_JOYSTICK_ADI is not set +# CONFIG_JOYSTICK_COBRA is not set +# CONFIG_JOYSTICK_GF2K is not set +# CONFIG_JOYSTICK_GRIP is not set +# CONFIG_JOYSTICK_GRIP_MP is not set +# CONFIG_JOYSTICK_GUILLEMOT is not set +# CONFIG_JOYSTICK_INTERACT is not set +# CONFIG_JOYSTICK_SIDEWINDER is not set +# CONFIG_JOYSTICK_TMDC is not set +# CONFIG_JOYSTICK_IFORCE is not set +# CONFIG_JOYSTICK_WARRIOR is not set +# CONFIG_JOYSTICK_MAGELLAN is not set +# CONFIG_JOYSTICK_SPACEORB is not set +# CONFIG_JOYSTICK_SPACEBALL is not set +# CONFIG_JOYSTICK_STINGER is not set +# CONFIG_JOYSTICK_TWIDJOY is not set +# CONFIG_JOYSTICK_ZHENHUA is not set +# CONFIG_JOYSTICK_AS5011 is not set +# CONFIG_JOYSTICK_JOYDUMP is not set +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +# CONFIG_TOUCHDISC_VTD518_SHINETSU is not set +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_TABLET_USB_WACOM=y +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_FOCAL is not set +# CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set +# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_EGALAX is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_ILI210X is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MAX11801 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C=y +CONFIG_SYNA_MULTI_TOUCH=y +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_PIXCIR is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC_SERIO is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_MSM_LEGACY is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +# CONFIG_TOUCHSCREEN_CY8C_TS is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC is not set +# CONFIG_TOUCHSCREEN_FT5X06 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set +CONFIG_TOUCHSCREEN_FT6306=y +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_BMA150 is not set +# CONFIG_INPUT_MMA8450 is not set +# CONFIG_INPUT_MPU3050 is not set +# CONFIG_INPUT_GP2A is not set +# CONFIG_INPUT_GPIO_TILT_POLLED is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_KXTJ9 is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_ISA1200_FF_MEMLESS is not set +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_BOSCH_BMA150 is not set +# CONFIG_INPUT_LTR502 is not set +# CONFIG_INPUT_LTR558 is not set +# CONFIG_BOSCH_BMA250 is not set +# CONFIG_AVAGO_APDS990X is not set +# CONFIG_INPUT_ISL29028 is not set +# CONFIG_INPUT_LIS3DH is not set +# CONFIG_SENSORS_TMD2771X 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=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_N_SMUX 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_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_MSM_HS=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +# CONFIG_SERIAL_MSM_HSL is not set +# CONFIG_SERIAL_BCM_BT_LPM is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_MSM_SMD is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set + +# +# Diag Support +# +CONFIG_DIAG_CHAR=y + +# +# DIAG traffic over USB +# +CONFIG_DIAG_OVER_USB=y + +# +# SDIO support for DIAG +# + +# +# HSIC support for DIAG +# +# 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_MMC_GENERIC_CSDIO is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_ALGOBIT=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +CONFIG_I2C_GPIO=y +# CONFIG_I2C_MSM is not set +CONFIG_I2C_QUP=y +# CONFIG_I2C_OCORES 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_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SPI is not set +# CONFIG_SPMI is not set +# CONFIG_SLIMBUS is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# 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=y +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set +CONFIG_GPIO_MSM_V1=y +# CONFIG_GPIO_MSM_V2 is not set +# CONFIG_GPIO_MSM_V3 is not set +# CONFIG_GPIO_FSM9XXX 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=y +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MCP23S08 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_GPIO_PM8XXX_RPC is not set +# 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_CHARGER_ISP1704 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_MANAGER is not set +CONFIG_BATTERY_MSM=y +# CONFIG_BATTERY_MSM_DEBUG is not set +# CONFIG_BATTERY_MSM8X60 is not set +# CONFIG_SMB137B_CHARGER is not set +# CONFIG_SMB349_CHARGER is not set +# CONFIG_BATTERY_EARLYSUSPEND is not set +# CONFIG_BATTERY_MSM_FAKE is not set +# CONFIG_BATTERY_BQ27520 is not set +# CONFIG_BATTERY_BQ27541 is not set +# CONFIG_LTC4088_CHARGER is not set +# CONFIG_CHARGER_SMB347 is not set +# CONFIG_FORCE_FAST_CHARGE is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS620 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_GPIO_FAN is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_LINEAGE is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4151 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_LM95245 is not set +# CONFIG_SENSORS_MAX16065 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX1668 is not set +# CONFIG_SENSORS_MAX6639 is not set +# CONFIG_SENSORS_MAX6642 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MCP3021 is not set +# CONFIG_SENSORS_NTC_THERMISTOR is not set +CONFIG_SENSORS_MSM_ADC=y +CONFIG_SENSORS_BMA2X2=y +CONFIG_SENSORS_LTR559=y +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_PMBUS is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SHT21 is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_EMC6W201 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SCH56XX_COMMON is not set +# CONFIG_SENSORS_SCH5627 is not set +# CONFIG_SENSORS_SCH5636 is not set +# CONFIG_SENSORS_ADS1015 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF 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_MARIMBA_CORE=y +# CONFIG_MARIMBA_CODEC is not set +# CONFIG_TIMPANI_CODEC 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_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_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_PM8821_IRQ is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_WCD9304_CODEC is not set +# CONFIG_WCD9310_CODEC is not set +# CONFIG_WCD9320_CODEC is not set +# CONFIG_MFD_RC5T583 is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_GPIO is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_REGULATOR_FAN53555=y +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +CONFIG_REGULATOR_ONSEMI_NCP6335D=y +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +CONFIG_REGULATOR_MSM_GPIO=y +# CONFIG_REGULATOR_STUB is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +CONFIG_VIDEO_V4L2_SUBDEV_API=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 is not set +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEOBUF2_MEMOPS=y +CONFIG_VIDEOBUF2_DMA_CONTIG=y +CONFIG_VIDEOBUF2_VMALLOC=y +CONFIG_VIDEOBUF2_DMA_SG=y +CONFIG_VIDEOBUF2_MSM_MEM=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_TLV320AIC23B 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_MT9M032 is not set +# CONFIG_VIDEO_MT9P031 is not set +# CONFIG_VIDEO_MT9T001 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_MT9V032 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set +# CONFIG_VIDEO_NOON010PC30 is not set +# CONFIG_VIDEO_M5MOLS is not set +# CONFIG_VIDEO_S5K6AA is not set + +# +# Flash devices +# +# CONFIG_VIDEO_ADP1653 is not set +# CONFIG_VIDEO_AS3645A is not set + +# +# 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_MSM_VCAP=y +CONFIG_V4L_USB_DRIVERS=y +# CONFIG_USB_VIDEO_CLASS is not set +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +CONFIG_USB_GSPCA=m +# CONFIG_USB_M5602 is not set +# CONFIG_USB_STV06XX is not set +# CONFIG_USB_GL860 is not set +# CONFIG_USB_GSPCA_BENQ is not set +# CONFIG_USB_GSPCA_CONEX is not set +# CONFIG_USB_GSPCA_CPIA1 is not set +# CONFIG_USB_GSPCA_ETOMS is not set +# CONFIG_USB_GSPCA_FINEPIX is not set +# CONFIG_USB_GSPCA_JEILINJ is not set +# CONFIG_USB_GSPCA_JL2005BCD is not set +# CONFIG_USB_GSPCA_KINECT is not set +# CONFIG_USB_GSPCA_KONICA is not set +# CONFIG_USB_GSPCA_MARS is not set +# CONFIG_USB_GSPCA_MR97310A is not set +# CONFIG_USB_GSPCA_NW80X is not set +# CONFIG_USB_GSPCA_OV519 is not set +# CONFIG_USB_GSPCA_OV534 is not set +# CONFIG_USB_GSPCA_OV534_9 is not set +# CONFIG_USB_GSPCA_PAC207 is not set +# CONFIG_USB_GSPCA_PAC7302 is not set +# CONFIG_USB_GSPCA_PAC7311 is not set +# CONFIG_USB_GSPCA_SE401 is not set +# CONFIG_USB_GSPCA_SN9C2028 is not set +# CONFIG_USB_GSPCA_SN9C20X is not set +# CONFIG_USB_GSPCA_SONIXB is not set +# CONFIG_USB_GSPCA_SONIXJ is not set +# CONFIG_USB_GSPCA_SPCA500 is not set +# CONFIG_USB_GSPCA_SPCA501 is not set +# CONFIG_USB_GSPCA_SPCA505 is not set +# CONFIG_USB_GSPCA_SPCA506 is not set +# CONFIG_USB_GSPCA_SPCA508 is not set +# CONFIG_USB_GSPCA_SPCA561 is not set +# CONFIG_USB_GSPCA_SPCA1528 is not set +# CONFIG_USB_GSPCA_SQ905 is not set +# CONFIG_USB_GSPCA_SQ905C is not set +# CONFIG_USB_GSPCA_SQ930X is not set +# CONFIG_USB_GSPCA_STK014 is not set +# CONFIG_USB_GSPCA_STV0680 is not set +# CONFIG_USB_GSPCA_SUNPLUS is not set +# CONFIG_USB_GSPCA_T613 is not set +# CONFIG_USB_GSPCA_TOPRO is not set +# CONFIG_USB_GSPCA_TV8532 is not set +# CONFIG_USB_GSPCA_VC032X is not set +# CONFIG_USB_GSPCA_VICAM is not set +# CONFIG_USB_GSPCA_XIRLINK_CIT is not set +# CONFIG_USB_GSPCA_ZC3XX is not set +# CONFIG_VIDEO_PVRUSB2 is not set +# CONFIG_VIDEO_HDPVR is not set +# CONFIG_VIDEO_EM28XX is not set +# CONFIG_VIDEO_USBVISION is not set +# CONFIG_USB_ET61X251 is not set +# CONFIG_USB_SN9C102 is not set +# CONFIG_USB_PWC is not set +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_S2255 is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +# CONFIG_SOC_CAMERA is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set +CONFIG_MSM_CAMERA_V4L2=y + +# +# Camera Sensor Selection +# +# CONFIG_IMX074 is not set +CONFIG_S5K5CA=y +CONFIG_S5K5CA_DARLING=y +# CONFIG_HI351_SUNNY_Q3H01B is not set +# CONFIG_OV5647 is not set +# CONFIG_OV5647_TRULY_CM6868 is not set +# CONFIG_OV5648_TRULY_CM8352 is not set +# CONFIG_WEBCAM_OV7692_QRD is not set +# CONFIG_MT9M114 is not set +# CONFIG_WEBCAM_OV9726 is not set +# CONFIG_MT9E013 is not set +# CONFIG_IMX074_ACT is not set +# CONFIG_S5K4E1 is not set +# CONFIG_AR0543 is not set +# CONFIG_A8140 is not set +# CONFIG_S5K3H2_SUNNY_Q8S02E is not set +CONFIG_AR0542=y +# CONFIG_MSM_CAMERA_FLASH_SC628A is not set +# CONFIG_MSM_CAMERA_FLASH_TPS61310 is not set +# CONFIG_IMX072 is not set +# CONFIG_OV2720 is not set +CONFIG_OV8825=y +CONFIG_MSM_CAMERA_FLASH=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_ACTUATOR=y +# CONFIG_MSM_EEPROM is not set +# CONFIG_IMX074_EEPROM is not set +# CONFIG_IMX091_EEPROM is not set +CONFIG_QUP_EXCLUSIVE_TO_CAMERA=y +# CONFIG_S5K3L1YX is not set +# CONFIG_IMX091 is not set +# CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE is not set +# CONFIG_OV7692 is not set +# CONFIG_OV7695 is not set +# CONFIG_OV7695_RAW is not set +# CONFIG_MT9V115 is not set +# CONFIG_OV5647_DALING_OVBD631AC is not set +# CONFIG_OV5647_PARTRON_CM500 is not set +# CONFIG_OV5647_HQ_O9B5_DG806T is not set +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +CONFIG_RADIO_ADAPTERS=y +# CONFIG_RADIO_SI470X is not set +# CONFIG_USB_MR800 is not set +# CONFIG_USB_DSBR is not set +# CONFIG_I2C_SI4713 is not set +# CONFIG_RADIO_SI4713 is not set +# CONFIG_USB_KEENE is not set +# CONFIG_RADIO_TEA5764 is not set +# CONFIG_RADIO_SAA7706H is not set +# CONFIG_RADIO_TEF6862 is not set +# CONFIG_RADIO_WL1273 is not set + +# +# Texas Instruments WL128x FM driver (ST based) +# +# CONFIG_RADIO_WL128X is not set +CONFIG_RADIO_TAVARUA=y +# CONFIG_RADIO_IRIS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y +CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_MSM_VIDC_CONTENT_PROTECTION is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_MIPI_OTM9608A_PT=y +# CONFIG_FB_MSM_BACKLIGHT is not set +# CONFIG_FB_MSM_LOGO is not set +CONFIG_FB_MSM_LCDC_HW=y +CONFIG_FB_MSM_TRIPLE_BUFFER=y +CONFIG_FB_MSM_MDP_HW=y +# CONFIG_FB_MSM_MDP22 is not set +CONFIG_FB_MSM_MDP30=y +CONFIG_FB_MSM_MDP303=y +# CONFIG_FB_MSM_MDP31 is not set +# CONFIG_FB_MSM_MDP40 is not set +# CONFIG_FB_MSM_MDSS is not set +# CONFIG_FB_MSM_MDP_NONE is not set +# CONFIG_FB_MSM_EBI2 is not set +# CONFIG_FB_MSM_MDDI is not set +CONFIG_FB_MSM_MIPI_DSI=y +CONFIG_FB_MSM_LCDC=y +# CONFIG_FB_MSM_LVDS is not set +# CONFIG_FB_MSM_EXTMDDI is not set +# CONFIG_FB_MSM_TVOUT is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_COMMON is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_COMMON_VGA is not set +# CONFIG_FB_MSM_MDDI_ORISE is not set +# CONFIG_FB_MSM_MDDI_QUICKVX is not set +# CONFIG_FB_MSM_MDDI_AUTO_DETECT is not set +# CONFIG_FB_MSM_LCDC_AUTO_DETECT is not set +CONFIG_FB_MSM_LCDC_PANEL=y +CONFIG_FB_MSM_MIPI_DSI_TOSHIBA=y +CONFIG_FB_MSM_MIPI_DSI_RENESAS=y +CONFIG_FB_MSM_MIPI_DSI_TRULY=y +CONFIG_FB_MSM_MIPI_DSI_SIMULATOR=y +CONFIG_FB_MSM_MIPI_DSI_NOVATEK=y +CONFIG_FB_MSM_MIPI_DSI_NT35510=y +CONFIG_FB_MSM_MIPI_DSI_HX8389B=y +CONFIG_FB_MSM_MIPI_DSI_HX8392A=y +CONFIG_FB_MSM_MIPI_DSI_OTM9605A=y +CONFIG_FB_MSM_MIPI_DSI_ORISE=y +CONFIG_FB_MSM_MIPI_DSI_NT35516=y +CONFIG_FB_MSM_MIPI_DSI_NT35590=y +CONFIG_FB_MSM_MIPI_DSI_TC358764_DSI2LVDS=y +# CONFIG_FB_MSM_LCDC_ST15_WXGA is not set +# CONFIG_FB_MSM_LCDC_ST15_PANEL is not set +# CONFIG_FB_MSM_LCDC_PRISM_WVGA is not set +# CONFIG_FB_MSM_LCDC_SAMSUNG_WSVGA is not set +# CONFIG_FB_MSM_LCDC_CHIMEI_WXGA is not set +# CONFIG_FB_MSM_LCDC_GORDON_VGA is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_WVGA_PT is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_FWVGA_PT is not set +# CONFIG_FB_MSM_LCDC_SHARP_WVGA_PT is not set +# CONFIG_FB_MSM_LCDC_AUO_WVGA is not set +CONFIG_FB_MSM_LCDC_TRULY_HVGA_IPS3P2335=y +CONFIG_FB_MSM_LCDC_TRULY_HVGA_IPS3P2335_PT_PANEL=y +# CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT is not set +# CONFIG_FB_MSM_LCDC_NT35582_WVGA is not set +# CONFIG_FB_MSM_LCDC_WXGA is not set +# CONFIG_FB_MSM_LVDS_CHIMEI_WXGA is not set +# CONFIG_FB_MSM_LVDS_FRC_FHD is not set +CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT=y +CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT=y +CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA=y +CONFIG_FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT=y +CONFIG_FB_MSM_MIPI_NOVATEK_CMD_QHD_PT=y +CONFIG_FB_MSM_MIPI_ORISE_VIDEO_720P_PT=y +CONFIG_FB_MSM_MIPI_ORISE_CMD_720P_PT=y +CONFIG_FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT=y +CONFIG_FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT=y +CONFIG_FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT=y +CONFIG_FB_MSM_MIPI_NT35510_CMD_WVGA_PT=y +CONFIG_FB_MSM_MIPI_HX8389B_VIDEO_QHD_PT=y +CONFIG_FB_MSM_MIPI_HX8392A_VIDEO_720P_PT=y +CONFIG_FB_MSM_MIPI_OTM9605A_VIDEO_QHD_PT=y +CONFIG_FB_MSM_MIPI_NT35516_VIDEO_QHD_PT=y +CONFIG_FB_MSM_MIPI_NT35516_CMD_QHD_PT=y +CONFIG_FB_MSM_MIPI_NT35590_CMD_720P_PT=y +CONFIG_FB_MSM_MIPI_NT35590_VIDEO_720P_PT=y +# CONFIG_FB_MSM_MIPI_DIV4_WVGA_NT35510_PANEL is not set +CONFIG_FB_MSM_MIPI_CHIMEI_WXGA=y +CONFIG_FB_MSM_MIPI_CHIMEI_WUXGA=y +CONFIG_FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT=y +CONFIG_FB_MSM_MIPI_SIMULATOR_VIDEO=y +# CONFIG_FB_MSM_LCDC_PRISM_WVGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_SAMSUNG_WSVGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_CHIMEI_WXGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_GORDON_VGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_FWVGA_PT_PANEL is not set +# CONFIG_FB_MSM_LCDC_SHARP_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_LCDC_AUO_WVGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_NT35582_PANEL is not set +# CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT_PANEL is not set +# CONFIG_FB_MSM_LVDS_CHIMEI_WXGA_PANEL is not set +# CONFIG_FB_MSM_LVDS_FRC_FHD_PANEL is not set +# CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM is not set +CONFIG_FB_MSM_MIPI_PANEL_DETECT=y +# CONFIG_FB_MSM_MDDI_PANEL_AUTO_DETECT is not set +# CONFIG_FB_MSM_LCDC_PANEL_AUTO_DETECT is not set +# CONFIG_FB_MSM_LCDC_MIPI_PANEL_AUTO_DETECT is not set +# CONFIG_FB_MSM_LVDS_MIPI_PANEL_DETECT is not set +# CONFIG_FB_MSM_MDDI_PRISM_WVGA is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_WVGA_PORTRAIT is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_VGA is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_WVGA is not set +# CONFIG_FB_MSM_MDDI_SHARP_QVGA_128x128 is not set +# CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA_PANEL is not set +# CONFIG_FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NOVATEK_CMD_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_ORISE_VIDEO_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_ORISE_CMD_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_CHIMEI_WXGA_PANEL is not set +# CONFIG_FB_MSM_MIPI_CHIMEI_WUXGA_PANEL is not set +# CONFIG_FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35510_CMD_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_HX8389B_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_HX8392A_VIDEO_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_OTM9605A_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35516_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35516_CMD_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35590_CMD_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35590_VIDEO_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_SIMULATOR_VIDEO_PANEL is not set +# CONFIG_FB_MSM_EBI2_TMD_QVGA_EPSON_QCIF is not set +# CONFIG_FB_MSM_PANEL_NONE is not set +# CONFIG_FB_MSM_EXT_INTERFACE_COMMON is not set +# CONFIG_FB_MSM_HDMI_COMMON is not set +# CONFIG_FB_MSM_HDMI_3D is not set +# CONFIG_FB_MSM_DEFAULT_DEPTH_RGB565 is not set +# CONFIG_FB_MSM_DEFAULT_DEPTH_ARGB8888 is not set +CONFIG_FB_MSM_DEFAULT_DEPTH_RGBA8888=y +# CONFIG_FB_MSM_EBI2_EPSON_S1D_QVGA_PANEL is not set +# CONFIG_FB_MSM_EBI2_PANEL_DETECT is not set +# CONFIG_EXYNOS_VIDEO is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=m +# CONFIG_LCD_PLATFORM is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=y +# CONFIG_BACKLIGHT_ADP8860 is not set +# CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_LP855X is not set +# CONFIG_LOGO 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_RAWMIDI=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER 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_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_SOC=y + +# +# MSM SoC Audio support +# +CONFIG_SND_MSM_DAI_SOC=y +CONFIG_SND_MSM_SOC_MSM7K=y +CONFIG_SND_MSM_SOC=y +# CONFIG_SND_SOC_QDSP6 is not set +# CONFIG_SND_SOC_QDSP6V2 is not set +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +CONFIG_USB_HIDDEV=y + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +# CONFIG_HOLTEK_FF is not set +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_LOGIWHEELS_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +# CONFIG_HID_PICOLCD_FB is not set +# CONFIG_HID_PICOLCD_BACKLIGHT is not set +# CONFIG_HID_PICOLCD_LEDS is not set +CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +# CONFIG_THRUSTMASTER_FF is not set +CONFIG_HID_WACOM=y +# CONFIG_HID_WACOM_POWER_SUPPLY is not set +CONFIG_HID_WIIMOTE=y +CONFIG_HID_WIIMOTE_EXT=y +CONFIG_HID_ZEROPLUS=y +# CONFIG_ZEROPLUS_FF is not set +CONFIG_HID_ZYDACRON=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_SUPPORT_NON_STANDARD_WALL_CHARGER=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=y +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y +# CONFIG_USB_OTG 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_EHCI_HCD=y +CONFIG_USB_EHCI_EHSET=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +# CONFIG_USB_EHCI_MSM is not set +# CONFIG_USB_EHCI_MSM_HSIC is not set +# CONFIG_USB_OXU210HP_HCD is not set +CONFIG_USB_EHCI_MSM_72K=y +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_EHCI_HCD_PLATFORM is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_PEHCI_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=y +# 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=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_ALAUDA=y +CONFIG_USB_STORAGE_ONETOUCH=y +CONFIG_USB_STORAGE_KARMA=y +CONFIG_USB_STORAGE_CYPRESS_ATACB=y +# 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_SISUSBVGA 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_EHSET_TEST_FIXTURE=y +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_QCOM_DIAG_BRIDGE is not set +# CONFIG_USB_QCOM_MDM_BRIDGE is not set +# CONFIG_USB_QCOM_KS_BRIDGE 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=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 + +# +# USB Peripheral Controller +# +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_CI13XXX_MSM is not set +# CONFIG_USB_CI13XXX_MSM_HSIC is not set +# CONFIG_USB_DWC3_MSM is not set +CONFIG_USB_MSM_72K=y +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_GADGET_SUPERSPEED is not set +# 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_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_G_ANDROID=y +# 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 +CONFIG_USB_CSW_HACK=y +# CONFIG_USB_MSC_PROFILING is not set +CONFIG_MODEM_SUPPORT=y +CONFIG_RMNET_SMD_CTL_CHANNEL="DATA40_CNTL" +CONFIG_RMNET_SMD_DATA_CHANNEL="DATA40" +# CONFIG_USB_ANDROID_RMNET_CTRL_SMD is not set + +# +# OTG and related infrastructure +# +CONFIG_USB_OTG_UTILS=y +CONFIG_USB_OTG_WAKELOCK=y +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +CONFIG_USB_MSM_OTG_72K=y +# CONFIG_MSM_OTG_ENABLE_A_WAIT_BCON_TIMEOUT is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG is not set +# CONFIG_USB_MSM_ACA is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +# CONFIG_MMC_EMBEDDED_SDIO is not set +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=32 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +CONFIG_MMC_TEST=m +CONFIG_MMC_BLOCK_TEST=m + +# +# 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_MSM=y +CONFIG_MMC_MSM_RAW=y +CONFIG_MMC_MSM_SDC1_SUPPORT=y +# CONFIG_MMC_MSM_SDC1_8_BIT_SUPPORT is not set +CONFIG_MMC_MSM_SDC2_SUPPORT=y +# CONFIG_MMC_MSM_SDC2_8_BIT_SUPPORT is not set +CONFIG_MMC_MSM_SDC3_SUPPORT=y +CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT=y +# CONFIG_MMC_MSM_SDC3_WP_SUPPORT is not set +# CONFIG_MMC_MSM_SDC4_SUPPORT is not set +# CONFIG_MMC_MSM_SDC5_SUPPORT is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_MSM_PDM=y +CONFIG_LEDS_PMIC_MPP=y +CONFIG_LEDS_MSM_TRICOLOR=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_CPLD 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_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_MSM_PMIC is not set +CONFIG_LEDS_TRICOLOR_FLAHSLIGHT=y +# CONFIG_LEDS_PMIC8029 is not set +# 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 is not set + +# +# LED Triggers +# +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# 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_ALARM=y +CONFIG_RTC_INTF_ALARM_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 +# + +# +# 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_MSM=y +CONFIG_RTC_SECURE_TIME_SUPPORT=y +# CONFIG_RTC_DRV_MSM7X00A 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 +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +CONFIG_STAGING=y +# CONFIG_USBIP_CORE is not set +# CONFIG_PRISM2_USB is not set +# CONFIG_ECHO is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_RTLLIB is not set +# CONFIG_R8712U is not set +# CONFIG_RTS5139 is not set +# CONFIG_TRANZPORT is not set +# CONFIG_LINE6_USB is not set +# CONFIG_VT6656 is not set +# CONFIG_IIO is not set +# CONFIG_SNAPPY_COMPRESS is not set +# CONFIG_SNAPPY_DECOMPRESS is not set +# CONFIG_FB_SM7XX is not set +# CONFIG_ZCACHE is not set +# CONFIG_USB_ENESTORAGE is not set +# CONFIG_BCM_WIMAX is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_STAGING_MEDIA is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +# CONFIG_ANDROID_LOGGER_SYSFS is not set +CONFIG_ANDROID_PERSISTENT_RAM=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y +# CONFIG_ANDROID_SWITCH is not set +# CONFIG_ANDROID_INTF_ALARM_DEV is not set +# CONFIG_PHONE is not set +# CONFIG_USB_WPAN_HCD is not set + +# +# Qualcomm MSM specific device drivers +# +# CONFIG_MSM_SSBI is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y + +# +# Hardware Spinlock drivers +# +CONFIG_IOMMU_SUPPORT=y + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_MOBICORE_SUPPORT is not set +# CONFIG_MSM_QDSS is not set + +# +# NVBK share memory +# +CONFIG_NVBK_SHARE_MEM=y + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_SECURITY is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +CONFIG_EXT4_FS_SECURITY=y +# CONFIG_EXT4_DEBUG is not set +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_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FS_POSIX_ACL=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=y +# CONFIG_CUSE 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_EXFAT_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_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_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_LOGFS 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_F2FS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_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=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 is not set +# CONFIG_DYNAMIC_FSYNC is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_PRINTK_RTC_TIME=y +CONFIG_PRINTK_CPU_ID=y +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# 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=y +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +CONFIG_DEBUG_SPINLOCK=y +# 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=y +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_HIGHMEM is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED 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_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_RCU_CPU_STALL_VERBOSE=y +# CONFIG_RCU_CPU_STALL_INFO is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_KPROBES_SANITY_TEST 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_DEBUG_PER_CPU_MAPS is not set +# CONFIG_LKDTM is not set +# CONFIG_CPU_NOTIFIER_ERROR_INJECT is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_NOP_TRACER=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +CONFIG_KPROBE_EVENT=y +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +CONFIG_DYNAMIC_DEBUG=y +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_ARM_KPROBES_TEST is not set +# CONFIG_PID_IN_CONTEXTIDR is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY=y +# CONFIG_SECURITYFS is not set +CONFIG_SECURITY_NETWORK=y +# CONFIG_SECURITY_NETWORK_XFRM is not set +# CONFIG_SECURITY_PATH is not set +CONFIG_LSM_MMAP_MIN_ADDR=32768 +CONFIG_SECURITY_SELINUX=y +# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set +# CONFIG_SECURITY_SELINUX_DISABLE is not set +CONFIG_SECURITY_SELINUX_DEVELOP=y +CONFIG_SECURITY_SELINUX_FORCE_PERMISSIVE=y +CONFIG_SECURITY_SELINUX_AVC_STATS=y +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_SECURITY_YAMA is not set +# CONFIG_IMA is not set +CONFIG_DEFAULT_SECURITY_SELINUX=y +# CONFIG_DEFAULT_SECURITY_DAC is not set +CONFIG_DEFAULT_SECURITY="selinux" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=m +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_PCRYPT is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# 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=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# 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=y +# CONFIG_CRYPTO_SHA1_ARM is not set +CONFIG_CRYPTO_SHA256=y +# 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_AES_ARM is not set +# CONFIG_CRYPTO_AES_ARM_BS is not set +# 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=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=m +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_CRYPTO_DEV_QCRYPTO is not set +# CONFIG_CRYPTO_DEV_QCE is not set +# CONFIG_CRYPTO_DEV_QCEDEV is not set +# CONFIG_CRYPTO_DEV_OTA_CRYPTO is not set +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_CRC_CCITT=y +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=y +# CONFIG_CRC8 is not set +CONFIG_AUDIT_GENERIC=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_LZ4_COMPRESS=y +CONFIG_LZ4_DECOMPRESS=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_NLATTR=y +# CONFIG_AVERAGE is not set +# CONFIG_CORDIC is not set diff --git "a/arch/arm/configs/weritos_defconfig (\320\272\320\276\320\277\320\270\321\217)" "b/arch/arm/configs/weritos_defconfig (\320\272\320\276\320\277\320\270\321\217)" new file mode 100644 index 00000000..c7c05881 --- /dev/null +++ "b/arch/arm/configs/weritos_defconfig (\320\272\320\276\320\277\320\270\321\217)" @@ -0,0 +1,3482 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.110 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_LOCKBREAK=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=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_FIQ=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_NEED_MACH_IO_H=y +CONFIG_NEED_MACH_MEMORY_H=y +CONFIG_PHYS_OFFSET=0x00200000 +CONFIG_GENERIC_BUG=y +# CONFIG_ARCH_RANDOM is not set +# CONFIG_GENERIC_TIME_VSYSCALL is not set +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y +CONFIG_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="-WERITOS-MM" +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_HAVE_KERNEL_LZ4=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +# CONFIG_KERNEL_LZ4 is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_AUDIT_WATCH=y +CONFIG_AUDIT_TREE=y +# CONFIG_AUDIT_LOGINUID_IMMUTABLE is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_IRQ_DOMAIN=y +# CONFIG_IRQ_DOMAIN_DEBUG is not set + +# +# RCU Subsystem +# +CONFIG_TREE_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_RCU_FAST_NO_HZ is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_RCU_BOOST is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=20 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED=y +# CONFIG_CGROUP_MEM_RES_CTLR_KMEM is not set +# CONFIG_CGROUP_PERF is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_CFS_BANDWIDTH is not set +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +CONFIG_MM_OWNER=y +# 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_RD_LZ4 is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y +# CONFIG_OPTIMIZE_INLINING is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +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_ASHMEM=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=y +CONFIG_TRACEPOINTS=y +CONFIG_KPROBES=y +# CONFIG_JUMP_LABEL is not set +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_KRETPROBES=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_USE_GENERIC_SMP_HELPERS=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BRKPT_RESERVED_RW_ACCESS=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 is not set +# CONFIG_MODULE_FORCE_LOAD_ATH6KL is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_STOP_MACHINE=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# 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=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_BFQ=y +# CONFIG_CGROUP_BFQIO is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_FIOPS=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_ROW=y +CONFIG_IOSCHED_SIO=y +CONFIG_IOSCHED_SIOPLUS=y +CONFIG_IOSCHED_TEST=m +CONFIG_IOSCHED_VR=y +CONFIG_IOSCHED_ZEN=y +# CONFIG_DEFAULT_BFQ is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_FIOPS is not set +# CONFIG_DEFAULT_NOOP is not set +# CONFIG_DEFAULT_ROW is not set +# CONFIG_DEFAULT_SIO is not set +# CONFIG_DEFAULT_SIOPLUS is not set +# CONFIG_DEFAULT_VR is not set +# CONFIG_DEFAULT_ZEN 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_UNINLINE_SPIN_UNLOCK=y +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# 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 is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# 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 is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +CONFIG_MUTEX_SPIN_ON_OWNER=y +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=y +# 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_GPIO_PCA953X is not set + +# +# MSM SoC Type +# +# CONFIG_ARCH_MSM7X01A is not set +# CONFIG_ARCH_MSM7X25 is not set +CONFIG_ARCH_MSM7X27=y +# CONFIG_ARCH_MSM7X30 is not set +# CONFIG_ARCH_QSD8X50 is not set +# CONFIG_ARCH_MSM8X60 is not set +# CONFIG_ARCH_MSM8960 is not set +# CONFIG_ARCH_MSM8930 is not set +# CONFIG_ARCH_APQ8064 is not set +# CONFIG_ARCH_MSM8974 is not set +# CONFIG_ARCH_FSM9XXX is not set +# CONFIG_ARCH_MSM9615 is not set +CONFIG_ARCH_MSM8625=y +# CONFIG_ARCH_MSM9625 is not set +# CONFIG_ARCH_MSM8910 is not set +# CONFIG_MSM_SOC_REV_NONE is not set +CONFIG_MSM_SOC_REV_A=y +CONFIG_MSM_SMP=y +CONFIG_ARCH_MSM_CORTEXMP=y +CONFIG_ARCH_MSM_CORTEX_A5=y +CONFIG_ARCH_MSM7X27A=y +CONFIG_MSM_VIC=y +# CONFIG_MSM_RPM is not set +# CONFIG_MSM_RPM_SMD is not set +# CONFIG_MSM_MPM is not set + +# +# MSM Board Selection +# +# CONFIG_MACH_MSM7X27_SURF is not set +# CONFIG_MACH_MSM7X27_FFA is not set +CONFIG_MACH_MSM7X27A_RUMI3=y +CONFIG_MACH_MSM7X27A_SURF=y +CONFIG_MACH_MSM7X27A_FFA=y +CONFIG_WERITOS_KERNEL=y +CONFIG_MACH_MSM7625A_SURF=y +CONFIG_MACH_MSM7625A_FFA=y +CONFIG_MACH_MSM7627A_QRD1=y +CONFIG_MACH_MSM7627A_QRD3=y +CONFIG_MACH_MSM7627A_EVB=y +CONFIG_MACH_MSM8625_RUMI3=y +CONFIG_MACH_MSM8625_SURF=y +CONFIG_MACH_MSM8625_FFA=y +CONFIG_MACH_MSM8625_EVB=y +CONFIG_MACH_MSM7X27A_QRD5A=y +CONFIG_MACH_MSM8625_QRD7=y +CONFIG_MACH_MSM8625_QRD5=y +CONFIG_MACH_MSM8625Q_EVBD=y +CONFIG_MACH_MSM8625Q_SKUD=y +CONFIG_MACH_MSM8625Q_SKUE=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_MSM_AMSS_VERSION=6225 +# CONFIG_MSM_AMSS_VERSION_6210 is not set +# CONFIG_MSM_AMSS_VERSION_6220 is not set +CONFIG_MSM_AMSS_VERSION_6225=y +# CONFIG_MSM7X00A_USE_GP_TIMER is not set +CONFIG_MSM7X00A_USE_DG_TIMER=y +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=20000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET=1000000000 +CONFIG_CPU_HAS_L2_PMU=y +# CONFIG_HTC_HEADSET is not set +# CONFIG_HTC_PWRSINK is not set +CONFIG_MSM_FIQ_SUPPORT=y +# CONFIG_MSM_SERIAL_DEBUGGER is not set +CONFIG_MSM_PROC_COMM=y +CONFIG_MSM_SMD=y +CONFIG_MSM_AMSS_ENHANCE_DEBUG=y +# CONFIG_MSM_SMD_PKG3 is not set +CONFIG_MSM_SMD_PKG4=y +CONFIG_MSM_FUSE_INFO_DEBUG=y +# CONFIG_MSM_SMD_DEBUG is not set +CONFIG_MSM_FAST_BOOT=y +CONFIG_MSM_N_WAY_SMD=y +CONFIG_MSM_N_WAY_SMSM=y +# CONFIG_MSM_RESET_MODEM is not set +CONFIG_MSM_SMD_LOGGING=y +# CONFIG_MSM_IPC_LOGGING is not set +# CONFIG_MSM_SMD_NMEA is not set +CONFIG_MSM_SMD_TTY=y +# CONFIG_MSM_SMD_QMI is not set +CONFIG_MSM_SMD_PKT=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_IPC_ROUTER=y +# CONFIG_MSM_IPC_ROUTER_SMD_XPRT is not set +CONFIG_MSM_ONCRPCROUTER_DEBUG=y +# CONFIG_MSM_RPC_LOOPBACK_XPRT is not set +# CONFIG_MSM_RPCSERVER_TIME_REMOTE is not set +CONFIG_MSM_RPCSERVER_WATCHDOG=y +# CONFIG_MSM_RPC_WATCHDOG is not set +CONFIG_MSM_RPC_PING=y +CONFIG_MSM_RPC_PROC_COMM_TEST=y +CONFIG_MSM_RPC_OEM_RAPI=y +CONFIG_MSM_RPCSERVER_HANDSET=y +CONFIG_MSM_RMT_STORAGE_CLIENT=y +# CONFIG_MSM_RMT_STORAGE_CLIENT_STATS is not set +CONFIG_MSM_DALRPC=y +CONFIG_MSM_DALRPC_TEST=m +# CONFIG_MSM_CPU_FREQ_SET_MIN_MAX is not set +# CONFIG_MSM_AVS_HW is not set +# CONFIG_MSM_HW3D is not set +CONFIG_MSM_ADSP=y +CONFIG_ADSP_RPC_VER=0x30002 +CONFIG_AMSS_7X25_VERSION_2009=y +# CONFIG_AMSS_7X25_VERSION_2008 is not set +CONFIG_MSM_ADSP_REPORT_EVENTS=y +CONFIG_RTAC=y +CONFIG_MSM7X27A_AUDIO=y +CONFIG_MSM_PROC_COMM_REGULATOR=y +# CONFIG_MSM_VREG_SWITCH_INVERTED is not set +CONFIG_MSM_DMA_TEST=y +# CONFIG_WIFI_CONTROL_FUNC is not set +CONFIG_AUDIO_AAC_PLUS=y +CONFIG_AUDIO_ENHANCED_AAC_PLUS=y +CONFIG_SURF_FFA_GPIO_KEYPAD=y +CONFIG_MSM_SLEEP_TIME_OVERRIDE=y +# CONFIG_MSM_MEMORY_LOW_POWER_MODE is not set +CONFIG_MSM_PM_TIMEOUT_HALT=y +# CONFIG_MSM_PM_TIMEOUT_RESET_MODEM is not set +# CONFIG_MSM_PM_TIMEOUT_RESET_CHIP is not set +CONFIG_MSM_IDLE_WAIT_ON_MODEM=0 +# CONFIG_MSM_PIL is not set +# CONFIG_MSM_SCM is not set +# CONFIG_MSM_TZ_LOG is not set +# CONFIG_MSM_DIRECT_SCLK_ACCESS is not set +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_MODEM_RESTART=y +CONFIG_MSM_PM2=y +CONFIG_MSM_EVENT_TIMER=y +# CONFIG_MSM_BUS_SCALING is not set +# CONFIG_MSM_WATCHDOG_V2 is not set +# CONFIG_MSM_JTAG is not set +CONFIG_MSM_SLEEP_STATS_DEVICE=y +CONFIG_MSM_RUN_QUEUE_STATS=y +CONFIG_MSM_SM_EVENT=y +CONFIG_MSM_SM_EVENT_LOG=m +CONFIG_UNCACHED_BUF=y +CONFIG_MSM_SLEEP_MONITOR=m +# CONFIG_MSM_STANDALONE_POWER_COLLAPSE is not set +# CONFIG_MSM_GSBI9_UART is not set +CONFIG_MSM_SHOW_RESUME_IRQ=y +CONFIG_BT_MSM_PINTEST=y +# CONFIG_MSM_FAKE_BATTERY is not set +# CONFIG_MSM_QDSP6_APR is not set +# CONFIG_MSM_QDSP6_CODECS is not set +# CONFIG_MSM_QDSP6V2_CODECS is not set +# CONFIG_MSM_AUDIO_QDSP6 is not set +# CONFIG_MSM_AUDIO_QDSP6V2 is not set +CONFIG_MSM_RPC_VIBRATOR=y +CONFIG_PM8XXX_RPC_VIBRATOR=y +# CONFIG_MSM_SPM_V1 is not set +CONFIG_MSM_SPM_V2=y +# CONFIG_MSM_L2_SPM is not set +CONFIG_MSM_MULTIMEDIA_USE_ION=y +# CONFIG_MSM_OCMEM is not set +# CONFIG_MSM_RTB is not set +# CONFIG_MSM_EBI_ERP is not set +CONFIG_MSM_CPR=y +CONFIG_MSM_VP_REGULATOR=y +# CONFIG_MSM_CACHE_DUMP is not set +# CONFIG_MSM_HSIC_SYSMON is not set +# CONFIG_MSM_8X60_FUSION_GPIO_GLITCH is not set +CONFIG_MSM_FIQ=y +CONFIG_MSM_PBUS=y +CONFIG_HAS_MACH_MEMUTILS=y +# CONFIG_MSM_OVERCLOCK_GPU is not set +# CONFIG_INTELLI_PLUG is not set +CONFIG_BRICKED_HOTPLUG=y + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +CONFIG_SWP_EMULATE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_CACHE_L2X0=y +CONFIG_CACHE_PL310=y +# CONFIG_CACHE_PL310_ERP is not set +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_ARCH_HAS_BARRIERS=y +# CONFIG_VCM is not set +# CONFIG_STRICT_MEMORY_RWX is not set +CONFIG_ARM_NR_BANKS=8 +# CONFIG_RESERVE_FIRST_PAGE is not set +CONFIG_CPU_HAS_PMU=y +CONFIG_MULTI_IRQ_HANDLER=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_742230 is not set +# CONFIG_ARM_ERRATA_742231 is not set +# CONFIG_PL310_ERRATA_588369 is not set +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_PL310_ERRATA_727915 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_ARM_ERRATA_751472 is not set +# CONFIG_PL310_ERRATA_753970 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_PL310_ERRATA_769419 is not set +# CONFIG_ARM_ERRATA_775420 is not set +CONFIG_ARM_GIC=y +CONFIG_GIC_SECURE=y +# 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_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_HAVE_SMP=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_ARM_CPU_TOPOLOGY=y +# CONFIG_SCHED_MC is not set +# CONFIG_SCHED_SMT is not set +CONFIG_HAVE_ARM_SCU=y +# CONFIG_ARM_ARCH_TIMER is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_NR_CPUS=4 +CONFIG_HOTPLUG_CPU=y +CONFIG_LOCAL_TIMERS=y +CONFIG_ARCH_NR_GPIO=0 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +CONFIG_HZ=300 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HIGHMEM=y +# CONFIG_HIGHPTE is not set +CONFIG_HW_PERF_EVENTS=y +CONFIG_VMALLOC_RESERVE=0xC800000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_CLEANCACHE=y +# CONFIG_DYNAMIC_PAGE_WRITEBACK is not set +# CONFIG_USE_USER_ACCESSIBLE_TIMERS is not set +# CONFIG_ARCH_MEMORY_PROBE is not set +# CONFIG_ARCH_MEMORY_REMOVE is not set +# CONFIG_ARCH_POPULATES_NODE_MAP is not set +# CONFIG_ENABLE_DMM is not set +# CONFIG_FIX_MOVABLE_ZONE is not set +CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0=y +# CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG is not set +# CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set +CONFIG_CP_ACCESS=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="init=/sbin/init root=/dev/ram rw initrd=0x11000000,16M console=ttyDCC0 mem=88M" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASSH3 is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIACTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ADAPTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIMM is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_STOCKDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_YANKDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_LIONHEART is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_HYPER is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMANDX is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVEX is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ALUCARD is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_SMARTASS2=y +CONFIG_CPU_FREQ_GOV_SMARTASSH3=y +# CONFIG_CPU_FREQ_GOV_INTELLIACTIVE is not set +CONFIG_CPU_FREQ_GOV_ADAPTIVE=y +CONFIG_CPU_FREQ_GOV_INTELLIMM=y +# CONFIG_CPU_FREQ_GOV_STOCKDEMAND is not set +# CONFIG_CPU_FREQ_GOV_YANKDEMAND is not set +CONFIG_CPU_FREQ_GOV_INTELLIDEMAND=y +CONFIG_CPU_FREQ_GOV_LIONHEART=y +CONFIG_CPU_FREQ_GOV_HYPER=y +CONFIG_CPU_FREQ_GOV_ONDEMANDX=y +CONFIG_CPU_FREQ_GOV_INTERACTIVEX=y +CONFIG_CPU_FREQ_GOV_ALUCARD=y + +# +# ARM CPU frequency scaling drivers +# +# CONFIG_ARM_EXYNOS4210_CPUFREQ is not set +# CONFIG_ARM_EXYNOS4X12_CPUFREQ is not set +# CONFIG_ARM_EXYNOS5250_CPUFREQ is not set +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y +# CONFIG_KERNEL_MODE_NEON is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +# CONFIG_HAVE_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_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_PM_RUNTIME=y +CONFIG_PM=y +CONFIG_PM_DEBUG=y +# CONFIG_PM_ADVANCED_DEBUG is not set +# CONFIG_PM_TEST_SUSPEND is not set +CONFIG_CAN_PM_TRACE=y +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +CONFIG_SUSPEND_TIME=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +CONFIG_XFRM=y +CONFIG_XFRM_USER=y +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_FIB_TRIE_STATS is not set +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_XFRM_TUNNEL=y +CONFIG_INET_TUNNEL=y +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 is not set +CONFIG_TCP_CONG_ADVANCED=y +# CONFIG_TCP_CONG_BIC is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_TCP_CONG_WESTWOOD=y +# CONFIG_TCP_CONG_HTCP is not set +# CONFIG_TCP_CONG_HSTCP is not set +# CONFIG_TCP_CONG_HYBLA is not set +CONFIG_TCP_CONG_VEGAS=y +# CONFIG_TCP_CONG_SCALABLE is not set +# CONFIG_TCP_CONG_LP is not set +CONFIG_TCP_CONG_VENO=y +# CONFIG_TCP_CONG_YEAH is not set +# CONFIG_TCP_CONG_ILLINOIS is not set +# CONFIG_DEFAULT_CUBIC is not set +# CONFIG_DEFAULT_VEGAS is not set +# CONFIG_DEFAULT_VENO is not set +CONFIG_DEFAULT_WESTWOOD=y +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_TCP_CONG="westwood" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETLABEL is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +CONFIG_NETWORK_SECMARK=y +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +# CONFIG_NETFILTER_NETLINK_ACCT is not set +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_PROCFS=y +CONFIG_NF_CONNTRACK_EVENTS=y +# CONFIG_NF_CONNTRACK_TIMEOUT is not set +# CONFIG_NF_CONNTRACK_TIMESTAMP is not set +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_BROADCAST=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +# CONFIG_NF_CONNTRACK_SNMP is not set +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +# CONFIG_NF_CT_NETLINK_TIMEOUT is not set +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +# CONFIG_NETFILTER_XT_TARGET_HL is not set +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +CONFIG_NETFILTER_XT_MATCH_ECN=y +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_SET is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +# CONFIG_IP_NF_MATCH_RPFILTER is not set +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +# CONFIG_IP_NF_TARGET_REJECT_SKERR is not set +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +# CONFIG_NF_CONNTRACK_IPV6 is not set +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=y +# CONFIG_IP6_NF_MATCH_AH is not set +# CONFIG_IP6_NF_MATCH_EUI64 is not set +# CONFIG_IP6_NF_MATCH_FRAG is not set +# CONFIG_IP6_NF_MATCH_OPTS is not set +# CONFIG_IP6_NF_MATCH_HL is not set +# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set +# CONFIG_IP6_NF_MATCH_MH is not set +# CONFIG_IP6_NF_MATCH_RPFILTER is not set +# CONFIG_IP6_NF_MATCH_RT is not set +# CONFIG_IP6_NF_TARGET_HL is not set +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +# CONFIG_IP6_NF_TARGET_REJECT_SKERR is not set +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_IP6_NF_SECURITY=y +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +CONFIG_L2TP=y +CONFIG_L2TP_DEBUGFS=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA 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_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFB is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +# CONFIG_NET_SCH_MQPRIO is not set +# CONFIG_NET_SCH_CHOKE is not set +# CONFIG_NET_SCH_QFQ is not set +# CONFIG_NET_SCH_INGRESS is not set +# CONFIG_NET_SCH_PLUG is not set + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +# CONFIG_NET_ACT_POLICE is not set +# CONFIG_NET_ACT_GACT is not set +# CONFIG_NET_ACT_MIRRED is not set +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +# CONFIG_NETPRIO_CGROUP 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_NET_TCPPROBE is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCISMD is not set +# CONFIG_BT_HCIBTUSB is not set +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +CONFIG_BT_HCIUART_IBS=y +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBPA10X is not set +CONFIG_BT_MSM_SLEEP=y +# CONFIG_BT_HCIBFUSB is not set +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +CONFIG_MSM_BT_POWER=y +CONFIG_HUAWEI_BT_WCN2243=y +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +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=m +CONFIG_NL80211_TESTMODE=y +# 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 is not set +# CONFIG_LIB80211 is not set +# CONFIG_CFG80211_ALLOW_RECONNECT is not set +# CONFIG_MAC80211 is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_REGULATOR is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +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_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SW_SYNC_USER=y +# CONFIG_CMA is not set +# CONFIG_CONNECTOR is not set +# CONFIG_MTD 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=8192 +# 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 +# CONFIG_BLK_DEV_RBD is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ATMEL_PWM is not set +# CONFIG_ICS932S401 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_DS1682 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +# CONFIG_APANIC is not set +CONFIG_APANIC_MMC=y +CONFIG_APANIC_PLABEL="kpanic" +# CONFIG_TSIF is not set +CONFIG_GLANCEVIEW=y +# CONFIG_TSPP is not set +# CONFIG_HAPTIC_ISA1200 is not set +# CONFIG_QSEECOM is not set +# CONFIG_QFP_FUSE is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST 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_TGT=y +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# 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=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +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=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_ISCSI_BOOT_SYSFS is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM_BUILTIN=y +CONFIG_BLK_DEV_DM=y +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_THIN_PROVISIONING is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +# CONFIG_DM_FLAKEY is not set +# CONFIG_DM_VERITY 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=y +# CONFIG_IFB is not set +# CONFIG_NET_TEAM is not set +# CONFIG_MACVLAN is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_BROADCOM=y +# CONFIG_B44 is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +CONFIG_NET_VENDOR_CHELSIO=y +CONFIG_NET_VENDOR_CIRRUS=y +# CONFIG_CS89x0 is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +CONFIG_NET_VENDOR_FARADAY=y +# CONFIG_FTMAC100 is not set +# CONFIG_FTGMAC100 is not set +CONFIG_NET_VENDOR_INTEL=y +CONFIG_NET_VENDOR_I825XX=y +CONFIG_NET_VENDOR_MARVELL=y +CONFIG_NET_VENDOR_MICREL=y +# CONFIG_KS8851_MLL is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_QFEC is not set +CONFIG_NET_VENDOR_NATSEMI=y +CONFIG_NET_VENDOR_8390=y +# CONFIG_AX88796 is not set +# CONFIG_ETHOC is not set +CONFIG_NET_VENDOR_SEEQ=y +# CONFIG_SEEQ8005 is not set +CONFIG_NET_VENDOR_SMSC=y +CONFIG_SMC91X=y +# CONFIG_SMC911X is not set +CONFIG_SMSC911X=y +# CONFIG_SMSC911X_ARCH_HOOKS is not set +CONFIG_NET_VENDOR_STMICRO=y +# CONFIG_STMMAC_ETH is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_AMD_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPPOL2TP=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=y +CONFIG_USB_NET_CDCETHER=y +# CONFIG_USB_NET_CDC_EEM is not set +CONFIG_USB_NET_CDC_NCM=y +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_SMSC75XX is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_GL620A is not set +CONFIG_USB_NET_NET1080=y +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_MCS7830 is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +CONFIG_USB_NET_CDC_SUBSET=y +# CONFIG_USB_ALI_M5632 is not set +# CONFIG_USB_AN2720 is not set +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_KC2190 is not set +CONFIG_USB_NET_ZAURUS=y +# CONFIG_USB_NET_CX82310_ETH is not set +# CONFIG_USB_NET_KALMIA is not set +# CONFIG_USB_NET_QMI_WWAN is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_USB_SIERRA_NET is not set +# CONFIG_USB_VL600 is not set +# CONFIG_MSM_RMNET_USB is not set +CONFIG_WLAN=y +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +CONFIG_LIBRA_SDIOIF=m +# CONFIG_ATH6K_LEGACY_EXT is not set +CONFIG_ATH_COMMON=m +# CONFIG_ATH_DEBUG is not set +CONFIG_ATH6KL=m +CONFIG_ATH6KL_SDIO=m +# CONFIG_WIFI_MAC_FROM_NV is not set +# CONFIG_ATH6KL_USB is not set +# CONFIG_ATH6KL_DEBUG is not set +# CONFIG_ATH6KL_REGDOMAIN is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_IWM is not set +# CONFIG_LIBERTAS 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=y +# 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=m +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +# CONFIG_JOYSTICK_ANALOG is not set +# CONFIG_JOYSTICK_A3D is not set +# CONFIG_JOYSTICK_ADI is not set +# CONFIG_JOYSTICK_COBRA is not set +# CONFIG_JOYSTICK_GF2K is not set +# CONFIG_JOYSTICK_GRIP is not set +# CONFIG_JOYSTICK_GRIP_MP is not set +# CONFIG_JOYSTICK_GUILLEMOT is not set +# CONFIG_JOYSTICK_INTERACT is not set +# CONFIG_JOYSTICK_SIDEWINDER is not set +# CONFIG_JOYSTICK_TMDC is not set +# CONFIG_JOYSTICK_IFORCE is not set +# CONFIG_JOYSTICK_WARRIOR is not set +# CONFIG_JOYSTICK_MAGELLAN is not set +# CONFIG_JOYSTICK_SPACEORB is not set +# CONFIG_JOYSTICK_SPACEBALL is not set +# CONFIG_JOYSTICK_STINGER is not set +# CONFIG_JOYSTICK_TWIDJOY is not set +# CONFIG_JOYSTICK_ZHENHUA is not set +# CONFIG_JOYSTICK_AS5011 is not set +# CONFIG_JOYSTICK_JOYDUMP is not set +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +# CONFIG_TOUCHDISC_VTD518_SHINETSU is not set +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_TABLET_USB_WACOM=y +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_FOCAL is not set +# CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set +# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_EGALAX is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_ILI210X is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MAX11801 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C=y +CONFIG_SYNA_MULTI_TOUCH=y +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_PIXCIR is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC_SERIO is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_MSM_LEGACY is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +# CONFIG_TOUCHSCREEN_CY8C_TS is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC is not set +# CONFIG_TOUCHSCREEN_FT5X06 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set +CONFIG_TOUCHSCREEN_FT6306=y +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_BMA150 is not set +# CONFIG_INPUT_MMA8450 is not set +# CONFIG_INPUT_MPU3050 is not set +# CONFIG_INPUT_GP2A is not set +# CONFIG_INPUT_GPIO_TILT_POLLED is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_KXTJ9 is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_ISA1200_FF_MEMLESS is not set +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_BOSCH_BMA150 is not set +# CONFIG_INPUT_LTR502 is not set +# CONFIG_INPUT_LTR558 is not set +# CONFIG_BOSCH_BMA250 is not set +# CONFIG_AVAGO_APDS990X is not set +# CONFIG_INPUT_ISL29028 is not set +# CONFIG_INPUT_LIS3DH is not set +# CONFIG_SENSORS_TMD2771X 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=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_N_SMUX 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_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_MSM_HS=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +# CONFIG_SERIAL_MSM_HSL is not set +# CONFIG_SERIAL_BCM_BT_LPM is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_MSM_SMD is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set + +# +# Diag Support +# +CONFIG_DIAG_CHAR=y + +# +# DIAG traffic over USB +# +CONFIG_DIAG_OVER_USB=y + +# +# SDIO support for DIAG +# + +# +# HSIC support for DIAG +# +# 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_MMC_GENERIC_CSDIO is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_ALGOBIT=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +CONFIG_I2C_GPIO=y +# CONFIG_I2C_MSM is not set +CONFIG_I2C_QUP=y +# CONFIG_I2C_OCORES 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_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SPI is not set +# CONFIG_SPMI is not set +# CONFIG_SLIMBUS is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# 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=y +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set +CONFIG_GPIO_MSM_V1=y +# CONFIG_GPIO_MSM_V2 is not set +# CONFIG_GPIO_MSM_V3 is not set +# CONFIG_GPIO_FSM9XXX 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=y +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MCP23S08 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_GPIO_PM8XXX_RPC is not set +# 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_CHARGER_ISP1704 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_MANAGER is not set +CONFIG_BATTERY_MSM=y +# CONFIG_BATTERY_MSM_DEBUG is not set +# CONFIG_BATTERY_MSM8X60 is not set +# CONFIG_SMB137B_CHARGER is not set +# CONFIG_SMB349_CHARGER is not set +# CONFIG_BATTERY_EARLYSUSPEND is not set +# CONFIG_BATTERY_MSM_FAKE is not set +# CONFIG_BATTERY_BQ27520 is not set +# CONFIG_BATTERY_BQ27541 is not set +# CONFIG_LTC4088_CHARGER is not set +# CONFIG_CHARGER_SMB347 is not set +# CONFIG_FORCE_FAST_CHARGE is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS620 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_GPIO_FAN is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_LINEAGE is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4151 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_LM95245 is not set +# CONFIG_SENSORS_MAX16065 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX1668 is not set +# CONFIG_SENSORS_MAX6639 is not set +# CONFIG_SENSORS_MAX6642 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MCP3021 is not set +# CONFIG_SENSORS_NTC_THERMISTOR is not set +CONFIG_SENSORS_MSM_ADC=y +CONFIG_SENSORS_BMA2X2=y +CONFIG_SENSORS_LTR559=y +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_PMBUS is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SHT21 is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_EMC6W201 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SCH56XX_COMMON is not set +# CONFIG_SENSORS_SCH5627 is not set +# CONFIG_SENSORS_SCH5636 is not set +# CONFIG_SENSORS_ADS1015 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF 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_MARIMBA_CORE=y +# CONFIG_MARIMBA_CODEC is not set +# CONFIG_TIMPANI_CODEC 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_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_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_PM8821_IRQ is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_WCD9304_CODEC is not set +# CONFIG_WCD9310_CODEC is not set +# CONFIG_WCD9320_CODEC is not set +# CONFIG_MFD_RC5T583 is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_GPIO is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_REGULATOR_FAN53555=y +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +CONFIG_REGULATOR_ONSEMI_NCP6335D=y +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +CONFIG_REGULATOR_MSM_GPIO=y +# CONFIG_REGULATOR_STUB is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +CONFIG_VIDEO_V4L2_SUBDEV_API=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 is not set +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEOBUF2_MEMOPS=y +CONFIG_VIDEOBUF2_DMA_CONTIG=y +CONFIG_VIDEOBUF2_VMALLOC=y +CONFIG_VIDEOBUF2_DMA_SG=y +CONFIG_VIDEOBUF2_MSM_MEM=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_TLV320AIC23B 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_MT9M032 is not set +# CONFIG_VIDEO_MT9P031 is not set +# CONFIG_VIDEO_MT9T001 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_MT9V032 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set +# CONFIG_VIDEO_NOON010PC30 is not set +# CONFIG_VIDEO_M5MOLS is not set +# CONFIG_VIDEO_S5K6AA is not set + +# +# Flash devices +# +# CONFIG_VIDEO_ADP1653 is not set +# CONFIG_VIDEO_AS3645A is not set + +# +# 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_MSM_VCAP=y +CONFIG_V4L_USB_DRIVERS=y +# CONFIG_USB_VIDEO_CLASS is not set +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +CONFIG_USB_GSPCA=m +# CONFIG_USB_M5602 is not set +# CONFIG_USB_STV06XX is not set +# CONFIG_USB_GL860 is not set +# CONFIG_USB_GSPCA_BENQ is not set +# CONFIG_USB_GSPCA_CONEX is not set +# CONFIG_USB_GSPCA_CPIA1 is not set +# CONFIG_USB_GSPCA_ETOMS is not set +# CONFIG_USB_GSPCA_FINEPIX is not set +# CONFIG_USB_GSPCA_JEILINJ is not set +# CONFIG_USB_GSPCA_JL2005BCD is not set +# CONFIG_USB_GSPCA_KINECT is not set +# CONFIG_USB_GSPCA_KONICA is not set +# CONFIG_USB_GSPCA_MARS is not set +# CONFIG_USB_GSPCA_MR97310A is not set +# CONFIG_USB_GSPCA_NW80X is not set +# CONFIG_USB_GSPCA_OV519 is not set +# CONFIG_USB_GSPCA_OV534 is not set +# CONFIG_USB_GSPCA_OV534_9 is not set +# CONFIG_USB_GSPCA_PAC207 is not set +# CONFIG_USB_GSPCA_PAC7302 is not set +# CONFIG_USB_GSPCA_PAC7311 is not set +# CONFIG_USB_GSPCA_SE401 is not set +# CONFIG_USB_GSPCA_SN9C2028 is not set +# CONFIG_USB_GSPCA_SN9C20X is not set +# CONFIG_USB_GSPCA_SONIXB is not set +# CONFIG_USB_GSPCA_SONIXJ is not set +# CONFIG_USB_GSPCA_SPCA500 is not set +# CONFIG_USB_GSPCA_SPCA501 is not set +# CONFIG_USB_GSPCA_SPCA505 is not set +# CONFIG_USB_GSPCA_SPCA506 is not set +# CONFIG_USB_GSPCA_SPCA508 is not set +# CONFIG_USB_GSPCA_SPCA561 is not set +# CONFIG_USB_GSPCA_SPCA1528 is not set +# CONFIG_USB_GSPCA_SQ905 is not set +# CONFIG_USB_GSPCA_SQ905C is not set +# CONFIG_USB_GSPCA_SQ930X is not set +# CONFIG_USB_GSPCA_STK014 is not set +# CONFIG_USB_GSPCA_STV0680 is not set +# CONFIG_USB_GSPCA_SUNPLUS is not set +# CONFIG_USB_GSPCA_T613 is not set +# CONFIG_USB_GSPCA_TOPRO is not set +# CONFIG_USB_GSPCA_TV8532 is not set +# CONFIG_USB_GSPCA_VC032X is not set +# CONFIG_USB_GSPCA_VICAM is not set +# CONFIG_USB_GSPCA_XIRLINK_CIT is not set +# CONFIG_USB_GSPCA_ZC3XX is not set +# CONFIG_VIDEO_PVRUSB2 is not set +# CONFIG_VIDEO_HDPVR is not set +# CONFIG_VIDEO_EM28XX is not set +# CONFIG_VIDEO_USBVISION is not set +# CONFIG_USB_ET61X251 is not set +# CONFIG_USB_SN9C102 is not set +# CONFIG_USB_PWC is not set +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_S2255 is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +# CONFIG_SOC_CAMERA is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set +CONFIG_MSM_CAMERA_V4L2=y + +# +# Camera Sensor Selection +# +# CONFIG_IMX074 is not set +CONFIG_S5K5CA=y +CONFIG_S5K5CA_DARLING=y +# CONFIG_HI351_SUNNY_Q3H01B is not set +# CONFIG_OV5647 is not set +# CONFIG_OV5647_TRULY_CM6868 is not set +# CONFIG_OV5648_TRULY_CM8352 is not set +# CONFIG_WEBCAM_OV7692_QRD is not set +# CONFIG_MT9M114 is not set +# CONFIG_WEBCAM_OV9726 is not set +# CONFIG_MT9E013 is not set +# CONFIG_IMX074_ACT is not set +# CONFIG_S5K4E1 is not set +# CONFIG_AR0543 is not set +# CONFIG_A8140 is not set +# CONFIG_S5K3H2_SUNNY_Q8S02E is not set +CONFIG_AR0542=y +# CONFIG_MSM_CAMERA_FLASH_SC628A is not set +# CONFIG_MSM_CAMERA_FLASH_TPS61310 is not set +# CONFIG_IMX072 is not set +# CONFIG_OV2720 is not set +CONFIG_OV8825=y +CONFIG_MSM_CAMERA_FLASH=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_ACTUATOR=y +# CONFIG_MSM_EEPROM is not set +# CONFIG_IMX074_EEPROM is not set +# CONFIG_IMX091_EEPROM is not set +CONFIG_QUP_EXCLUSIVE_TO_CAMERA=y +# CONFIG_S5K3L1YX is not set +# CONFIG_IMX091 is not set +# CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE is not set +# CONFIG_OV7692 is not set +# CONFIG_OV7695 is not set +# CONFIG_OV7695_RAW is not set +# CONFIG_MT9V115 is not set +# CONFIG_OV5647_DALING_OVBD631AC is not set +# CONFIG_OV5647_PARTRON_CM500 is not set +# CONFIG_OV5647_HQ_O9B5_DG806T is not set +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +CONFIG_RADIO_ADAPTERS=y +# CONFIG_RADIO_SI470X is not set +# CONFIG_USB_MR800 is not set +# CONFIG_USB_DSBR is not set +# CONFIG_I2C_SI4713 is not set +# CONFIG_RADIO_SI4713 is not set +# CONFIG_USB_KEENE is not set +# CONFIG_RADIO_TEA5764 is not set +# CONFIG_RADIO_SAA7706H is not set +# CONFIG_RADIO_TEF6862 is not set +# CONFIG_RADIO_WL1273 is not set + +# +# Texas Instruments WL128x FM driver (ST based) +# +# CONFIG_RADIO_WL128X is not set +CONFIG_RADIO_TAVARUA=y +# CONFIG_RADIO_IRIS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y +CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_MSM_VIDC_CONTENT_PROTECTION is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_MIPI_OTM9608A_PT=y +# CONFIG_FB_MSM_BACKLIGHT is not set +# CONFIG_FB_MSM_LOGO is not set +CONFIG_FB_MSM_LCDC_HW=y +CONFIG_FB_MSM_TRIPLE_BUFFER=y +CONFIG_FB_MSM_MDP_HW=y +# CONFIG_FB_MSM_MDP22 is not set +CONFIG_FB_MSM_MDP30=y +CONFIG_FB_MSM_MDP303=y +# CONFIG_FB_MSM_MDP31 is not set +# CONFIG_FB_MSM_MDP40 is not set +# CONFIG_FB_MSM_MDSS is not set +# CONFIG_FB_MSM_MDP_NONE is not set +# CONFIG_FB_MSM_EBI2 is not set +# CONFIG_FB_MSM_MDDI is not set +CONFIG_FB_MSM_MIPI_DSI=y +CONFIG_FB_MSM_LCDC=y +# CONFIG_FB_MSM_LVDS is not set +# CONFIG_FB_MSM_EXTMDDI is not set +# CONFIG_FB_MSM_TVOUT is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_COMMON is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_COMMON_VGA is not set +# CONFIG_FB_MSM_MDDI_ORISE is not set +# CONFIG_FB_MSM_MDDI_QUICKVX is not set +# CONFIG_FB_MSM_MDDI_AUTO_DETECT is not set +# CONFIG_FB_MSM_LCDC_AUTO_DETECT is not set +CONFIG_FB_MSM_LCDC_PANEL=y +CONFIG_FB_MSM_MIPI_DSI_TOSHIBA=y +CONFIG_FB_MSM_MIPI_DSI_RENESAS=y +CONFIG_FB_MSM_MIPI_DSI_TRULY=y +CONFIG_FB_MSM_MIPI_DSI_SIMULATOR=y +CONFIG_FB_MSM_MIPI_DSI_NOVATEK=y +CONFIG_FB_MSM_MIPI_DSI_NT35510=y +CONFIG_FB_MSM_MIPI_DSI_HX8389B=y +CONFIG_FB_MSM_MIPI_DSI_HX8392A=y +CONFIG_FB_MSM_MIPI_DSI_OTM9605A=y +CONFIG_FB_MSM_MIPI_DSI_ORISE=y +CONFIG_FB_MSM_MIPI_DSI_NT35516=y +CONFIG_FB_MSM_MIPI_DSI_NT35590=y +CONFIG_FB_MSM_MIPI_DSI_TC358764_DSI2LVDS=y +# CONFIG_FB_MSM_LCDC_ST15_WXGA is not set +# CONFIG_FB_MSM_LCDC_ST15_PANEL is not set +# CONFIG_FB_MSM_LCDC_PRISM_WVGA is not set +# CONFIG_FB_MSM_LCDC_SAMSUNG_WSVGA is not set +# CONFIG_FB_MSM_LCDC_CHIMEI_WXGA is not set +# CONFIG_FB_MSM_LCDC_GORDON_VGA is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_WVGA_PT is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_FWVGA_PT is not set +# CONFIG_FB_MSM_LCDC_SHARP_WVGA_PT is not set +# CONFIG_FB_MSM_LCDC_AUO_WVGA is not set +CONFIG_FB_MSM_LCDC_TRULY_HVGA_IPS3P2335=y +CONFIG_FB_MSM_LCDC_TRULY_HVGA_IPS3P2335_PT_PANEL=y +# CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT is not set +# CONFIG_FB_MSM_LCDC_NT35582_WVGA is not set +# CONFIG_FB_MSM_LCDC_WXGA is not set +# CONFIG_FB_MSM_LVDS_CHIMEI_WXGA is not set +# CONFIG_FB_MSM_LVDS_FRC_FHD is not set +CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT=y +CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT=y +CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA=y +CONFIG_FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT=y +CONFIG_FB_MSM_MIPI_NOVATEK_CMD_QHD_PT=y +CONFIG_FB_MSM_MIPI_ORISE_VIDEO_720P_PT=y +CONFIG_FB_MSM_MIPI_ORISE_CMD_720P_PT=y +CONFIG_FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT=y +CONFIG_FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT=y +CONFIG_FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT=y +CONFIG_FB_MSM_MIPI_NT35510_CMD_WVGA_PT=y +CONFIG_FB_MSM_MIPI_HX8389B_VIDEO_QHD_PT=y +CONFIG_FB_MSM_MIPI_HX8392A_VIDEO_720P_PT=y +CONFIG_FB_MSM_MIPI_OTM9605A_VIDEO_QHD_PT=y +CONFIG_FB_MSM_MIPI_NT35516_VIDEO_QHD_PT=y +CONFIG_FB_MSM_MIPI_NT35516_CMD_QHD_PT=y +CONFIG_FB_MSM_MIPI_NT35590_CMD_720P_PT=y +CONFIG_FB_MSM_MIPI_NT35590_VIDEO_720P_PT=y +# CONFIG_FB_MSM_MIPI_DIV4_WVGA_NT35510_PANEL is not set +CONFIG_FB_MSM_MIPI_CHIMEI_WXGA=y +CONFIG_FB_MSM_MIPI_CHIMEI_WUXGA=y +CONFIG_FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT=y +CONFIG_FB_MSM_MIPI_SIMULATOR_VIDEO=y +# CONFIG_FB_MSM_LCDC_PRISM_WVGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_SAMSUNG_WSVGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_CHIMEI_WXGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_GORDON_VGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_FWVGA_PT_PANEL is not set +# CONFIG_FB_MSM_LCDC_SHARP_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_LCDC_AUO_WVGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_NT35582_PANEL is not set +# CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT_PANEL is not set +# CONFIG_FB_MSM_LVDS_CHIMEI_WXGA_PANEL is not set +# CONFIG_FB_MSM_LVDS_FRC_FHD_PANEL is not set +# CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM is not set +CONFIG_FB_MSM_MIPI_PANEL_DETECT=y +# CONFIG_FB_MSM_MDDI_PANEL_AUTO_DETECT is not set +# CONFIG_FB_MSM_LCDC_PANEL_AUTO_DETECT is not set +# CONFIG_FB_MSM_LCDC_MIPI_PANEL_AUTO_DETECT is not set +# CONFIG_FB_MSM_LVDS_MIPI_PANEL_DETECT is not set +# CONFIG_FB_MSM_MDDI_PRISM_WVGA is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_WVGA_PORTRAIT is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_VGA is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_WVGA is not set +# CONFIG_FB_MSM_MDDI_SHARP_QVGA_128x128 is not set +# CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA_PANEL is not set +# CONFIG_FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NOVATEK_CMD_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_ORISE_VIDEO_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_ORISE_CMD_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_CHIMEI_WXGA_PANEL is not set +# CONFIG_FB_MSM_MIPI_CHIMEI_WUXGA_PANEL is not set +# CONFIG_FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35510_CMD_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_HX8389B_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_HX8392A_VIDEO_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_OTM9605A_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35516_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35516_CMD_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35590_CMD_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35590_VIDEO_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_SIMULATOR_VIDEO_PANEL is not set +# CONFIG_FB_MSM_EBI2_TMD_QVGA_EPSON_QCIF is not set +# CONFIG_FB_MSM_PANEL_NONE is not set +# CONFIG_FB_MSM_EXT_INTERFACE_COMMON is not set +# CONFIG_FB_MSM_HDMI_COMMON is not set +# CONFIG_FB_MSM_HDMI_3D is not set +# CONFIG_FB_MSM_DEFAULT_DEPTH_RGB565 is not set +# CONFIG_FB_MSM_DEFAULT_DEPTH_ARGB8888 is not set +CONFIG_FB_MSM_DEFAULT_DEPTH_RGBA8888=y +# CONFIG_FB_MSM_EBI2_EPSON_S1D_QVGA_PANEL is not set +# CONFIG_FB_MSM_EBI2_PANEL_DETECT is not set +# CONFIG_EXYNOS_VIDEO is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=m +# CONFIG_LCD_PLATFORM is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=y +# CONFIG_BACKLIGHT_ADP8860 is not set +# CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_LP855X is not set +# CONFIG_LOGO 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_RAWMIDI=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER 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_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_SOC=y + +# +# MSM SoC Audio support +# +CONFIG_SND_MSM_DAI_SOC=y +CONFIG_SND_MSM_SOC_MSM7K=y +CONFIG_SND_MSM_SOC=y +# CONFIG_SND_SOC_QDSP6 is not set +# CONFIG_SND_SOC_QDSP6V2 is not set +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +CONFIG_USB_HIDDEV=y + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +# CONFIG_HOLTEK_FF is not set +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_LOGIWHEELS_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +# CONFIG_HID_PICOLCD_FB is not set +# CONFIG_HID_PICOLCD_BACKLIGHT is not set +# CONFIG_HID_PICOLCD_LEDS is not set +CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +# CONFIG_THRUSTMASTER_FF is not set +CONFIG_HID_WACOM=y +# CONFIG_HID_WACOM_POWER_SUPPLY is not set +CONFIG_HID_WIIMOTE=y +CONFIG_HID_WIIMOTE_EXT=y +CONFIG_HID_ZEROPLUS=y +# CONFIG_ZEROPLUS_FF is not set +CONFIG_HID_ZYDACRON=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_SUPPORT_NON_STANDARD_WALL_CHARGER=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=y +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y +# CONFIG_USB_OTG 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_EHCI_HCD=y +CONFIG_USB_EHCI_EHSET=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +# CONFIG_USB_EHCI_MSM is not set +# CONFIG_USB_EHCI_MSM_HSIC is not set +# CONFIG_USB_OXU210HP_HCD is not set +CONFIG_USB_EHCI_MSM_72K=y +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_EHCI_HCD_PLATFORM is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_PEHCI_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=y +# 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=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_ALAUDA=y +CONFIG_USB_STORAGE_ONETOUCH=y +CONFIG_USB_STORAGE_KARMA=y +CONFIG_USB_STORAGE_CYPRESS_ATACB=y +# 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_SISUSBVGA 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_EHSET_TEST_FIXTURE=y +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_QCOM_DIAG_BRIDGE is not set +# CONFIG_USB_QCOM_MDM_BRIDGE is not set +# CONFIG_USB_QCOM_KS_BRIDGE 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=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 + +# +# USB Peripheral Controller +# +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_CI13XXX_MSM is not set +# CONFIG_USB_CI13XXX_MSM_HSIC is not set +# CONFIG_USB_DWC3_MSM is not set +CONFIG_USB_MSM_72K=y +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_GADGET_SUPERSPEED is not set +# 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_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_G_ANDROID=y +# 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 +CONFIG_USB_CSW_HACK=y +# CONFIG_USB_MSC_PROFILING is not set +CONFIG_MODEM_SUPPORT=y +CONFIG_RMNET_SMD_CTL_CHANNEL="DATA40_CNTL" +CONFIG_RMNET_SMD_DATA_CHANNEL="DATA40" +# CONFIG_USB_ANDROID_RMNET_CTRL_SMD is not set + +# +# OTG and related infrastructure +# +CONFIG_USB_OTG_UTILS=y +CONFIG_USB_OTG_WAKELOCK=y +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +CONFIG_USB_MSM_OTG_72K=y +# CONFIG_MSM_OTG_ENABLE_A_WAIT_BCON_TIMEOUT is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG is not set +# CONFIG_USB_MSM_ACA is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +# CONFIG_MMC_EMBEDDED_SDIO is not set +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=32 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +CONFIG_MMC_TEST=m +CONFIG_MMC_BLOCK_TEST=m + +# +# 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_MSM=y +CONFIG_MMC_MSM_RAW=y +CONFIG_MMC_MSM_SDC1_SUPPORT=y +# CONFIG_MMC_MSM_SDC1_8_BIT_SUPPORT is not set +CONFIG_MMC_MSM_SDC2_SUPPORT=y +# CONFIG_MMC_MSM_SDC2_8_BIT_SUPPORT is not set +CONFIG_MMC_MSM_SDC3_SUPPORT=y +CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT=y +# CONFIG_MMC_MSM_SDC3_WP_SUPPORT is not set +# CONFIG_MMC_MSM_SDC4_SUPPORT is not set +# CONFIG_MMC_MSM_SDC5_SUPPORT is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_MSM_PDM=y +CONFIG_LEDS_PMIC_MPP=y +CONFIG_LEDS_MSM_TRICOLOR=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_CPLD 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_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_MSM_PMIC is not set +CONFIG_LEDS_TRICOLOR_FLAHSLIGHT=y +# CONFIG_LEDS_PMIC8029 is not set +# 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 is not set + +# +# LED Triggers +# +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# 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_ALARM=y +CONFIG_RTC_INTF_ALARM_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 +# + +# +# 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_MSM=y +CONFIG_RTC_SECURE_TIME_SUPPORT=y +# CONFIG_RTC_DRV_MSM7X00A 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 +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +CONFIG_STAGING=y +# CONFIG_USBIP_CORE is not set +# CONFIG_PRISM2_USB is not set +# CONFIG_ECHO is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_RTLLIB is not set +# CONFIG_R8712U is not set +# CONFIG_RTS5139 is not set +# CONFIG_TRANZPORT is not set +# CONFIG_LINE6_USB is not set +# CONFIG_VT6656 is not set +# CONFIG_IIO is not set +# CONFIG_SNAPPY_COMPRESS is not set +# CONFIG_SNAPPY_DECOMPRESS is not set +CONFIG_ZRAM=y +# CONFIG_ZRAM_DEBUG is not set +CONFIG_ZRAM_LZO=y +# CONFIG_ZRAM_SNAPPY is not set +# CONFIG_ZCACHE is not set +CONFIG_ZSMALLOC=y +# CONFIG_PGTABLE_MAPPING is not set +# CONFIG_FB_SM7XX is not set +# CONFIG_USB_ENESTORAGE is not set +# CONFIG_BCM_WIMAX is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_STAGING_MEDIA is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +# CONFIG_ANDROID_LOGGER_SYSFS is not set +CONFIG_ANDROID_PERSISTENT_RAM=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y +# CONFIG_ANDROID_SWITCH is not set +# CONFIG_ANDROID_INTF_ALARM_DEV is not set +# CONFIG_PHONE is not set +# CONFIG_USB_WPAN_HCD is not set + +# +# Qualcomm MSM specific device drivers +# +# CONFIG_MSM_SSBI is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y + +# +# Hardware Spinlock drivers +# +CONFIG_IOMMU_SUPPORT=y + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_MOBICORE_SUPPORT is not set +# CONFIG_MSM_QDSS is not set + +# +# NVBK share memory +# +CONFIG_NVBK_SHARE_MEM=y + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_SECURITY is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +CONFIG_EXT4_FS_SECURITY=y +# CONFIG_EXT4_DEBUG is not set +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_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FS_POSIX_ACL=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=y +# CONFIG_CUSE 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_EXFAT_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_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_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_LOGFS 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_F2FS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_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=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 is not set +# CONFIG_DYNAMIC_FSYNC is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_PRINTK_RTC_TIME=y +CONFIG_PRINTK_CPU_ID=y +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# 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=y +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +CONFIG_DEBUG_SPINLOCK=y +# 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=y +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_HIGHMEM is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED 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_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_RCU_CPU_STALL_VERBOSE=y +# CONFIG_RCU_CPU_STALL_INFO is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_KPROBES_SANITY_TEST 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_DEBUG_PER_CPU_MAPS is not set +# CONFIG_LKDTM is not set +# CONFIG_CPU_NOTIFIER_ERROR_INJECT is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_NOP_TRACER=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +CONFIG_KPROBE_EVENT=y +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +CONFIG_DYNAMIC_DEBUG=y +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_ARM_KPROBES_TEST is not set +# CONFIG_PID_IN_CONTEXTIDR is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY=y +# CONFIG_SECURITYFS is not set +CONFIG_SECURITY_NETWORK=y +# CONFIG_SECURITY_NETWORK_XFRM is not set +# CONFIG_SECURITY_PATH is not set +CONFIG_LSM_MMAP_MIN_ADDR=32768 +CONFIG_SECURITY_SELINUX=y +# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set +# CONFIG_SECURITY_SELINUX_DISABLE is not set +CONFIG_SECURITY_SELINUX_DEVELOP=y +CONFIG_SECURITY_SELINUX_FORCE_PERMISSIVE=y +CONFIG_SECURITY_SELINUX_AVC_STATS=y +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_SECURITY_YAMA is not set +# CONFIG_IMA is not set +CONFIG_DEFAULT_SECURITY_SELINUX=y +# CONFIG_DEFAULT_SECURITY_DAC is not set +CONFIG_DEFAULT_SECURITY="selinux" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=m +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_PCRYPT is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# 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=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# 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=y +# CONFIG_CRYPTO_SHA1_ARM is not set +CONFIG_CRYPTO_SHA256=y +# 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_AES_ARM is not set +# 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=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=m +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_CRYPTO_DEV_QCRYPTO is not set +# CONFIG_CRYPTO_DEV_QCE is not set +# CONFIG_CRYPTO_DEV_QCEDEV is not set +# CONFIG_CRYPTO_DEV_OTA_CRYPTO is not set +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_CRC_CCITT=y +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=y +# CONFIG_CRC8 is not set +CONFIG_AUDIT_GENERIC=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_NLATTR=y +# CONFIG_AVERAGE is not set +# CONFIG_CORDIC is not set diff --git a/arch/arm/include/asm.zip b/arch/arm/include/asm.zip new file mode 100644 index 00000000..9ee4794a Binary files /dev/null and b/arch/arm/include/asm.zip differ diff --git a/arch/arm/include/asm/device.h b/arch/arm/include/asm/device.h index 7aa36800..b69c0d32 100644 --- a/arch/arm/include/asm/device.h +++ b/arch/arm/include/asm/device.h @@ -7,12 +7,16 @@ #define ASMARM_DEVICE_H struct dev_archdata { + struct dma_map_ops *dma_ops; #ifdef CONFIG_DMABOUNCE struct dmabounce_device_info *dmabounce; #endif #ifdef CONFIG_IOMMU_API void *iommu; /* private IOMMU data */ #endif +#ifdef CONFIG_ARM_DMA_USE_IOMMU + struct dma_iommu_mapping *mapping; +#endif }; struct omap_device; diff --git a/arch/arm/include/asm/hw_breakpoint.h b/arch/arm/include/asm/hw_breakpoint.h index eef55ea9..c190bc99 100644 --- a/arch/arm/include/asm/hw_breakpoint.h +++ b/arch/arm/include/asm/hw_breakpoint.h @@ -85,9 +85,6 @@ static inline void decode_ctrl_reg(u32 reg, #define ARM_DSCR_HDBGEN (1 << 14) #define ARM_DSCR_MDBGEN (1 << 15) -/* OSLSR os lock model bits */ -#define ARM_OSLSR_OSLM0 (1 << 0) - /* opcode2 numbers for the co-processor instructions. */ #define ARM_OP2_BVR 4 #define ARM_OP2_BCR 5 @@ -101,12 +98,12 @@ static inline void decode_ctrl_reg(u32 reg, #define ARM_BASE_WCR 112 /* Accessor macros for the debug registers. */ -#define ARM_DBG_READ(N, M, OP2, VAL) do {\ - asm volatile("mrc p14, 0, %0, " #N "," #M ", " #OP2 : "=r" (VAL));\ +#define ARM_DBG_READ(M, OP2, VAL) do {\ + asm volatile("mrc p14, 0, %0, c0," #M ", " #OP2 : "=r" (VAL));\ } while (0) -#define ARM_DBG_WRITE(N, M, OP2, VAL) do {\ - asm volatile("mcr p14, 0, %0, " #N "," #M ", " #OP2 : : "r" (VAL));\ +#define ARM_DBG_WRITE(M, OP2, VAL) do {\ + asm volatile("mcr p14, 0, %0, c0," #M ", " #OP2 : : "r" (VAL));\ } while (0) struct notifier_block; diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index 9e17f7ac..b12032b5 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -50,20 +49,17 @@ static int core_num_wrps; /* Debug architecture version. */ static u8 debug_arch; -/* Does debug architecture support OS Save and Restore? */ -static bool has_ossr; - /* Maximum supported watchpoint length. */ static u8 max_watchpoint_len; -#define READ_WB_REG_CASE(OP2, M, VAL) \ - case ((OP2 << 4) + M): \ - ARM_DBG_READ(c0, c ## M, OP2, VAL); \ +#define READ_WB_REG_CASE(OP2, M, VAL) \ + case ((OP2 << 4) + M): \ + ARM_DBG_READ(c ## M, OP2, VAL); \ break -#define WRITE_WB_REG_CASE(OP2, M, VAL) \ - case ((OP2 << 4) + M): \ - ARM_DBG_WRITE(c0, c ## M, OP2, VAL); \ +#define WRITE_WB_REG_CASE(OP2, M, VAL) \ + case ((OP2 << 4) + M): \ + ARM_DBG_WRITE(c ## M, OP2, VAL);\ break #define GEN_READ_WB_REG_CASES(OP2, VAL) \ @@ -140,12 +136,12 @@ static u8 get_debug_arch(void) /* Do we implement the extended CPUID interface? */ if (((read_cpuid_id() >> 16) & 0xf) != 0xf) { - pr_warn_once("CPUID feature registers not supported. " - "Assuming v6 debug is present.\n"); + pr_warning("CPUID feature registers not supported. " + "Assuming v6 debug is present.\n"); return ARM_DEBUG_ARCH_V6; } - ARM_DBG_READ(c0, c0, 0, didr); + ARM_DBG_READ(c0, 0, didr); return (didr >> 16) & 0xf; } @@ -173,7 +169,7 @@ static int debug_exception_updates_fsr(void) static int get_num_wrp_resources(void) { u32 didr; - ARM_DBG_READ(c0, c0, 0, didr); + ARM_DBG_READ(c0, 0, didr); return ((didr >> 28) & 0xf) + 1; } @@ -181,7 +177,7 @@ static int get_num_wrp_resources(void) static int get_num_brp_resources(void) { u32 didr; - ARM_DBG_READ(c0, c0, 0, didr); + ARM_DBG_READ(c0, 0, didr); return ((didr >> 24) & 0xf) + 1; } @@ -232,17 +228,19 @@ static int get_num_brps(void) * be put into halting debug mode at any time by an external debugger * but there is nothing we can do to prevent that. */ -static int monitor_mode_enabled(void) -{ - u32 dscr; - ARM_DBG_READ(c0, c1, 0, dscr); - return !!(dscr & ARM_DSCR_MDBGEN); -} - static int enable_monitor_mode(void) { u32 dscr; - ARM_DBG_READ(c0, c1, 0, dscr); + int ret = 0; + + ARM_DBG_READ(c1, 0, dscr); + + /* Ensure that halting mode is disabled. */ + if (WARN_ONCE(dscr & ARM_DSCR_HDBGEN, + "halting debug mode enabled. Unable to access hardware resources.\n")) { + ret = -EPERM; + goto out; + } /* If monitor mode is already enabled, just return. */ if (dscr & ARM_DSCR_MDBGEN) @@ -252,27 +250,24 @@ static int enable_monitor_mode(void) switch (get_debug_arch()) { case ARM_DEBUG_ARCH_V6: case ARM_DEBUG_ARCH_V6_1: - ARM_DBG_WRITE(c0, c1, 0, (dscr | ARM_DSCR_MDBGEN)); + ARM_DBG_WRITE(c1, 0, (dscr | ARM_DSCR_MDBGEN)); break; case ARM_DEBUG_ARCH_V7_ECP14: case ARM_DEBUG_ARCH_V7_1: - ARM_DBG_WRITE(c0, c2, 2, (dscr | ARM_DSCR_MDBGEN)); - isb(); + ARM_DBG_WRITE(c2, 2, (dscr | ARM_DSCR_MDBGEN)); break; default: - return -ENODEV; + ret = -ENODEV; + goto out; } /* Check that the write made it through. */ - ARM_DBG_READ(c0, c1, 0, dscr); - if (!(dscr & ARM_DSCR_MDBGEN)) { - pr_warn_once("Failed to enable monitor mode on CPU %d.\n", - smp_processor_id()); - return -EPERM; - } + ARM_DBG_READ(c1, 0, dscr); + if (!(dscr & ARM_DSCR_MDBGEN)) + ret = -EPERM; out: - return 0; + return ret; } int hw_breakpoint_slots(int type) @@ -333,9 +328,14 @@ int arch_install_hw_breakpoint(struct perf_event *bp) { struct arch_hw_breakpoint *info = counter_arch_bp(bp); struct perf_event **slot, **slots; - int i, max_slots, ctrl_base, val_base; + int i, max_slots, ctrl_base, val_base, ret = 0; u32 addr, ctrl; + /* Ensure that we are in monitor mode and halting mode is disabled. */ + ret = enable_monitor_mode(); + if (ret) + goto out; + addr = info->address; ctrl = encode_ctrl_reg(info->ctrl) | 0x1; @@ -362,9 +362,9 @@ int arch_install_hw_breakpoint(struct perf_event *bp) } } - if (i == max_slots) { - pr_warning("Can't find any breakpoint slot\n"); - return -EBUSY; + if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot\n")) { + ret = -EBUSY; + goto out; } /* Override the breakpoint data with the step data. */ @@ -383,7 +383,9 @@ int arch_install_hw_breakpoint(struct perf_event *bp) /* Setup the control register. */ write_wb_reg(ctrl_base + i, ctrl); - return 0; + +out: + return ret; } void arch_uninstall_hw_breakpoint(struct perf_event *bp) @@ -414,10 +416,8 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp) } } - if (i == max_slots) { - pr_warning("Can't find any breakpoint slot\n"); + if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot\n")) return; - } /* Ensure that we disable the mismatch breakpoint. */ if (info->ctrl.type != ARM_BREAKPOINT_EXECUTE && @@ -596,10 +596,6 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) int ret = 0; u32 offset, alignment_mask = 0x3; - /* Ensure that we are in monitor debug mode. */ - if (!monitor_mode_enabled()) - return -ENODEV; - /* Build the arch_hw_breakpoint. */ ret = arch_build_bp_info(bp); if (ret) @@ -614,14 +610,13 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) /* Aligned */ break; case 1: + /* Allow single byte watchpoint. */ + if (info->ctrl.len == ARM_BREAKPOINT_LEN_1) + break; case 2: /* Allow halfword watchpoints and breakpoints. */ if (info->ctrl.len == ARM_BREAKPOINT_LEN_2) break; - case 3: - /* Allow single byte watchpoint. */ - if (info->ctrl.len == ARM_BREAKPOINT_LEN_1) - break; default: ret = -EINVAL; goto out; @@ -862,7 +857,7 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, local_irq_enable(); /* We only handle watchpoints and hardware breakpoints. */ - ARM_DBG_READ(c0, c1, 0, dscr); + ARM_DBG_READ(c1, 0, dscr); /* Perform perf callbacks. */ switch (ARM_DSCR_MOE(dscr)) { @@ -883,6 +878,18 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, return ret; } +static void reset_brps_reserved_reg(int n) +{ + int i; + + /* we must also reset any reserved registers. */ + for (i = 0; i < n; ++i) { + write_wb_reg(ARM_BASE_BCR + i, 0UL); + write_wb_reg(ARM_BASE_BVR + i, 0UL); + } + +} + /* * One-time initialisation. */ @@ -907,27 +914,10 @@ static struct undef_hook debug_reg_hook = { .fn = debug_reg_trap, }; -/* Does this core support OS Save and Restore? */ -static bool core_has_os_save_restore(void) -{ - u32 oslsr; - - switch (get_debug_arch()) { - case ARM_DEBUG_ARCH_V7_1: - return true; - case ARM_DEBUG_ARCH_V7_ECP14: - ARM_DBG_READ(c1, c1, 4, oslsr); - if (oslsr & ARM_OSLSR_OSLM0) - return true; - default: - return false; - } -} - static void reset_ctrl_regs(void *unused) { int i, raw_num_brps, err = 0, cpu = smp_processor_id(); - u32 val; + u32 dbg_power; /* * v7 debug contains save and restore registers so that debug state @@ -940,89 +930,67 @@ static void reset_ctrl_regs(void *unused) switch (debug_arch) { case ARM_DEBUG_ARCH_V6: case ARM_DEBUG_ARCH_V6_1: - /* ARMv6 cores clear the registers out of reset. */ - goto out_mdbgen; + /* ARMv6 cores just need to reset the registers. */ + goto reset_regs; case ARM_DEBUG_ARCH_V7_ECP14: /* * Ensure sticky power-down is clear (i.e. debug logic is * powered up). */ - ARM_DBG_READ(c1, c5, 4, val); - if ((val & 0x1) == 0) + asm volatile("mrc p14, 0, %0, c1, c5, 4" : "=r" (dbg_power)); + if ((dbg_power & 0x1) == 0) err = -EPERM; - - if (!has_ossr) - goto clear_vcr; break; case ARM_DEBUG_ARCH_V7_1: /* * Ensure the OS double lock is clear. */ - ARM_DBG_READ(c1, c3, 4, val); - if ((val & 0x1) == 1) + asm volatile("mrc p14, 0, %0, c1, c3, 4" : "=r" (dbg_power)); + if ((dbg_power & 0x1) == 1) err = -EPERM; break; } if (err) { - pr_warn_once("CPU %d debug is powered down!\n", cpu); + pr_warning("CPU %d debug is powered down!\n", cpu); cpumask_or(&debug_err_mask, &debug_err_mask, cpumask_of(cpu)); return; } /* - * Unconditionally clear the OS lock by writing a value + * Unconditionally clear the lock by writing a value * other than 0xC5ACCE55 to the access register. */ - ARM_DBG_WRITE(c1, c0, 4, 0); + asm volatile("mcr p14, 0, %0, c1, c0, 4" : : "r" (0)); isb(); /* * Clear any configured vector-catch events before * enabling monitor mode. */ -clear_vcr: - ARM_DBG_WRITE(c0, c7, 0, 0); + asm volatile("mcr p14, 0, %0, c0, c7, 0" : : "r" (0)); isb(); - if (cpumask_intersects(&debug_err_mask, cpumask_of(cpu))) { - pr_warn_once("CPU %d failed to disable vector catch\n", cpu); +reset_regs: + if (enable_monitor_mode()) return; - } - /* - * The control/value register pairs are UNKNOWN out of reset so - * clear them to avoid spurious debug events. - */ - raw_num_brps = get_num_brp_resources(); - for (i = 0; i < raw_num_brps; ++i) { - write_wb_reg(ARM_BASE_BCR + i, 0UL); - write_wb_reg(ARM_BASE_BVR + i, 0UL); - } +#ifdef CONFIG_HAVE_HW_BRKPT_RESERVED_RW_ACCESS + reset_brps_reserved_reg(core_num_brps); +#else + reset_brps_reserved_reg(core_num_brps + core_num_reserved_brps); +#endif for (i = 0; i < core_num_wrps; ++i) { write_wb_reg(ARM_BASE_WCR + i, 0UL); write_wb_reg(ARM_BASE_WVR + i, 0UL); } - - if (cpumask_intersects(&debug_err_mask, cpumask_of(cpu))) { - pr_warn_once("CPU %d failed to clear debug register pairs\n", cpu); - return; - } - - /* - * Have a crack at enabling monitor mode. We don't actually need - * it yet, but reporting an error early is useful if it fails. - */ -out_mdbgen: - if (enable_monitor_mode()) - cpumask_or(&debug_err_mask, &debug_err_mask, cpumask_of(cpu)); } static int dbg_reset_notify(struct notifier_block *self, unsigned long action, void *cpu) { - if ((action & ~CPU_TASKS_FROZEN) == CPU_ONLINE) + if (action == CPU_ONLINE) smp_call_function_single((int)cpu, reset_ctrl_regs, NULL, 1); return NOTIFY_OK; @@ -1032,32 +1000,10 @@ static struct notifier_block dbg_reset_nb = { .notifier_call = dbg_reset_notify, }; -#ifdef CONFIG_CPU_PM -static int dbg_cpu_pm_notify(struct notifier_block *self, unsigned long action, - void *v) -{ - if (action == CPU_PM_EXIT) - reset_ctrl_regs(NULL); - - return NOTIFY_OK; -} - -static struct notifier_block dbg_cpu_pm_nb = { - .notifier_call = dbg_cpu_pm_notify, -}; - -static void __init pm_init(void) -{ - cpu_pm_register_notifier(&dbg_cpu_pm_nb); -} -#else -static inline void pm_init(void) -{ -} -#endif - static int __init arch_hw_breakpoint_init(void) { + u32 dscr; + debug_arch = get_debug_arch(); if (!debug_arch_supported()) { @@ -1065,8 +1011,6 @@ static int __init arch_hw_breakpoint_init(void) return 0; } - has_ossr = core_has_os_save_restore(); - /* Determine how many BRPs/WRPs are available. */ core_num_brps = get_num_brps(); core_num_wrps = get_num_wrps(); @@ -1094,10 +1038,17 @@ static int __init arch_hw_breakpoint_init(void) core_num_brps, core_has_mismatch_brps() ? "(+1 reserved) " : "", core_num_wrps); - /* Work out the maximum supported watchpoint length. */ - max_watchpoint_len = get_max_wp_len(); - pr_info("maximum watchpoint size is %u bytes.\n", - max_watchpoint_len); + ARM_DBG_READ(c1, 0, dscr); + if (dscr & ARM_DSCR_HDBGEN) { + max_watchpoint_len = 4; + pr_warning("halting debug mode enabled. Assuming maximum watchpoint size of %u bytes.\n", + max_watchpoint_len); + } else { + /* Work out the maximum supported watchpoint length. */ + max_watchpoint_len = get_max_wp_len(); + pr_info("maximum watchpoint size is %u bytes.\n", + max_watchpoint_len); + } /* Register debug fault handler. */ hook_fault_code(FAULT_CODE_DEBUG, hw_breakpoint_pending, SIGTRAP, @@ -1105,11 +1056,8 @@ static int __init arch_hw_breakpoint_init(void) hook_ifault_code(FAULT_CODE_DEBUG, hw_breakpoint_pending, SIGTRAP, TRAP_HWBKPT, "breakpoint debug exception"); - /* Register hotplug and PM notifiers. */ - __register_cpu_notifier(&dbg_reset_nb); - - cpu_notifier_register_done(); - pm_init(); + /* Register hotplug notifier. */ + register_cpu_notifier(&dbg_reset_nb); return 0; } arch_initcall(arch_hw_breakpoint_init); diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 2cdc5462..40a4bb1b 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -409,6 +409,7 @@ config ARCH_MSM_KRAITMP select HAVE_ARCH_HAS_CURRENT_TIMER select MSM_JTAG if MSM_QDSS bool + select HAVE_HW_BRKPT_RESERVED_RW_ACCESS config ARCH_MSM_CORTEXMP select MSM_SMP @@ -419,6 +420,7 @@ config MSM_KRAIT_WFE_FIXUP config ARCH_MSM_CORTEX_A5 bool + select HAVE_HW_BRKPT_RESERVED_RW_ACCESS config ARCH_MSM7X27A bool @@ -532,6 +534,12 @@ config MACH_MSM7X27A_FFA help Support for the Qualcomm MSM7x27A FFA. +config WERITOS_KERNEL + depends on ARCH_MSM7X27A + bool "Kernel weritos" + help + Support for the Weritos Kernel + config MACH_MSM7625A_SURF depends on ARCH_MSM7X27A depends on !MSM_STACKED_MEMORY @@ -983,20 +991,17 @@ config PHYS_OFFSET default "0x40200000" if ARCH_MSM8X60 default "0x10000000" -config KERNEL_MSM_CONTIG_MEM_REGION - bool "Enable in-kernel contiguous memory region" +config KERNEL_PMEM_EBI_REGION + bool "Enable in-kernel PMEM region for EBI" default y if ARCH_MSM8X60 - depends on ANDROID_PMEM && (ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_MSM8974) + depends on (ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_MSM8974) help - Enable the in-kernel contiguous memory allocator. Sets up a - region of physically contiguous memory. This memory is - reserved during initialization, and can be used - generically. + Enable the in-kernel PMEM allocator to use EBI memory. config KERNEL_PMEM_SMI_REGION bool "Enable in-kernel PMEM region for SMI" default y if ARCH_MSM8X60 - depends on (ARCH_QSD8X50 && !PMEM_GPU0) || (ARCH_MSM8X60 && !VCM) + depends on ((ARCH_QSD8X50 && !PMEM_GPU0) || (ARCH_MSM8X60 && !VCM)) help Enable the in-kernel PMEM allocator to use SMI memory. @@ -1350,6 +1355,20 @@ config MSM_PCIE Enables the PCIe functionality by configures PCIe core on MSM chipset and by enabling the ARM PCI framework extension. +config MSM_RPC_SDIO_XPRT + depends on MSM_SDIO_AL + default y + bool "MSM SDIO XPRT Layer" + help + SDIO Transport Layer for RPC Rouer + +config MSM_RPC_SDIO_DEBUG + depends on MSM_RPC_SDIO_XPRT + default y + bool "MSM SDIO XPRT debug support" + help + Support for debugging SDIO XPRT + config MSM_FUSE_INFO_DEBUG default y bool "MSM FUSE debug support" @@ -1370,6 +1389,21 @@ config MSM_FAST_BOOT help Enable the fast power on support in MSM platform. +config MSM_SDIO_AL + depends on ((ARCH_MSM7X30 || MACH_MSM8X60_FUSN_FFA || MACH_TYPE_MSM8X60_FUSION) && HAS_WAKELOCK) + default y + tristate "SDIO-Abstraction-Layer" + help + Support MSM<->MDM Communication over SDIO bus. + MDM SDIO-Client should have pipes support. + +config MSM_SDIO_DMUX + bool "SDIO Data Mux Driver" + depends on MSM_SDIO_AL + default n + help + Support Muxed Data Channels over SDIO interface. + config MSM_BAM_DMUX bool "BAM Data Mux Driver" depends on SPS @@ -1429,6 +1463,16 @@ config MSM_SMD_NMEA If in doubt, say yes. +config MSM_SDIO_TTY + bool "SDIO TTY Driver" + depends on MSM_SDIO_AL + default n + help + Provides a TTY driver SDIO TTY + This driver can be used by user space + applications for passing data through the + SDIO interface. + config MSM_SMD_TTY bool "SMD TTY Driver" depends on MSM_SMD @@ -1456,6 +1500,13 @@ config MSM_SMD_PKT If in doubt, say yes. +config MSM_SDIO_CMUX + bool "SDIO CMUX Driver" + depends on MSM_SDIO_AL + default n + help + Provides a Muxed port interface over SDIO QMI + config MSM_DSPS bool "Sensors DSPS driver" depends on (MSM_PIL && (ARCH_MSM8X60 || ARCH_MSM8960)) @@ -1468,6 +1519,13 @@ config MSM_DSPS The number of clocks and their name may vary between targets. It also triggers the PIL to load the DSPS firmware. +config MSM_SDIO_CTL + bool "SDIO CTL Driver" + depends on MSM_SDIO_CMUX + default n + help + Provides a binary SDIO control port interface. + config MSM_ONCRPCROUTER depends on MSM_SMD default n @@ -1575,6 +1633,15 @@ config MSM_RMT_STORAGE_CLIENT_STATS Collects performance statistics and shows this information through a debugfs file rmt_storage_stats. +config MSM_SDIO_SMEM + depends on MSM_SDIO_AL + default n + bool "SDIO SMEM for remote storage" + help + Copies data from remote MDM9K memory to local MSM8x60 + memory. Used by remote storage client to shadow + MDM9K filesystem. + config MSM_DALRPC bool "DAL RPC support" default n @@ -1631,7 +1698,6 @@ config MSM_AVS_HW config MSM_HW3D tristate "MSM Hardware 3D Register Driver" - default y help Provides access to registers needed by the userspace OpenGL|ES library. @@ -2119,6 +2185,10 @@ config MSM_PM8X60 depends on PM bool +config MSM_NOPM + default y if !PM + bool + config MSM_EVENT_TIMER bool "Event timer" help @@ -2127,10 +2197,6 @@ config MSM_EVENT_TIMER events that require the core to be awake and ready to handle the event. -config MSM_NOPM - default y if !PM - bool - config MSM_BUS_SCALING bool "Bus scaling driver" default n @@ -2201,7 +2267,7 @@ config MSM_RUN_QUEUE_STATS on when to switch off/on the other cores. config MSM_SM_EVENT - bool "Enable system event monitor" + bool "Enable system event monitor select UNCACHED_BUF default n @@ -2581,4 +2647,18 @@ config MSM_OVERCLOCK_GPU Enables overclocking of adreno 203 gpu. This is rather dangerous. If unsure, say N. +config INTELLI_PLUG + bool "Enable intelli-plug cpu hotplug driver" + default n + help + Generic Intelli-plug cpu hotplug driver for ARM SOCs + +config BRICKED_HOTPLUG + bool "Enable kernel based brick" + depends on MSM_RUN_QUEUE_STATS + default n + help + This enables kernel based multi core control. + (up/down hotplug based on load) + endif diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index de1e7d83..ae13d3ff 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -43,6 +43,9 @@ obj-$(CONFIG_ARCH_MSM_SCORPION) += msm_fault_handlers.o endif obj-$(CONFIG_MSM_VIC) += irq-vic.o +obj-$(CONFIG_MSM_EVENT_TIMER) += event_timer.o +obj-$(CONFIG_BRICKED_HOTPLUG) += bricked_hotplug.o +obj-$(CONFIG_INTELLI_PLUG) += intelli_plug.o ifdef CONFIG_ARCH_QSD8X50 obj-$(CONFIG_MSM_SOC_REV_NONE) += acpuclock-8x50.o @@ -93,6 +96,10 @@ obj-$(CONFIG_ARCH_QSD8X50) += sirc.o obj-$(CONFIG_ARCH_FSM9XXX) += sirc-fsm9xxx.o obj-$(CONFIG_MSM_FIQ_SUPPORT) += fiq_glue.o obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o +obj-$(CONFIG_MSM_SDIO_AL) += sdio_al.o +obj-$(CONFIG_MSM_SDIO_AL) += sdio_al_test.o +obj-$(CONFIG_MSM_SDIO_AL) += sdio_al_dloader.o +obj-$(CONFIG_MSM_SDIO_DMUX) += sdio_dmux.o obj-$(CONFIG_MSM_BAM_DMUX) += bam_dmux.o obj-$(CONFIG_MSM_SMD_LOGGING) += smem_log.o obj-$(CONFIG_MSM_IPC_LOGGING) += ipc_logging.o @@ -124,10 +131,13 @@ endif endif endif endif +obj-$(CONFIG_MSM_SDIO_TTY) += sdio_tty.o obj-$(CONFIG_MSM_SMD_TTY) += smd_tty.o obj-$(CONFIG_MSM_SMD_QMI) += smd_qmi.o obj-$(CONFIG_MSM_SMD_PKT) += smd_pkt.o +obj-$(CONFIG_MSM_SDIO_CMUX) += sdio_cmux.o obj-$(CONFIG_MSM_DSPS) += msm_dsps.o +obj-$(CONFIG_MSM_SDIO_CTL) += sdio_ctl.o obj-$(CONFIG_MSM_SMD_NMEA) += smd_nmea.o obj-$(CONFIG_MSM_RESET_MODEM) += reset_modem.o obj-$(CONFIG_MSM_IPC_ROUTER_SMD_XPRT) += ipc_router_smd_xprt.o @@ -140,6 +150,7 @@ obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_clients.o obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_xdr.o obj-$(CONFIG_MSM_ONCRPCROUTER) += rpcrouter_smd_xprt.o +obj-$(CONFIG_MSM_RPC_SDIO_XPRT) += rpcrouter_sdio_xprt.o obj-$(CONFIG_MSM_RPC_PING) += ping_mdm_rpc_client.o obj-$(CONFIG_MSM_RPC_PROC_COMM_TEST) += proc_comm_test.o obj-$(CONFIG_MSM_RPC_PING) += ping_mdm_rpc_client.o ping_apps_server.o @@ -312,6 +323,7 @@ obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o obj-$(CONFIG_HTC_PWRSINK) += htc_pwrsink.o obj-$(CONFIG_HTC_HEADSET) += htc_headset.o obj-$(CONFIG_MSM_RMT_STORAGE_CLIENT) += rmt_storage_client.o +obj-$(CONFIG_MSM_SDIO_SMEM) += sdio_smem.o obj-$(CONFIG_MSM_RPM) += rpm.o ifdef CONFIG_MSM_RPM obj-$(CONFIG_ARCH_APQ8064) += rpm_resources.o @@ -332,7 +344,6 @@ obj-$(CONFIG_MSM_BUS_SCALING) += msm_bus/ obj-$(CONFIG_MSM_BUSPM_DEV) += msm-buspm-dev.o obj-$(CONFIG_MSM_IOMMU) += devices-iommu.o iommu_domains.o -obj-$(CONFIG_MSM_EVENT_TIMER) += event_timer.o ifdef CONFIG_VCM obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60-vcm.o @@ -356,7 +367,7 @@ obj-$(CONFIG_MSM_SM_EVENT) += sm_event.o obj-$(CONFIG_MSM_SM_EVENT_LOG) += sm_event_log.o sm_event_driver.o obj-$(CONFIG_MSM_SLEEP_MONITOR) += sleep_monitor.o obj-$(CONFIG_MSM_SLEEP_STATS_DEVICE) += idle_stats_device.o -obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o +obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o msm_dcvs_idle.o obj-$(CONFIG_MSM_RUN_QUEUE_STATS) += msm_rq_stats.o obj-$(CONFIG_MSM_SHOW_RESUME_IRQ) += msm_show_resume_irq.o obj-$(CONFIG_BT_MSM_PINTEST) += btpintest.o diff --git a/arch/arm/mach-msm/audio-7627a-devices.c b/arch/arm/mach-msm/audio-7627a-devices.c index 30a079fd..2530ab3e 100644 --- a/arch/arm/mach-msm/audio-7627a-devices.c +++ b/arch/arm/mach-msm/audio-7627a-devices.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include "board-msm7627a.h" diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c index 23eb3a7f..5f6f3e0a 100644 --- a/arch/arm/mach-msm/board-8064.c +++ b/arch/arm/mach-msm/board-8064.c @@ -95,7 +95,7 @@ #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION #define HOLE_SIZE 0x20000 -#define MSM_CONTIG_MEM_SIZE 0x65000 +#define MSM_PMEM_KERNEL_EBI1_SIZE 0x65000 #ifdef CONFIG_MSM_IOMMU #define MSM_ION_MM_SIZE 0x3800000 #define MSM_ION_SF_SIZE 0 @@ -111,7 +111,7 @@ #define MSM_ION_MFC_SIZE SZ_8K #define MSM_ION_AUDIO_SIZE MSM_PMEM_AUDIO_SIZE #else -#define MSM_CONTIG_MEM_SIZE 0x110C000 +#define MSM_PMEM_KERNEL_EBI1_SIZE 0x110C000 #define MSM_ION_HEAP_NUM 1 #endif @@ -132,14 +132,14 @@ #define PCIE_PWR_EN_PMIC_GPIO 13 #define PCIE_RST_N_PMIC_MPP 1 -#ifdef CONFIG_KERNEL_MSM_CONTIG_MEM_REGION -static unsigned msm_contig_mem_size = MSM_CONTIG_MEM_SIZE; -static int __init msm_contig_mem_size_setup(char *p) +#ifdef CONFIG_KERNEL_PMEM_EBI_REGION +static unsigned pmem_kernel_ebi1_size = MSM_PMEM_KERNEL_EBI1_SIZE; +static int __init pmem_kernel_ebi1_size_setup(char *p) { - msm_contig_mem_size = memparse(p, NULL); + pmem_kernel_ebi1_size = memparse(p, NULL); return 0; } -early_param("msm_contig_mem_size", msm_contig_mem_size_setup); +early_param("pmem_kernel_ebi1_size", pmem_kernel_ebi1_size_setup); #endif #ifdef CONFIG_ANDROID_PMEM @@ -262,7 +262,7 @@ static void __init reserve_pmem_memory(void) reserve_memory_for(&android_pmem_pdata); reserve_memory_for(&android_pmem_audio_pdata); #endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ - apq8064_reserve_table[MEMTYPE_EBI1].size += msm_contig_mem_size; + apq8064_reserve_table[MEMTYPE_EBI1].size += pmem_kernel_ebi1_size; #endif /*CONFIG_ANDROID_PMEM*/ } diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c index f65da89e..cff798e5 100644 --- a/arch/arm/mach-msm/board-8930.c +++ b/arch/arm/mach-msm/board-8930.c @@ -137,7 +137,7 @@ struct sx150x_platform_data msm8930_sx150x_data[] = { #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION #define HOLE_SIZE 0x20000 -#define MSM_CONTIG_MEM_SIZE 0x65000 +#define MSM_PMEM_KERNEL_EBI1_SIZE 0x65000 #ifdef CONFIG_MSM_IOMMU #define MSM_ION_MM_SIZE 0x3800000 /* Need to be multiple of 64K */ #define MSM_ION_SF_SIZE 0x0 @@ -164,18 +164,18 @@ struct sx150x_platform_data msm8930_sx150x_data[] = { #define MSM8930_FW_START MSM8930_FIXED_AREA_START #else -#define MSM_CONTIG_MEM_SIZE 0x110C000 +#define MSM_PMEM_KERNEL_EBI1_SIZE 0x110C000 #define MSM_ION_HEAP_NUM 1 #endif -#ifdef CONFIG_KERNEL_MSM_CONTIG_MEM_REGION -static unsigned msm_contig_mem_size = MSM_CONTIG_MEM_SIZE; -static int __init msm_contig_mem_size_setup(char *p) +#ifdef CONFIG_KERNEL_PMEM_EBI_REGION +static unsigned pmem_kernel_ebi1_size = MSM_PMEM_KERNEL_EBI1_SIZE; +static int __init pmem_kernel_ebi1_size_setup(char *p) { - msm_contig_mem_size = memparse(p, NULL); + pmem_kernel_ebi1_size = memparse(p, NULL); return 0; } -early_param("msm_contig_mem_size", msm_contig_mem_size_setup); +early_param("pmem_kernel_ebi1_size", pmem_kernel_ebi1_size_setup); #endif #ifdef CONFIG_ANDROID_PMEM @@ -318,7 +318,7 @@ static void __init reserve_pmem_memory(void) reserve_memory_for(&android_pmem_pdata); reserve_memory_for(&android_pmem_audio_pdata); #endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ - msm8930_reserve_table[MEMTYPE_EBI1].size += msm_contig_mem_size; + msm8930_reserve_table[MEMTYPE_EBI1].size += pmem_kernel_ebi1_size; #endif /*CONFIG_ANDROID_PMEM*/ } diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c index d6dd2006..59abf588 100644 --- a/arch/arm/mach-msm/board-8960.c +++ b/arch/arm/mach-msm/board-8960.c @@ -146,7 +146,7 @@ struct sx150x_platform_data msm8960_sx150x_data[] = { #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION #define HOLE_SIZE 0x20000 -#define MSM_CONTIG_MEM_SIZE 0x65000 +#define MSM_PMEM_KERNEL_EBI1_SIZE 0x65000 #ifdef CONFIG_MSM_IOMMU #define MSM_ION_MM_SIZE 0x3800000 /* Need to be multiple of 64K */ #define MSM_ION_SF_SIZE 0x0 @@ -174,18 +174,18 @@ struct sx150x_platform_data msm8960_sx150x_data[] = { static unsigned msm_ion_sf_size = MSM_ION_SF_SIZE; #else -#define MSM_CONTIG_MEM_SIZE 0x110C000 +#define MSM_PMEM_KERNEL_EBI1_SIZE 0x110C000 #define MSM_ION_HEAP_NUM 1 #endif -#ifdef CONFIG_KERNEL_MSM_CONTIG_MEM_REGION -static unsigned msm_contig_mem_size = MSM_CONTIG_MEM_SIZE; -static int __init msm_contig_mem_size_setup(char *p) +#ifdef CONFIG_KERNEL_PMEM_EBI_REGION +static unsigned pmem_kernel_ebi1_size = MSM_PMEM_KERNEL_EBI1_SIZE; +static int __init pmem_kernel_ebi1_size_setup(char *p) { - msm_contig_mem_size = memparse(p, NULL); + pmem_kernel_ebi1_size = memparse(p, NULL); return 0; } -early_param("msm_contig_mem_size", msm_contig_mem_size_setup); +early_param("pmem_kernel_ebi1_size", pmem_kernel_ebi1_size_setup); #endif #ifdef CONFIG_ANDROID_PMEM @@ -337,7 +337,7 @@ static void __init reserve_pmem_memory(void) reserve_memory_for(&android_pmem_pdata); reserve_memory_for(&android_pmem_audio_pdata); #endif - msm8960_reserve_table[MEMTYPE_EBI1].size += msm_contig_mem_size; + msm8960_reserve_table[MEMTYPE_EBI1].size += pmem_kernel_ebi1_size; #endif } diff --git a/arch/arm/mach-msm/board-msm7627a_i6-display.c b/arch/arm/mach-msm/board-msm7627a_i6-display.c new file mode 100644 index 00000000..cf457bc7 --- /dev/null +++ b/arch/arm/mach-msm/board-msm7627a_i6-display.c @@ -0,0 +1,535 @@ +/* Copyright (c) 2012, Code Aurora Forum. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" +#include "board-msm7627a.h" +#include +#include + +#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER +#define MSM_FB_SIZE 0x4BF000 +#define MSM7x25A_MSM_FB_SIZE 0x1C2000 +#define MSM8x25_MSM_FB_SIZE 0x5FA000 +#else +#define MSM_FB_SIZE 0x32A000 +#define MSM7x25A_MSM_FB_SIZE 0x12C000 +#define MSM8x25_MSM_FB_SIZE 0x3FC000 +#endif + +//linxc 12-06-19 +++ +#define MSM8x25_I6_MSM_FB_SIZE 0x5FB000 //add linxc 2012-12-21 +++ +#define GPIO_I6_LCD_BRDG_RESET_N 85 +#define GPIO_I6_LCD_BACKLIGHT_EN 96 +#define GPIO_I6_LCD_VDD_3V3_EN 12 +#define GPIO_I6_MIPI_DETECT 107 +//linxc 12-06-19 - - - + + +#define MACHINE_IS_JSR_I6 \ + (machine_is_msm7627a_evb() || machine_is_msm8625_evb() || machine_is_msm8625_qrd5() || machine_is_msm7x27a_qrd5a()) + +#define MACHINE_IS_JSR_I6Q \ + (machine_is_msm8625_skud()) + +/* + * Reserve enough v4l2 space for a double buffered full screen + * res image (864x480x1.5x2) + */ +#define MSM_V4L2_VIDEO_OVERLAY_BUF_SIZE 1244160 + +static unsigned fb_size = MSM_FB_SIZE; +static int __init fb_size_setup(char *p) +{ + fb_size = memparse(p, NULL); + return 0; +} + +early_param("fb_size", fb_size_setup); + + +static struct resource msm_fb_resources[] = { + { + .flags = IORESOURCE_DMA, + } +}; + +#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE +static struct resource msm_v4l2_video_overlay_resources[] = { + { + .flags = IORESOURCE_DMA, + } +}; +#endif + + +static int i6_mipi_gpio_initialized = 0; +static int i6_mipi_gpio_status = 0; + +#define GPIO_I6_MIPI_DETECT 107 + +static int I6_mipi_panel_is_novatek_nt35516(void) +{ + unsigned int conf; + pr_err("%s\n", __func__); + if (!i6_mipi_gpio_initialized) { + if (gpio_request(GPIO_I6_MIPI_DETECT, "I6_mipi_detect") < 0) { + pr_err("%s: gpio_request I6_mipi_detect failed!", __func__); + return i6_mipi_gpio_status; + } + conf = GPIO_CFG(GPIO_I6_MIPI_DETECT, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA); // 0x6B0 + if (gpio_tlmm_config(conf, GPIO_CFG_ENABLE) >= 0) { + i6_mipi_gpio_status = gpio_get_value(GPIO_I6_MIPI_DETECT); + pr_err("%s: is_nt35516 = %d\n", __func__, i6_mipi_gpio_status); + i6_mipi_gpio_initialized = 1; + } else { + pr_err("%s: unable to config I6_mipi_detect\n", __func__); + } + gpio_free(GPIO_I6_MIPI_DETECT); + } + return i6_mipi_gpio_status; +} + +static int msm_fb_detect_panel(const char *name) +{ + int ret = -ENODEV; + pr_err("%s: name = \"%s\"\n", __func__, name); + if (MACHINE_IS_JSR_I6) { + if (!strncmp(name, "mipi_cmd_novatek_nt35516_qhd", 28)) { + if (I6_mipi_panel_is_novatek_nt35516()) { + pr_err("I6 LCD is cmd novatek nt35516\n"); + ret = 0; + } + } + if (!strncmp(name, "mipi_cmd_truly_nt35516_qhd", 25)) { + if (!I6_mipi_panel_is_novatek_nt35516()) { + pr_err("I6 LCD is truly\n"); + ret = 0; + } + } + } + return ret; +} + + +static struct msm_fb_platform_data msm_fb_pdata = { + .detect_client = msm_fb_detect_panel, +}; + +static struct platform_device msm_fb_device = { + .name = "msm_fb", + .id = 0, + .num_resources = ARRAY_SIZE(msm_fb_resources), + .resource = msm_fb_resources, + .dev = { + .platform_data = &msm_fb_pdata, + } +}; + +#if 0 +#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE +static struct platform_device msm_v4l2_video_overlay_device = { + .name = "msm_v4l2_overlay_pd", + .id = 0, + .num_resources = ARRAY_SIZE(msm_v4l2_video_overlay_resources), + .resource = msm_v4l2_video_overlay_resources, + }; +#endif +#endif + +static int i6_bl_level = 0; + +static int mipi_I6_qhd_set_bl(int level) +{ + int ret = 0; + int value = 0; + ret = pmapp_disp_backlight_set_brightness(level); + if (ret) + pr_err("%s: can't set lcd backlight! \n", __func__); + if (level == i6_bl_level) { + pr_err("%s: no change\n", __func__); + return 0; + } + if (level == 255) { + value = 0; + } else { + if (i6_bl_level != 255) { + i6_bl_level = level; + return 0; + } + msleep(25); + value = 1; + } + gpio_set_value(GPIO_I6_LCD_BACKLIGHT_EN, value); + i6_bl_level = level; + return 0; +} + +// ------------------------------------------------------------------- + +static int mipi_truly_nt35516_qhd_rotate_panel(void) +{ + int rotate = 0; + if (machine_is_msm8625_qrd5() || machine_is_msm7x27a_qrd5a()) + rotate = 1; + return rotate; +} + +static int mipi_nt35516_qhd_rotate_panel(void) +{ + int rotate = 0; + if (machine_is_msm8625_qrd5() || machine_is_msm7x27a_qrd5a()) + rotate = 1; + return rotate; +} + +// ------------------------------------------------------------------- + +static struct msm_panel_common_pdata mipi_truly_nt35516_qhd_pdata = { + .pmic_backlight = NULL, + .rotate_panel = mipi_truly_nt35516_qhd_rotate_panel, +}; + +static struct platform_device mipi_dsi_truly_nt35516_qhd_panel_device = { + .name = "mipi_truly_nt35516_qhd", + .id = 0, + .dev = { + .platform_data = &mipi_truly_nt35516_qhd_pdata, + } +}; + +// ------------------------------------------------------------------- + +static struct msm_panel_common_pdata mipi_novatek_nt35516_qhd_pdata = { + .pmic_backlight = NULL, + .rotate_panel = mipi_nt35516_qhd_rotate_panel, +}; + +static struct platform_device mipi_dsi_novatek_nt35516_qhd_panel_device = { + .name = "mipi_novatek_nt35516_qhd", + .id = 0, + .dev = { + .platform_data = &mipi_novatek_nt35516_qhd_pdata, + } +}; + +// ------------------------------------------------------------------- + +static struct msm_panel_common_pdata mipi_video_nt35516_qhd_qhd_v2_pdata = { + .pmic_backlight = mipi_I6_qhd_set_bl, + .rotate_panel = mipi_truly_nt35516_qhd_rotate_panel, +}; + +static struct platform_device mipi_dsi_truly_nt35516_qhd_v2_panel_device = { + .name = "mipi_truly_nt35516_qhd", + .id = 0, + .dev = { + .platform_data = &mipi_video_nt35516_qhd_qhd_v2_pdata, + } +}; + +// ------------------------------------------------------------------- + +static struct msm_panel_common_pdata mipi_novatek_nt35516_qhd_v2_pdata = { + .pmic_backlight = mipi_I6_qhd_set_bl, + .rotate_panel = mipi_nt35516_qhd_rotate_panel, +}; + +static struct platform_device mipi_dsi_novatek_nt35516_qhd_v2_panel_device = { + .name = "mipi_novatek_nt35516_qhd", + .id = 0, + .dev = { + .platform_data = &mipi_novatek_nt35516_qhd_v2_pdata, + } +}; + +// ------------------------------------------------------------------- + +static struct platform_device *evb_fb_devices[] __initdata = { + &msm_fb_device, + //&mipi_dsi_NT35510_panel_device, + //&mipi_dsi_NT35516_panel_device, + &mipi_dsi_truly_nt35516_qhd_panel_device, + &mipi_dsi_novatek_nt35516_qhd_panel_device, +}; + +static struct platform_device *evb2_fb_devices[] __initdata = { + &msm_fb_device, + &mipi_dsi_truly_nt35516_qhd_v2_panel_device, + &mipi_dsi_novatek_nt35516_qhd_v2_panel_device, +}; + +// ------------------------------------------------------------------- + +void __init msm_msm7627a_allocate_memory_regions(void) +{ + void *addr; + unsigned long fb_size = MSM_FB_SIZE; + + if (machine_is_msm7625a_surf() || machine_is_msm7625a_ffa()) { + fb_size = MSM7x25A_MSM_FB_SIZE; + } else + if (MACHINE_IS_JSR_I6) { + fb_size = MSM8x25_MSM_FB_SIZE; + if (machine_is_msm8625_qrd5()) + fb_size = MSM8x25_I6_MSM_FB_SIZE; + } else + if (machine_is_msm8625q_skud()) { + fb_size = MSM8x25_MSM_FB_SIZE; + } + + addr = alloc_bootmem_align(fb_size, 0x1000); + msm_fb_resources[0].start = __pa(addr); + msm_fb_resources[0].end = msm_fb_resources[0].start + fb_size - 1; + pr_info("allocating %lu bytes at %p (%lx physical) for fb\n", fb_size, addr, __pa(addr)); + +#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE + fb_size = MSM_V4L2_VIDEO_OVERLAY_BUF_SIZE; + addr = alloc_bootmem_align(fb_size, 0x1000); + msm_v4l2_video_overlay_resources[0].start = __pa(addr); + msm_v4l2_video_overlay_resources[0].end = + msm_v4l2_video_overlay_resources[0].start + fb_size - 1; + pr_debug("allocating %lu bytes at %p (%lx physical) for v4l2\n", + fb_size, addr, __pa(addr)); +#endif + +} + +static struct msm_panel_common_pdata mdp_pdata = { + .gpio = 97, + .mdp_rev = MDP_REV_303, + .cont_splash_enabled = 0x1, +}; + +#define GPIO_LCDC_BRDG_PD 128 +#define GPIO_LCDC_BRDG_RESET_N 129 +#define GPIO_LCD_DSI_SEL 125 +#define LCDC_RESET_PHYS 0x90008014 + +enum { + DSI_SINGLE_LANE = 1, + DSI_TWO_LANES, +}; + +static int msm_fb_get_lane_config(void) +{ + /* For MSM7627A SURF/FFA and QRD */ + int rc = DSI_TWO_LANES; + if (machine_is_msm7625a_surf() || machine_is_msm7625a_ffa()) { + rc = DSI_SINGLE_LANE; + pr_info("DSI_SINGLE_LANES\n"); + } else { + pr_info("DSI_TWO_LANES\n"); + } + return rc; +} + +static int msm_fb_dsi_client_I6_reset(void) +{ + int rc = 0; + pr_err("linxc: %s \n", __func__); + rc = gpio_request(GPIO_I6_LCD_BRDG_RESET_N, "i6_lcd_brdg_reset_n"); + if (rc < 0) + pr_err("%s: failed to request i6 lcd brdg reset_n\n", __func__); + return rc; +} + +static int msm_fb_dsi_client_reset(void) +{ + int rc = 0; + if (MACHINE_IS_JSR_I6) + rc = msm_fb_dsi_client_I6_reset(); + return rc; +} + +static struct regulator_bulk_data regs_dsi[] = { + { .supply = "gp2", .min_uV = 2850000, .max_uV = 2850000 }, + { .supply = "msme1", .min_uV = 1800000, .max_uV = 1800000 }, +}; + +static int dsi_gpio_initialized; + +/* Common EXT power control for QRD */ +static struct regulator *gpio_reg_2p85v; +static struct regulator *gpio_reg_1p8v; +//static struct regulator *gpio_reg_3p3v; + +static int mipi_dsi_panel_msm_power(int on) +{ + int rc = 0; + + /* I2C-controlled GPIO Expander -init of the GPIOs very late */ + if (unlikely(!dsi_gpio_initialized)) { + pmapp_disp_backlight_init(); + + rc = gpio_request(GPIO_DISPLAY_PWR_EN, "gpio_disp_pwr"); + if (rc < 0) { + pr_err("failed to request gpio_disp_pwr\n"); + return rc; + } + rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs_dsi), regs_dsi); + if (rc) { + pr_err("%s: could not get regulators: %d\n", + __func__, rc); + goto fail_gpio2; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(regs_dsi), + regs_dsi); + if (rc) { + pr_err("%s: could not set voltages: %d\n", + __func__, rc); + goto fail_vreg; + } + if (pmapp_disp_backlight_set_brightness(100)) + pr_err("%s: backlight set brightness failed\n", __func__); + + dsi_gpio_initialized = 1; + } + + if (on) { + gpio_set_value_cansleep(GPIO_LCDC_BRDG_PD, 0); + gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 0); + msleep(20); + gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 1); + msleep(20); + } else { + gpio_set_value_cansleep(GPIO_LCDC_BRDG_PD, 1); + } + + rc = on ? regulator_bulk_enable(ARRAY_SIZE(regs_dsi), regs_dsi) : + regulator_bulk_disable(ARRAY_SIZE(regs_dsi), regs_dsi); + + if (rc) + pr_err("%s: could not %sable regulators: %d\n", + __func__, on ? "en" : "dis", rc); + + return rc; +fail_vreg: + regulator_bulk_free(ARRAY_SIZE(regs_dsi), regs_dsi); +fail_gpio2: +// gpio_free(GPIO_BACKLIGHT_EN); +//fail_gpio1: + gpio_free(GPIO_DISPLAY_PWR_EN); + dsi_gpio_initialized = 0; + return rc; +} + +static char mipi_dsi_splash_is_enabled(void); + +static int mipi_dsi_panel_power(int on) +{ + return mipi_dsi_panel_msm_power(on); +} + +#define MDP_303_VSYNC_GPIO 97 + +#ifdef CONFIG_FB_MSM_MIPI_DSI +static struct mipi_dsi_platform_data mipi_dsi_pdata = { + .vsync_gpio = MDP_303_VSYNC_GPIO, + .dsi_power_save = mipi_dsi_panel_power, + .dsi_client_reset = msm_fb_dsi_client_reset, + .get_lane_config = msm_fb_get_lane_config, + .splash_is_enabled = mipi_dsi_splash_is_enabled, +}; +#endif + +static char mipi_dsi_splash_is_enabled(void) +{ + return mdp_pdata.cont_splash_enabled; +} + +static char prim_panel_name[PANEL_NAME_MAX_LEN]; +static int __init prim_display_setup(char *param) +{ + if (strnlen(param, PANEL_NAME_MAX_LEN)) + strlcpy(prim_panel_name, param, PANEL_NAME_MAX_LEN); + return 0; +} +early_param("prim_display", prim_display_setup); + +static int disable_splash; + +void msm7x27a_set_display_params(char *prim_panel) +{ + int n0, n1; + const char * pn0 = "mipi_cmd_novatek_nt35516_qhd"; + const char * pn1 = "mipi_cmd_truly_nt35516_qhd"; + char * ppn = (char *)msm_fb_pdata.prim_panel_name; + if (strnlen(prim_panel, PANEL_NAME_MAX_LEN)) { + strlcpy(ppn, prim_panel, PANEL_NAME_MAX_LEN); + pr_err("msm_fb_pdata.prim_panel_name = %s\n", ppn); + } + if (strnlen(ppn, PANEL_NAME_MAX_LEN)) { + n0 = strncmp(ppn, pn0, strlen(pn0)); + n1 = strncmp(ppn, pn1, strlen(pn1)); + if (n0 && n1) + disable_splash = 1; + } +} + +void __init msm_fb_add_devices(void) +{ + int rc = 0; + + msm7x27a_set_display_params(prim_panel_name); + + if (MACHINE_IS_JSR_I6) { + if (disable_splash) + mdp_pdata.cont_splash_enabled = 0x0; + if (socinfo_get_platform_version() == 0x10000) { + platform_add_devices(evb_fb_devices, ARRAY_SIZE(evb_fb_devices)); + } + if (socinfo_get_platform_version() == 0x20000) { + platform_add_devices(evb2_fb_devices, ARRAY_SIZE(evb2_fb_devices)); + } + } + + msm_fb_register_device("mdp", &mdp_pdata); + +#ifdef CONFIG_FB_MSM_MIPI_DSI + msm_fb_register_device("mipi_dsi", &mipi_dsi_pdata); +#endif + if (MACHINE_IS_JSR_I6) { + gpio_reg_2p85v = regulator_get(&mipi_dsi_device.dev, "lcd_vdd"); + if (IS_ERR(gpio_reg_2p85v)) + pr_err("%s:ext_2p85v regulator get failed", __func__); + + gpio_reg_1p8v = regulator_get(&mipi_dsi_device.dev, "lcd_vddi"); + if (IS_ERR(gpio_reg_1p8v)) + pr_err("%s:ext_1p8v regulator get failed", __func__); + + if (mdp_pdata.cont_splash_enabled) { + /*Enable EXT_2.85 and 1.8 regulators*/ + rc = regulator_enable(gpio_reg_2p85v); + if (rc < 0) + pr_err("%s: reg enable failed\n", __func__); + rc = regulator_enable(gpio_reg_1p8v); + if (rc < 0) + pr_err("%s: reg enable failed\n", __func__); + } + } +} diff --git a/arch/arm/mach-msm/board-msm7x27.c b/arch/arm/mach-msm/board-msm7x27.c index 5f0d75f2..cca38b0f 100644 --- a/arch/arm/mach-msm/board-msm7x27.c +++ b/arch/arm/mach-msm/board-msm7x27.c @@ -54,7 +54,6 @@ #include #include #include -#include #include #ifdef CONFIG_USB_G_ANDROID diff --git a/arch/arm/mach-msm/board-msm7x27a.c b/arch/arm/mach-msm/board-msm7x27a.c index cf93590b..497d5704 100644 --- a/arch/arm/mach-msm/board-msm7x27a.c +++ b/arch/arm/mach-msm/board-msm7x27a.c @@ -38,7 +38,7 @@ #include #include #include -#include + #include #include #include @@ -49,7 +49,7 @@ #include #include #include -#include +#include #include #include #include "devices.h" @@ -68,8 +68,8 @@ #include -#define PMEM_KERNEL_EBI1_SIZE 0x3A000 -#define MSM_PMEM_AUDIO_SIZE 0xF0000 +#define RESERVE_KERNEL_EBI1_SIZE 0x3A000 +#define MSM_RESERVE_AUDIO_SIZE 0xF0000 #define BOOTLOADER_BASE_ADDR 0x10000 //start yang.chenglei@byd added for smem_kpanic @@ -207,20 +207,12 @@ static struct msm_i2c_platform_data msm_gsbi1_qup_i2c_pdata = { }; #ifdef CONFIG_ARCH_MSM7X27A -//lwl modify -#define MSM_PMEM_MDP_SIZE 0xC65D40 -//#define MSM_PMEM_MDP_SIZE 0 -//end -#define MSM7x25A_MSM_PMEM_MDP_SIZE 0x1500000 -#endif +#define MSM_RESERVE_MDP_SIZE 0x2300000 +#define MSM7x25A_MSM_RESERVE_MDP_SIZE 0x1500000 -#ifdef CONFIG_CMA -#define MSM_PMEM_ADSP_SIZE 0 -#define MSM7x25A_MSM_PMEM_ADSP_SIZE 0 +#define MSM_RESERVE_ADSP_SIZE 0x1300000 +#define MSM7x25A_MSM_RESERVE_ADSP_SIZE 0xB91000 #define CAMERA_ZSL_SIZE (SZ_1M * 60) -#else -#define MSM_PMEM_ADSP_SIZE 0xBAEB90 -#define MSM7x25A_MSM_PMEM_ADSP_SIZE 0xB91000 #endif #ifdef CONFIG_ION_MSM @@ -544,62 +536,22 @@ static struct msm_pm_boot_platform_data msm_pm_8625_boot_pdata __initdata = { .v_addr = MSM_CFG_CTL_BASE, }; -static struct android_pmem_platform_data android_pmem_adsp_pdata = { - .name = "pmem_adsp", - .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, - .cached = 1, - .memory_type = MEMTYPE_EBI1, -}; - -static struct platform_device android_pmem_adsp_device = { - .name = "android_pmem", - .id = 1, - .dev = { .platform_data = &android_pmem_adsp_pdata }, -}; - -static unsigned pmem_mdp_size = MSM_PMEM_MDP_SIZE; -static int __init pmem_mdp_size_setup(char *p) +static unsigned reserve_mdp_size = MSM_RESERVE_MDP_SIZE; +static int __init reserve_mdp_size_setup(char *p) { - pmem_mdp_size = memparse(p, NULL); + reserve_mdp_size = memparse(p, NULL); return 0; } -early_param("pmem_mdp_size", pmem_mdp_size_setup); +early_param("reserve_mdp_size", reserve_mdp_size_setup); -static unsigned pmem_adsp_size = MSM_PMEM_ADSP_SIZE; -static int __init pmem_adsp_size_setup(char *p) +static unsigned reserve_adsp_size = MSM_RESERVE_ADSP_SIZE; +static int __init reserve_adsp_size_setup(char *p) { - pmem_adsp_size = memparse(p, NULL); + reserve_adsp_size = memparse(p, NULL); return 0; } - -early_param("pmem_adsp_size", pmem_adsp_size_setup); - -static struct android_pmem_platform_data android_pmem_audio_pdata = { - .name = "pmem_audio", - .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, - .cached = 0, - .memory_type = MEMTYPE_EBI1, -}; - -static struct platform_device android_pmem_audio_device = { - .name = "android_pmem", - .id = 2, - .dev = { .platform_data = &android_pmem_audio_pdata }, -}; - -static struct android_pmem_platform_data android_pmem_pdata = { - .name = "pmem", - .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, - .cached = 1, - .memory_type = MEMTYPE_EBI1, -}; - -static struct platform_device android_pmem_device = { - .name = "android_pmem", - .id = 0, - .dev = { .platform_data = &android_pmem_pdata }, -}; +early_param("reserve_adsp_size", reserve_adsp_size_setup); static u32 msm_calculate_batt_capacity(u32 current_voltage); @@ -773,9 +725,6 @@ static struct platform_device *msm7627a_surf_ffa_devices[] __initdata = { static struct platform_device *common_devices[] __initdata = { &android_usb_device, - &android_pmem_device, - &android_pmem_adsp_device, - &android_pmem_audio_device, &msm_device_nand, &msm_device_snd, &msm_device_cad, @@ -803,44 +752,46 @@ static struct platform_device *msm8625_surf_devices[] __initdata = { &msm8625_kgsl_3d0, }; -static unsigned pmem_kernel_ebi1_size = PMEM_KERNEL_EBI1_SIZE; -static int __init pmem_kernel_ebi1_size_setup(char *p) +static unsigned reserve_kernel_ebi1_size = RESERVE_KERNEL_EBI1_SIZE; +static int __init reserve_kernel_ebi1_size_setup(char *p) { - pmem_kernel_ebi1_size = memparse(p, NULL); + reserve_kernel_ebi1_size = memparse(p, NULL); return 0; } -early_param("pmem_kernel_ebi1_size", pmem_kernel_ebi1_size_setup); +early_param("reserve_kernel_ebi1_size", reserve_kernel_ebi1_size_setup); -static unsigned pmem_audio_size = MSM_PMEM_AUDIO_SIZE; -static int __init pmem_audio_size_setup(char *p) +static unsigned reserve_audio_size = MSM_RESERVE_AUDIO_SIZE; +static int __init reserve_audio_size_setup(char *p) { - pmem_audio_size = memparse(p, NULL); + reserve_audio_size = memparse(p, NULL); return 0; } -early_param("pmem_audio_size", pmem_audio_size_setup); +early_param("reserve_audio_size", reserve_audio_size_setup); static void fix_sizes(void) { if (machine_is_msm7625a_surf() || machine_is_msm7625a_ffa()) { - pmem_mdp_size = MSM7x25A_MSM_PMEM_MDP_SIZE; - pmem_adsp_size = MSM7x25A_MSM_PMEM_ADSP_SIZE; + reserve_mdp_size = MSM7x25A_MSM_RESERVE_MDP_SIZE;; + reserve_adsp_size = MSM7x25A_MSM_RESERVE_ADSP_SIZE; } else { - pmem_mdp_size = MSM_PMEM_MDP_SIZE; - pmem_adsp_size = MSM_PMEM_ADSP_SIZE; + reserve_mdp_size = MSM_RESERVE_MDP_SIZE; + reserve_adsp_size = MSM_RESERVE_ADSP_SIZE; } //if (get_ddr_size() > SZ_512M) - // pmem_adsp_size = CAMERA_ZSL_SIZE; + // reserve_adsp_size = CAMERA_ZSL_SIZE; #ifdef CONFIG_ION_MSM - msm_ion_audio_size = MSM_PMEM_AUDIO_SIZE; - msm_ion_sf_size = pmem_mdp_size; + msm_ion_audio_size = MSM_RESERVE_AUDIO_SIZE; + msm_ion_sf_size = reserve_mdp_size; #ifdef CONFIG_CMA - msm_ion_camera_size = CAMERA_ZSL_SIZE; - msm_ion_camera_size_carving = 0; + if (get_ddr_size() > SZ_256M) + reserve_adsp_size = CAMERA_ZSL_SIZE; + msm_ion_camera_size = reserve_adsp_size; + msm_ion_camera_size_carving = 0; #else - msm_ion_camera_size = pmem_adsp_size; - msm_ion_camera_size_carving = msm_ion_camera_size; + msm_ion_camera_size = reserve_adsp_size; + msm_ion_camera_size_carving = msm_ion_camera_size; #endif #endif } @@ -880,7 +831,7 @@ struct ion_platform_heap msm7x27a_heaps[] = { .name = ION_VMALLOC_HEAP_NAME, }, #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION - /* PMEM_ADSP = CAMERA */ + /* ION_ADSP = CAMERA */ { .id = ION_CAMERA_HEAP_ID, .type = CAMERA_HEAP_TYPE, @@ -897,7 +848,7 @@ struct ion_platform_heap msm7x27a_heaps[] = { .memory_type = ION_EBI_TYPE, .extra_data = (void *)&co_ion_pdata, }, - /* PMEM_MDP = SF */ + /* ION_MDP = SF */ { .id = ION_SF_HEAP_ID, .type = ION_HEAP_TYPE_CARVEOUT, @@ -942,54 +893,11 @@ static struct memtype_reserve msm7x27a_reserve_table[] __initdata = { }, }; -#ifdef CONFIG_ANDROID_PMEM -#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION -static struct android_pmem_platform_data *pmem_pdata_array[] __initdata = { - &android_pmem_adsp_pdata, - &android_pmem_audio_pdata, - &android_pmem_pdata, -}; -#endif -#endif - -static void __init size_pmem_devices(void) -{ -#ifdef CONFIG_ANDROID_PMEM -#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION - android_pmem_adsp_pdata.size = pmem_adsp_size; - android_pmem_pdata.size = pmem_mdp_size; - android_pmem_audio_pdata.size = pmem_audio_size; -#endif -#endif -} - -#ifdef CONFIG_ANDROID_PMEM -#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION -static void __init reserve_memory_for(struct android_pmem_platform_data *p) -{ - msm7x27a_reserve_table[p->memory_type].size += p->size; -} -#endif -#endif - -static void __init reserve_pmem_memory(void) -{ -#ifdef CONFIG_ANDROID_PMEM -#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION - unsigned int i; - for (i = 0; i < ARRAY_SIZE(pmem_pdata_array); ++i) - reserve_memory_for(pmem_pdata_array[i]); - - msm7x27a_reserve_table[MEMTYPE_EBI1].size += pmem_kernel_ebi1_size; -#endif -#endif -} - static void __init size_ion_devices(void) { #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION ion_pdata.heaps[1].size = msm_ion_camera_size; - ion_pdata.heaps[2].size = PMEM_KERNEL_EBI1_SIZE; + ion_pdata.heaps[2].size = RESERVE_KERNEL_EBI1_SIZE; ion_pdata.heaps[3].size = msm_ion_sf_size; ion_pdata.heaps[4].size = msm_ion_audio_size; #endif @@ -998,8 +906,8 @@ static void __init size_ion_devices(void) static void __init reserve_ion_memory(void) { #if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) - msm7x27a_reserve_table[MEMTYPE_EBI1].size += PMEM_KERNEL_EBI1_SIZE; - msm7x27a_reserve_table[MEMTYPE_EBI1].size += + msm7x27a_reserve_table[MEMTYPE_EBI1].size += RESERVE_KERNEL_EBI1_SIZE; + msm7x27a_reserve_table[MEMTYPE_EBI1].size += msm_ion_camera_size_carving; msm7x27a_reserve_table[MEMTYPE_EBI1].size += msm_ion_sf_size; #endif @@ -1008,8 +916,6 @@ static void __init reserve_ion_memory(void) static void __init msm7x27a_calculate_reserve_sizes(void) { fix_sizes(); - size_pmem_devices(); - reserve_pmem_memory(); size_ion_devices(); reserve_ion_memory(); } diff --git a/arch/arm/mach-msm/board-msm7x27a_i6.c b/arch/arm/mach-msm/board-msm7x27a_i6.c new file mode 100644 index 00000000..3bdcb7bc --- /dev/null +++ b/arch/arm/mach-msm/board-msm7x27a_i6.c @@ -0,0 +1,1191 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" +#include "timer.h" +#include "board-msm7x27a-regulator.h" +#include "devices-msm7x2xa.h" +#include "pm.h" +#include +#include +#include "pm-boot.h" +#include "board-msm7627a.h" + +#define CAMERA_HEAP_BASE 0x0 +#ifdef CONFIG_CMA +#define CAMERA_HEAP_TYPE ION_HEAP_TYPE_DMA +#else +#define CAMERA_HEAP_TYPE ION_HEAP_TYPE_CARVEOUT +#endif + +#define RESERVE_KERNEL_EBI1_SIZE 0x3A000 +#define MSM_RESERVE_AUDIO_SIZE 0xF0000 +#define BOOTLOADER_BASE_ADDR 0x10000 + +#if defined(CONFIG_GPIO_SX150X) +enum { + SX150X_CORE, +}; + +static struct sx150x_platform_data sx150x_data[] __initdata = { + [SX150X_CORE] = { + .gpio_base = GPIO_CORE_EXPANDER_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0, + .io_pulldn_ena = 0x02, + .io_open_drain_ena = 0xfef8, + .irq_summary = -1, + }, +}; +#endif + + +#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE) +static struct platform_device msm_wlan_ar6000_pm_device = { + .name = "wlan_ar6000_pm_dev", + .id = -1, +}; +#endif + +#if defined(CONFIG_I2C) && defined(CONFIG_GPIO_SX150X) +static struct i2c_board_info core_exp_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("sx1509q", 0x3e), + }, +}; + +static void __init register_i2c_devices(void) +{ + if (machine_is_msm7x27a_surf() || machine_is_msm7625a_surf() || + machine_is_msm8625_surf()) + sx150x_data[SX150X_CORE].io_open_drain_ena = 0xe0f0; + + core_exp_i2c_info[0].platform_data = + &sx150x_data[SX150X_CORE]; + + i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID, + core_exp_i2c_info, + ARRAY_SIZE(core_exp_i2c_info)); +} +#endif + +static struct msm_gpio qup_i2c_gpios_io[] = { + { GPIO_CFG(60, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(61, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, + { GPIO_CFG(131, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(132, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, +}; + +static struct msm_gpio qup_i2c_gpios_hw[] = { + { GPIO_CFG(60, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(61, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, + { GPIO_CFG(131, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(132, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, +}; + +static void gsbi_qup_i2c_gpio_config(int adap_id, int config_type) +{ + int rc; + + if (adap_id < 0 || adap_id > 1) + return; + + /* Each adapter gets 2 lines from the table */ + if (config_type) + rc = msm_gpios_request_enable(&qup_i2c_gpios_hw[adap_id*2], 2); + else + rc = msm_gpios_request_enable(&qup_i2c_gpios_io[adap_id*2], 2); + if (rc < 0) + pr_err("QUP GPIO request/enable failed: %d\n", rc); +} + +static struct msm_i2c_platform_data msm_gsbi0_qup_i2c_pdata = { + .clk_freq = 100000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; + +static struct msm_i2c_platform_data msm_gsbi1_qup_i2c_pdata = { + .clk_freq = 100000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; + +#ifdef CONFIG_ARCH_MSM7X27A +#define MSM_RESERVE_MDP_SIZE 0x2300000 +#define MSM7x25A_MSM_RESERVE_MDP_SIZE 0x1500000 + +#define MSM_RESERVE_ADSP_SIZE 0x2000000 +#define MSM7x25A_MSM_RESERVE_ADSP_SIZE 0xB91000 +#define CAMERA_ZSL_SIZE (SZ_1M * 60) +#endif + +#ifdef CONFIG_ION_MSM +#define MSM_ION_HEAP_NUM 5 +static struct platform_device ion_dev; +static int msm_ion_camera_size; +static int msm_ion_audio_size; +static int msm_ion_sf_size; +static int msm_ion_camera_size_carving; +#endif + +#define CAMERA_HEAP_BASE 0x0 +#ifdef CONFIG_CMA +#define CAMERA_HEAP_TYPE ION_HEAP_TYPE_DMA +#else +#define CAMERA_HEAP_TYPE ION_HEAP_TYPE_CARVEOUT +#endif + +static struct android_usb_platform_data android_usb_pdata = { + .update_pid_and_serial_num = usb_diag_update_pid_and_serial_num, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + +#ifdef CONFIG_USB_EHCI_MSM_72K +static void msm_hsusb_vbus_power(unsigned phy_info, int on) +{ + int rc = 0; + unsigned gpio; + + gpio = GPIO_HOST_VBUS_EN; + + rc = gpio_request(gpio, "i2c_host_vbus_en"); + if (rc < 0) { + pr_err("failed to request %d GPIO\n", gpio); + return; + } + gpio_direction_output(gpio, !!on); + gpio_set_value_cansleep(gpio, !!on); + gpio_free(gpio); +} + +static struct msm_usb_host_platform_data msm_usb_host_pdata = { + .phy_info = (USB_PHY_INTEGRATED | USB_PHY_MODEL_45NM), +}; + +static void __init msm7x2x_init_host(void) +{ + msm_add_host(0, &msm_usb_host_pdata); +} +#endif + +#ifdef CONFIG_USB_MSM_OTG_72K +static int hsusb_rpc_connect(int connect) +{ + if (connect) + return msm_hsusb_rpc_connect(); + else + return msm_hsusb_rpc_close(); +} + +static struct regulator *reg_hsusb; +static int msm_hsusb_ldo_init(int init) +{ + int rc = 0; + + if (init) { + reg_hsusb = regulator_get(NULL, "usb"); + if (IS_ERR(reg_hsusb)) { + rc = PTR_ERR(reg_hsusb); + pr_err("%s: could not get regulator: %d\n", + __func__, rc); + goto out; + } + + rc = regulator_set_voltage(reg_hsusb, 3300000, 3300000); + if (rc) { + pr_err("%s: could not set voltage: %d\n", + __func__, rc); + goto reg_free; + } + + return 0; + } + /* else fall through */ +reg_free: + regulator_put(reg_hsusb); +out: + reg_hsusb = NULL; + return rc; +} + +static int msm_hsusb_ldo_enable(int enable) +{ + static int ldo_status; + + if (IS_ERR_OR_NULL(reg_hsusb)) + return reg_hsusb ? PTR_ERR(reg_hsusb) : -ENODEV; + + if (ldo_status == enable) + return 0; + + ldo_status = enable; + + return enable ? + regulator_enable(reg_hsusb) : + regulator_disable(reg_hsusb); +} + +#ifndef CONFIG_USB_EHCI_MSM_72K +static int msm_hsusb_pmic_notif_init(void (*callback)(int online), int init) +{ + int ret = 0; + + if (init) + ret = msm_pm_app_rpc_init(callback); + else + msm_pm_app_rpc_deinit(callback); + + return ret; +} +#endif + +static struct msm_otg_platform_data msm_otg_pdata = { +#ifndef CONFIG_USB_EHCI_MSM_72K + .pmic_vbus_notif_init = msm_hsusb_pmic_notif_init, +#else + .vbus_power = msm_hsusb_vbus_power, +#endif + .rpc_connect = hsusb_rpc_connect, + .pemp_level = PRE_EMPHASIS_WITH_20_PERCENT, + .cdr_autoreset = CDR_AUTO_RESET_DISABLE, + .drv_ampl = HS_DRV_AMPLITUDE_DEFAULT, + .se1_gating = SE1_GATING_DISABLE, + .ldo_init = msm_hsusb_ldo_init, + .ldo_enable = msm_hsusb_ldo_enable, + .chg_init = hsusb_chg_init, + .chg_connected = hsusb_chg_connected, + .chg_vbus_draw = hsusb_chg_vbus_draw, +}; +#endif + +static struct msm_hsusb_gadget_platform_data msm_gadget_pdata = { + .is_phy_status_timer_on = 1, +}; + +static struct resource smc91x_resources[] = { + [0] = { + .start = 0x90000300, + .end = 0x900003ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MSM_GPIO_TO_INT(4), + .end = MSM_GPIO_TO_INT(4), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +#ifdef CONFIG_SERIAL_MSM_HS +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .inject_rx_on_wakeup = 1, + .rx_to_inject = 0xFD, +}; +#endif +static struct msm_pm_platform_data msm7x27a_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 16000, + .residency = 20000, + }, + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 12000, + .residency = 20000, + }, + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 1, + .latency = 2000, + .residency = 0, + }, + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 0, + }, +}; + +static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS, + .p_addr = 0, +}; + +/* 8625 PM platform data */ +static struct msm_pm_platform_data msm8625_pm_data[MSM_PM_SLEEP_MODE_NR * 2] = { + /* CORE0 entries */ + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 16000, + .residency = 20000, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 12000, + .residency = 20000, + }, + + /* picked latency & redisdency values from 7x30 */ + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 500, + .residency = 6000, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 10, + }, + + /* picked latency & redisdency values from 7x30 */ + [MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 500, + .residency = 6000, + }, + + [MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 10, + }, + +}; + +static struct msm_pm_boot_platform_data msm_pm_8625_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR, + .v_addr = MSM_CFG_CTL_BASE, +}; + +static unsigned reserve_mdp_size = MSM_RESERVE_MDP_SIZE; +static int __init reserve_mdp_size_setup(char *p) +{ + reserve_mdp_size = memparse(p, NULL); + return 0; +} + +early_param("reserve_mdp_size", reserve_mdp_size_setup); + +static unsigned reserve_adsp_size = MSM_RESERVE_ADSP_SIZE; +static int __init reserve_adsp_size_setup(char *p) +{ + reserve_adsp_size = memparse(p, NULL); + return 0; +} + +early_param("reserve_adsp_size", reserve_adsp_size_setup); + +static u32 msm_calculate_batt_capacity(u32 current_voltage); + +static struct msm_psy_batt_pdata msm_psy_batt_data = { + .voltage_min_design = 3200, + .voltage_max_design = 4200, +// .voltage_fail_safe = 3340, + .avail_chg_sources = AC_CHG | USB_CHG , + .batt_technology = POWER_SUPPLY_TECHNOLOGY_LION, + .calculate_capacity = &msm_calculate_batt_capacity, +}; + +static u32 msm_calculate_batt_capacity(u32 current_voltage) +{ + u32 low_voltage = msm_psy_batt_data.voltage_min_design; + u32 high_voltage = msm_psy_batt_data.voltage_max_design; + + if (current_voltage <= low_voltage) + return 0; + else if (current_voltage >= high_voltage) + return 100; + else + return (current_voltage - low_voltage) * 100 + / (high_voltage - low_voltage); +} + +static struct platform_device msm_batt_device = { + .name = "msm-battery", + .id = -1, + .dev.platform_data = &msm_psy_batt_data, +}; + +static struct smsc911x_platform_config smsc911x_config = { + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH, + .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, + .flags = SMSC911X_USE_16BIT, +}; + +static struct resource smsc911x_resources[] = { + [0] = { + .start = 0x90000000, + .end = 0x90007fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MSM_GPIO_TO_INT(48), + .end = MSM_GPIO_TO_INT(48), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL, + }, +}; + +static struct platform_device smsc911x_device = { + .name = "smsc911x", + .id = 0, + .num_resources = ARRAY_SIZE(smsc911x_resources), + .resource = smsc911x_resources, + .dev = { + .platform_data = &smsc911x_config, + }, +}; + +static struct msm_gpio smsc911x_gpios[] = { + { GPIO_CFG(48, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_6MA), + "smsc911x_irq" }, + { GPIO_CFG(49, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_6MA), + "eth_fifo_sel" }, +}; + +static char *msm_adc_surf_device_names[] = { + "XO_ADC", +}; + +static struct msm_adc_platform_data msm_adc_pdata = { + .dev_names = msm_adc_surf_device_names, + .num_adc = ARRAY_SIZE(msm_adc_surf_device_names), + .target_hw = MSM_8x25, +}; + +static struct platform_device msm_adc_device = { + .name = "msm_adc", + .id = -1, + .dev = { + .platform_data = &msm_adc_pdata, + }, +}; + +#define ETH_FIFO_SEL_GPIO 49 +static void msm7x27a_cfg_smsc911x(void) +{ + int res; + + res = msm_gpios_request_enable(smsc911x_gpios, + ARRAY_SIZE(smsc911x_gpios)); + if (res) { + pr_err("%s: unable to enable gpios for SMSC911x\n", __func__); + return; + } + + /* ETH_FIFO_SEL */ + res = gpio_direction_output(ETH_FIFO_SEL_GPIO, 0); + if (res) { + pr_err("%s: unable to get direction for gpio %d\n", __func__, + ETH_FIFO_SEL_GPIO); + msm_gpios_disable_free(smsc911x_gpios, + ARRAY_SIZE(smsc911x_gpios)); + return; + } + gpio_set_value(ETH_FIFO_SEL_GPIO, 0); +} + +#if defined(CONFIG_SERIAL_MSM_HSL_CONSOLE) \ + && defined(CONFIG_MSM_SHARED_GPIO_FOR_UART2DM) +static struct msm_gpio uart2dm_gpios[] = { + {GPIO_CFG(19, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "uart2dm_rfr_n" }, + {GPIO_CFG(20, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "uart2dm_cts_n" }, + {GPIO_CFG(21, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "uart2dm_rx" }, + {GPIO_CFG(108, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "uart2dm_tx" }, +}; + +static void msm7x27a_cfg_uart2dm_serial(void) +{ + int ret; + ret = msm_gpios_request_enable(uart2dm_gpios, + ARRAY_SIZE(uart2dm_gpios)); + if (ret) + pr_err("%s: unable to enable gpios for uart2dm\n", __func__); +} +#else +static void msm7x27a_cfg_uart2dm_serial(void) { } +#endif + +static struct platform_device *rumi_sim_devices[] __initdata = { + &msm_device_dmov, + &msm_device_smd, + &smc91x_device, + &msm_device_uart1, + &msm_device_nand, + &msm_device_uart_dm1, + &msm_gsbi0_qup_i2c_device, + &msm_gsbi1_qup_i2c_device, +}; + +static struct platform_device *msm8625_rumi3_devices[] __initdata = { + &msm8625_device_dmov, + &msm8625_device_smd, + &msm8625_device_uart1, + &msm8625_gsbi0_qup_i2c_device, +}; + +static struct platform_device *msm7627a_surf_ffa_devices[] __initdata = { + &msm_device_dmov, + &msm_device_smd, + &msm_device_uart1, + &msm_device_uart_dm1, + &msm_device_uart_dm2, + &msm_gsbi0_qup_i2c_device, + &msm_gsbi1_qup_i2c_device, + &msm_device_otg, + &msm_device_gadget_peripheral, + &smsc911x_device, + &msm_kgsl_3d0, +}; + +static struct platform_device *common_devices[] __initdata = { + &android_usb_device, + &msm_device_nand, + &msm_device_snd, + &msm_device_cad, + &msm_device_adspdec, + &asoc_msm_pcm, + &asoc_msm_dai0, + &asoc_msm_dai1, + &msm_batt_device, + &msm_adc_device, +#ifdef CONFIG_ION_MSM + &ion_dev, +#endif +}; + +static struct platform_device *msm8625_surf_devices[] __initdata = { + &msm8625_device_dmov, + &msm8625_device_uart1, + &msm8625_device_uart_dm1, + &msm8625_device_uart_dm2, + &msm8625_gsbi0_qup_i2c_device, + &msm8625_gsbi1_qup_i2c_device, + &msm8625_device_smd, + &msm8625_device_otg, + &msm8625_device_gadget_peripheral, + &msm8625_kgsl_3d0, +}; + +static unsigned reserve_kernel_ebi1_size = RESERVE_KERNEL_EBI1_SIZE; +static int __init reserve_kernel_ebi1_size_setup(char *p) +{ + reserve_kernel_ebi1_size = memparse(p, NULL); + return 0; +} +early_param("reserve_kernel_ebi1_size", reserve_kernel_ebi1_size_setup); + +static unsigned reserve_audio_size = MSM_RESERVE_AUDIO_SIZE; +static int __init reserve_audio_size_setup(char *p) +{ + reserve_audio_size = memparse(p, NULL); + return 0; +} +early_param("reserve_audio_size", reserve_audio_size_setup); + +static void fix_sizes(void) +{ + if (machine_is_msm7625a_surf() || machine_is_msm7625a_ffa()) { + reserve_mdp_size = MSM7x25A_MSM_RESERVE_MDP_SIZE; + reserve_adsp_size = MSM7x25A_MSM_RESERVE_ADSP_SIZE; + } else { + reserve_mdp_size = MSM_RESERVE_MDP_SIZE; + reserve_adsp_size = MSM_RESERVE_ADSP_SIZE; + } + + if (get_ddr_size() > SZ_512M) + reserve_adsp_size = CAMERA_ZSL_SIZE; +#ifdef CONFIG_ION_MSM + msm_ion_audio_size = MSM_RESERVE_AUDIO_SIZE; + msm_ion_sf_size = reserve_mdp_size; +#ifdef CONFIG_CMA + if (get_ddr_size() > SZ_256M) + reserve_adsp_size = CAMERA_ZSL_SIZE; + msm_ion_camera_size = reserve_adsp_size; + msm_ion_camera_size_carving = 0; +#else + msm_ion_camera_size = reserve_adsp_size; + msm_ion_camera_size_carving = msm_ion_camera_size; +#endif +#endif +} + +#ifdef CONFIG_ION_MSM +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION +static struct ion_co_heap_pdata co_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, +}; + +static struct ion_co_heap_pdata co_mm_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, +}; + +static u64 msm_dmamask = DMA_BIT_MASK(32); + +static struct platform_device ion_cma_device = { + .name = "ion-cma-device", + .id = -1, + .dev = { + .dma_mask = &msm_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + } +}; +#endif + +/** + * These heaps are listed in the order they will be allocated. + * Don't swap the order unless you know what you are doing! + */ +struct ion_platform_heap msm7x27a_heaps[] = { + { + .id = ION_SYSTEM_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM, + .name = ION_VMALLOC_HEAP_NAME, + }, +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + /* ION_ADSP = CAMERA */ + { + .id = ION_CAMERA_HEAP_ID, + .type = CAMERA_HEAP_TYPE, + .name = ION_CAMERA_HEAP_NAME, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_mm_ion_pdata, + .priv = (void *)&ion_cma_device.dev, + }, + /* AUDIO HEAP 1*/ + { + .id = ION_AUDIO_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_AUDIO_HEAP_NAME, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + }, + /* ION_MDP = SF */ + { + .id = ION_SF_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_SF_HEAP_NAME, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + }, + /* AUDIO HEAP 2*/ + { + .id = ION_AUDIO_HEAP_BL_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_AUDIO_BL_HEAP_NAME, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + .base = BOOTLOADER_BASE_ADDR, + }, + +#endif +}; + +static struct ion_platform_data ion_pdata = { + .nr = MSM_ION_HEAP_NUM, + .has_outer_cache = 1, + .heaps = msm7x27a_heaps, +}; + +static struct platform_device ion_dev = { + .name = "ion-msm", + .id = 1, + .dev = { .platform_data = &ion_pdata }, +}; +#endif + +static struct memtype_reserve msm7x27a_reserve_table[] __initdata = { + [MEMTYPE_SMI] = { + }, + [MEMTYPE_EBI0] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, + [MEMTYPE_EBI1] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, +}; + +static void __init size_ion_devices(void) +{ +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + ion_pdata.heaps[1].size = msm_ion_camera_size; + ion_pdata.heaps[2].size = RESERVE_KERNEL_EBI1_SIZE; + ion_pdata.heaps[3].size = msm_ion_sf_size; + ion_pdata.heaps[4].size = msm_ion_audio_size; +#endif +} + +static void __init reserve_ion_memory(void) +{ +#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + msm7x27a_reserve_table[MEMTYPE_EBI1].size += RESERVE_KERNEL_EBI1_SIZE; + msm7x27a_reserve_table[MEMTYPE_EBI1].size += + msm_ion_camera_size_carving; + msm7x27a_reserve_table[MEMTYPE_EBI1].size += msm_ion_sf_size; +#endif +} + +static void __init msm7x27a_calculate_reserve_sizes(void) +{ + fix_sizes(); + size_ion_devices(); + reserve_ion_memory(); +} + +static int msm7x27a_paddr_to_memtype(unsigned int paddr) +{ + return MEMTYPE_EBI1; +} + +static struct reserve_info msm7x27a_reserve_info __initdata = { + .memtype_reserve_table = msm7x27a_reserve_table, + .calculate_reserve_sizes = msm7x27a_calculate_reserve_sizes, + .paddr_to_memtype = msm7x27a_paddr_to_memtype, +}; + +static void __init msm7x27a_reserve(void) +{ + reserve_info = &msm7x27a_reserve_info; + memblock_remove(BOOTLOADER_BASE_ADDR, msm_ion_audio_size); + msm_reserve(); +#ifdef CONFIG_CMA + dma_declare_contiguous( + &ion_cma_device.dev, + msm_ion_camera_size, + CAMERA_HEAP_BASE, + 0x26000000); +#endif + +#ifdef CONFIG_SRECORDER_MSM + if (0x0 != get_mempools_pstart_addr()) + { + s_srecorder_reserved_mem_phys_start_addr = get_mempools_pstart_addr();// - SRECORDER_RESERVED_MEM_SIZE; + } + else + { + printk(">>>> Can't know the start address for S-Recorder's reserved memory!\n"); + } +#endif /* CONFIG_SRECORDER_MSM */ +} + +static void __init msm8625_reserve(void) +{ + msm7x27a_reserve(); + memblock_remove(MSM8625_SECONDARY_PHYS, SZ_8); + memblock_remove(MSM8625_WARM_BOOT_PHYS, SZ_32); +} + +static void __init msm7x27a_device_i2c_init(void) +{ + msm_gsbi0_qup_i2c_device.dev.platform_data = &msm_gsbi0_qup_i2c_pdata; + msm_gsbi1_qup_i2c_device.dev.platform_data = &msm_gsbi1_qup_i2c_pdata; +} + +static void __init msm8625_device_i2c_init(void) +{ + msm8625_gsbi0_qup_i2c_device.dev.platform_data = + &msm_gsbi0_qup_i2c_pdata; + msm8625_gsbi1_qup_i2c_device.dev.platform_data = + &msm_gsbi1_qup_i2c_pdata; +} + +#define MSM_EBI2_PHYS 0xa0d00000 +#define MSM_EBI2_XMEM_CS2_CFG1 0xa0d10030 + +static void __init msm7x27a_init_ebi2(void) +{ + uint32_t ebi2_cfg; + void __iomem *ebi2_cfg_ptr; + + ebi2_cfg_ptr = ioremap_nocache(MSM_EBI2_PHYS, sizeof(uint32_t)); + if (!ebi2_cfg_ptr) + return; + + ebi2_cfg = readl(ebi2_cfg_ptr); + if (machine_is_msm7x27a_rumi3() || machine_is_msm7x27a_surf() || + machine_is_msm7625a_surf() || machine_is_msm8625_surf()) + ebi2_cfg |= (1 << 4); /* CS2 */ + + writel(ebi2_cfg, ebi2_cfg_ptr); + iounmap(ebi2_cfg_ptr); + + /* Enable A/D MUX[bit 31] from EBI2_XMEM_CS2_CFG1 */ + ebi2_cfg_ptr = ioremap_nocache(MSM_EBI2_XMEM_CS2_CFG1, + sizeof(uint32_t)); + if (!ebi2_cfg_ptr) + return; + + ebi2_cfg = readl(ebi2_cfg_ptr); + if (machine_is_msm7x27a_surf() || machine_is_msm7625a_surf()) + ebi2_cfg |= (1 << 31); + + writel(ebi2_cfg, ebi2_cfg_ptr); + iounmap(ebi2_cfg_ptr); +} + +static struct platform_device msm_proccomm_regulator_dev = { + .name = PROCCOMM_REGULATOR_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &msm7x27a_proccomm_regulator_data + } +}; + +static void msm_adsp_add_pdev(void) +{ + int rc = 0; + struct rpc_board_dev *rpc_adsp_pdev; + + rpc_adsp_pdev = kzalloc(sizeof(struct rpc_board_dev), GFP_KERNEL); + if (rpc_adsp_pdev == NULL) { + pr_err("%s: Memory Allocation failure\n", __func__); + return; + } + rpc_adsp_pdev->prog = ADSP_RPC_PROG; + + if (cpu_is_msm8625() || cpu_is_msm8625q()) + rpc_adsp_pdev->pdev = msm8625_device_adsp; + else + rpc_adsp_pdev->pdev = msm_adsp_device; + rc = msm_rpc_add_board_dev(rpc_adsp_pdev, 1); + if (rc < 0) { + pr_err("%s: return val: %d\n", __func__, rc); + kfree(rpc_adsp_pdev); + } +} + +static void __init msm7627a_rumi3_init(void) +{ + msm7x27a_init_ebi2(); + platform_add_devices(rumi_sim_devices, + ARRAY_SIZE(rumi_sim_devices)); +} + +static void __init msm8625_rumi3_init(void) +{ + msm7x2x_misc_init(); + msm_adsp_add_pdev(); + msm8625_device_i2c_init(); + platform_add_devices(msm8625_rumi3_devices, + ARRAY_SIZE(msm8625_rumi3_devices)); + + msm_pm_set_platform_data(msm8625_pm_data, + ARRAY_SIZE(msm8625_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_8625_boot_pdata)); + msm8x25_spm_device_init(); + msm_pm_register_cpr_ops(); +} + +#define UART1DM_RX_GPIO 45 + +#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE) +static int __init msm7x27a_init_ar6000pm(void) +{ + msm_wlan_ar6000_pm_device.dev.platform_data = &ar600x_wlan_power; + return platform_device_register(&msm_wlan_ar6000_pm_device); +} +#else +static int __init msm7x27a_init_ar6000pm(void) { return 0; } +#endif + +static void __init msm7x27a_init_regulators(void) +{ + int rc = platform_device_register(&msm_proccomm_regulator_dev); + if (rc) + pr_err("%s: could not register regulator device: %d\n", + __func__, rc); +} + +static void __init msm7x27a_add_footswitch_devices(void) +{ + platform_add_devices(msm_footswitch_devices, + msm_num_footswitch_devices); +} + +static void __init msm7x27a_add_platform_devices(void) +{ + if (machine_is_msm8625_surf() || machine_is_msm8625_ffa()) { + platform_add_devices(msm8625_surf_devices, + ARRAY_SIZE(msm8625_surf_devices)); + } else { + platform_add_devices(msm7627a_surf_ffa_devices, + ARRAY_SIZE(msm7627a_surf_ffa_devices)); + } + + platform_add_devices(common_devices, + ARRAY_SIZE(common_devices)); +} + +static void __init msm7x27a_uartdm_config(void) +{ + msm7x27a_cfg_uart2dm_serial(); + msm_uart_dm1_pdata.wakeup_irq = gpio_to_irq(UART1DM_RX_GPIO); + if (cpu_is_msm8625() || cpu_is_msm8625q()) + msm8625_device_uart_dm1.dev.platform_data = + &msm_uart_dm1_pdata; + else + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; +} + +static void __init msm7x27a_otg_gadget(void) +{ + if (cpu_is_msm8625() || cpu_is_msm8625q()) { + msm_otg_pdata.swfi_latency = + msm8625_pm_data[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].latency; + msm8625_device_otg.dev.platform_data = &msm_otg_pdata; + msm8625_device_gadget_peripheral.dev.platform_data = + &msm_gadget_pdata; + } else { + msm_otg_pdata.swfi_latency = + msm7x27a_pm_data[ + MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency; + msm_device_otg.dev.platform_data = &msm_otg_pdata; + msm_device_gadget_peripheral.dev.platform_data = + &msm_gadget_pdata; + } +} + +static void __init msm7x27a_pm_init(void) +{ + if (machine_is_msm8625_surf() || machine_is_msm8625_ffa()) { + msm_pm_set_platform_data(msm8625_pm_data, + ARRAY_SIZE(msm8625_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_8625_boot_pdata)); + msm8x25_spm_device_init(); + msm_pm_register_cpr_ops(); + } else { + msm_pm_set_platform_data(msm7x27a_pm_data, + ARRAY_SIZE(msm7x27a_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + } + + msm_pm_register_irqs(); +} + +static void __init msm7x2x_init(void) +{ + msm7x2x_misc_init(); + + /* Initialize regulators first so that other devices can use them */ + msm7x27a_init_regulators(); + msm_adsp_add_pdev(); + if (cpu_is_msm8625()) + msm8625_device_i2c_init(); + else + msm7x27a_device_i2c_init(); + msm7x27a_init_ebi2(); + msm7x27a_uartdm_config(); + + msm7x27a_otg_gadget(); + msm7x27a_cfg_smsc911x(); + + msm7x27a_add_footswitch_devices(); + msm7x27a_add_platform_devices(); + /* Ensure ar6000pm device is registered before MMC/SDC */ + msm7x27a_init_ar6000pm(); + msm7627a_init_mmc(); + msm_fb_add_devices(); + msm7x2x_init_host(); + msm7x27a_pm_init(); + register_i2c_devices(); +#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE) + msm7627a_bt_power_init(); +#endif + msm7627a_camera_init(); + msm7627a_add_io_devices(); + /*7x25a kgsl initializations*/ + msm7x25a_kgsl_3d0_init(); + /*8x25 kgsl initializations*/ + msm8x25_kgsl_3d0_init(); +} + +static void __init msm7x2x_init_early(void) +{ + msm_msm7627a_allocate_memory_regions(); +} + +MACHINE_START(MSM7X27A_RUMI3, "QCT MSM7x27a RUMI3") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7627a_rumi3_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7X27A_SURF, "QCT MSM7x27a SURF") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7X27A_FFA, "QCT MSM7x27a FFA") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7625A_SURF, "QCT MSM7625a SURF") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7625A_FFA, "QCT MSM7625a FFA") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM8625_RUMI3, "QCT MSM8625 RUMI3") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm8625_rumi3_init, + .timer = &msm_timer, + .handle_irq = gic_handle_irq, +MACHINE_END +MACHINE_START(MSM8625_SURF, "QCT MSM8625 SURF") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = gic_handle_irq, +MACHINE_END +MACHINE_START(MSM8625_FFA, "QCT MSM8625 FFA") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = gic_handle_irq, +MACHINE_END diff --git a/arch/arm/mach-msm/board-msm7x27a_i6.cd b/arch/arm/mach-msm/board-msm7x27a_i6.cd new file mode 100644 index 00000000..660bbece --- /dev/null +++ b/arch/arm/mach-msm/board-msm7x27a_i6.cd @@ -0,0 +1,1237 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" +#include "timer.h" +#include "board-msm7x27a-regulator.h" +#include "devices-msm7x2xa.h" +#include "pm.h" +#include +#include +#include "pm-boot.h" +#include "board-msm7627a.h" + +#define CAMERA_HEAP_BASE 0x0 +#ifdef CONFIG_CMA +#define CAMERA_HEAP_TYPE ION_HEAP_TYPE_DMA +#else +#define CAMERA_HEAP_TYPE ION_HEAP_TYPE_CARVEOUT +#endif + +#define RESERVE_KERNEL_EBI1_SIZE 0x3A000 +#define MSM_RESERVE_AUDIO_SIZE 0xF0000 +#define BOOTLOADER_BASE_ADDR 0x10000 + +#if defined(CONFIG_GPIO_SX150X) +enum { + SX150X_CORE, +}; + +static struct sx150x_platform_data sx150x_data[] __initdata = { + [SX150X_CORE] = { + .gpio_base = GPIO_CORE_EXPANDER_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0, + .io_pulldn_ena = 0x02, + .io_open_drain_ena = 0xfef8, + .irq_summary = -1, + }, +}; +#endif + + +#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE) +static struct platform_device msm_wlan_ar6000_pm_device = { + .name = "wlan_ar6000_pm_dev", + .id = -1, +}; +#endif + +#if defined(CONFIG_I2C) && defined(CONFIG_GPIO_SX150X) +static struct i2c_board_info core_exp_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("sx1509q", 0x3e), + }, +}; + +static void __init register_i2c_devices(void) +{ + if (machine_is_msm7x27a_surf() || machine_is_msm7625a_surf() || + machine_is_msm8625_surf()) + sx150x_data[SX150X_CORE].io_open_drain_ena = 0xe0f0; + + core_exp_i2c_info[0].platform_data = + &sx150x_data[SX150X_CORE]; + + i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID, + core_exp_i2c_info, + ARRAY_SIZE(core_exp_i2c_info)); +} +#endif + +static struct msm_gpio qup_i2c_gpios_io[] = { + { GPIO_CFG(60, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(61, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, + { GPIO_CFG(131, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(132, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, +}; + +static struct msm_gpio qup_i2c_gpios_hw[] = { + { GPIO_CFG(60, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(61, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, + { GPIO_CFG(131, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(132, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, +}; + +static void gsbi_qup_i2c_gpio_config(int adap_id, int config_type) +{ + int rc; + + if (adap_id < 0 || adap_id > 1) + return; + + /* Each adapter gets 2 lines from the table */ + if (config_type) + rc = msm_gpios_request_enable(&qup_i2c_gpios_hw[adap_id*2], 2); + else + rc = msm_gpios_request_enable(&qup_i2c_gpios_io[adap_id*2], 2); + if (rc < 0) + pr_err("QUP GPIO request/enable failed: %d\n", rc); +} + +static struct msm_i2c_platform_data msm_gsbi0_qup_i2c_pdata = { + .clk_freq = 100000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; + +static struct msm_i2c_platform_data msm_gsbi1_qup_i2c_pdata = { + .clk_freq = 100000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; + +#ifdef CONFIG_ARCH_MSM7X27A +#define MSM_RESERVE_MDP_SIZE 0x2300000 +#define MSM7x25A_MSM_RESERVE_MDP_SIZE 0x1500000 + +#define MSM_RESERVE_ADSP_SIZE 0x2000000 +#define MSM7x25A_MSM_RESERVE_ADSP_SIZE 0xB91000 +#define CAMERA_ZSL_SIZE (SZ_1M * 60) +#endif + +#ifdef CONFIG_ION_MSM +#define MSM_ION_HEAP_NUM 5 +static struct platform_device ion_dev; +static int msm_ion_camera_size; +static int msm_ion_audio_size; +static int msm_ion_sf_size; +static int msm_ion_camera_size_carving; +#endif + +#define CAMERA_HEAP_BASE 0x0 +#ifdef CONFIG_CMA +#define CAMERA_HEAP_TYPE ION_HEAP_TYPE_DMA +#else +#define CAMERA_HEAP_TYPE ION_HEAP_TYPE_CARVEOUT +#endif + +static struct android_usb_platform_data android_usb_pdata = { + .update_pid_and_serial_num = usb_diag_update_pid_and_serial_num, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + +#ifdef CONFIG_USB_EHCI_MSM_72K +static void msm_hsusb_vbus_power(unsigned phy_info, int on) +{ + int rc = 0; + unsigned gpio; + + gpio = GPIO_HOST_VBUS_EN; + + rc = gpio_request(gpio, "i2c_host_vbus_en"); + if (rc < 0) { + pr_err("failed to request %d GPIO\n", gpio); + return; + } + gpio_direction_output(gpio, !!on); + gpio_set_value_cansleep(gpio, !!on); + gpio_free(gpio); +} + +static struct msm_usb_host_platform_data msm_usb_host_pdata = { + .phy_info = (USB_PHY_INTEGRATED | USB_PHY_MODEL_45NM), +}; + +static void __init msm7x2x_init_host(void) +{ + msm_add_host(0, &msm_usb_host_pdata); +} +#endif + +#ifdef CONFIG_USB_MSM_OTG_72K +static int hsusb_rpc_connect(int connect) +{ + if (connect) + return msm_hsusb_rpc_connect(); + else + return msm_hsusb_rpc_close(); +} + +static struct regulator *reg_hsusb; +static int msm_hsusb_ldo_init(int init) +{ + int rc = 0; + + if (init) { + reg_hsusb = regulator_get(NULL, "usb"); + if (IS_ERR(reg_hsusb)) { + rc = PTR_ERR(reg_hsusb); + pr_err("%s: could not get regulator: %d\n", + __func__, rc); + goto out; + } + + rc = regulator_set_voltage(reg_hsusb, 3300000, 3300000); + if (rc) { + pr_err("%s: could not set voltage: %d\n", + __func__, rc); + goto reg_free; + } + + return 0; + } + /* else fall through */ +reg_free: + regulator_put(reg_hsusb); +out: + reg_hsusb = NULL; + return rc; +} + +static int msm_hsusb_ldo_enable(int enable) +{ + static int ldo_status; + + if (IS_ERR_OR_NULL(reg_hsusb)) + return reg_hsusb ? PTR_ERR(reg_hsusb) : -ENODEV; + + if (ldo_status == enable) + return 0; + + ldo_status = enable; + + return enable ? + regulator_enable(reg_hsusb) : + regulator_disable(reg_hsusb); +} + +#ifndef CONFIG_USB_EHCI_MSM_72K +static int msm_hsusb_pmic_notif_init(void (*callback)(int online), int init) +{ + int ret = 0; + + if (init) + ret = msm_pm_app_rpc_init(callback); + else + msm_pm_app_rpc_deinit(callback); + + return ret; +} +#endif + +static struct msm_otg_platform_data msm_otg_pdata = { +#ifndef CONFIG_USB_EHCI_MSM_72K + .pmic_vbus_notif_init = msm_hsusb_pmic_notif_init, +#else + .vbus_power = msm_hsusb_vbus_power, +#endif + .rpc_connect = hsusb_rpc_connect, + .pemp_level = PRE_EMPHASIS_WITH_20_PERCENT, + .cdr_autoreset = CDR_AUTO_RESET_DISABLE, + .drv_ampl = HS_DRV_AMPLITUDE_DEFAULT, + .se1_gating = SE1_GATING_DISABLE, + .ldo_init = msm_hsusb_ldo_init, + .ldo_enable = msm_hsusb_ldo_enable, + .chg_init = hsusb_chg_init, + .chg_connected = hsusb_chg_connected, + .chg_vbus_draw = hsusb_chg_vbus_draw, +}; +#endif + +static struct msm_hsusb_gadget_platform_data msm_gadget_pdata = { + .is_phy_status_timer_on = 1, +}; + +static struct resource smc91x_resources[] = { + [0] = { + .start = 0x90000300, + .end = 0x900003ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MSM_GPIO_TO_INT(4), + .end = MSM_GPIO_TO_INT(4), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +#ifdef CONFIG_SERIAL_MSM_HS +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .inject_rx_on_wakeup = 1, + .rx_to_inject = 0xFD, +}; +#endif +static struct msm_pm_platform_data msm7x27a_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 16000, + .residency = 20000, + }, + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 12000, + .residency = 20000, + }, + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 1, + .latency = 2000, + .residency = 0, + }, + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 0, + }, +}; + +static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS, + .p_addr = 0, +}; + +/* 8625 PM platform data */ +static struct msm_pm_platform_data + msm8625_pm_data[MSM_PM_SLEEP_MODE_NR * CONFIG_NR_CPUS] = { + /* CORE0 entries */ + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 16000, + .residency = 20000, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 12000, + .residency = 20000, + }, + + /* picked latency & redisdency values from 7x30 */ + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 500, + .residency = 6000, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 10, + }, + + /* picked latency & redisdency values from 7x30 */ + [MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 500, + .residency = 6000, + }, + + [MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 10, + }, + +}; + +static struct msm_pm_boot_platform_data msm_pm_8625_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR, + .v_addr = MSM_CFG_CTL_BASE, +}; + +static unsigned reserve_mdp_size = MSM_RESERVE_MDP_SIZE; +static int __init reserve_mdp_size_setup(char *p) +{ + reserve_mdp_size = memparse(p, NULL); + return 0; +} + +early_param("reserve_mdp_size", reserve_mdp_size_setup); + +static unsigned reserve_adsp_size = MSM_RESERVE_ADSP_SIZE; +static int __init reserve_adsp_size_setup(char *p) +{ + reserve_adsp_size = memparse(p, NULL); + return 0; +} + +early_param("reserve_adsp_size", reserve_adsp_size_setup); + +static u32 msm_calculate_batt_capacity(u32 current_voltage); + +static struct msm_psy_batt_pdata msm_psy_batt_data = { + .voltage_min_design = 3200, + .voltage_max_design = 4200, +// .voltage_fail_safe = 3340, + .avail_chg_sources = AC_CHG | USB_CHG , + .batt_technology = POWER_SUPPLY_TECHNOLOGY_LION, + .calculate_capacity = &msm_calculate_batt_capacity, +}; + +static u32 msm_calculate_batt_capacity(u32 current_voltage) +{ + u32 low_voltage = msm_psy_batt_data.voltage_min_design; + u32 high_voltage = msm_psy_batt_data.voltage_max_design; + + if (current_voltage <= low_voltage) + return 0; + else if (current_voltage >= high_voltage) + return 100; + else + return (current_voltage - low_voltage) * 100 + / (high_voltage - low_voltage); +} + +static struct platform_device msm_batt_device = { + .name = "msm-battery", + .id = -1, + .dev.platform_data = &msm_psy_batt_data, +}; + +static struct smsc911x_platform_config smsc911x_config = { + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH, + .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, + .flags = SMSC911X_USE_16BIT, +}; + +static struct resource smsc911x_resources[] = { + [0] = { + .start = 0x90000000, + .end = 0x90007fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MSM_GPIO_TO_INT(48), + .end = MSM_GPIO_TO_INT(48), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL, + }, +}; + +static struct platform_device smsc911x_device = { + .name = "smsc911x", + .id = 0, + .num_resources = ARRAY_SIZE(smsc911x_resources), + .resource = smsc911x_resources, + .dev = { + .platform_data = &smsc911x_config, + }, +}; + +static struct msm_gpio smsc911x_gpios[] = { + { GPIO_CFG(48, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_6MA), + "smsc911x_irq" }, + { GPIO_CFG(49, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_6MA), + "eth_fifo_sel" }, +}; + +static char *msm_adc_surf_device_names[] = { + "XO_ADC", +}; + +static struct msm_adc_platform_data msm_adc_pdata = { + .dev_names = msm_adc_surf_device_names, + .num_adc = ARRAY_SIZE(msm_adc_surf_device_names), + .target_hw = MSM_8x25, +}; + +static struct platform_device msm_adc_device = { + .name = "msm_adc", + .id = -1, + .dev = { + .platform_data = &msm_adc_pdata, + }, +}; + +#define ETH_FIFO_SEL_GPIO 49 +static void msm7x27a_cfg_smsc911x(void) +{ + int res; + + res = msm_gpios_request_enable(smsc911x_gpios, + ARRAY_SIZE(smsc911x_gpios)); + if (res) { + pr_err("%s: unable to enable gpios for SMSC911x\n", __func__); + return; + } + + /* ETH_FIFO_SEL */ + res = gpio_direction_output(ETH_FIFO_SEL_GPIO, 0); + if (res) { + pr_err("%s: unable to get direction for gpio %d\n", __func__, + ETH_FIFO_SEL_GPIO); + msm_gpios_disable_free(smsc911x_gpios, + ARRAY_SIZE(smsc911x_gpios)); + return; + } + gpio_set_value(ETH_FIFO_SEL_GPIO, 0); +} + +#if defined(CONFIG_SERIAL_MSM_HSL_CONSOLE) \ + && defined(CONFIG_MSM_SHARED_GPIO_FOR_UART2DM) +static struct msm_gpio uart2dm_gpios[] = { + {GPIO_CFG(19, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "uart2dm_rfr_n" }, + {GPIO_CFG(20, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "uart2dm_cts_n" }, + {GPIO_CFG(21, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "uart2dm_rx" }, + {GPIO_CFG(108, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "uart2dm_tx" }, +}; + +static void msm7x27a_cfg_uart2dm_serial(void) +{ + int ret; + ret = msm_gpios_request_enable(uart2dm_gpios, + ARRAY_SIZE(uart2dm_gpios)); + if (ret) + pr_err("%s: unable to enable gpios for uart2dm\n", __func__); +} +#else +static void msm7x27a_cfg_uart2dm_serial(void) { } +#endif + +static struct platform_device *rumi_sim_devices[] __initdata = { + &msm_device_dmov, + &msm_device_smd, + &smc91x_device, + &msm_device_uart1, + &msm_device_nand, + &msm_device_uart_dm1, + &msm_gsbi0_qup_i2c_device, + &msm_gsbi1_qup_i2c_device, +}; + +static struct platform_device *msm8625_rumi3_devices[] __initdata = { + &msm8625_device_dmov, + &msm8625_device_smd, + &msm8625_device_uart1, + &msm8625_gsbi0_qup_i2c_device, +}; + +static struct platform_device *msm7627a_surf_ffa_devices[] __initdata = { + &msm_device_dmov, + &msm_device_smd, + &msm_device_uart1, + &msm_device_uart_dm1, + &msm_device_uart_dm2, + &msm_gsbi0_qup_i2c_device, + &msm_gsbi1_qup_i2c_device, + &msm_device_otg, + &msm_device_gadget_peripheral, + &smsc911x_device, + &msm_kgsl_3d0, +}; + +static struct platform_device *common_devices[] __initdata = { + &android_usb_device, + &msm_device_nand, + &msm_device_snd, + &msm_device_cad, + &msm_device_adspdec, + &asoc_msm_pcm, + &asoc_msm_dai0, + &asoc_msm_dai1, + &msm_batt_device, + &msm_adc_device, +#ifdef CONFIG_ION_MSM + &ion_dev, +#endif +}; + +static struct platform_device *msm8625_surf_devices[] __initdata = { + &msm8625_device_dmov, + &msm8625_device_uart1, + &msm8625_device_uart_dm1, + &msm8625_device_uart_dm2, + &msm8625_gsbi0_qup_i2c_device, + &msm8625_gsbi1_qup_i2c_device, + &msm8625_device_smd, + &msm8625_device_otg, + &msm8625_device_gadget_peripheral, + &msm8625_kgsl_3d0, +}; + +static unsigned reserve_kernel_ebi1_size = RESERVE_KERNEL_EBI1_SIZE; +static int __init reserve_kernel_ebi1_size_setup(char *p) +{ + reserve_kernel_ebi1_size = memparse(p, NULL); + return 0; +} +early_param("reserve_kernel_ebi1_size", reserve_kernel_ebi1_size_setup); + +static unsigned reserve_audio_size = MSM_RESERVE_AUDIO_SIZE; +static int __init reserve_audio_size_setup(char *p) +{ + reserve_audio_size = memparse(p, NULL); + return 0; +} +early_param("reserve_audio_size", reserve_audio_size_setup); + +static void fix_sizes(void) +{ + if (machine_is_msm7625a_surf() || machine_is_msm7625a_ffa()) { + reserve_mdp_size = MSM7x25A_MSM_RESERVE_MDP_SIZE; + reserve_adsp_size = MSM7x25A_MSM_RESERVE_ADSP_SIZE; + } else { + reserve_mdp_size = MSM_RESERVE_MDP_SIZE; + reserve_adsp_size = MSM_RESERVE_ADSP_SIZE; + } + + if (get_ddr_size() > SZ_512M) + reserve_adsp_size = CAMERA_ZSL_SIZE; +#ifdef CONFIG_ION_MSM + msm_ion_audio_size = MSM_RESERVE_AUDIO_SIZE; + msm_ion_sf_size = reserve_mdp_size; +#ifdef CONFIG_CMA + if (get_ddr_size() > SZ_256M) + reserve_adsp_size = CAMERA_ZSL_SIZE; + msm_ion_camera_size = reserve_adsp_size; + msm_ion_camera_size_carving = 0; +#else + msm_ion_camera_size = reserve_adsp_size; + msm_ion_camera_size_carving = msm_ion_camera_size; +#endif +#endif +} + +#ifdef CONFIG_ION_MSM +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION +static struct ion_co_heap_pdata co_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, +}; + +static struct ion_co_heap_pdata co_mm_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, +}; + +static u64 msm_dmamask = DMA_BIT_MASK(32); + +static struct platform_device ion_cma_device = { + .name = "ion-cma-device", + .id = -1, + .dev = { + .dma_mask = &msm_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + } +}; +#endif + +/** + * These heaps are listed in the order they will be allocated. + * Don't swap the order unless you know what you are doing! + */ +struct ion_platform_heap msm7x27a_heaps[] = { + { + .id = ION_SYSTEM_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM, + .name = ION_VMALLOC_HEAP_NAME, + }, +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + /* ION_ADSP = CAMERA */ + { + .id = ION_CAMERA_HEAP_ID, + .type = CAMERA_HEAP_TYPE, + .name = ION_CAMERA_HEAP_NAME, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_mm_ion_pdata, + .priv = (void *)&ion_cma_device.dev, + }, + /* AUDIO HEAP 1*/ + { + .id = ION_AUDIO_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_AUDIO_HEAP_NAME, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + }, + /* ION_MDP = SF */ + { + .id = ION_SF_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_SF_HEAP_NAME, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + }, + /* AUDIO HEAP 2*/ + { + .id = ION_AUDIO_HEAP_BL_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_AUDIO_BL_HEAP_NAME, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + .base = BOOTLOADER_BASE_ADDR, + }, + +#endif +}; + +static struct ion_platform_data ion_pdata = { + .nr = MSM_ION_HEAP_NUM, + .has_outer_cache = 1, + .heaps = msm7x27a_heaps, +}; + +static struct platform_device ion_dev = { + .name = "ion-msm", + .id = 1, + .dev = { .platform_data = &ion_pdata }, +}; +#endif + +static struct memtype_reserve msm7x27a_reserve_table[] __initdata = { + [MEMTYPE_SMI] = { + }, + [MEMTYPE_EBI0] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, + [MEMTYPE_EBI1] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, +}; + +static void __init size_ion_devices(void) +{ +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + ion_pdata.heaps[1].size = msm_ion_camera_size; + ion_pdata.heaps[2].size = RESERVE_KERNEL_EBI1_SIZE; + ion_pdata.heaps[3].size = msm_ion_sf_size; + ion_pdata.heaps[4].size = msm_ion_audio_size; +#endif +} + +static void __init reserve_ion_memory(void) +{ +#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + msm7x27a_reserve_table[MEMTYPE_EBI1].size += RESERVE_KERNEL_EBI1_SIZE; + msm7x27a_reserve_table[MEMTYPE_EBI1].size += + msm_ion_camera_size_carving; + msm7x27a_reserve_table[MEMTYPE_EBI1].size += msm_ion_sf_size; +#endif +} + +static void __init msm7x27a_calculate_reserve_sizes(void) +{ + fix_sizes(); + size_ion_devices(); + reserve_ion_memory(); +} + +static int msm7x27a_paddr_to_memtype(unsigned int paddr) +{ + return MEMTYPE_EBI1; +} + +static struct reserve_info msm7x27a_reserve_info __initdata = { + .memtype_reserve_table = msm7x27a_reserve_table, + .calculate_reserve_sizes = msm7x27a_calculate_reserve_sizes, + .paddr_to_memtype = msm7x27a_paddr_to_memtype, +}; + +/* define size of reserve memory is 1M */ +#ifdef CONFIG_SRECORDER_MSM +#define SRECORDER_RESERVED_MEM_SIZE (SZ_512K) + +/* define start address of reserve memory */ +static unsigned long s_srecorder_reserved_mem_phys_start_addr = 0x0; + +/* + * Function: unsigned long get_srecorder_reserved_mem_size(void) + * Description: get size of reserve memory + * Calls: No + * Called By: setup_arch + * Table Accessed: No + * Table Updated: No + * Input: No + * Output: No + * Return: SRECORDER_RESERVED_MEM_SIZE: size of reserve memory + * Others: No + */ +unsigned long get_srecorder_reserved_mem_size(void) +{ + return SRECORDER_RESERVED_MEM_SIZE; +} + +/* + * Function: unsigned long get_mempools_pstart_addr(void) + * Description: get start address of reserve memory + * Calls: No + * Called By: msm8625_reserve + * Table Accessed: No + * Table Updated: No + * Input: No + * Output: No + * Return: s_srecorder_reserved_mem_phys_start_addr:start address of reserve memory + * Others: No + */ +unsigned long get_srecorder_reserved_mem_phys_start_addr(void) +{ + return s_srecorder_reserved_mem_phys_start_addr; +} + +extern unsigned long get_mempools_pstart_addr(void); +#endif /* CONFIG_SRECORDER_MSM */ + +static void __init msm7x27a_reserve(void) +{ + reserve_info = &msm7x27a_reserve_info; + memblock_remove(MSM8625_NON_CACHE_MEM, SZ_2K); + memblock_remove(BOOTLOADER_BASE_ADDR, msm_ion_audio_size); + msm_reserve(); +#ifdef CONFIG_CMA + dma_declare_contiguous( + &ion_cma_device.dev, + msm_ion_camera_size, + CAMERA_HEAP_BASE, + 0x26000000); +#endif + +#ifdef CONFIG_SRECORDER_MSM + if (0x0 != get_mempools_pstart_addr()) + { + s_srecorder_reserved_mem_phys_start_addr = get_mempools_pstart_addr();// - SRECORDER_RESERVED_MEM_SIZE; + } + else + { + printk(">>>> Can't know the start address for S-Recorder's reserved memory!\n"); + } +#endif /* CONFIG_SRECORDER_MSM */ +} + +static void __init msm8625_reserve(void) +{ + msm7x27a_reserve(); + memblock_remove(MSM8625_CPU_PHYS, SZ_8); + memblock_remove(MSM8625_WARM_BOOT_PHYS, SZ_32); + memblock_remove(MSM8625_NON_CACHE_MEM, SZ_2K); +} + +static void __init msm7x27a_device_i2c_init(void) +{ + msm_gsbi0_qup_i2c_device.dev.platform_data = &msm_gsbi0_qup_i2c_pdata; + msm_gsbi1_qup_i2c_device.dev.platform_data = &msm_gsbi1_qup_i2c_pdata; +} + +static void __init msm8625_device_i2c_init(void) +{ + msm8625_gsbi0_qup_i2c_device.dev.platform_data = + &msm_gsbi0_qup_i2c_pdata; + msm8625_gsbi1_qup_i2c_device.dev.platform_data = + &msm_gsbi1_qup_i2c_pdata; +} + +#define MSM_EBI2_PHYS 0xa0d00000 +#define MSM_EBI2_XMEM_CS2_CFG1 0xa0d10030 + +static void __init msm7x27a_init_ebi2(void) +{ + uint32_t ebi2_cfg; + void __iomem *ebi2_cfg_ptr; + + ebi2_cfg_ptr = ioremap_nocache(MSM_EBI2_PHYS, sizeof(uint32_t)); + if (!ebi2_cfg_ptr) + return; + + ebi2_cfg = readl(ebi2_cfg_ptr); + if (machine_is_msm7x27a_rumi3() || machine_is_msm7x27a_surf() || + machine_is_msm7625a_surf() || machine_is_msm8625_surf()) + ebi2_cfg |= (1 << 4); /* CS2 */ + + writel(ebi2_cfg, ebi2_cfg_ptr); + iounmap(ebi2_cfg_ptr); + + /* Enable A/D MUX[bit 31] from EBI2_XMEM_CS2_CFG1 */ + ebi2_cfg_ptr = ioremap_nocache(MSM_EBI2_XMEM_CS2_CFG1, + sizeof(uint32_t)); + if (!ebi2_cfg_ptr) + return; + + ebi2_cfg = readl(ebi2_cfg_ptr); + if (machine_is_msm7x27a_surf() || machine_is_msm7625a_surf()) + ebi2_cfg |= (1 << 31); + + writel(ebi2_cfg, ebi2_cfg_ptr); + iounmap(ebi2_cfg_ptr); +} + +static struct platform_device msm_proccomm_regulator_dev = { + .name = PROCCOMM_REGULATOR_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &msm7x27a_proccomm_regulator_data + } +}; + +static void msm_adsp_add_pdev(void) +{ + int rc = 0; + struct rpc_board_dev *rpc_adsp_pdev; + + rpc_adsp_pdev = kzalloc(sizeof(struct rpc_board_dev), GFP_KERNEL); + if (rpc_adsp_pdev == NULL) { + pr_err("%s: Memory Allocation failure\n", __func__); + return; + } + rpc_adsp_pdev->prog = ADSP_RPC_PROG; + + if (cpu_is_msm8625()) + rpc_adsp_pdev->pdev = msm8625_device_adsp; + else + rpc_adsp_pdev->pdev = msm_adsp_device; + rc = msm_rpc_add_board_dev(rpc_adsp_pdev, 1); + if (rc < 0) { + pr_err("%s: return val: %d\n", __func__, rc); + kfree(rpc_adsp_pdev); + } +} + +static void __init msm7627a_rumi3_init(void) +{ + msm7x27a_init_ebi2(); + platform_add_devices(rumi_sim_devices, + ARRAY_SIZE(rumi_sim_devices)); +} + +static void __init msm8625_rumi3_init(void) +{ + msm7x2x_misc_init(); + msm_adsp_add_pdev(); + msm8625_device_i2c_init(); + platform_add_devices(msm8625_rumi3_devices, + ARRAY_SIZE(msm8625_rumi3_devices)); + + msm_pm_set_platform_data(msm8625_pm_data, + ARRAY_SIZE(msm8625_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_8625_boot_pdata)); + msm8x25_spm_device_init(); + msm_pm_register_cpr_ops(); +} + +#define UART1DM_RX_GPIO 45 + +#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE) +static int __init msm7x27a_init_ar6000pm(void) +{ + msm_wlan_ar6000_pm_device.dev.platform_data = &ar600x_wlan_power; + return platform_device_register(&msm_wlan_ar6000_pm_device); +} +#else +static int __init msm7x27a_init_ar6000pm(void) { return 0; } +#endif + +static void __init msm7x27a_init_regulators(void) +{ + int rc = platform_device_register(&msm_proccomm_regulator_dev); + if (rc) + pr_err("%s: could not register regulator device: %d\n", + __func__, rc); +} + +static void __init msm7x27a_add_footswitch_devices(void) +{ + platform_add_devices(msm_footswitch_devices, + msm_num_footswitch_devices); +} + +static void __init msm7x27a_add_platform_devices(void) +{ + if (machine_is_msm8625_surf() || machine_is_msm8625_ffa()) { + platform_add_devices(msm8625_surf_devices, + ARRAY_SIZE(msm8625_surf_devices)); + } else { + platform_add_devices(msm7627a_surf_ffa_devices, + ARRAY_SIZE(msm7627a_surf_ffa_devices)); + } + + platform_add_devices(common_devices, + ARRAY_SIZE(common_devices)); +} + +static void __init msm7x27a_uartdm_config(void) +{ + msm7x27a_cfg_uart2dm_serial(); + msm_uart_dm1_pdata.wakeup_irq = gpio_to_irq(UART1DM_RX_GPIO); + if (cpu_is_msm8625()) + msm8625_device_uart_dm1.dev.platform_data = + &msm_uart_dm1_pdata; + else + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; +} + +static void __init msm7x27a_otg_gadget(void) +{ + if (cpu_is_msm8625()) { + msm_otg_pdata.swfi_latency = + msm8625_pm_data[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].latency; + msm8625_device_otg.dev.platform_data = &msm_otg_pdata; + msm8625_device_gadget_peripheral.dev.platform_data = + &msm_gadget_pdata; + } else { + msm_otg_pdata.swfi_latency = + msm7x27a_pm_data[ + MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency; + msm_device_otg.dev.platform_data = &msm_otg_pdata; + msm_device_gadget_peripheral.dev.platform_data = + &msm_gadget_pdata; + } +} + +static void __init msm7x27a_pm_init(void) +{ + if (machine_is_msm8625_surf() || machine_is_msm8625_ffa()) { + msm_pm_set_platform_data(msm8625_pm_data, + ARRAY_SIZE(msm8625_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_8625_boot_pdata)); + msm8x25_spm_device_init(); + msm_pm_register_cpr_ops(); + } else { + msm_pm_set_platform_data(msm7x27a_pm_data, + ARRAY_SIZE(msm7x27a_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + } + + msm_pm_register_irqs(); +} + +static void __init msm7x2x_init(void) +{ + msm7x2x_misc_init(); + + /* Initialize regulators first so that other devices can use them */ + msm7x27a_init_regulators(); + msm_adsp_add_pdev(); + if (cpu_is_msm8625()) + msm8625_device_i2c_init(); + else + msm7x27a_device_i2c_init(); + msm7x27a_init_ebi2(); + msm7x27a_uartdm_config(); + + msm7x27a_otg_gadget(); + msm7x27a_cfg_smsc911x(); + + msm7x27a_add_footswitch_devices(); + msm7x27a_add_platform_devices(); + /* Ensure ar6000pm device is registered before MMC/SDC */ + msm7x27a_init_ar6000pm(); + msm7627a_init_mmc(); + msm_fb_add_devices(); + msm7x2x_init_host(); + msm7x27a_pm_init(); + register_i2c_devices(); + //msm7627a_bt_power_init() will check QC or BCM bt chip powers inner. + msm7627a_bt_power_init(); + msm7627a_camera_init(); + msm7627a_add_io_devices(); + /*7x25a kgsl initializations*/ + msm7x25a_kgsl_3d0_init(); + /*8x25 kgsl initializations*/ + msm8x25_kgsl_3d0_init(); +} + +static void __init msm7x2x_init_early(void) +{ + msm_msm7627a_allocate_memory_regions(); +} + +MACHINE_START(MSM7X27A_RUMI3, "QCT MSM7x27a RUMI3") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7627a_rumi3_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7X27A_SURF, "QCT MSM7x27a SURF") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7X27A_FFA, "QCT MSM7x27a FFA") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7625A_SURF, "QCT MSM7625a SURF") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7625A_FFA, "QCT MSM7625a FFA") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM8625_RUMI3, "QCT MSM8625 RUMI3") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm8625_rumi3_init, + .timer = &msm_timer, + .handle_irq = gic_handle_irq, +MACHINE_END +MACHINE_START(MSM8625_SURF, "QCT MSM8625 SURF") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = gic_handle_irq, +MACHINE_END +MACHINE_START(MSM8625_FFA, "QCT MSM8625 FFA") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = gic_handle_irq, +MACHINE_END diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c index 89b86a3c..e349a4d0 100644 --- a/arch/arm/mach-msm/board-qrd7627a.c +++ b/arch/arm/mach-msm/board-qrd7627a.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -33,7 +32,7 @@ #include #include #include -#include +#include #include #include #include @@ -70,8 +69,8 @@ #include "board-msm7627a.h" #include "board-msm7627a-sensor.h" -#define PMEM_KERNEL_EBI1_SIZE 0x3A000 -#define MSM_PMEM_AUDIO_SIZE 0xF0000 +#define RESERVE_KERNEL_EBI1_SIZE 0x3A000 +#define MSM_RESERVE_AUDIO_SIZE 0xF0000 #define BOOTLOADER_BASE_ADDR 0x10000 #define BAHAMA_SLAVE_ID_FM_REG 0x02 #define FM_GPIO 83 @@ -182,8 +181,8 @@ static struct platform_device msm8625q_i2c_gpio = { }; #ifdef CONFIG_ARCH_MSM7X27A -#define MSM_PMEM_MDP_SIZE 0x2300000 -#define MSM_PMEM_ADSP_SIZE 0x1200000 +#define MSM_RESERVE_MDP_SIZE 0x2300000 +#define MSM_RESERVE_ADSP_SIZE 0x9600000 #define CAMERA_ZSL_SIZE (SZ_1M * 60) #ifdef CONFIG_ION_MSM @@ -491,61 +490,23 @@ static struct msm_pm_boot_platform_data msm_pm_8625_boot_pdata __initdata = { .v_addr = MSM_CFG_CTL_BASE, }; -static struct android_pmem_platform_data android_pmem_adsp_pdata = { - .name = "pmem_adsp", - .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, - .cached = 1, - .memory_type = MEMTYPE_EBI1, -}; - -static struct platform_device android_pmem_adsp_device = { - .name = "android_pmem", - .id = 1, - .dev = { .platform_data = &android_pmem_adsp_pdata }, -}; - -static unsigned pmem_mdp_size = MSM_PMEM_MDP_SIZE; -static int __init pmem_mdp_size_setup(char *p) +static unsigned reserve_mdp_size = MSM_RESERVE_MDP_SIZE; +static int __init reserve_mdp_size_setup(char *p) { - pmem_mdp_size = memparse(p, NULL); + reserve_mdp_size = memparse(p, NULL); return 0; } -early_param("pmem_mdp_size", pmem_mdp_size_setup); +early_param("reserve_mdp_size", reserve_mdp_size_setup); -static unsigned pmem_adsp_size = MSM_PMEM_ADSP_SIZE; -static int __init pmem_adsp_size_setup(char *p) +static unsigned reserve_adsp_size = MSM_RESERVE_ADSP_SIZE; +static int __init reserve_adsp_size_setup(char *p) { - pmem_adsp_size = memparse(p, NULL); + reserve_adsp_size = memparse(p, NULL); return 0; } -early_param("pmem_adsp_size", pmem_adsp_size_setup); - -static struct android_pmem_platform_data android_pmem_audio_pdata = { - .name = "pmem_audio", - .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, - .cached = 0, - .memory_type = MEMTYPE_EBI1, -}; - -static struct platform_device android_pmem_audio_device = { - .name = "android_pmem", - .id = 2, - .dev = { .platform_data = &android_pmem_audio_pdata }, -}; - -static struct android_pmem_platform_data android_pmem_pdata = { - .name = "pmem", - .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, - .cached = 1, - .memory_type = MEMTYPE_EBI1, -}; -static struct platform_device android_pmem_device = { - .name = "android_pmem", - .id = 0, - .dev = { .platform_data = &android_pmem_pdata }, -}; +early_param("reserve_adsp_size", reserve_adsp_size_setup); static u32 msm_calculate_batt_capacity(u32 current_voltage); @@ -793,9 +754,6 @@ static struct i2c_board_info i2c2_info[] __initdata = { static struct platform_device *common_devices[] __initdata = { &android_usb_device, - &android_pmem_device, - &android_pmem_adsp_device, - &android_pmem_audio_device, &msm_batt_device, &msm_device_adspdec, &msm_device_snd, @@ -858,42 +816,44 @@ static struct platform_device *msm8625q_lcd_camera_devices[] __initdata = { &evbd_vreg_gpio_ext_1p8v, }; -static unsigned pmem_kernel_ebi1_size = PMEM_KERNEL_EBI1_SIZE; -static int __init pmem_kernel_ebi1_size_setup(char *p) +static unsigned reserve_kernel_ebi1_size = RESERVE_KERNEL_EBI1_SIZE; +static int __init reserve_kernel_ebi1_size_setup(char *p) { - pmem_kernel_ebi1_size = memparse(p, NULL); + reserve_kernel_ebi1_size = memparse(p, NULL); return 0; } -early_param("pmem_kernel_ebi1_size", pmem_kernel_ebi1_size_setup); +early_param("reserve_kernel_ebi1_size", reserve_kernel_ebi1_size_setup); -static unsigned pmem_audio_size = MSM_PMEM_AUDIO_SIZE; -static int __init pmem_audio_size_setup(char *p) +static unsigned reserve_audio_size = MSM_RESERVE_AUDIO_SIZE; +static int __init reserve_audio_size_setup(char *p) { - pmem_audio_size = memparse(p, NULL); + reserve_audio_size = memparse(p, NULL); return 0; } -early_param("pmem_audio_size", pmem_audio_size_setup); +early_param("reserve_audio_size", reserve_audio_size_setup); static void fix_sizes(void) { if (get_ddr_size() > SZ_512M) - pmem_adsp_size = CAMERA_ZSL_SIZE; + reserve_adsp_size = CAMERA_ZSL_SIZE; else { if (machine_is_msm8625q_skue() || machine_is_msm8625q_evbd() || machine_is_msm8625q_skud()) - pmem_mdp_size = 0; + reserve_mdp_size = 0; } #ifdef CONFIG_ION_MSM - msm_ion_audio_size = MSM_PMEM_AUDIO_SIZE; + msm_ion_audio_size = MSM_RESERVE_AUDIO_SIZE; + msm_ion_sf_size = reserve_mdp_size; #ifdef CONFIG_CMA - msm_ion_camera_size = CAMERA_ZSL_SIZE; - msm_ion_camera_size_carving = 0; + if (get_ddr_size() > SZ_256M) + reserve_adsp_size = CAMERA_ZSL_SIZE; + msm_ion_camera_size = reserve_adsp_size; + msm_ion_camera_size_carving = 0; #else - msm_ion_camera_size = pmem_adsp_size; - msm_ion_camera_size_carving = msm_ion_camera_size; + msm_ion_camera_size = reserve_adsp_size; + msm_ion_camera_size_carving = msm_ion_camera_size; #endif - msm_ion_sf_size = pmem_mdp_size; #endif } @@ -932,7 +892,7 @@ struct ion_platform_heap msm7627a_heaps[] = { .name = ION_VMALLOC_HEAP_NAME, }, #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION - /* PMEM_ADSP = CAMERA */ + /* ION_ADSP = CAMERA */ { .id = ION_CAMERA_HEAP_ID, .type = CAMERA_HEAP_TYPE, @@ -949,7 +909,7 @@ struct ion_platform_heap msm7627a_heaps[] = { .memory_type = ION_EBI_TYPE, .extra_data = (void *)&co_ion_pdata, }, - /* PMEM_MDP = SF */ + /* ION_MDP = SF */ { .id = ION_SF_HEAP_ID, .type = ION_HEAP_TYPE_CARVEOUT, @@ -994,56 +954,11 @@ static struct memtype_reserve msm7627a_reserve_table[] __initdata = { }, }; -#ifdef CONFIG_ANDROID_PMEM -#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION -static struct android_pmem_platform_data *pmem_pdata_array[] __initdata = { - &android_pmem_adsp_pdata, - &android_pmem_audio_pdata, - &android_pmem_pdata, -}; -#endif -#endif - -static void __init size_pmem_devices(void) -{ -#ifdef CONFIG_ANDROID_PMEM -#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION - unsigned int i; - - android_pmem_adsp_pdata.size = pmem_adsp_size; - android_pmem_pdata.size = pmem_mdp_size; - android_pmem_audio_pdata.size = pmem_audio_size; -#endif -#endif -} - -#ifdef CONFIG_ANDROID_PMEM -#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION -static void __init reserve_memory_for(struct android_pmem_platform_data *p) -{ - msm7627a_reserve_table[p->memory_type].size += p->size; -} -#endif -#endif - -static void __init reserve_pmem_memory(void) -{ -#ifdef CONFIG_ANDROID_PMEM -#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION - unsigned int i; - for (i = 0; i < ARRAY_SIZE(pmem_pdata_array); ++i) - reserve_memory_for(pmem_pdata_array[i]); - - msm7627a_reserve_table[MEMTYPE_EBI1].size += pmem_kernel_ebi1_size; -#endif -#endif -} - static void __init size_ion_devices(void) { #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION ion_pdata.heaps[1].size = msm_ion_camera_size; - ion_pdata.heaps[2].size = PMEM_KERNEL_EBI1_SIZE; + ion_pdata.heaps[2].size = RESERVE_KERNEL_EBI1_SIZE; ion_pdata.heaps[3].size = msm_ion_sf_size; ion_pdata.heaps[4].size = msm_ion_audio_size; #endif @@ -1052,18 +967,16 @@ static void __init size_ion_devices(void) static void __init reserve_ion_memory(void) { #if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) - msm7627a_reserve_table[MEMTYPE_EBI1].size += PMEM_KERNEL_EBI1_SIZE; - msm7627a_reserve_table[MEMTYPE_EBI1].size += msm_ion_sf_size; - msm7627a_reserve_table[MEMTYPE_EBI1].size += + msm7627a_reserve_table[MEMTYPE_EBI1].size += RESERVE_KERNEL_EBI1_SIZE; + msm7627a_reserve_table[MEMTYPE_EBI1].size += msm_ion_camera_size_carving; + msm7627a_reserve_table[MEMTYPE_EBI1].size += msm_ion_sf_size; #endif } static void __init msm7627a_calculate_reserve_sizes(void) { fix_sizes(); - size_pmem_devices(); - reserve_pmem_memory(); size_ion_devices(); reserve_ion_memory(); } diff --git a/arch/arm/mach-msm/board-qrd7627a_i6.c b/arch/arm/mach-msm/board-qrd7627a_i6.c new file mode 100644 index 00000000..8be83db7 --- /dev/null +++ b/arch/arm/mach-msm/board-qrd7627a_i6.c @@ -0,0 +1,1232 @@ +/* Copyright (c) 2011-2012, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "board-msm7x27a-regulator.h" +#include "devices.h" +#include "devices-msm7x2xa.h" +#include "pm.h" +#include "timer.h" +#include "pm-boot.h" +#include "board-msm7x27a-regulator.h" +#include "board-msm7627a.h" +#include "board-msm7627a_i6-sensor.h" + +#define RESERVE_KERNEL_EBI1_SIZE 0x3A000 +#define MSM_RESERVE_AUDIO_SIZE 0xF0000 +#define BOOTLOADER_BASE_ADDR 0x10000 +#define BAHAMA_SLAVE_ID_FM_REG 0x02 +#define FM_GPIO 83 +#define BT_PCM_BCLK_MODE 0x88 +#define BT_PCM_DIN_MODE 0x89 +#define BT_PCM_DOUT_MODE 0x8A +#define BT_PCM_SYNC_MODE 0x8B +#define FM_I2S_SD_MODE 0x8E +#define FM_I2S_WS_MODE 0x8F +#define FM_I2S_SCK_MODE 0x90 +#define I2C_PIN_CTL 0x15 +#define I2C_NORMAL 0x40 + + +static struct msm_gpio qup_i2c_gpios_io[] = { + { GPIO_CFG(60, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(61, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, + { GPIO_CFG(131, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(132, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, +}; + +static struct msm_gpio qup_i2c_gpios_hw[] = { + { GPIO_CFG(60, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(61, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, + { GPIO_CFG(131, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(132, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, +}; + +static void gsbi_qup_i2c_gpio_config(int adap_id, int config_type) +{ + int rc; + + if (adap_id < 0 || adap_id > 1) + return; + + /* Each adapter gets 2 lines from the table */ + if (config_type) + rc = msm_gpios_request_enable(&qup_i2c_gpios_hw[adap_id*2], 2); + else + rc = msm_gpios_request_enable(&qup_i2c_gpios_io[adap_id*2], 2); + if (rc < 0) + pr_err("QUP GPIO request/enable failed: %d\n", rc); +} + +static struct msm_i2c_platform_data msm_gsbi0_qup_i2c_pdata = { + .clk_freq = 100000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; + +#if 1 //linxc 2012-12-24 for I2C recovery +static void gsbi1_quptable_i2c_gpio_config(int adap_id,int config_type) +{ + int rc; + + if (adap_id < 0 || adap_id > 1) + return; + + printk("linxc %s adap_id=%d, config_type=%d\n", __func__, adap_id, config_type); + + if(config_type == 0) + { + rc = msm_gpios_request_enable(&qup_i2c_gpios_io[adap_id*2], 2); + if(rc < 0){ + printk("linxc %s check 1111 \n", __func__); + msm_gpios_free(&qup_i2c_gpios_hw[adap_id*2], 2); + rc = msm_gpios_request_enable(&qup_i2c_gpios_io[adap_id*2], 2); + } + } + else + { + rc = msm_gpios_request_enable(&qup_i2c_gpios_hw[adap_id*2], 2); + if(rc < 0){ + printk("linxc %s check 2222 \n", __func__); + msm_gpios_free(&qup_i2c_gpios_io[adap_id*2], 2); + rc = msm_gpios_request_enable(&qup_i2c_gpios_hw[adap_id*2], 2); + } + } + + if (rc < 0) + printk("QUP GPIO request/enable failed: %d\n", rc); + +} + +static struct msm_i2c_platform_data msm_gsbi1_qup_i2c_pdata = { + .clk_freq = 100000, + .pri_clk =131, + .pri_dat = 132, + .msm_i2c_config_gpio = gsbi1_quptable_i2c_gpio_config, +}; +#else +static struct msm_i2c_platform_data msm_gsbi1_qup_i2c_pdata = { + .clk_freq = 100000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; +#endif + +#ifdef CONFIG_ARCH_MSM7X27A +#define MSM_RESERVE_MDP_SIZE 0x2D00000 //0x2300000 --> 0x2D00000 45M for QHD Panel linxc 2012-09-04 modified +#define MSM_RESERVE_ADSP_SIZE 0x1200000 +#define CAMERA_ZSL_SIZE (SZ_1M * 60) + +#ifdef CONFIG_ION_MSM +#define MSM_ION_HEAP_NUM 4 +static struct platform_device ion_dev; +static int msm_ion_camera_size; +static int msm_ion_audio_size; +static int msm_ion_sf_size; +static int msm_ion_camera_size_carving; +#endif +#endif + +#define CAMERA_HEAP_BASE 0x0 +#ifdef CONFIG_CMA +#define CAMERA_HEAP_TYPE ION_HEAP_TYPE_DMA +#else +#define CAMERA_HEAP_TYPE ION_HEAP_TYPE_CARVEOUT +#endif + +static struct android_usb_platform_data android_usb_pdata = { + .update_pid_and_serial_num = usb_diag_update_pid_and_serial_num, + .cdrom = 1, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + +#ifdef CONFIG_USB_EHCI_MSM_72K +static void msm_hsusb_vbus_power(unsigned phy_info, int on) +{ + int rc = 0; + unsigned gpio; + + gpio = QRD_GPIO_HOST_VBUS_EN; + + rc = gpio_request(gpio, "i2c_host_vbus_en"); + if (rc < 0) { + pr_err("failed to request %d GPIO\n", gpio); + return; + } + gpio_direction_output(gpio, !!on); + gpio_set_value_cansleep(gpio, !!on); + gpio_free(gpio); +} + +static struct msm_usb_host_platform_data msm_usb_host_pdata = { + .phy_info = (USB_PHY_INTEGRATED | USB_PHY_MODEL_45NM), +}; + +static void __init msm7627a_init_host(void) +{ + msm_add_host(0, &msm_usb_host_pdata); +} +#endif + +#ifdef CONFIG_USB_MSM_OTG_72K +static int hsusb_rpc_connect(int connect) +{ + if (connect) + return msm_hsusb_rpc_connect(); + else + return msm_hsusb_rpc_close(); +} + +static struct regulator *reg_hsusb; +static int msm_hsusb_ldo_init(int init) +{ + int rc = 0; + + if (init) { + reg_hsusb = regulator_get(NULL, "usb"); + if (IS_ERR(reg_hsusb)) { + rc = PTR_ERR(reg_hsusb); + pr_err("%s: could not get regulator: %d\n", + __func__, rc); + goto out; + } + + rc = regulator_set_voltage(reg_hsusb, 3300000, 3300000); + if (rc) { + pr_err("%s: could not set voltage: %d\n", + __func__, rc); + goto reg_free; + } + + return 0; + } + /* else fall through */ +reg_free: + regulator_put(reg_hsusb); +out: + reg_hsusb = NULL; + return rc; +} + +static int msm_hsusb_ldo_enable(int enable) +{ + static int ldo_status; + + if (IS_ERR_OR_NULL(reg_hsusb)) + return reg_hsusb ? PTR_ERR(reg_hsusb) : -ENODEV; + + if (ldo_status == enable) + return 0; + + ldo_status = enable; + + return enable ? + regulator_enable(reg_hsusb) : + regulator_disable(reg_hsusb); +} + +#ifndef CONFIG_USB_EHCI_MSM_72K +static int msm_hsusb_pmic_notif_init(void (*callback)(int online), int init) +{ + int ret = 0; + + if (init) + ret = msm_pm_app_rpc_init(callback); + else + msm_pm_app_rpc_deinit(callback); + + return ret; +} +#endif + +static struct msm_otg_platform_data msm_otg_pdata = { +#ifndef CONFIG_USB_EHCI_MSM_72K + .pmic_vbus_notif_init = msm_hsusb_pmic_notif_init, +#else + .vbus_power = msm_hsusb_vbus_power, +#endif + .rpc_connect = hsusb_rpc_connect, + .pemp_level = PRE_EMPHASIS_WITH_20_PERCENT, + .cdr_autoreset = CDR_AUTO_RESET_DISABLE, + .drv_ampl = HS_DRV_AMPLITUDE_DEFAULT, + .se1_gating = SE1_GATING_DISABLE, + .ldo_init = msm_hsusb_ldo_init, + .ldo_enable = msm_hsusb_ldo_enable, + .chg_init = hsusb_chg_init, + .chg_connected = hsusb_chg_connected, + .chg_vbus_draw = hsusb_chg_vbus_draw, +}; +#endif + +static struct msm_hsusb_gadget_platform_data msm_gadget_pdata = { + .is_phy_status_timer_on = 1, + .prop_chg = 0, +}; + +#ifdef CONFIG_SERIAL_MSM_HS +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .inject_rx_on_wakeup = 1, + .rx_to_inject = 0xFD, +}; +#endif +static struct msm_pm_platform_data msm7627a_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 16000, + .residency = 20000, + }, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 12000, + .residency = 20000, + }, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 1, + .latency = 2000, + .residency = 0, + }, + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 0, + }, +}; + +static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS, + .p_addr = 0, +}; + +/* 8625 PM platform data */ +static struct msm_pm_platform_data msm8625_pm_data[MSM_PM_SLEEP_MODE_NR * 2] = { + /* CORE0 entries */ + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 16000, + .residency = 20000, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 12000, + .residency = 20000, + }, + + /* picked latency & redisdency values from 7x30 */ + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 500, + .residency = 6000, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 10, + }, + + /* picked latency & redisdency values from 7x30 */ + [MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 500, + .residency = 6000, + }, + + [MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 10, + }, + +}; + +static struct msm_pm_boot_platform_data msm_pm_8625_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR, + .v_addr = MSM_CFG_CTL_BASE, +}; + +static unsigned reserve_mdp_size = MSM_RESERVE_MDP_SIZE; +static int __init reserve_mdp_size_setup(char *p) + { + reserve_mdp_size = memparse(p, NULL); + return 0; + } + +early_param("reserve_mdp_size", reserve_mdp_size_setup); + +static unsigned reserve_adsp_size = MSM_RESERVE_ADSP_SIZE; +static int __init reserve_adsp_size_setup(char *p) + { + reserve_adsp_size = memparse(p, NULL); + return 0; + } + +early_param("reserve_adsp_size", reserve_adsp_size_setup); + +static u32 msm_calculate_batt_capacity(u32 current_voltage); + +static struct msm_psy_batt_pdata msm_psy_batt_data = { + .voltage_min_design = 2800, + .voltage_max_design = 4300, + .avail_chg_sources = AC_CHG | USB_CHG | UNKNOWN_CHG, + .batt_technology = POWER_SUPPLY_TECHNOLOGY_LION, + .calculate_capacity = &msm_calculate_batt_capacity, +}; + +static u32 msm_calculate_batt_capacity(u32 current_voltage) +{ + u32 low_voltage = msm_psy_batt_data.voltage_min_design; + u32 high_voltage = msm_psy_batt_data.voltage_max_design; + + if (current_voltage <= low_voltage) + return 0; + else if (current_voltage >= high_voltage) + return 100; + else + return (current_voltage - low_voltage) * 100 + / (high_voltage - low_voltage); +} + +static struct platform_device msm_fastboot_device = { + .name = "fastboot", + .id = -1, +}; + +static struct platform_device msm_batt_device = { + .name = "msm-battery", + .id = -1, + .dev.platform_data = &msm_psy_batt_data, +}; + +static char *msm_adc_surf_device_names[] = { + "XO_ADC", +}; + +static struct msm_adc_platform_data msm_adc_pdata = { + .dev_names = msm_adc_surf_device_names, + .num_adc = ARRAY_SIZE(msm_adc_surf_device_names), + .target_hw = MSM_8x25, +}; + +static struct platform_device msm_adc_device = { + .name = "msm_adc", + .id = -1, + .dev = { + .platform_data = &msm_adc_pdata, + }, +}; + +#define GPIO_VREG_INIT(_id, _reg_name, _gpio_label, _gpio, _active_low) \ + [GPIO_VREG_ID_##_id] = { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + }, \ + .regulator_name = _reg_name, \ + .active_low = _active_low, \ + .gpio_label = _gpio_label, \ + .gpio = _gpio, \ + } + +#define GPIO_VREG_ID_EXT_2P85V 0 +#define GPIO_VREG_ID_EXT_1P8V 1 + +#if 1 //add by lf 2012.12.20 +#define GPIO_VREG_ID_EXT_3P3V 2 + +static struct regulator_consumer_supply vreg_consumers_EXT_3P3V[] = { + REGULATOR_SUPPLY("lcd_vddj", "mipi_dsi.1"), + }; +#endif + +static struct regulator_consumer_supply vreg_consumers_EXT_2P85V[] = { + REGULATOR_SUPPLY("cam_ov5647_avdd", "0-006c"), + REGULATOR_SUPPLY("cam_ov5647_truly_cm6868_avdd", "0-006c"), + REGULATOR_SUPPLY("cam_ov7692_avdd", "0-0078"), + REGULATOR_SUPPLY("cam_ov8825_avdd", "0-000d"), + REGULATOR_SUPPLY("cam_ov8825_truly_cm8313_avdd", "0-000d"), //add by hanjue + REGULATOR_SUPPLY("cam_gc0339_avdd", "0-0036"), //add by hanjue + REGULATOR_SUPPLY("lcd_vdd", "mipi_dsi.1"), +#ifdef CONFIG_AR0543 + REGULATOR_SUPPLY("cam_ar0543_avdd", "0-0064"), +#endif +}; + +static struct regulator_consumer_supply vreg_consumers_EXT_1P8V[] = { + REGULATOR_SUPPLY("cam_ov5647_vdd", "0-006c"), + REGULATOR_SUPPLY("cam_ov5647_truly_cm6868_vdd", "0-006c"), + REGULATOR_SUPPLY("cam_ov7692_vdd", "0-0078"), + REGULATOR_SUPPLY("cam_ov8825_truly_cm8313_vdd", "0-000d"), //add by hanjue + REGULATOR_SUPPLY("cam_gc0399_vdd", "0-0036"), //add by hanjue + REGULATOR_SUPPLY("lcd_vddi", "mipi_dsi.1"), +#ifdef CONFIG_AR0543 + REGULATOR_SUPPLY("cam_ar0543_vdd", "0-0064"), +#endif +}; + +/* GPIO regulator constraints */ +static struct gpio_regulator_platform_data msm_gpio_regulator_pdata[] = { + GPIO_VREG_INIT(EXT_2P85V, "ext_2p85v", "ext_2p85v_en", 35, 0), + GPIO_VREG_INIT(EXT_1P8V, "ext_1p8v", "ext_1p8v_en", 40, 0), + GPIO_VREG_INIT(EXT_3P3V, "ext_3p3v", "ext_3p3v_en", 12, 0), //linxc 2012-12-21 +++ +}; + +/* GPIO regulator */ +static struct platform_device qrd_vreg_gpio_ext_2p85v __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = 35, + .dev = { + .platform_data = + &msm_gpio_regulator_pdata[GPIO_VREG_ID_EXT_2P85V], + }, +}; + +static struct platform_device qrd_vreg_gpio_ext_1p8v __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = 40, + .dev = { + .platform_data = + &msm_gpio_regulator_pdata[GPIO_VREG_ID_EXT_1P8V], + }, +}; + +//linxc 2012-12-20 +++ +static struct platform_device qrd_vreg_gpio_ext_3p3v __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = 12, + .dev = { + .platform_data = + &msm_gpio_regulator_pdata[GPIO_VREG_ID_EXT_3P3V], + }, +}; +//linxc 2012-12-20 --- + +static struct platform_device *common_devices[] __initdata = { + &android_usb_device, + &msm_batt_device, + &msm_device_adspdec, + &msm_device_snd, + &msm_device_cad, + &asoc_msm_pcm, + &asoc_msm_dai0, + &asoc_msm_dai1, + &msm_adc_device, +#ifdef CONFIG_ION_MSM + &ion_dev, +#endif + &msm_fastboot_device, +}; + + +static struct platform_device msm_wlan_ar6000_pm_device = { + .name = "wlan_ar6000_pm_dev", + .id = 1, + .num_resources = 0, + .resource = NULL, +}; + +static struct platform_device *qrd7627a_devices[] __initdata = { + &msm_device_dmov, + &msm_device_smd, + &msm_device_uart1, + &msm_device_uart_dm1, + &msm_gsbi0_qup_i2c_device, + &msm_gsbi1_qup_i2c_device, + &msm_device_otg, + &msm_device_gadget_peripheral, + &msm_kgsl_3d0, + &qrd_vreg_gpio_ext_2p85v, + &qrd_vreg_gpio_ext_1p8v, +}; + +static struct platform_device *qrd3_devices[] __initdata = { + &msm_device_nand, +}; + +static struct platform_device *msm8625_evb_devices[] __initdata = { + &msm8625_device_dmov, + &msm8625_device_smd, + &msm8625_gsbi0_qup_i2c_device, + &msm8625_gsbi1_qup_i2c_device, + &msm8625_device_uart1, + &msm8625_device_uart_dm1, + &msm8625_device_otg, + &msm8625_device_gadget_peripheral, + &msm8625_kgsl_3d0, + &qrd_vreg_gpio_ext_2p85v, + &qrd_vreg_gpio_ext_1p8v, + &qrd_vreg_gpio_ext_3p3v, //linxc add +++ 2012-12-20 +}; + +static unsigned reserve_kernel_ebi1_size = RESERVE_KERNEL_EBI1_SIZE; +static int __init reserve_kernel_ebi1_size_setup(char *p) +{ + reserve_kernel_ebi1_size = memparse(p, NULL); + return 0; +} +early_param("reserve_kernel_ebi1_size", reserve_kernel_ebi1_size_setup); + +static unsigned reserve_audio_size = MSM_RESERVE_AUDIO_SIZE; +static int __init reserve_audio_size_setup(char *p) +{ + reserve_audio_size = memparse(p, NULL); + return 0; +} +early_param("reserve_audio_size", reserve_audio_size_setup); + +static void fix_sizes(void) +{ + if (get_ddr_size() > SZ_512M) + reserve_adsp_size = CAMERA_ZSL_SIZE; +#ifdef CONFIG_ION_MSM + msm_ion_audio_size = MSM_RESERVE_AUDIO_SIZE; + msm_ion_sf_size = reserve_mdp_size; +#ifdef CONFIG_CMA + if (get_ddr_size() > SZ_256M) + reserve_adsp_size = CAMERA_ZSL_SIZE; + msm_ion_camera_size = reserve_adsp_size; + msm_ion_camera_size_carving = 0; +#else + msm_ion_camera_size = reserve_adsp_size; + msm_ion_camera_size_carving = msm_ion_camera_size; +#endif +#endif +} + +#ifdef CONFIG_ION_MSM +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION +static struct ion_co_heap_pdata co_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, +}; + +static struct ion_co_heap_pdata co_mm_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, +}; + +static u64 msm_dmamask = DMA_BIT_MASK(32); + +static struct platform_device ion_cma_device = { + .name = "ion-cma-device", + .id = -1, + .dev = { + .dma_mask = &msm_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + } +}; +#endif + +/** + * These heaps are listed in the order they will be allocated. + * Don't swap the order unless you know what you are doing! + */ +struct ion_platform_heap qrd7627a_heaps[] = { + { + .id = ION_SYSTEM_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM, + .name = ION_VMALLOC_HEAP_NAME, + }, +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + /* ION_ADSP = CAMERA */ + { + .id = ION_CAMERA_HEAP_ID, + .type = CAMERA_HEAP_TYPE, + .name = ION_CAMERA_HEAP_NAME, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_mm_ion_pdata, + .priv = (void *)&ion_cma_device.dev, + }, + /* AUDIO HEAP 1*/ + { + .id = ION_AUDIO_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_AUDIO_HEAP_NAME, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + }, + /* ION_MDP = SF */ + { + .id = ION_SF_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_SF_HEAP_NAME, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + }, + /* AUDIO HEAP 2*/ + { + .id = ION_AUDIO_HEAP_BL_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_AUDIO_BL_HEAP_NAME, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + .base = BOOTLOADER_BASE_ADDR, + }, + +#endif +}; + +static struct ion_platform_data ion_pdata = { + .nr = MSM_ION_HEAP_NUM, + .has_outer_cache = 1, + .heaps = qrd7627a_heaps, +}; + +static struct platform_device ion_dev = { + .name = "ion-msm", + .id = 1, + .dev = { .platform_data = &ion_pdata }, +}; +#endif + +static struct memtype_reserve msm7627a_reserve_table[] __initdata = { + [MEMTYPE_SMI] = { + }, + [MEMTYPE_EBI0] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, + [MEMTYPE_EBI1] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, +}; + +static void __init size_ion_devices(void) +{ +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + ion_pdata.heaps[1].size = msm_ion_camera_size; + ion_pdata.heaps[2].size = RESERVE_KERNEL_EBI1_SIZE; + ion_pdata.heaps[3].size = msm_ion_sf_size; + ion_pdata.heaps[4].size = msm_ion_audio_size; +#endif +} + +static void __init reserve_ion_memory(void) +{ +#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + msm7627a_reserve_table[MEMTYPE_EBI1].size += RESERVE_KERNEL_EBI1_SIZE; + msm7627a_reserve_table[MEMTYPE_EBI1].size += + msm_ion_camera_size_carving; + msm7627a_reserve_table[MEMTYPE_EBI1].size += msm_ion_sf_size; +#endif +} + +static void __init msm7627a_calculate_reserve_sizes(void) +{ + fix_sizes(); + size_ion_devices(); + reserve_ion_memory(); +} + +static int msm7627a_paddr_to_memtype(unsigned int paddr) +{ + return MEMTYPE_EBI1; +} + +static struct reserve_info msm7627a_reserve_info __initdata = { + .memtype_reserve_table = msm7627a_reserve_table, + .calculate_reserve_sizes = msm7627a_calculate_reserve_sizes, + .paddr_to_memtype = msm7627a_paddr_to_memtype, +}; + +static void __init msm7627a_reserve(void) +{ + reserve_info = &msm7627a_reserve_info; + msm_reserve(); + memblock_remove(MSM8625_WARM_BOOT_PHYS, SZ_32); +} + +static void __init msm8625_reserve(void) +{ + memblock_remove(MSM8625_SECONDARY_PHYS, SZ_8); + msm7627a_reserve(); +} + +static void msmqrd_adsp_add_pdev(void) +{ + int rc = 0; + struct rpc_board_dev *rpc_adsp_pdev; + + rpc_adsp_pdev = kzalloc(sizeof(struct rpc_board_dev), GFP_KERNEL); + if (rpc_adsp_pdev == NULL) { + pr_err("%s: Memory Allocation failure\n", __func__); + return; + } + rpc_adsp_pdev->prog = ADSP_RPC_PROG; + + if (cpu_is_msm8625()) + rpc_adsp_pdev->pdev = msm8625_device_adsp; + else + rpc_adsp_pdev->pdev = msm_adsp_device; + rc = msm_rpc_add_board_dev(rpc_adsp_pdev, 1); + if (rc < 0) { + pr_err("%s: return val: %d\n", __func__, rc); + kfree(rpc_adsp_pdev); + } +} + +static void __init msm7627a_device_i2c_init(void) +{ + msm_gsbi0_qup_i2c_device.dev.platform_data = &msm_gsbi0_qup_i2c_pdata; + msm_gsbi1_qup_i2c_device.dev.platform_data = &msm_gsbi1_qup_i2c_pdata; +} + +static void __init msm8625_device_i2c_init(void) +{ + msm8625_gsbi0_qup_i2c_device.dev.platform_data + = &msm_gsbi0_qup_i2c_pdata; + msm8625_gsbi1_qup_i2c_device.dev.platform_data + = &msm_gsbi1_qup_i2c_pdata; +} + +static struct platform_device msm_proccomm_regulator_dev = { + .name = PROCCOMM_REGULATOR_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &msm7x27a_proccomm_regulator_data + } +}; + +static void __init msm7627a_init_regulators(void) +{ + int rc = platform_device_register(&msm_proccomm_regulator_dev); + if (rc) + pr_err("%s: could not register regulator device: %d\n", + __func__, rc); +} +/*start by lf 2012.12.26 for button-backlight*/ +#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) +#if 1//def D9_BOARD_MBV2P0_BSP +#define D9_LED_GPIO_KEYBOARD 49 +#else +#define D9_LED_GPIO_KEYBOARD 82 +#endif +static struct gpio_led gpio_leds[] = { + [0] = { + .gpio = D9_LED_GPIO_KEYBOARD, + .name = "button-backlight", + .default_trigger = "none", + .active_low = 0, + }, +}; + +static struct gpio_led_platform_data gpio_led_pdata = { + .num_leds = ARRAY_SIZE(gpio_leds), + .leds = gpio_leds, +}; + +static struct platform_device led_device = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &gpio_led_pdata, + }, +}; + +static void __init keyboard_leds_init(void) +{ + int rc; + rc = gpio_tlmm_config(GPIO_CFG(D9_LED_GPIO_KEYBOARD, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("%s: linxc say unable to enable keypad led gpio!\n", __func__); + return; + } + platform_device_register(&led_device); +} +#endif +/*end by lf 2012.12.26*/ +#define NV_ITEM_WLAN_MAC_ADDR 4678 +extern int msm_read_nv(unsigned int nv_item, void *buf); +unsigned char wlan_mac_addr[6]; + +static int __init msm_qrd_init_ar6000pm(void) +{ + msm_wlan_ar6000_pm_device.dev.platform_data = &ar600x_wlan_power; + msm_read_nv(NV_ITEM_WLAN_MAC_ADDR,wlan_mac_addr); + return platform_device_register(&msm_wlan_ar6000_pm_device); +} + +static void __init msm_add_footswitch_devices(void) +{ + platform_add_devices(msm_footswitch_devices, + msm_num_footswitch_devices); +} + +static void __init add_platform_devices(void) +{ + if (machine_is_msm8625_evb() || machine_is_msm8625_qrd7() + || machine_is_msm8625_qrd5() || machine_is_msm8625q_skud() + || machine_is_msm8625q_skue()) { + platform_add_devices(msm8625_evb_devices, + ARRAY_SIZE(msm8625_evb_devices)); + platform_add_devices(qrd3_devices, + ARRAY_SIZE(qrd3_devices)); + } + else { + platform_add_devices(qrd7627a_devices, + ARRAY_SIZE(qrd7627a_devices)); + } + + if (machine_is_msm7627a_qrd3() || machine_is_msm7627a_evb()) + platform_add_devices(qrd3_devices, + ARRAY_SIZE(qrd3_devices)); +/*start by lf 2012.12.26 for button-backlight*/ + if (machine_is_msm8625_qrd5()){ + #if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) + keyboard_leds_init(); + #endif +/*end by lf 2012.12.26 */ + } + platform_add_devices(common_devices, + ARRAY_SIZE(common_devices)); +} + +#define UART1DM_RX_GPIO 45 +static void __init qrd7627a_uart1dm_config(void) +{ + msm_uart_dm1_pdata.wakeup_irq = gpio_to_irq(UART1DM_RX_GPIO); + if (cpu_is_msm8625()) + msm8625_device_uart_dm1.dev.platform_data = + &msm_uart_dm1_pdata; + else + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; +} + +static void __init qrd7627a_otg_gadget(void) +{ + if (cpu_is_msm8625()) { + msm_otg_pdata.swfi_latency = msm8625_pm_data + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].latency; + msm8625_device_otg.dev.platform_data = &msm_otg_pdata; + msm8625_device_gadget_peripheral.dev.platform_data = + &msm_gadget_pdata; + + } else { + msm_otg_pdata.swfi_latency = msm7627a_pm_data + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency; + msm_device_otg.dev.platform_data = &msm_otg_pdata; + msm_device_gadget_peripheral.dev.platform_data = + &msm_gadget_pdata; + } +} + +static void __init msm_pm_init(void) +{ + + if (!cpu_is_msm8625()) { + msm_pm_set_platform_data(msm7627a_pm_data, + ARRAY_SIZE(msm7627a_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + } else { + msm_pm_set_platform_data(msm8625_pm_data, + ARRAY_SIZE(msm8625_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_8625_boot_pdata)); + msm8x25_spm_device_init(); + msm_pm_register_cpr_ops(); + } +} + +static void __init msm_qrd_init(void) +{ + msm7x2x_misc_init(); + msm7627a_init_regulators(); + msmqrd_adsp_add_pdev(); + + if (cpu_is_msm8625()) + msm8625_device_i2c_init(); + else + msm7627a_device_i2c_init(); + + /* uart1dm*/ + qrd7627a_uart1dm_config(); + /*OTG gadget*/ + qrd7627a_otg_gadget(); + + msm_add_footswitch_devices(); + add_platform_devices(); + + /* Ensure ar6000pm device is registered before MMC/SDC */ + msm_qrd_init_ar6000pm(); + msm7627a_init_mmc(); + +#ifdef CONFIG_USB_EHCI_MSM_72K + msm7627a_init_host(); +#endif + msm_pm_init(); + + msm_pm_register_irqs(); + msm_fb_add_devices(); + +#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE) + msm7627a_bt_power_init(); +#endif + msm7627a_sensor_init(); + msm7627a_camera_init(); + qrd7627a_add_io_devices(); + msm7x25a_kgsl_3d0_init(); + msm8x25_kgsl_3d0_init(); +} + +static void __init qrd7627a_init_early(void) +{ + msm_msm7627a_allocate_memory_regions(); +} + +#ifdef CONFIG_MSM_AMSS_ENHANCE_DEBUG +#define TASK_OFFSET_SEND(member, name) \ + do { \ + input.extension.len = 2; \ + input.extension.data[0] = (uint32_t)TASK_STRUCT_TAG; \ + input.extension.data[1] = offsetof(struct task_struct, member); \ + input.address = (uint32_t)__virt_to_phys((unsigned long)&init_task); \ + input.size = sizeof(struct task_struct); \ + strncpy(input.file_name, name, NZI_ITEM_FILE_NAME_LENGTH); \ + input.file_name[NZI_ITEM_FILE_NAME_LENGTH - 1] = 0; \ + send_modem_logaddr(&input); \ + } while (0) + +static int __init qrd7627a_logbuf_init(void) +{ + nzi_buf_item_type input; + extern char __log_buf[]; + extern unsigned long totalram_pages; + extern atomic_long_t vm_stat[]; + +#ifdef CONFIG_PRINTK + /* send the kernel log address */ + input.extension.len = 0; + input.address = (uint32_t)__virt_to_phys((unsigned long)__log_buf); + input.size = (1 << CONFIG_LOG_BUF_SHIFT); + strncpy(input.file_name, "dmesg", NZI_ITEM_FILE_NAME_LENGTH); + input.file_name[NZI_ITEM_FILE_NAME_LENGTH - 1] = 0; + send_modem_logaddr(&input); +#endif + + /* ******struct task_struct part ******/ + TASK_OFFSET_SEND(tasks, "tasks_of"); + TASK_OFFSET_SEND(thread_group, "tg_of"); + TASK_OFFSET_SEND(mm, "mm_of"); + TASK_OFFSET_SEND(comm, "comm_of"); + TASK_OFFSET_SEND(pid, "pid_of"); + TASK_OFFSET_SEND(tgid, "tgid_of"); + /* ******struct task_struct end ******/ + + /* totalram_pages */ + input.extension.len = 1; + input.extension.data[0] = (uint32_t)MEM_INFO_TAG; + input.address = (uint32_t)__virt_to_phys((unsigned long)&totalram_pages); + input.size = sizeof(totalram_pages); + strncpy(input.file_name, "totalram", NZI_ITEM_FILE_NAME_LENGTH); + input.file_name[NZI_ITEM_FILE_NAME_LENGTH - 1] = 0; + send_modem_logaddr(&input); + + /* vm_stat[vm_stat[NR_VM_ZONE_STAT_ITEMS]; */ + input.extension.len = 1; + input.extension.data[0] = (uint32_t)MEM_INFO_TAG; + input.address = (uint32_t)__virt_to_phys((unsigned long)vm_stat); + input.size = sizeof(vm_stat); + strncpy(input.file_name, "vm_stat", NZI_ITEM_FILE_NAME_LENGTH); + input.file_name[NZI_ITEM_FILE_NAME_LENGTH - 1] = 0; + send_modem_logaddr(&input); + + return 0; +} +late_initcall(qrd7627a_logbuf_init); +#endif + +MACHINE_START(MSM7627A_QRD1, "QRD MSM7627a QRD1") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7627a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7627A_QRD3, "QRD MSM7627a QRD3") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7627a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7627A_EVB, "QRD MSM7627a EVB") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7627a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM8625_EVB, "QRD MSM8625 EVB") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = gic_handle_irq, +MACHINE_END + +MACHINE_START(MSM7X27A_QRD5A, "QRD MSM7x27A QRD5A") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7627a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END + +MACHINE_START(MSM8625_QRD7, "QRD MSM8625 QRD7") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = gic_handle_irq, +MACHINE_END +MACHINE_START(MSM8625_QRD5, "QRD MSM8625 QRD5") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = gic_handle_irq, +MACHINE_END + +MACHINE_START(MSM8625Q_SKUD, "QRD MSM8625Q SKUD") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = gic_handle_irq, +MACHINE_END + +MACHINE_START(MSM8625Q_SKUE, "QRD MSM8625Q SKUE") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = gic_handle_irq, +MACHINE_END diff --git a/arch/arm/mach-msm/bricked_hotplug.c b/arch/arm/mach-msm/bricked_hotplug.c new file mode 100644 index 00000000..5179de69 --- /dev/null +++ b/arch/arm/mach-msm/bricked_hotplug.c @@ -0,0 +1,892 @@ +/* + * Bricked Hotplug Driver + * + * Copyright (c) 2013-2014, Dennis Rassmann + * Copyright (c) 2013-2014, Pranav Vashi + * Copyright (c) 2010-2014, The Linux Foundation. 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 +#ifdef CONFIG_STATE_NOTIFIER +#include +#else +#include +#endif + +#define DEBUG 0 + +#define MPDEC_TAG "bricked_hotplug" +#define HOTPLUG_ENABLED 0 +#define MSM_MPDEC_STARTDELAY 20000 +#define MSM_MPDEC_DELAY 70 +#define DEFAULT_MIN_CPUS_ONLINE 1 +#define DEFAULT_MAX_CPUS_ONLINE NR_CPUS +#define DEFAULT_MAX_CPUS_ONLINE_SUSP 1 +#define DEFAULT_SUSPEND_DEFER_TIME 10 +#define DEFAULT_DOWN_LOCK_DUR 500 + +#define MSM_MPDEC_IDLE_FREQ 320000 + +enum { + MSM_MPDEC_DISABLED = 0, + MSM_MPDEC_IDLE, + MSM_MPDEC_DOWN, + MSM_MPDEC_UP, +}; + +static struct notifier_block notif; +static struct delayed_work hotplug_work; +static struct delayed_work suspend_work; +static struct work_struct resume_work; +static struct workqueue_struct *hotplug_wq; +static struct workqueue_struct *susp_wq; + +static struct cpu_hotplug { + unsigned int startdelay; + unsigned int suspended; + unsigned int suspend_defer_time; + unsigned int min_cpus_online_res; + unsigned int max_cpus_online_res; + unsigned int max_cpus_online_susp; + unsigned int delay; + unsigned int down_lock_dur; + unsigned long int idle_freq; + unsigned int max_cpus_online; + unsigned int min_cpus_online; + unsigned int bricked_enabled; + struct mutex bricked_hotplug_mutex; + struct mutex bricked_cpu_mutex; +} hotplug = { + .startdelay = MSM_MPDEC_STARTDELAY, + .suspended = 0, + .suspend_defer_time = DEFAULT_SUSPEND_DEFER_TIME, + .min_cpus_online_res = DEFAULT_MIN_CPUS_ONLINE, + .max_cpus_online_res = DEFAULT_MAX_CPUS_ONLINE, + .max_cpus_online_susp = DEFAULT_MAX_CPUS_ONLINE_SUSP, + .delay = MSM_MPDEC_DELAY, + .down_lock_dur = DEFAULT_DOWN_LOCK_DUR, + .idle_freq = MSM_MPDEC_IDLE_FREQ, + .max_cpus_online = DEFAULT_MAX_CPUS_ONLINE, + .min_cpus_online = DEFAULT_MIN_CPUS_ONLINE, + .bricked_enabled = HOTPLUG_ENABLED, +}; + +static unsigned int NwNs_Threshold[8] = {12, 0, 25, 7, 30, 10, 0, 18}; +static unsigned int TwTs_Threshold[8] = {140, 0, 140, 190, 140, 190, 0, 190}; + +struct down_lock { + unsigned int locked; + struct delayed_work lock_rem; +}; +static DEFINE_PER_CPU(struct down_lock, lock_info); + +static void apply_down_lock(unsigned int cpu) +{ + struct down_lock *dl = &per_cpu(lock_info, cpu); + + dl->locked = 1; + queue_delayed_work_on(0, hotplug_wq, &dl->lock_rem, + msecs_to_jiffies(hotplug.down_lock_dur)); +} + +static void remove_down_lock(struct work_struct *work) +{ + struct down_lock *dl = container_of(work, struct down_lock, + lock_rem.work); + dl->locked = 0; +} + +static int check_down_lock(unsigned int cpu) +{ + struct down_lock *dl = &per_cpu(lock_info, cpu); + return dl->locked; +} + +extern unsigned int get_rq_info(void); + +unsigned int state = MSM_MPDEC_DISABLED; + +static int get_slowest_cpu(void) { + unsigned int cpu, slow_cpu = 0, rate, slow_rate = 0; + + for_each_online_cpu(cpu) { + if (cpu == 0) + continue; + rate = cpufreq_quick_get(cpu); + if (rate > 0 && slow_rate <= rate) { + slow_rate = rate; + slow_cpu = cpu; + } + } + + return slow_cpu; +} + +static unsigned int get_slowest_cpu_rate(void) { + unsigned int cpu, rate, slow_rate = 0; + + for_each_online_cpu(cpu) { + rate = cpufreq_quick_get(cpu); + if (rate > 0 && slow_rate <= rate) + slow_rate = rate; + } + + return slow_rate; +} + +static int mp_decision(void) { + static bool first_call = true; + int new_state = MSM_MPDEC_IDLE; + int nr_cpu_online; + int index; + unsigned int rq_depth; + static cputime64_t total_time = 0; + static cputime64_t last_time; + cputime64_t current_time; + cputime64_t this_time = 0; + + if (!hotplug.bricked_enabled) + return MSM_MPDEC_DISABLED; + + current_time = ktime_to_ms(ktime_get()); + + if (first_call) { + first_call = false; + } else { + this_time = current_time - last_time; + } + total_time += this_time; + + rq_depth = get_rq_info(); + nr_cpu_online = num_online_cpus(); + + index = (nr_cpu_online - 1) * 2; + if ((nr_cpu_online < DEFAULT_MAX_CPUS_ONLINE) && (rq_depth >= NwNs_Threshold[index])) { + if ((total_time >= TwTs_Threshold[index]) && + (nr_cpu_online < hotplug.max_cpus_online)) { + new_state = MSM_MPDEC_UP; + if (get_slowest_cpu_rate() <= hotplug.idle_freq) + new_state = MSM_MPDEC_IDLE; + } + } else if ((nr_cpu_online > 1) && (rq_depth <= NwNs_Threshold[index+1])) { + if ((total_time >= TwTs_Threshold[index+1]) && + (nr_cpu_online > hotplug.min_cpus_online)) { + new_state = MSM_MPDEC_DOWN; + if (get_slowest_cpu_rate() > hotplug.idle_freq) + new_state = MSM_MPDEC_IDLE; + } + } else { + new_state = MSM_MPDEC_IDLE; + total_time = 0; + } + + if (new_state != MSM_MPDEC_IDLE) { + total_time = 0; + } + + last_time = ktime_to_ms(ktime_get()); +#if DEBUG + pr_info(MPDEC_TAG"[DEBUG] rq: %u, new_state: %i | Mask=[%d%d%d%d]\n", + rq_depth, new_state, cpu_online(0), cpu_online(1), cpu_online(2), cpu_online(3)); +#endif + return new_state; +} + +static void __ref bricked_hotplug_work(struct work_struct *work) { + unsigned int cpu; + + if (hotplug.suspended && hotplug.max_cpus_online_susp <= 1) + goto out; + + if (!mutex_trylock(&hotplug.bricked_cpu_mutex)) + goto out; + + state = mp_decision(); + switch (state) { + case MSM_MPDEC_DISABLED: + case MSM_MPDEC_IDLE: + break; + case MSM_MPDEC_DOWN: + cpu = get_slowest_cpu(); + if (cpu > 0) { + if (cpu_online(cpu) && !check_down_lock(cpu)) + cpu_down(cpu); + } + break; + case MSM_MPDEC_UP: + cpu = cpumask_next_zero(0, cpu_online_mask); + if (cpu < DEFAULT_MAX_CPUS_ONLINE) { + if (!cpu_online(cpu)) { + cpu_up(cpu); + apply_down_lock(cpu); + } + } + break; + default: + pr_err(MPDEC_TAG": %s: invalid mpdec hotplug state %d\n", + __func__, state); + } + mutex_unlock(&hotplug.bricked_cpu_mutex); + +out: + if (hotplug.bricked_enabled) + queue_delayed_work(hotplug_wq, &hotplug_work, + msecs_to_jiffies(hotplug.delay)); + return; +} + +static void bricked_hotplug_suspend(struct work_struct *work) +{ + int cpu; + + mutex_lock(&hotplug.bricked_hotplug_mutex); + hotplug.suspended = 1; + hotplug.min_cpus_online_res = hotplug.min_cpus_online; + hotplug.min_cpus_online = 1; + hotplug.max_cpus_online_res = hotplug.max_cpus_online; + hotplug.max_cpus_online = hotplug.max_cpus_online_susp; + mutex_unlock(&hotplug.bricked_hotplug_mutex); + + if (hotplug.max_cpus_online_susp > 1) { + pr_info(MPDEC_TAG": Screen -> off\n"); + return; + } + + /* main work thread can sleep now */ + cancel_delayed_work_sync(&hotplug_work); + + for_each_possible_cpu(cpu) { + if ((cpu >= 1) && (cpu_online(cpu))) + cpu_down(cpu); + } + + pr_info(MPDEC_TAG": Screen -> off. Deactivated bricked hotplug. | Mask=[%d%d%d%d]\n", + cpu_online(0), cpu_online(1), cpu_online(2), cpu_online(3)); +} + +static void __ref bricked_hotplug_resume(struct work_struct *work) +{ + int cpu, required_reschedule = 0, required_wakeup = 0; + + if (hotplug.suspended) { + mutex_lock(&hotplug.bricked_hotplug_mutex); + hotplug.suspended = 0; + hotplug.min_cpus_online = hotplug.min_cpus_online_res; + hotplug.max_cpus_online = hotplug.max_cpus_online_res; + mutex_unlock(&hotplug.bricked_hotplug_mutex); + required_wakeup = 1; + /* Initiate hotplug work if it was cancelled */ + if (hotplug.max_cpus_online_susp <= 1) { + required_reschedule = 1; + INIT_DELAYED_WORK(&hotplug_work, bricked_hotplug_work); + } + } + + if (required_wakeup) { + /* Fire up all CPUs */ + for_each_cpu_not(cpu, cpu_online_mask) { + if (cpu == 0) + continue; + cpu_up(cpu); + apply_down_lock(cpu); + } + } + + /* Resume hotplug workqueue if required */ + if (required_reschedule) { + queue_delayed_work(hotplug_wq, &hotplug_work, 0); + pr_info(MPDEC_TAG": Screen -> on. Activated bricked hotplug. | Mask=[%d%d%d%d]\n", + cpu_online(0), cpu_online(1), cpu_online(2), cpu_online(3)); + } +} + +static void __bricked_hotplug_resume(void) +{ + if (!hotplug.bricked_enabled) + return; + + flush_workqueue(susp_wq); + cancel_delayed_work_sync(&suspend_work); + queue_work_on(0, susp_wq, &resume_work); +} + +static void __bricked_hotplug_suspend(void) +{ + if (!hotplug.bricked_enabled || hotplug.suspended) + return; + + INIT_DELAYED_WORK(&suspend_work, bricked_hotplug_suspend); + queue_delayed_work_on(0, susp_wq, &suspend_work, + msecs_to_jiffies(hotplug.suspend_defer_time * 1000)); +} + +#ifdef CONFIG_STATE_NOTIFIER +static int state_notifier_callback(struct notifier_block *this, + unsigned long event, void *data) +{ + switch (event) { + case STATE_NOTIFIER_ACTIVE: + __bricked_hotplug_resume(); + break; + case STATE_NOTIFIER_SUSPEND: + __bricked_hotplug_suspend(); + break; + default: + break; + } + + return NOTIFY_OK; +} +#else +static int prev_fb = FB_BLANK_UNBLANK; + +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + + if (evdata && evdata->data && event == FB_EVENT_BLANK) { + blank = evdata->data; + switch (*blank) { + case FB_BLANK_UNBLANK: + if (prev_fb == FB_BLANK_POWERDOWN) { + __bricked_hotplug_resume(); + prev_fb = FB_BLANK_UNBLANK; + } + break; + case FB_BLANK_POWERDOWN: + if (prev_fb == FB_BLANK_UNBLANK) { + __bricked_hotplug_suspend(); + prev_fb = FB_BLANK_POWERDOWN; + } + break; + } + } + + return NOTIFY_OK; +} +#endif + +static int bricked_hotplug_start(void) +{ + int cpu, ret = 0; + struct down_lock *dl; + + hotplug_wq = alloc_workqueue("bricked_hotplug", WQ_HIGHPRI | WQ_FREEZABLE, 0); + if (!hotplug_wq) { + ret = -ENOMEM; + goto err_out; + } + + susp_wq = + alloc_workqueue("susp_wq", WQ_FREEZABLE, 0); + if (!susp_wq) { + pr_err("%s: Failed to allocate suspend workqueue\n", + MPDEC_TAG); + ret = -ENOMEM; + goto err_dev; + } + +#ifdef CONFIG_STATE_NOTIFIER + notif.notifier_call = state_notifier_callback; + if (state_register_client(¬if)) { + pr_err("%s: Failed to register State notifier callback\n", + MPDEC_TAG); + goto err_susp; + } +#else + notif.notifier_call = fb_notifier_callback; + if (fb_register_client(¬if)) { + pr_err("%s: Failed to register FB notifier callback\n", + MPDEC_TAG); + goto err_susp; + } +#endif + + mutex_init(&hotplug.bricked_cpu_mutex); + mutex_init(&hotplug.bricked_hotplug_mutex); + + INIT_DELAYED_WORK(&hotplug_work, bricked_hotplug_work); + INIT_DELAYED_WORK(&suspend_work, bricked_hotplug_suspend); + INIT_WORK(&resume_work, bricked_hotplug_resume); + + for_each_possible_cpu(cpu) { + dl = &per_cpu(lock_info, cpu); + INIT_DELAYED_WORK(&dl->lock_rem, remove_down_lock); + } + + if (hotplug.bricked_enabled) + queue_delayed_work(hotplug_wq, &hotplug_work, + msecs_to_jiffies(hotplug.startdelay)); + + return ret; +err_susp: + destroy_workqueue(susp_wq); +err_dev: + destroy_workqueue(hotplug_wq); +err_out: + hotplug.bricked_enabled = 0; + return ret; +} + +static void bricked_hotplug_stop(void) +{ + int cpu; + struct down_lock *dl; + + for_each_possible_cpu(cpu) { + dl = &per_cpu(lock_info, cpu); + cancel_delayed_work_sync(&dl->lock_rem); + } + + flush_workqueue(susp_wq); + cancel_work_sync(&resume_work); + cancel_delayed_work_sync(&suspend_work); + cancel_delayed_work_sync(&hotplug_work); + mutex_destroy(&hotplug.bricked_hotplug_mutex); + mutex_destroy(&hotplug.bricked_cpu_mutex); +#ifdef CONFIG_STATE_NOTIFIER + state_unregister_client(¬if); +#else + fb_unregister_client(¬if); +#endif + notif.notifier_call = NULL; + destroy_workqueue(susp_wq); + destroy_workqueue(hotplug_wq); + + /* Put all sibling cores to sleep */ + for_each_online_cpu(cpu) { + if (cpu == 0) + continue; + cpu_down(cpu); + } +} + +/**************************** SYSFS START ****************************/ + +#define show_one(file_name, object) \ +static ssize_t show_##file_name \ +(struct device *dev, struct device_attribute *bricked_hotplug_attrs, \ + char *buf) \ +{ \ + return sprintf(buf, "%u\n", hotplug.object); \ +} + +show_one(startdelay, startdelay); +show_one(delay, delay); +show_one(down_lock_duration, down_lock_dur); +show_one(min_cpus_online, min_cpus_online); +show_one(max_cpus_online, max_cpus_online); +show_one(max_cpus_online_susp, max_cpus_online_susp); +show_one(suspend_defer_time, suspend_defer_time); +show_one(bricked_enabled, bricked_enabled); + +#define define_one_twts(file_name, arraypos) \ +static ssize_t show_##file_name \ +(struct device *dev, struct device_attribute *bricked_hotplug_attrs, \ + char *buf) \ +{ \ + return sprintf(buf, "%u\n", TwTs_Threshold[arraypos]); \ +} \ +static ssize_t store_##file_name \ +(struct device *dev, struct device_attribute *bricked_hotplug_attrs, \ + const char *buf, size_t count) \ +{ \ + unsigned int input; \ + int ret; \ + ret = sscanf(buf, "%u", &input); \ + if (ret != 1) \ + return -EINVAL; \ + TwTs_Threshold[arraypos] = input; \ + return count; \ +} \ +static DEVICE_ATTR(file_name, 644, show_##file_name, store_##file_name); +define_one_twts(twts_threshold_0, 0); +define_one_twts(twts_threshold_1, 1); +define_one_twts(twts_threshold_2, 2); +define_one_twts(twts_threshold_3, 3); +define_one_twts(twts_threshold_4, 4); +define_one_twts(twts_threshold_5, 5); +define_one_twts(twts_threshold_6, 6); +define_one_twts(twts_threshold_7, 7); + +#define define_one_nwns(file_name, arraypos) \ +static ssize_t show_##file_name \ +(struct device *dev, struct device_attribute *bricked_hotplug_attrs, \ + char *buf) \ +{ \ + return sprintf(buf, "%u\n", NwNs_Threshold[arraypos]); \ +} \ +static ssize_t store_##file_name \ +(struct device *dev, struct device_attribute *bricked_hotplug_attrs, \ + const char *buf, size_t count) \ +{ \ + unsigned int input; \ + int ret; \ + ret = sscanf(buf, "%u", &input); \ + if (ret != 1) \ + return -EINVAL; \ + NwNs_Threshold[arraypos] = input; \ + return count; \ +} \ +static DEVICE_ATTR(file_name, 644, show_##file_name, store_##file_name); +define_one_nwns(nwns_threshold_0, 0); +define_one_nwns(nwns_threshold_1, 1); +define_one_nwns(nwns_threshold_2, 2); +define_one_nwns(nwns_threshold_3, 3); +define_one_nwns(nwns_threshold_4, 4); +define_one_nwns(nwns_threshold_5, 5); +define_one_nwns(nwns_threshold_6, 6); +define_one_nwns(nwns_threshold_7, 7); + +static ssize_t show_idle_freq (struct device *dev, + struct device_attribute *bricked_hotplug_attrs, + char *buf) +{ + return sprintf(buf, "%lu\n", hotplug.idle_freq); +} + +static ssize_t store_startdelay(struct device *dev, + struct device_attribute *bricked_hotplug_attrs, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + hotplug.startdelay = input; + + return count; +} + +static ssize_t store_delay(struct device *dev, + struct device_attribute *bricked_hotplug_attrs, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + hotplug.delay = input; + + return count; +} + +static ssize_t store_down_lock_duration(struct device *dev, + struct device_attribute *bricked_hotplug_attrs, + const char *buf, size_t count) +{ + int ret; + unsigned int val; + + ret = sscanf(buf, "%u", &val); + if (ret != 1) + return -EINVAL; + + hotplug.down_lock_dur = val; + + return count; +} + +static ssize_t store_idle_freq(struct device *dev, + struct device_attribute *bricked_hotplug_attrs, + const char *buf, size_t count) +{ + long unsigned int input; + int ret; + ret = sscanf(buf, "%lu", &input); + if (ret != 1) + return -EINVAL; + + hotplug.idle_freq = input; + + return count; +} + +static ssize_t __ref store_min_cpus_online(struct device *dev, + struct device_attribute *bricked_hotplug_attrs, + const char *buf, size_t count) +{ + unsigned int input, cpu; + int ret; + ret = sscanf(buf, "%u", &input); + if ((ret != 1) || input < 1 || input > DEFAULT_MAX_CPUS_ONLINE) + return -EINVAL; + + if (hotplug.max_cpus_online < input) + hotplug.max_cpus_online = input; + + hotplug.min_cpus_online = input; + + if (!hotplug.bricked_enabled) + return count; + + if (num_online_cpus() < hotplug.min_cpus_online) { + for (cpu = 1; cpu < DEFAULT_MAX_CPUS_ONLINE; cpu++) { + if (num_online_cpus() >= hotplug.min_cpus_online) + break; + if (cpu_online(cpu)) + continue; + cpu_up(cpu); + } + pr_info(MPDEC_TAG": min_cpus_online set to %u. Affected CPUs were hotplugged!\n", input); + } + + return count; +} + +static ssize_t store_max_cpus_online(struct device *dev, + struct device_attribute *bricked_hotplug_attrs, + const char *buf, size_t count) +{ + unsigned int input, cpu; + int ret; + ret = sscanf(buf, "%u", &input); + if ((ret != 1) || input < 1 || input > DEFAULT_MAX_CPUS_ONLINE) + return -EINVAL; + + if (hotplug.min_cpus_online > input) + hotplug.min_cpus_online = input; + + hotplug.max_cpus_online = input; + + if (!hotplug.bricked_enabled) + return count; + + if (num_online_cpus() > hotplug.max_cpus_online) { + for (cpu = DEFAULT_MAX_CPUS_ONLINE; cpu > 0; cpu--) { + if (num_online_cpus() <= hotplug.max_cpus_online) + break; + if (!cpu_online(cpu)) + continue; + cpu_down(cpu); + } + pr_info(MPDEC_TAG": max_cpus set to %u. Affected CPUs were unplugged!\n", input); + } + + return count; +} + +static ssize_t store_max_cpus_online_susp(struct device *dev, + struct device_attribute *bricked_hotplug_attrs, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if ((ret != 1) || input < 1 || input > DEFAULT_MAX_CPUS_ONLINE) + return -EINVAL; + + hotplug.max_cpus_online_susp = input; + + return count; +} + +static ssize_t store_suspend_defer_time(struct device *dev, + struct device_attribute *bricked_hotplug_attrs, + const char *buf, size_t count) +{ + int ret; + unsigned int val; + + ret = sscanf(buf, "%u", &val); + if (ret != 1) + return -EINVAL; + + hotplug.suspend_defer_time = val; + + return count; +} + +static ssize_t store_bricked_enabled(struct device *dev, + struct device_attribute *bricked_hotplug_attrs, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + if (input > 1) + input = 1; + + if (input == hotplug.bricked_enabled) + return count; + + hotplug.bricked_enabled = input; + + if (!hotplug.bricked_enabled) { + state = MSM_MPDEC_DISABLED; + bricked_hotplug_stop(); + pr_info(MPDEC_TAG": Disabled\n"); + } else { + state = MSM_MPDEC_IDLE; + bricked_hotplug_start(); + pr_info(MPDEC_TAG": Enabled\n"); + } + + return count; +} + +static DEVICE_ATTR(startdelay, 644, show_startdelay, store_startdelay); +static DEVICE_ATTR(delay, 644, show_delay, store_delay); +static DEVICE_ATTR(down_lock_duration, 644, show_down_lock_duration, store_down_lock_duration); +static DEVICE_ATTR(idle_freq, 644, show_idle_freq, store_idle_freq); +static DEVICE_ATTR(min_cpus, 644, show_min_cpus_online, store_min_cpus_online); +static DEVICE_ATTR(max_cpus, 644, show_max_cpus_online, store_max_cpus_online); +static DEVICE_ATTR(min_cpus_online, 644, show_min_cpus_online, store_min_cpus_online); +static DEVICE_ATTR(max_cpus_online, 644, show_max_cpus_online, store_max_cpus_online); +static DEVICE_ATTR(max_cpus_online_susp, 644, show_max_cpus_online_susp, store_max_cpus_online_susp); +static DEVICE_ATTR(suspend_defer_time, 644, show_suspend_defer_time, store_suspend_defer_time); +static DEVICE_ATTR(enabled, 644, show_bricked_enabled, store_bricked_enabled); + +static struct attribute *bricked_hotplug_attrs[] = { + &dev_attr_startdelay.attr, + &dev_attr_delay.attr, + &dev_attr_down_lock_duration.attr, + &dev_attr_idle_freq.attr, + &dev_attr_min_cpus.attr, + &dev_attr_max_cpus.attr, + &dev_attr_min_cpus_online.attr, + &dev_attr_max_cpus_online.attr, + &dev_attr_max_cpus_online_susp.attr, + &dev_attr_suspend_defer_time.attr, + &dev_attr_enabled.attr, + &dev_attr_twts_threshold_0.attr, + &dev_attr_twts_threshold_1.attr, + &dev_attr_twts_threshold_2.attr, + &dev_attr_twts_threshold_3.attr, + &dev_attr_twts_threshold_4.attr, + &dev_attr_twts_threshold_5.attr, + &dev_attr_twts_threshold_6.attr, + &dev_attr_twts_threshold_7.attr, + &dev_attr_nwns_threshold_0.attr, + &dev_attr_nwns_threshold_1.attr, + &dev_attr_nwns_threshold_2.attr, + &dev_attr_nwns_threshold_3.attr, + &dev_attr_nwns_threshold_4.attr, + &dev_attr_nwns_threshold_5.attr, + &dev_attr_nwns_threshold_6.attr, + &dev_attr_nwns_threshold_7.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = bricked_hotplug_attrs, + .name = "conf", +}; + +/**************************** SYSFS END ****************************/ + +static int bricked_hotplug_probe(struct platform_device *pdev) +{ + int ret = 0; + struct kobject *bricked_kobj; + + bricked_kobj = + kobject_create_and_add("msm_mpdecision", kernel_kobj); + if (!bricked_kobj) { + pr_err("%s kobject create failed!\n", + __func__); + return -ENOMEM; + } + + ret = sysfs_create_group(bricked_kobj, + &attr_group); + + if (ret) { + pr_err("%s bricked_kobj create failed!\n", + __func__); + goto err_dev; + } + + if (hotplug.bricked_enabled) { + ret = bricked_hotplug_start(); + if (ret != 0) + goto err_dev; + } + + return ret; +err_dev: + if (bricked_kobj != NULL) + kobject_put(bricked_kobj); + return ret; +} + +static struct platform_device bricked_hotplug_device = { + .name = MPDEC_TAG, + .id = -1, +}; + +static int bricked_hotplug_remove(struct platform_device *pdev) +{ + if (hotplug.bricked_enabled) + bricked_hotplug_stop(); + + return 0; +} + +static struct platform_driver bricked_hotplug_driver = { + .probe = bricked_hotplug_probe, + .remove = bricked_hotplug_remove, + .driver = { + .name = MPDEC_TAG, + .owner = THIS_MODULE, + }, +}; + +static int __init msm_mpdec_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&bricked_hotplug_driver); + if (ret) { + pr_err("%s: Driver register failed: %d\n", MPDEC_TAG, ret); + return ret; + } + + ret = platform_device_register(&bricked_hotplug_device); + if (ret) { + pr_err("%s: Device register failed: %d\n", MPDEC_TAG, ret); + return ret; + } + + pr_info(MPDEC_TAG": %s init complete.", __func__); + + return ret; +} + +void msm_mpdec_exit(void) +{ + platform_device_unregister(&bricked_hotplug_device); + platform_driver_unregister(&bricked_hotplug_driver); +} + +late_initcall(msm_mpdec_init); +module_exit(msm_mpdec_exit); + +MODULE_AUTHOR("Pranav Vashi "); +MODULE_DESCRIPTION("Bricked Hotplug Driver"); +MODULE_LICENSE("GPLv2"); diff --git a/arch/arm/mach-msm/devices-iommu.c b/arch/arm/mach-msm/devices-iommu.c index 281d1cd9..a965a4dd 100644 --- a/arch/arm/mach-msm/devices-iommu.c +++ b/arch/arm/mach-msm/devices-iommu.c @@ -365,25 +365,25 @@ static struct msm_iommu_dev vcodec_b_iommu = { static struct msm_iommu_dev gfx3d_iommu = { .name = "gfx3d", .ncb = 3, - .ttbr_split = 1, + .ttbr_split = 2, }; static struct msm_iommu_dev gfx3d1_iommu = { .name = "gfx3d1", .ncb = 3, - .ttbr_split = 1, + .ttbr_split = 2, }; static struct msm_iommu_dev gfx2d0_iommu = { .name = "gfx2d0", .ncb = 2, - .ttbr_split = 1, + .ttbr_split = 2, }; static struct msm_iommu_dev gfx2d1_iommu = { .name = "gfx2d1", .ncb = 2, - .ttbr_split = 1, + .ttbr_split = 2, }; static struct msm_iommu_dev vcap_iommu = { diff --git a/arch/arm/mach-msm/devices-msm7x27.c b/arch/arm/mach-msm/devices-msm7x27.c index 57fef659..3336f2d4 100644 --- a/arch/arm/mach-msm/devices-msm7x27.c +++ b/arch/arm/mach-msm/devices-msm7x27.c @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c index a394cbbf..34718d45 100644 --- a/arch/arm/mach-msm/devices-msm7x27a.c +++ b/arch/arm/mach-msm/devices-msm7x27a.c @@ -919,6 +919,14 @@ static struct resource kgsl_3d0_resources[] = { static struct kgsl_device_platform_data kgsl_3d0_pdata = { .pwrlevel = { + { + .gpu_freq = 300000000, + .bus_freq = 200000000, + }, + { + .gpu_freq = 266000000, + .bus_freq = 200000000, + }, { .gpu_freq = 245760000, .bus_freq = 200000000, @@ -928,7 +936,7 @@ static struct kgsl_device_platform_data kgsl_3d0_pdata = { .bus_freq = 160000000, }, { - .gpu_freq = 133330000, + .gpu_freq = 192000000, .bus_freq = 0, }, }, @@ -1873,7 +1881,7 @@ static void __init msm_cpr_init(void) uint8_t ring_osc = 0; #if defined(CONFIG_MSM_FUSE_INFO_DEBUG) char tmp_buf[100] = ""; - uint32_t fuse_len = 0; + uint32_t fuse_len = 0; #endif cpr_info = kzalloc(sizeof(struct cpr_info_type), GFP_KERNEL); diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h index bb14b0c1..f0a40b1a 100644 --- a/arch/arm/mach-msm/devices.h +++ b/arch/arm/mach-msm/devices.h @@ -380,6 +380,12 @@ extern struct platform_device msm_dsps_device_8064; extern struct platform_device *msm_8974_stub_regulator_devices[]; extern int msm_8974_stub_regulator_devices_len; +extern struct platform_device msm8960_cpu_idle_device; +extern struct platform_device msm8930_cpu_idle_device; +extern struct platform_device apq8064_cpu_idle_device; + +extern struct platform_device msm8960_msm_gov_device; +extern struct platform_device msm8930_msm_gov_device; extern struct platform_device apq8064_msm_gov_device; extern struct platform_device msm_bus_8930_apps_fabric; diff --git a/arch/arm/mach-msm/dma.c b/arch/arm/mach-msm/dma.c index 960ad1a0..afee25f6 100644 --- a/arch/arm/mach-msm/dma.c +++ b/arch/arm/mach-msm/dma.c @@ -1,11 +1,7 @@ /* linux/arch/arm/mach-msm/dma.c * * Copyright (C) 2007 Google, Inc. -<<<<<<< HEAD * Copyright (c) 2008-2010, 2012 The Linux Foundation. All rights reserved. -======= - * Copyright (c) 2008-2010, 2012, 2013 The Linux Foundation. All rights reserved. ->>>>>>> d7fe698... msm: dma: upstream to CAF Ifaa5efdde8150f932a12d4f9eeccfb36ecb8f88f * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -35,10 +31,8 @@ enum { CLK_DIS, - CLK_ENABLING, - CLK_EN, CLK_TO_BE_DIS, - CLK_DISABLING + CLK_EN }; struct msm_dmov_ci_conf { @@ -68,8 +62,8 @@ struct msm_dmov_conf { struct list_head staged_commands[MSM_DMOV_CHANNEL_COUNT]; struct list_head ready_commands[MSM_DMOV_CHANNEL_COUNT]; struct list_head active_commands[MSM_DMOV_CHANNEL_COUNT]; - struct mutex clock_lock; - spinlock_t lock; + struct mutex lock; + spinlock_t list_lock; unsigned int irq; struct clk *clk; struct clk *pclk; @@ -171,16 +165,16 @@ static struct msm_dmov_conf dmov_conf[] = { { .crci_conf = adm0_crci_conf, .chan_conf = adm0_chan_conf, - .clock_lock = __MUTEX_INITIALIZER(dmov_conf[0].clock_lock), - .lock = __SPIN_LOCK_UNLOCKED(dmov_lock), + .lock = __MUTEX_INITIALIZER(dmov_conf[0].lock), + .list_lock = __SPIN_LOCK_UNLOCKED(dmov_list_lock), .clk_ctl = CLK_DIS, .work = __DELAYED_WORK_INITIALIZER(dmov_conf[0].work, msm_dmov_clock_work), }, { .crci_conf = adm1_crci_conf, .chan_conf = adm1_chan_conf, - .clock_lock = __MUTEX_INITIALIZER(dmov_conf[1].clock_lock), - .lock = __SPIN_LOCK_UNLOCKED(dmov_lock), + .lock = __MUTEX_INITIALIZER(dmov_conf[1].lock), + .list_lock = __SPIN_LOCK_UNLOCKED(dmov_list_lock), .clk_ctl = CLK_DIS, .work = __DELAYED_WORK_INITIALIZER(dmov_conf[1].work, msm_dmov_clock_work), @@ -191,8 +185,8 @@ static struct msm_dmov_conf dmov_conf[] = { { .crci_conf = NULL, .chan_conf = NULL, - .clock_lock = __MUTEX_INITIALIZER(dmov_conf[0].clock_lock), - .lock = __SPIN_LOCK_UNLOCKED(dmov_lock), + .lock = __MUTEX_INITIALIZER(dmov_conf[0].lock), + .list_lock = __SPIN_LOCK_UNLOCKED(dmov_list_lock), .clk_ctl = CLK_DIS, .work = __DELAYED_WORK_INITIALIZER(dmov_conf[0].work, msm_dmov_clock_work), @@ -276,21 +270,13 @@ static void msm_dmov_clock_work(struct work_struct *work) struct msm_dmov_conf *conf = container_of(to_delayed_work(work), struct msm_dmov_conf, work); int adm = DMOV_IRQ_TO_ADM(conf->irq); - unsigned long irq_flags; - - mutex_lock(&conf->clock_lock); - spin_lock_irqsave(&conf->lock, irq_flags); + mutex_lock(&conf->lock); if (conf->clk_ctl == CLK_TO_BE_DIS) { BUG_ON(conf->channel_active); - conf->clk_ctl = CLK_DISABLING; - spin_unlock_irqrestore(&conf->lock, irq_flags); msm_dmov_clk_off(adm); - spin_lock_irqsave(&conf->lock, irq_flags); - if (conf->clk_ctl == CLK_DISABLING) - conf->clk_ctl = CLK_DIS; + conf->clk_ctl = CLK_DIS; } - spin_unlock_irqrestore(&conf->lock, irq_flags); - mutex_unlock(&conf->clock_lock); + mutex_unlock(&conf->lock); } enum { @@ -316,7 +302,7 @@ static struct msm_dmov_cmd *start_ready_cmd(unsigned ch, int adm) if (!dmov_conf[adm].channel_active) enable_irq(dmov_conf[adm].irq); dmov_conf[adm].channel_active |= BIT(ch); - PRINT_IO("start_ready_cmd, %x, ch %d\n", cmd->cmdptr, ch); + PRINT_IO("msm dmov enqueue command, %x, ch %d\n", cmd->cmdptr, ch); writel_relaxed(cmd->cmdptr, DMOV_REG(DMOV_CMD_PTR(ch), adm)); return cmd; @@ -330,86 +316,37 @@ static void msm_dmov_enqueue_cmd_ext_work(struct work_struct *work) unsigned status; unsigned long flags; int adm = DMOV_ID_TO_ADM(id); - int ch = 0; - - mutex_lock(&dmov_conf[adm].clock_lock); - /*No spin_lock is required for the condition check below*/ - BUG_ON(dmov_conf[adm].clk_ctl != CLK_ENABLING); - status = msm_dmov_clk_on(adm); - if (status != 0) { - PRINT_ERROR("ADM: Unexpected clock failure at dma.c:%d/%s()!\n", - __LINE__, __func__); - goto error; - } - - spin_lock_irqsave(&dmov_conf[adm].lock, flags); - - dmov_conf[adm].clk_ctl = CLK_EN; + int ch = DMOV_ID_TO_CHAN(id); - for (ch = 0; ch < MSM_DMOV_CHANNEL_COUNT; ch++) { - while (!list_empty(&dmov_conf[adm].staged_commands[ch])) { - cmd = list_entry(dmov_conf[adm].staged_commands[ch]. - next, typeof(*cmd), list); - list_del(&cmd->list); - list_add_tail(&cmd->list, &dmov_conf[adm]. - ready_commands[ch]); - } - if (!list_empty(&dmov_conf[adm].ready_commands[ch])) { - status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), adm)); - if (status & DMOV_STATUS_CMD_PTR_RDY) { - PRINT_IO("msm_dmov_enqueue_cmd(%d),"\ - " start command, status %x\n", - id, status); - cmd = start_ready_cmd(ch, adm); - /* - * We added something to the ready list, - * and still hold the list lock. - * Thus, no need to check for cmd == NULL - */ - if (cmd->toflush) { - int flush = (cmd->toflush == GRACEFUL) ? - 1 << 31 : 0; - writel_relaxed(flush, - DMOV_REG(DMOV_FLUSH0(ch), adm)); - } - } else { - if (list_empty(&dmov_conf[adm]. - active_commands[ch])) - PRINT_ERROR("msm_dmov_enqueue_cmd_ext"\ - "(%d), stalled, status %x\n", - id, status); - PRINT_IO("msm_dmov_enqueue_cmd(%d),"\ - "enqueue command, status " - "%x\n", id, status); - } - } + mutex_lock(&dmov_conf[adm].lock); + if (dmov_conf[adm].clk_ctl == CLK_DIS) { + status = msm_dmov_clk_on(adm); + if (status != 0) + goto error; } - spin_unlock_irqrestore(&dmov_conf[adm].lock, flags); -error: - mutex_unlock(&dmov_conf[adm].clock_lock); -} + dmov_conf[adm].clk_ctl = CLK_EN; -void msm_dmov_enqueue_cmd_ext_atomic(unsigned id, struct msm_dmov_cmd *cmd) -{ - unsigned int status; - int adm = DMOV_ID_TO_ADM(id); - int ch = DMOV_ID_TO_CHAN(id); + spin_lock_irqsave(&dmov_conf[adm].list_lock, flags); + cmd = list_entry(dmov_conf[adm].staged_commands[ch].next, typeof(*cmd), + list); + list_del(&cmd->list); + list_add_tail(&cmd->list, &dmov_conf[adm].ready_commands[ch]); status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), adm)); if (status & DMOV_STATUS_CMD_PTR_RDY) { PRINT_IO("msm_dmov_enqueue_cmd(%d), start command, status %x\n", id, status); - if (cmd->exec_func) - cmd->exec_func(cmd); - list_add_tail(&cmd->list, &dmov_conf[adm].active_commands[ch]); - if (!dmov_conf[adm].channel_active) - enable_irq(dmov_conf[adm].irq); - dmov_conf[adm].channel_active |= BIT(ch); - PRINT_IO("msm dmov enqueue command, %x, ch %d\n", cmd->cmdptr, - ch); - writel_relaxed(cmd->cmdptr, DMOV_REG(DMOV_CMD_PTR(ch), adm)); + cmd = start_ready_cmd(ch, adm); + /* + * We added something to the ready list, and still hold the + * list lock. Thus, no need to check for cmd == NULL + */ + if (cmd->toflush) { + int flush = (cmd->toflush == GRACEFUL) ? 1 << 31 : 0; + writel_relaxed(flush, DMOV_REG(DMOV_FLUSH0(ch), adm)); + } } else { - list_add_tail(&cmd->list, &dmov_conf[adm].ready_commands[ch]); + cmd->toflush = 0; if (list_empty(&dmov_conf[adm].active_commands[ch]) && !list_empty(&dmov_conf[adm].ready_commands[ch])) PRINT_ERROR("msm_dmov_enqueue_cmd_ext(%d), stalled, " @@ -417,10 +354,16 @@ void msm_dmov_enqueue_cmd_ext_atomic(unsigned id, struct msm_dmov_cmd *cmd) PRINT_IO("msm_dmov_enqueue_cmd(%d), enqueue command, status " "%x\n", id, status); } + if (!dmov_conf[adm].channel_active) { + dmov_conf[adm].clk_ctl = CLK_TO_BE_DIS; + schedule_delayed_work(&dmov_conf[adm].work, (HZ/10)); + } + spin_unlock_irqrestore(&dmov_conf[adm].list_lock, flags); +error: + mutex_unlock(&dmov_conf[adm].lock); } -static void __msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd, - int onstack) +static void __msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd) { int adm = DMOV_ID_TO_ADM(id); int ch = DMOV_ID_TO_CHAN(id); @@ -432,47 +375,12 @@ static void __msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd, list_add_tail(&cmd->list, &dmov_conf[adm].staged_commands[ch]); queue_work(dmov_conf[adm].cmd_wq, &cmd->work); spin_unlock_irqrestore(&dmov_conf[adm].list_lock, flags); - spin_lock_irqsave(&dmov_conf[adm].lock, flags); - switch (dmov_conf[adm].clk_ctl) { - case CLK_DIS: - case CLK_DISABLING: - if (onstack) - INIT_WORK_ONSTACK(&cmd->work, - msm_dmov_enqueue_cmd_ext_work); - else - INIT_WORK(&cmd->work, - msm_dmov_enqueue_cmd_ext_work); - list_add_tail(&cmd->list, &dmov_conf[adm].staged_commands[ch]); - dmov_conf[adm].clk_ctl = CLK_ENABLING; - queue_work(dmov_conf[adm].cmd_wq, &cmd->work); - break; - - case CLK_ENABLING: - list_add_tail(&cmd->list, &dmov_conf[adm].staged_commands[ch]); - break; - - case CLK_EN: - msm_dmov_enqueue_cmd_ext_atomic(id, cmd); - break; - - case CLK_TO_BE_DIS: - __cancel_delayed_work(&dmov_conf[adm].work); - dmov_conf[adm].clk_ctl = CLK_EN; - msm_dmov_enqueue_cmd_ext_atomic(id, cmd); - break; - - default: - PRINT_ERROR("ADM%d: Invalid CLK State.\n", adm); - BUG_ON(dmov_conf[adm].clk_ctl); - } - - spin_unlock_irqrestore(&dmov_conf[adm].lock, flags); - } void msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd) { - __msm_dmov_enqueue_cmd_ext(id, cmd, 0); + INIT_WORK(&cmd->work, msm_dmov_enqueue_cmd_ext_work); + __msm_dmov_enqueue_cmd_ext(id, cmd); } EXPORT_SYMBOL(msm_dmov_enqueue_cmd_ext); @@ -480,7 +388,8 @@ void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd) { /* Disable callback function (for backwards compatibility) */ cmd->exec_func = NULL; - __msm_dmov_enqueue_cmd_ext(id, cmd, 0); + INIT_WORK(&cmd->work, msm_dmov_enqueue_cmd_ext_work); + __msm_dmov_enqueue_cmd_ext(id, cmd); } EXPORT_SYMBOL(msm_dmov_enqueue_cmd); @@ -492,7 +401,7 @@ void msm_dmov_flush(unsigned int id, int graceful) int flush = graceful ? DMOV_FLUSH_TYPE : 0; struct msm_dmov_cmd *cmd; - spin_lock_irqsave(&dmov_conf[adm].lock, irq_flags); + spin_lock_irqsave(&dmov_conf[adm].list_lock, irq_flags); /* XXX not checking if flush cmd sent already */ if (!list_empty(&dmov_conf[adm].active_commands[ch])) { PRINT_IO("msm_dmov_flush(%d), send flush cmd\n", id); @@ -500,10 +409,8 @@ void msm_dmov_flush(unsigned int id, int graceful) } list_for_each_entry(cmd, &dmov_conf[adm].staged_commands[ch], list) cmd->toflush = graceful ? GRACEFUL : NONGRACEFUL; - list_for_each_entry(cmd, &dmov_conf[adm].ready_commands[ch], list) - cmd->toflush = graceful ? GRACEFUL : NONGRACEFUL; /* spin_unlock_irqrestore has the necessary barrier */ - spin_unlock_irqrestore(&dmov_conf[adm].lock, irq_flags); + spin_unlock_irqrestore(&dmov_conf[adm].list_lock, irq_flags); } EXPORT_SYMBOL(msm_dmov_flush); @@ -520,8 +427,7 @@ dmov_exec_cmdptr_complete_func(struct msm_dmov_cmd *_cmd, unsigned int result, struct msm_dmov_errdata *err) { - struct msm_dmov_exec_cmdptr_cmd *cmd = - container_of(_cmd, struct msm_dmov_exec_cmdptr_cmd, dmov_cmd); + struct msm_dmov_exec_cmdptr_cmd *cmd = container_of(_cmd, struct msm_dmov_exec_cmdptr_cmd, dmov_cmd); cmd->result = result; if (result != 0x80000002 && err) memcpy(&cmd->err, err, sizeof(struct msm_dmov_errdata)); @@ -540,17 +446,16 @@ int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr) cmd.dmov_cmd.exec_func = NULL; cmd.id = id; cmd.result = 0; + INIT_WORK_ONSTACK(&cmd.dmov_cmd.work, msm_dmov_enqueue_cmd_ext_work); init_completion(&cmd.complete); - __msm_dmov_enqueue_cmd_ext(id, &cmd.dmov_cmd, 1); + __msm_dmov_enqueue_cmd_ext(id, &cmd.dmov_cmd); wait_for_completion_io(&cmd.complete); if (cmd.result != 0x80000002) { - PRINT_ERROR("dmov_exec_cmdptr(%d): ERROR, result: %x\n", - id, cmd.result); + PRINT_ERROR("dmov_exec_cmdptr(%d): ERROR, result: %x\n", id, cmd.result); PRINT_ERROR("dmov_exec_cmdptr(%d): flush: %x %x %x %x\n", - id, cmd.err.flush[0], cmd.err.flush[1], - cmd.err.flush[2], cmd.err.flush[3]); + id, cmd.err.flush[0], cmd.err.flush[1], cmd.err.flush[2], cmd.err.flush[3]); return -EIO; } PRINT_FLOW("dmov_exec_cmdptr(%d, %x) done\n", id, cmdptr); @@ -581,17 +486,17 @@ static irqreturn_t msm_dmov_isr(int irq, void *dev_id) struct msm_dmov_cmd *cmd; int adm = DMOV_IRQ_TO_ADM(irq); - spin_lock_irqsave(&dmov_conf[adm].lock, irq_flags); + mutex_lock(&dmov_conf[adm].lock); /* read and clear isr */ int_status = readl_relaxed(DMOV_REG(DMOV_ISR, adm)); PRINT_FLOW("msm_datamover_irq_handler: DMOV_ISR %x\n", int_status); + spin_lock_irqsave(&dmov_conf[adm].list_lock, irq_flags); while (int_status) { mask = int_status & -int_status; ch = fls(mask) - 1; id = DMOV_CHAN_ADM_TO_ID(ch, adm); - PRINT_FLOW("msm_datamover_irq_handler %08x %08x id %d\n", - int_status, mask, id); + PRINT_FLOW("msm_datamover_irq_handler %08x %08x id %d\n", int_status, mask, id); int_status &= ~mask; ch_status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), adm)); if (!(ch_status & DMOV_STATUS_RSLT_VALID)) { @@ -603,9 +508,8 @@ static irqreturn_t msm_dmov_isr(int irq, void *dev_id) valid = 1; ch_result = readl_relaxed(DMOV_REG(DMOV_RSLT(ch), adm)); if (list_empty(&dmov_conf[adm].active_commands[ch])) { - PRINT_ERROR("msm_datamover_irq_handler id %d,"\ - " got result with no active command," - " status %x, result %x\n", + PRINT_ERROR("msm_datamover_irq_handler id %d, got result " + "with no active command, status %x, result %x\n", id, ch_status, ch_result); cmd = NULL; } else { @@ -613,33 +517,26 @@ static irqreturn_t msm_dmov_isr(int irq, void *dev_id) active_commands[ch].next, typeof(*cmd), list); } - PRINT_FLOW("msm_datamover_irq_handler id %d, status"\ - " %x, result %x\n", id, ch_status, ch_result); + PRINT_FLOW("msm_datamover_irq_handler id %d, status %x, result %x\n", id, ch_status, ch_result); if (ch_result & DMOV_RSLT_DONE) { - PRINT_FLOW("msm_datamover_irq_handler id %d,"\ - " status %x\n", id, ch_status); - PRINT_IO("msm_datamover_irq_handler id %d,"\ - " got result for %p," - " result %x\n", id, cmd, ch_result); + PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", + id, ch_status); + PRINT_IO("msm_datamover_irq_handler id %d, got result " + "for %p, result %x\n", id, cmd, ch_result); if (cmd) { list_del(&cmd->list); - cmd->complete_func(cmd, ch_result, - NULL); + cmd->complete_func(cmd, ch_result, NULL); } } if (ch_result & DMOV_RSLT_FLUSH) { struct msm_dmov_errdata errdata; fill_errdata(&errdata, ch, adm); - PRINT_FLOW("msm_datamover_irq_handler id %d,"\ - " status %x\n", id, ch_status); - PRINT_FLOW("msm_datamover_irq_handler id %d,"\ - " flush, result %x,flush0 %x\n", id, - ch_result, errdata.flush[0]); + PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); + PRINT_FLOW("msm_datamover_irq_handler id %d, flush, result %x, flush0 %x\n", id, ch_result, errdata.flush[0]); if (cmd) { list_del(&cmd->list); - cmd->complete_func(cmd, ch_result, - &errdata); + cmd->complete_func(cmd, ch_result, &errdata); } } if (ch_result & DMOV_RSLT_ERROR) { @@ -647,47 +544,30 @@ static irqreturn_t msm_dmov_isr(int irq, void *dev_id) fill_errdata(&errdata, ch, adm); - PRINT_ERROR("msm_datamover_irq_handler id %d,"\ - " status %x\n", id, ch_status); - PRINT_ERROR("msm_datamover_irq_handler id %d,"\ - " error, result %x, flush0 %x\n", - id, ch_result, errdata.flush[0]); + PRINT_ERROR("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); + PRINT_ERROR("msm_datamover_irq_handler id %d, error, result %x, flush0 %x\n", id, ch_result, errdata.flush[0]); if (cmd) { list_del(&cmd->list); - cmd->complete_func(cmd, ch_result, - &errdata); + cmd->complete_func(cmd, ch_result, &errdata); } - /* this does not seem to work, once we get an - * error. the datamover will no longer accept - * commands - */ + /* this does not seem to work, once we get an error */ + /* the datamover will no longer accept commands */ writel_relaxed(0, DMOV_REG(DMOV_FLUSH0(ch), adm)); } rmb(); ch_status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), adm)); - PRINT_FLOW("msm_datamover_irq_handler id %d, status"\ - " %x\n", id, ch_status); - if (ch_status & DMOV_STATUS_CMD_PTR_RDY) { - cmd = start_ready_cmd(ch, adm); - - if (cmd != NULL) { - if (cmd->toflush) { - int flush = (cmd->toflush == - GRACEFUL) ? 1 << 31 : 0; - writel_relaxed(flush, - DMOV_REG(DMOV_FLUSH0(ch), adm)); - } - } - } + PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); + if (ch_status & DMOV_STATUS_CMD_PTR_RDY) + start_ready_cmd(ch, adm); } while (ch_status & DMOV_STATUS_RSLT_VALID); if (list_empty(&dmov_conf[adm].active_commands[ch]) && - list_empty(&dmov_conf[adm].ready_commands[ch])) + list_empty(&dmov_conf[adm].ready_commands[ch])) dmov_conf[adm].channel_active &= ~(1U << ch); - PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, - ch_status); + PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); } + spin_unlock_irqrestore(&dmov_conf[adm].list_lock, irq_flags); if (!dmov_conf[adm].channel_active && valid) { disable_irq_nosync(dmov_conf[adm].irq); @@ -695,7 +575,7 @@ static irqreturn_t msm_dmov_isr(int irq, void *dev_id) schedule_delayed_work(&dmov_conf[adm].work, (HZ/10)); } - spin_unlock_irqrestore(&dmov_conf[adm].lock, irq_flags); + mutex_unlock(&dmov_conf[adm].lock); return valid ? IRQ_HANDLED : IRQ_NONE; } @@ -703,21 +583,13 @@ static int msm_dmov_suspend_late(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); int adm = (pdev->id >= 0) ? pdev->id : 0; - unsigned long irq_flags; - - mutex_lock(&dmov_conf[adm].clock_lock); - spin_lock_irqsave(&dmov_conf[adm].lock, irq_flags); + mutex_lock(&dmov_conf[adm].lock); if (dmov_conf[adm].clk_ctl == CLK_TO_BE_DIS) { BUG_ON(dmov_conf[adm].channel_active); - dmov_conf[adm].clk_ctl = CLK_DISABLING; - spin_unlock_irqrestore(&dmov_conf[adm].lock, irq_flags); msm_dmov_clk_off(adm); - spin_lock_irqsave(&dmov_conf[adm].lock, irq_flags); - if (dmov_conf[adm].clk_ctl == CLK_DISABLING) - dmov_conf[adm].clk_ctl = CLK_DIS; + dmov_conf[adm].clk_ctl = CLK_DIS; } - spin_unlock_irqrestore(&dmov_conf[adm].lock, irq_flags); - mutex_unlock(&dmov_conf[adm].clock_lock); + mutex_unlock(&dmov_conf[adm].lock); return 0; } @@ -739,7 +611,7 @@ static int msm_dmov_runtime_idle(struct device *dev) return 0; } -static const struct dev_pm_ops msm_dmov_dev_pm_ops = { +static struct dev_pm_ops msm_dmov_dev_pm_ops = { .runtime_suspend = msm_dmov_runtime_suspend, .runtime_resume = msm_dmov_runtime_resume, .runtime_idle = msm_dmov_runtime_idle, @@ -814,7 +686,6 @@ static int msm_dmov_probe(struct platform_device *pdev) platform_get_resource(pdev, IORESOURCE_IRQ, 0); struct resource *mres = platform_get_resource(pdev, IORESOURCE_MEM, 0); - char wq_name[12]; if (pdata) { dmov_conf[adm].sd = pdata->sd; @@ -833,16 +704,15 @@ static int msm_dmov_probe(struct platform_device *pdev) if (!dmov_conf[adm].base) return -ENOMEM; - snprintf(wq_name, sizeof(wq_name), "dmov%d_wq", adm); - dmov_conf[adm].cmd_wq = alloc_workqueue(wq_name, WQ_CPU_INTENSIVE, 1); + dmov_conf[adm].cmd_wq = alloc_ordered_workqueue("dmov%d_wq", 0, adm); if (!dmov_conf[adm].cmd_wq) { PRINT_ERROR("Couldn't allocate ADM%d workqueue.\n", adm); ret = -ENOMEM; goto out_map; } - ret = request_irq(dmov_conf[adm].irq, msm_dmov_isr, - 0, "msmdatamover", NULL); + ret = request_threaded_irq(dmov_conf[adm].irq, NULL, msm_dmov_isr, + IRQF_ONESHOT, "msmdatamover", NULL); if (ret) { PRINT_ERROR("Requesting ADM%d irq %d failed\n", adm, dmov_conf[adm].irq); diff --git a/arch/arm/mach-msm/include/mach/gpio.h b/arch/arm/mach-msm/include/mach/gpio.h index 3cb28882..1f0c2d8a 100644 --- a/arch/arm/mach-msm/include/mach/gpio.h +++ b/arch/arm/mach-msm/include/mach/gpio.h @@ -22,6 +22,9 @@ #include #include +#define gpio_get_value __gpio_get_value +#define gpio_set_value __gpio_set_value + #define FIRST_BOARD_GPIO NR_GPIO_IRQS extern struct irq_chip msm_gpio_irq_extn; diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h index 94c95534..60975979 100644 --- a/arch/arm/mach-msm/include/mach/memory.h +++ b/arch/arm/mach-msm/include/mach/memory.h @@ -79,6 +79,7 @@ int platform_physical_remove_pages(u64, u64); int platform_physical_active_pages(u64, u64); int platform_physical_low_power_pages(u64, u64); unsigned long get_ddr_size(void); +int msm_get_memory_type_from_name(const char *memtype_name); extern int (*change_memory_power)(u64, u64, int); @@ -119,6 +120,23 @@ void find_membank0_hole(void); (virt) - MEMBANK0_PAGE_OFFSET + MEMBANK0_PHYS_OFFSET) #endif +/* + * Need a temporary unique variable that no one will ever see to ++ * hold the compat string. Line number gives this easily. ++ * Need another layer of indirection to get __LINE__ to expand ++ * properly as opposed to appending and ending up with ++ * __compat___LINE__ ++ */ +#define __CONCAT(a, b) ___CONCAT(a, b) +#define ___CONCAT(a, b) a ## b + +#define EXPORT_COMPAT(com) \ +static char *__CONCAT(__compat_, __LINE__) __used \ + __attribute((__section__(".exportcompat.init"))) = com + +extern char *__compat_exports_start[]; +extern char *__compat_exports_end[]; + #endif #if defined CONFIG_ARCH_MSM_SCORPION || defined CONFIG_ARCH_MSM_KRAIT diff --git a/arch/arm/mach-msm/include/mach/msm_bus.h b/arch/arm/mach-msm/include/mach/msm_bus.h index 41b46a94..f6f83028 100644 --- a/arch/arm/mach-msm/include/mach/msm_bus.h +++ b/arch/arm/mach-msm/include/mach/msm_bus.h @@ -44,8 +44,8 @@ struct msm_bus_vectors { int src; /* Master */ int dst; /* Slave */ - uint64_t ab; /* Arbitrated bandwidth */ - uint64_t ib; /* Instantaneous bandwidth */ + uint64_t ab; /* Arbitrated bandwidth */ + uint64_t ib; /* Instantaneous bandwidth */ }; struct msm_bus_paths { @@ -95,7 +95,6 @@ static inline void msm_bus_cl_clear_pdata(struct msm_bus_scale_pdata *pdata) { } - static inline uint32_t msm_bus_scale_register_client(struct msm_bus_scale_pdata *pdata) { diff --git a/arch/arm/mach-msm/include/mach/msm_fb.h b/arch/arm/mach-msm/include/mach/msm_fb.h index 3bbaa25d..3e420483 100644 --- a/arch/arm/mach-msm/include/mach/msm_fb.h +++ b/arch/arm/mach-msm/include/mach/msm_fb.h @@ -77,7 +77,7 @@ struct msm_mddi_client_data { uint32_t reg); uint32_t (*remote_read)(struct msm_mddi_client_data *, uint32_t reg); void (*auto_hibernate)(struct msm_mddi_client_data *, int); - /* custom data that needs to be passed from the board file to a + /* custom data that needs to be passed from the board file to a * particular client */ void *private_client_data; struct resource *fb_resource; @@ -190,7 +190,7 @@ struct msm_mddi_bridge_platform_data { struct mdp_v4l2_req; int msm_fb_v4l2_enable(struct mdp_overlay *req, bool enable, void **par); -int msm_fb_v4l2_update(void *par, +int msm_fb_v4l2_update(void *par, bool bUserPtr, unsigned long srcp0_addr, unsigned long srcp0_size, unsigned long srcp1_addr, unsigned long srcp1_size, unsigned long srcp2_addr, unsigned long srcp2_size); diff --git a/arch/arm/mach-msm/include/mach/msm_memtypes.h b/arch/arm/mach-msm/include/mach/msm_memtypes.h index 64c28e84..6e19acf1 100644 --- a/arch/arm/mach-msm/include/mach/msm_memtypes.h +++ b/arch/arm/mach-msm/include/mach/msm_memtypes.h @@ -66,5 +66,7 @@ struct reserve_info { extern struct reserve_info *reserve_info; -unsigned long __init reserve_memory_for_fmem(unsigned long, unsigned long); +int __init dt_scan_for_memory_reserve(unsigned long node, const char *uname, + int depth, void *data); + #endif diff --git a/arch/arm/mach-msm/intelli_plug.c b/arch/arm/mach-msm/intelli_plug.c new file mode 100644 index 00000000..1b7d3c04 --- /dev/null +++ b/arch/arm/mach-msm/intelli_plug.c @@ -0,0 +1,606 @@ +/* + * Author: Paul Reioux aka Faux123 + * + * Copyright 2012~2014 Paul Reioux + * + * 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 + +#ifdef CONFIG_POWERSUSPEND +#include +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +//#define DEBUG_INTELLI_PLUG +#undef DEBUG_INTELLI_PLUG + +#define INTELLI_PLUG_MAJOR_VERSION 4 +#define INTELLI_PLUG_MINOR_VERSION 0 +#define INTELLI_PLUG_RELMOD_VERSION 7 + +#define DEF_SAMPLING_MS (268) + +#define DUAL_PERSISTENCE (2500 / DEF_SAMPLING_MS) +#define TRI_PERSISTENCE (1700 / DEF_SAMPLING_MS) +#define QUAD_PERSISTENCE (1000 / DEF_SAMPLING_MS) + +#define BUSY_PERSISTENCE (3500 / DEF_SAMPLING_MS) + +static DEFINE_MUTEX(intelli_plug_mutex); + +static struct delayed_work intelli_plug_work; +static struct delayed_work intelli_plug_boost; + +static struct workqueue_struct *intelliplug_wq; +static struct workqueue_struct *intelliplug_boost_wq; + +static unsigned int intelli_plug_active = 1; +module_param(intelli_plug_active, uint, 0664); + +static unsigned int touch_boost_active = 1; +module_param(touch_boost_active, uint, 0664); + +static unsigned int nr_run_profile_sel = 0; +module_param(nr_run_profile_sel, uint, 0664); + +static unsigned int turbo_active = 1; +module_param(turbo_active, uint, 0444); + +//default to something sane rather than zero +static unsigned int sampling_time = DEF_SAMPLING_MS; + +static int persist_count = 0; + +static bool suspended = false; + +struct ip_cpu_info { + unsigned int sys_max; + unsigned int cur_max; + unsigned long cpu_nr_running; +}; + +static DEFINE_PER_CPU(struct ip_cpu_info, ip_info); + +static unsigned int screen_off_max = UINT_MAX; +module_param(screen_off_max, uint, 0664); + +#define CAPACITY_RESERVE 50 + +#if defined(CONFIG_ARCH_APQ8084) || defined(CONFIG_ARM64) +#define THREAD_CAPACITY (430 - CAPACITY_RESERVE) +#elif defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_APQ8064) || \ +defined(CONFIG_ARCH_MSM8974) +#define THREAD_CAPACITY (339 - CAPACITY_RESERVE) +#elif defined(CONFIG_ARCH_MSM8226) || defined (CONFIG_ARCH_MSM8926) || \ +defined (CONFIG_ARCH_MSM8610) || defined (CONFIG_ARCH_MSM8228) +#define THREAD_CAPACITY (190 - CAPACITY_RESERVE) +#else +#define THREAD_CAPACITY (250 - CAPACITY_RESERVE) +#endif + +#define MULT_FACTOR 4 +#define DIV_FACTOR 100000 +#define NR_FSHIFT 3 + +static unsigned int nr_fshift = NR_FSHIFT; + +static unsigned int nr_run_thresholds_balance[] = { + (THREAD_CAPACITY * 625 * MULT_FACTOR) / DIV_FACTOR, + (THREAD_CAPACITY * 875 * MULT_FACTOR) / DIV_FACTOR, + (THREAD_CAPACITY * 1125 * MULT_FACTOR) / DIV_FACTOR, + UINT_MAX +}; + +static unsigned int nr_run_thresholds_performance[] = { + (THREAD_CAPACITY * 380 * MULT_FACTOR) / DIV_FACTOR, + (THREAD_CAPACITY * 625 * MULT_FACTOR) / DIV_FACTOR, + (THREAD_CAPACITY * 875 * MULT_FACTOR) / DIV_FACTOR, + UINT_MAX +}; + +static unsigned int nr_run_thresholds_conservative[] = { + (THREAD_CAPACITY * 875 * MULT_FACTOR) / DIV_FACTOR, + (THREAD_CAPACITY * 1625 * MULT_FACTOR) / DIV_FACTOR, + (THREAD_CAPACITY * 2125 * MULT_FACTOR) / DIV_FACTOR, + UINT_MAX +}; + +static unsigned int nr_run_thresholds_eco[] = { + (THREAD_CAPACITY * 380 * MULT_FACTOR) / DIV_FACTOR, + UINT_MAX +}; + +static unsigned int nr_run_thresholds_eco_extreme[] = { + (THREAD_CAPACITY * 750 * MULT_FACTOR) / DIV_FACTOR, + UINT_MAX +}; + +static unsigned int nr_run_thresholds_disable[] = { + 0, 0, 0, UINT_MAX +}; + +static unsigned int *nr_run_profiles[] = { + nr_run_thresholds_balance, + nr_run_thresholds_performance, + nr_run_thresholds_conservative, + nr_run_thresholds_eco, + nr_run_thresholds_eco_extreme, + nr_run_thresholds_disable, +}; + +#define NR_RUN_ECO_MODE_PROFILE 4 +#define NR_RUN_HYSTERESIS_QUAD 8 +#define NR_RUN_HYSTERESIS_DUAL 4 + +#define CPU_NR_THRESHOLD ((THREAD_CAPACITY << 1) + (THREAD_CAPACITY / 2)) + +static unsigned int nr_possible_cores; +module_param(nr_possible_cores, uint, 0444); + +static unsigned int cpu_nr_run_threshold = CPU_NR_THRESHOLD; +module_param(cpu_nr_run_threshold, uint, 0664); + +static unsigned int nr_run_hysteresis = NR_RUN_HYSTERESIS_QUAD; +module_param(nr_run_hysteresis, uint, 0664); + +static unsigned int nr_run_last; + +extern unsigned long avg_nr_running(void); +extern unsigned long avg_cpu_nr_running(unsigned int cpu); + +static unsigned int calculate_thread_stats(void) +{ + unsigned int avg_nr_run = avg_nr_running(); + unsigned int nr_run; + unsigned int threshold_size; + unsigned int *current_profile; + + current_profile = nr_run_profiles[nr_run_profile_sel]; + if (num_possible_cpus() > 2) { + if (nr_run_profile_sel >= NR_RUN_ECO_MODE_PROFILE) + threshold_size = + ARRAY_SIZE(nr_run_thresholds_eco); + else + threshold_size = + ARRAY_SIZE(nr_run_thresholds_balance); + } else + threshold_size = + ARRAY_SIZE(nr_run_thresholds_eco); + + if (nr_run_profile_sel >= NR_RUN_ECO_MODE_PROFILE) + nr_fshift = 1; + else + nr_fshift = num_possible_cpus() - 1; + + for (nr_run = 1; nr_run < threshold_size; nr_run++) { + unsigned int nr_threshold; + nr_threshold = current_profile[nr_run - 1]; + + if (nr_run_last <= nr_run) + nr_threshold += nr_run_hysteresis; + if (avg_nr_run <= (nr_threshold << (FSHIFT - nr_fshift))) + break; + } + nr_run_last = nr_run; + + return nr_run; +} + +static void __ref intelli_plug_boost_fn(struct work_struct *work) +{ + + int nr_cpus = num_online_cpus(); + + if (intelli_plug_active) + if (touch_boost_active) + if (nr_cpus < 2) + cpu_up(1); +} + +/* +static int cmp_nr_running(const void *a, const void *b) +{ + return *(unsigned long *)a - *(unsigned long *)b; +} +*/ + +static void update_per_cpu_stat(void) +{ + unsigned int cpu; + struct ip_cpu_info *l_ip_info; + + for_each_online_cpu(cpu) { + l_ip_info = &per_cpu(ip_info, cpu); + l_ip_info->cpu_nr_running = avg_cpu_nr_running(cpu); +#ifdef DEBUG_INTELLI_PLUG + pr_info("cpu %u nr_running => %lu\n", cpu, + l_ip_info->cpu_nr_running); +#endif + } +} + +static void unplug_cpu(int min_active_cpu) +{ + unsigned int cpu; + struct ip_cpu_info *l_ip_info; + int l_nr_threshold; + + for_each_online_cpu(cpu) { + l_nr_threshold = + cpu_nr_run_threshold << 1 / (num_online_cpus()); + if (cpu == 0) + continue; + l_ip_info = &per_cpu(ip_info, cpu); + if (cpu > min_active_cpu) + if (l_ip_info->cpu_nr_running < l_nr_threshold) + cpu_down(cpu); + } +} + +static void __ref intelli_plug_work_fn(struct work_struct *work) +{ + unsigned int nr_run_stat; + unsigned int cpu_count = 0; + unsigned int nr_cpus = 0; + + int i; + + if (intelli_plug_active) { + nr_run_stat = calculate_thread_stats(); + update_per_cpu_stat(); +#ifdef DEBUG_INTELLI_PLUG + pr_info("nr_run_stat: %u\n", nr_run_stat); +#endif + cpu_count = nr_run_stat; + nr_cpus = num_online_cpus(); + + if (!suspended) { + + if (persist_count > 0) + persist_count--; + + switch (cpu_count) { + case 1: + if (persist_count == 0) { + //take down everyone + unplug_cpu(0); + } +#ifdef DEBUG_INTELLI_PLUG + pr_info("case 1: %u\n", persist_count); +#endif + break; + case 2: + if (persist_count == 0) + persist_count = DUAL_PERSISTENCE; + if (nr_cpus < 2) { + for (i = 1; i < cpu_count; i++) + cpu_up(i); + } else { + unplug_cpu(1); + } +#ifdef DEBUG_INTELLI_PLUG + pr_info("case 2: %u\n", persist_count); +#endif + break; + case 3: + if (persist_count == 0) + persist_count = TRI_PERSISTENCE; + if (nr_cpus < 3) { + for (i = 1; i < cpu_count; i++) + cpu_up(i); + } else { + unplug_cpu(2); + } +#ifdef DEBUG_INTELLI_PLUG + pr_info("case 3: %u\n", persist_count); +#endif + break; + case 4: + if (persist_count == 0) + persist_count = QUAD_PERSISTENCE; + if (nr_cpus < 4) + for (i = 1; i < cpu_count; i++) + cpu_up(i); +#ifdef DEBUG_INTELLI_PLUG + pr_info("case 4: %u\n", persist_count); +#endif + break; + default: + pr_err("Run Stat Error: Bad value %u\n", nr_run_stat); + break; + } + } +#ifdef DEBUG_INTELLI_PLUG + else + pr_info("intelli_plug is suspened!\n"); +#endif + } + queue_delayed_work_on(0, intelliplug_wq, &intelli_plug_work, + msecs_to_jiffies(sampling_time)); +} + +#if defined(CONFIG_POWERSUSPEND) || defined(CONFIG_HAS_EARLYSUSPEND) + +static void screen_off_limit(bool on) +{ + + unsigned int cpu; + struct cpufreq_policy *policy; + struct ip_cpu_info *l_ip_info; + + /* not active, so exit */ + if (screen_off_max == UINT_MAX) { + turbo_active = 1; + return; + } + + for_each_online_cpu(cpu) { + l_ip_info = &per_cpu(ip_info, cpu); + policy = cpufreq_cpu_get(0); + + if (on) { + + turbo_active = 0; + /* save current instance */ + l_ip_info->cur_max = policy->max; + policy->max = screen_off_max; + policy->cpuinfo.max_freq = screen_off_max; +#ifdef DEBUG_INTELLI_PLUG + pr_info("cpuinfo max is (on): %u %u\n", + policy->cpuinfo.max_freq, l_ip_info->sys_max); +#endif + } else { + + turbo_active = 1; + /* restore */ + if (cpu != 0) { + l_ip_info = &per_cpu(ip_info, 0); + } + policy->cpuinfo.max_freq = l_ip_info->sys_max; + policy->max = l_ip_info->cur_max; +#ifdef DEBUG_INTELLI_PLUG + pr_info("cpuinfo max is (off): %u %u\n", + policy->cpuinfo.max_freq, l_ip_info->sys_max); +#endif + } + cpufreq_update_policy(cpu); + + } +} + +#ifdef CONFIG_POWERSUSPEND +static void intelli_plug_suspend(struct power_suspend *handler) +#else +static void intelli_plug_suspend(struct early_suspend *handler) +#endif +{ + if (intelli_plug_active) { + int cpu; + + flush_workqueue(intelliplug_wq); + + mutex_lock(&intelli_plug_mutex); + suspended = true; + screen_off_limit(true); + mutex_unlock(&intelli_plug_mutex); + + // put rest of the cores to sleep unconditionally! + for_each_online_cpu(cpu) { + if (cpu != 0) + cpu_down(cpu); + } + } +} + +static void wakeup_boost(void) +{ + unsigned int cpu; + struct cpufreq_policy *policy; + struct ip_cpu_info *l_ip_info; + + for_each_online_cpu(cpu) { + policy = cpufreq_cpu_get(0); + l_ip_info = &per_cpu(ip_info, 0); + policy->cur = l_ip_info->cur_max; + cpufreq_update_policy(cpu); + } +} + +#ifdef CONFIG_POWERSUSPEND +static void __ref intelli_plug_resume(struct power_suspend *handler) +#else +static void __ref intelli_plug_resume(struct early_suspend *handler) +#endif +{ + + if (intelli_plug_active) { + int cpu; + + mutex_lock(&intelli_plug_mutex); + /* keep cores awake long enough for faster wake up */ + persist_count = BUSY_PERSISTENCE; + suspended = false; + mutex_unlock(&intelli_plug_mutex); + + for_each_possible_cpu(cpu) { + if (cpu == 0) + continue; + cpu_up(cpu); + } + + wakeup_boost(); + screen_off_limit(false); + } + queue_delayed_work_on(0, intelliplug_wq, &intelli_plug_work, + msecs_to_jiffies(10)); +} +#endif + +#ifdef CONFIG_POWERSUSPEND +static struct power_suspend intelli_plug_power_suspend_driver = { + .suspend = intelli_plug_suspend, + .resume = intelli_plug_resume, +}; +#endif /* CONFIG_POWERSUSPEND */ + +#ifdef CONFIG_HAS_EARLYSUSPEND +static struct early_suspend intelli_plug_early_suspend_driver = { + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 10, + .suspend = intelli_plug_suspend, + .resume = intelli_plug_resume, +}; +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +static void intelli_plug_input_event(struct input_handle *handle, + unsigned int type, unsigned int code, int value) +{ +#ifdef DEBUG_INTELLI_PLUG + pr_info("intelli_plug touched!\n"); +#endif + queue_delayed_work_on(0, intelliplug_wq, &intelli_plug_boost, + msecs_to_jiffies(10)); +} + +static int intelli_plug_input_connect(struct input_handler *handler, + struct input_dev *dev, const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "intelliplug"; + + error = input_register_handle(handle); + if (error) + goto err2; + + error = input_open_device(handle); + if (error) + goto err1; + pr_info("%s found and connected!\n", dev->name); + return 0; +err1: + input_unregister_handle(handle); +err2: + kfree(handle); + return error; +} + +static void intelli_plug_input_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id intelli_plug_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) }, + .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] = + BIT_MASK(ABS_MT_POSITION_X) | + BIT_MASK(ABS_MT_POSITION_Y) }, + }, /* multi-touch touchscreen */ + { + .flags = INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, + .absbit = { [BIT_WORD(ABS_X)] = + BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, + }, /* touchpad */ + { }, +}; + +static struct input_handler intelli_plug_input_handler = { + .event = intelli_plug_input_event, + .connect = intelli_plug_input_connect, + .disconnect = intelli_plug_input_disconnect, + .name = "intelliplug_handler", + .id_table = intelli_plug_ids, +}; + +int __init intelli_plug_init(void) +{ + int rc; +#if defined (CONFIG_POWERSUSPEND) || defined(CONFIG_HAS_EARLYSUSPEND) + struct cpufreq_policy *policy; + struct ip_cpu_info *l_ip_info; +#endif + + nr_possible_cores = num_possible_cpus(); + + pr_info("intelli_plug: version %d.%d.m%d_jsr by faux123(modded by lukino563)\n", + INTELLI_PLUG_MAJOR_VERSION, + INTELLI_PLUG_MINOR_VERSION, + INTELLI_PLUG_RELMOD_VERSION); + + if (nr_possible_cores > 2) { + nr_run_hysteresis = NR_RUN_HYSTERESIS_QUAD; + nr_run_profile_sel = 0; + } else { + nr_run_hysteresis = NR_RUN_HYSTERESIS_DUAL; + nr_run_profile_sel = NR_RUN_ECO_MODE_PROFILE; + } + +#if defined (CONFIG_POWERSUSPEND) || defined(CONFIG_HAS_EARLYSUSPEND) + l_ip_info = &per_cpu(ip_info, 0); + policy = cpufreq_cpu_get(0); + l_ip_info->sys_max = policy->cpuinfo.max_freq; + l_ip_info->cur_max = policy->max; +#endif + + rc = input_register_handler(&intelli_plug_input_handler); +#ifdef CONFIG_POWERSUSPEND + register_power_suspend(&intelli_plug_power_suspend_driver); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + register_early_suspend(&intelli_plug_early_suspend_driver); +#endif + intelliplug_wq = alloc_workqueue("intelliplug", + WQ_HIGHPRI | WQ_UNBOUND, 1); + intelliplug_boost_wq = alloc_workqueue("iplug_boost", + WQ_HIGHPRI | WQ_UNBOUND, 1); + INIT_DELAYED_WORK(&intelli_plug_work, intelli_plug_work_fn); + INIT_DELAYED_WORK(&intelli_plug_boost, intelli_plug_boost_fn); + queue_delayed_work_on(0, intelliplug_wq, &intelli_plug_work, + msecs_to_jiffies(10)); + + return 0; +} + +int turbo_start_dec(void) +{ + return turbo_active; +} + +MODULE_AUTHOR("Paul Reioux (edit: Lukáš Machata "); +MODULE_DESCRIPTION("'intell_plug' - An intelligent cpu hotplug driver for " + "Low Latency Frequency Transition capable processors"); +MODULE_LICENSE("GPL"); + +late_initcall(intelli_plug_init); diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c index ee1440cf..6749246b 100644 --- a/arch/arm/mach-msm/memory.c +++ b/arch/arm/mach-msm/memory.c @@ -33,7 +33,6 @@ #include #include #endif -#include #include #include #include @@ -41,7 +40,6 @@ /* fixme */ #include #include <../../mm/mm.h> -#include #if defined(CONFIG_ARCH_MSM7X27) static void *strongly_ordered_page; @@ -365,16 +363,6 @@ void store_ttbr0(void) : "=r" (msm_ttbr0)); } -int request_fmem_c_region(void *unused) -{ - return fmem_set_state(FMEM_C_STATE); -} - -int release_fmem_c_region(void *unused) -{ - return fmem_set_state(FMEM_T_STATE); -} - unsigned long get_ddr_size(void) { unsigned int i; diff --git a/arch/arm/mach-msm/msm_bus/Makefile b/arch/arm/mach-msm/msm_bus/Makefile index bd24d7c6..ab62c20b 100644 --- a/arch/arm/mach-msm/msm_bus/Makefile +++ b/arch/arm/mach-msm/msm_bus/Makefile @@ -2,7 +2,7 @@ # Makefile for msm-bus driver specific files # obj-y += msm_bus_core.o msm_bus_fabric.o msm_bus_config.o msm_bus_arb.o -obj-y += msm_bus_rpm.o msm_bus_bimc.o msm_bus_noc.o msm_bus_of.o +obj-y += msm_bus_rpm.o msm_bus_bimc.o msm_bus_noc.o obj-$(CONFIG_ARCH_MSM8X60) += msm_bus_board_8660.o obj-$(CONFIG_ARCH_MSM8960) += msm_bus_board_8960.o obj-$(CONFIG_ARCH_MSM9615) += msm_bus_board_9615.o diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_arb.c b/arch/arm/mach-msm/msm_bus/msm_bus_arb.c index e5616b90..6babfd19 100644 --- a/arch/arm/mach-msm/msm_bus/msm_bus_arb.c +++ b/arch/arm/mach-msm/msm_bus/msm_bus_arb.c @@ -32,9 +32,8 @@ #define SEL_FAB_CLK 1 #define SEL_SLAVE_CLK 0 -#define BW_TO_CLK_FREQ_HZ(width, bw) \ - msm_bus_div64(width, bw) - +#define BW_TO_CLK_FREQ_HZ(width, bw) ((unsigned long)\ + DIV_ROUND_UP((bw), (width))) #define IS_MASTER_VALID(mas) \ (((mas >= MSM_BUS_MASTER_FIRST) && (mas <= MSM_BUS_MASTER_LAST)) \ ? 1 : 0) @@ -44,33 +43,6 @@ static DEFINE_MUTEX(msm_bus_lock); -/* This function uses shift operations to divide 64 bit value for higher - * efficiency. The divisor expected are number of ports or bus-width. - * These are expected to be 1, 2, 4, 8, 16 and 32 in most cases. - * - * To account for exception to the above divisor values, the standard - * do_div function is used. - * */ -uint64_t msm_bus_div64(unsigned int w, uint64_t bw) -{ - uint64_t *b = &bw; - - if ((bw > 0) && (bw < w)) - return 1; - - switch (w) { - case 1: return bw; - case 2: return (bw >> 1); - case 4: return (bw >> 2); - case 8: return (bw >> 3); - case 16: return (bw >> 4); - case 32: return (bw >> 5); - } - - do_div(*b, w); - return *b; -} - /** * add_path_node: Adds the path information to the current node * @info: Internal node info structure @@ -306,21 +278,21 @@ static int getpath(int src, int dest) * frequencies is calculated at each node on the path. Commit data to be sent * to RPM for each master and slave is also calculated here. */ -static int update_path(int curr, int pnode, uint64_t req_clk, uint64_t req_bw, - uint64_t curr_clk, uint64_t curr_bw, unsigned int ctx, unsigned int - cl_active_flag) +static int update_path(int curr, int pnode, unsigned long req_clk, unsigned + long req_bw, unsigned long curr_clk, unsigned long curr_bw, + unsigned int ctx, unsigned int cl_active_flag) { int index, ret = 0; struct msm_bus_inode_info *info; int next_pnode; - int64_t add_bw = req_bw - curr_bw; + long int add_bw = req_bw - curr_bw; unsigned bwsum = 0; - uint64_t req_clk_hz, curr_clk_hz, bwsum_hz; + unsigned req_clk_hz, curr_clk_hz, bwsum_hz; int *master_tiers; struct msm_bus_fabric_device *fabdev = msm_bus_get_fabric_device (GET_FABID(curr)); - MSM_BUS_DBG("args: %d %d %d %llu %llu %llu %llu %u\n", + MSM_BUS_DBG("args: %d %d %d %lu %lu %lu %lu %u\n", curr, GET_NODE(pnode), GET_INDEX(pnode), req_clk, req_bw, curr_clk, curr_bw, ctx); index = GET_INDEX(pnode); @@ -406,8 +378,8 @@ static int update_path(int curr, int pnode, uint64_t req_clk, uint64_t req_bw, req_clk); bwsum_hz = BW_TO_CLK_FREQ_HZ(hop->node_info->buswidth, bwsum); - MSM_BUS_DBG("up-clk: curr_hz: %llu, req_hz: %llu, bw_hz %llu\n", - curr_clk, req_clk, bwsum_hz); + MSM_BUS_DBG("Calling update-clks: curr_hz: %lu, req_hz: %lu," + " bw_hz %u\n", curr_clk, req_clk, bwsum_hz); ret = fabdev->algo->update_clks(fabdev, hop, index, curr_clk_hz, req_clk_hz, bwsum_hz, SEL_FAB_CLK, ctx, cl_active_flag); @@ -560,7 +532,7 @@ int msm_bus_scale_client_update_request(uint32_t cl, unsigned index) int i, ret = 0; struct msm_bus_scale_pdata *pdata; int pnode, src, curr, ctx; - uint64_t req_clk, req_bw, curr_clk, curr_bw; + unsigned long req_clk, req_bw, curr_clk, curr_bw; struct msm_bus_client *client = (struct msm_bus_client *)cl; if (IS_ERR(client)) { MSM_BUS_ERR("msm_bus_scale_client update req error %d\n", @@ -582,8 +554,9 @@ int msm_bus_scale_client_update_request(uint32_t cl, unsigned index) goto err; } - MSM_BUS_DBG("cl: %u index: %d curr: %d num_paths: %d\n", - cl, index, client->curr, client->pdata->usecase->num_paths); + MSM_BUS_DBG("cl: %u index: %d curr: %d" + " num_paths: %d\n", cl, index, client->curr, + client->pdata->usecase->num_paths); for (i = 0; i < pdata->usecase->num_paths; i++) { src = msm_bus_board_get_iid(client->pdata->usecase[index]. @@ -611,7 +584,7 @@ int msm_bus_scale_client_update_request(uint32_t cl, unsigned index) } else { curr_clk = client->pdata->usecase[curr].vectors[i].ib; curr_bw = client->pdata->usecase[curr].vectors[i].ab; - MSM_BUS_DBG("ab: %llu ib: %llu\n", curr_bw, curr_clk); + MSM_BUS_DBG("ab: %lu ib: %lu\n", curr_bw, curr_clk); } if (!pdata->active_only) { diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c index 4a14b638..f9889f06 100644 --- a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c +++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c @@ -1803,7 +1803,7 @@ static void msm_bus_bimc_update_bw(struct msm_bus_inode_info *hop, struct msm_bus_inode_info *info, struct msm_bus_fabric_registration *fab_pdata, void *sel_cdata, int *master_tiers, - int64_t add_bw) + long int add_bw) { struct msm_bus_bimc_info *binfo; struct msm_bus_bimc_qos_bw qbw; @@ -1813,7 +1813,7 @@ static void msm_bus_bimc_update_bw(struct msm_bus_inode_info *hop, struct msm_bus_bimc_commit *sel_cd = (struct msm_bus_bimc_commit *)sel_cdata; - MSM_BUS_DBG("BIMC: Update bw for ID %d, with IID: %d: %lld\n", + MSM_BUS_DBG("BIMC: Update bw for ID %d, with IID: %d: %ld\n", info->node_info->id, info->node_info->priv_id, add_bw); binfo = (struct msm_bus_bimc_info *)fab_pdata->hw_data; diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_core.h b/arch/arm/mach-msm/msm_bus/msm_bus_core.h index e472e888..00f0e43d 100644 --- a/arch/arm/mach-msm/msm_bus/msm_bus_core.h +++ b/arch/arm/mach-msm/msm_bus/msm_bus_core.h @@ -36,7 +36,7 @@ (((slv >= MSM_BUS_SLAVE_FIRST) && (slv <= MSM_BUS_SLAVE_LAST)) ? 1 : 0) #define INTERLEAVED_BW(fab_pdata, bw, ports) \ - ((fab_pdata->il_flag) ? msm_bus_div64((bw), (ports)) : (bw)) + ((fab_pdata->il_flag) ? DIV_ROUND_UP((bw), (ports)) : (bw)) #define INTERLEAVED_VAL(fab_pdata, n) \ ((fab_pdata->il_flag) ? (n) : 1) @@ -83,34 +83,34 @@ struct msm_bus_node_info { }; struct path_node { - uint64_t clk[NUM_CTX]; - uint64_t bw[NUM_CTX]; - uint64_t *sel_clk; - uint64_t *sel_bw; + unsigned long clk[NUM_CTX]; + unsigned long bw[NUM_CTX]; + unsigned long *sel_clk; + unsigned long *sel_bw; int next; }; struct msm_bus_link_info { - uint64_t clk[NUM_CTX]; - uint64_t *sel_clk; - uint64_t memclk; - int64_t bw[NUM_CTX]; - int64_t *sel_bw; + unsigned long clk[NUM_CTX]; + unsigned long *sel_clk; + unsigned long memclk; + long bw[NUM_CTX]; + long *sel_bw; int *tier; int num_tiers; }; struct nodeclk { struct clk *clk; - uint64_t rate; + unsigned long rate; bool dirty; bool enable; }; struct msm_bus_inode_info { struct msm_bus_node_info *node_info; - uint64_t max_bw; - uint64_t max_clk; + unsigned long max_bw; + unsigned long max_clk; struct msm_bus_link_info link_info; int num_pnodes; struct path_node *pnode; @@ -137,7 +137,7 @@ struct msm_bus_hw_algorithm { struct msm_bus_inode_info *info, struct msm_bus_fabric_registration *fab_pdata, void *sel_cdata, int *master_tiers, - int64_t add_bw); + long int add_bw); void (*fill_cdata_buffer)(int *curr, char *buf, const int max_size, void *cdata, int nmasters, int nslaves, int ntslaves); int (*commit)(struct msm_bus_fabric_registration @@ -164,8 +164,8 @@ struct msm_bus_fabric_device { struct msm_bus_fab_algorithm { int (*update_clks)(struct msm_bus_fabric_device *fabdev, struct msm_bus_inode_info *pme, int index, - uint64_t curr_clk, uint64_t req_clk, - uint64_t bwsum, int flag, int ctx, + unsigned long curr_clk, unsigned long req_clk, + unsigned long bwsum, int flag, int ctx, unsigned int cl_active_flag); int (*port_halt)(struct msm_bus_fabric_device *fabdev, int portid); int (*port_unhalt)(struct msm_bus_fabric_device *fabdev, int portid); @@ -177,7 +177,7 @@ struct msm_bus_fab_algorithm { struct list_head *(*get_gw_list)(struct msm_bus_fabric_device *fabdev); void (*update_bw)(struct msm_bus_fabric_device *fabdev, struct msm_bus_inode_info * hop, struct msm_bus_inode_info *info, - int64_t add_bw, int *master_tiers, int ctx); + long int add_bw, int *master_tiers, int ctx); }; struct msm_bus_board_algorithm { @@ -204,7 +204,6 @@ struct msm_bus_client { int curr; }; -uint64_t msm_bus_div64(unsigned int width, uint64_t bw); int msm_bus_fabric_device_register(struct msm_bus_fabric_device *fabric); void msm_bus_fabric_device_unregister(struct msm_bus_fabric_device *fabric); struct msm_bus_fabric_device *msm_bus_get_fabric_device(int fabid); diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c b/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c index 001c733a..8471b713 100644 --- a/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c +++ b/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c @@ -385,11 +385,11 @@ static int msm_bus_dbg_fill_cl_buffer(const struct msm_bus_scale_pdata *pdata, pdata->usecase[index].vectors[j].dst); i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\nab : "); for (j = 0; j < pdata->usecase->num_paths; j++) - i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%llu ", + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%u ", pdata->usecase[index].vectors[j].ab); i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\nib : "); for (j = 0; j < pdata->usecase->num_paths; j++) - i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%llu ", + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%u ", pdata->usecase[index].vectors[j].ib); i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\n"); diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c index 8897ae5e..b9571e3e 100644 --- a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c +++ b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c @@ -222,13 +222,13 @@ static int register_fabric_info(struct platform_device *pdev, */ static int msm_bus_fabric_update_clks(struct msm_bus_fabric_device *fabdev, struct msm_bus_inode_info *slave, int index, - uint64_t curr_clk_hz, uint64_t req_clk_hz, - uint64_t bwsum_hz, int clk_flag, int ctx, + unsigned long curr_clk_hz, unsigned long req_clk_hz, + unsigned long bwsum_hz, int clk_flag, int ctx, unsigned int cl_active_flag) { int i, status = 0; - uint64_t max_pclk = 0, rate; - uint64_t *pclk = NULL; + unsigned long max_pclk = 0, rate; + unsigned long *pclk = NULL; struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev); struct nodeclk *nodeclk; @@ -261,7 +261,7 @@ static int msm_bus_fabric_update_clks(struct msm_bus_fabric_device *fabdev, info->link_info.sel_clk = &info->link_info.clk[ctx]; max_pclk = max(max_pclk, *info->link_info.sel_clk); } - MSM_BUS_DBG("max_pclk from gateways: %llu\n", max_pclk); + MSM_BUS_DBG("max_pclk from gateways: %lu\n", max_pclk); /* Maximum of all slave clocks. */ @@ -278,7 +278,7 @@ static int msm_bus_fabric_update_clks(struct msm_bus_fabric_device *fabdev, } - MSM_BUS_DBG("max_pclk from slaves & gws: %llu\n", max_pclk); + MSM_BUS_DBG("max_pclk from slaves & gws: %lu\n", max_pclk); fabric->info.link_info.sel_clk = &fabric->info.link_info.clk[ctx]; pclk = fabric->info.link_info.sel_clk; @@ -296,7 +296,7 @@ static int msm_bus_fabric_update_clks(struct msm_bus_fabric_device *fabdev, if (clk_flag) { nodeclk = &fabric->info.nodeclk[ctx]; if (nodeclk->clk) { - MSM_BUS_DBG("clks: id: %d set-clk: %llu bws_hz:%llu\n", + MSM_BUS_DBG("clks: id: %d set-clk: %lu bwsum_hz:%lu\n", fabric->fabdev.id, *pclk, bwsum_hz); if (nodeclk->rate != *pclk) { nodeclk->dirty = true; @@ -308,8 +308,8 @@ static int msm_bus_fabric_update_clks(struct msm_bus_fabric_device *fabdev, nodeclk = &slave->nodeclk[ctx]; if (nodeclk->clk) { rate = *pclk; - MSM_BUS_DBG("clks: id: %d set-clk: %llu bws_hz: %llu\n", - slave->node_info->priv_id, rate, + MSM_BUS_DBG("AXI_clks: id: %d set-clk: %lu " + "bwsum_hz: %lu\n" , slave->node_info->priv_id, rate, bwsum_hz); if (nodeclk->rate != rate) { nodeclk->dirty = true; @@ -332,7 +332,7 @@ static int msm_bus_fabric_update_clks(struct msm_bus_fabric_device *fabdev, void msm_bus_fabric_update_bw(struct msm_bus_fabric_device *fabdev, struct msm_bus_inode_info *hop, struct msm_bus_inode_info *info, - int64_t add_bw, int *master_tiers, int ctx) + long int add_bw, int *master_tiers, int ctx) { struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev); void *sel_cdata; diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_noc.c b/arch/arm/mach-msm/msm_bus/msm_bus_noc.c index 6c308452..c33ebdee 100644 --- a/arch/arm/mach-msm/msm_bus/msm_bus_noc.c +++ b/arch/arm/mach-msm/msm_bus/msm_bus_noc.c @@ -503,7 +503,7 @@ static void msm_bus_noc_update_bw(struct msm_bus_inode_info *hop, struct msm_bus_inode_info *info, struct msm_bus_fabric_registration *fab_pdata, void *sel_cdata, int *master_tiers, - int64_t add_bw) + long int add_bw) { struct msm_bus_noc_info *ninfo; struct msm_bus_noc_qos_bw qos_bw; @@ -528,7 +528,7 @@ static void msm_bus_noc_update_bw(struct msm_bus_inode_info *hop, bw = INTERLEAVED_BW(fab_pdata, add_bw, ports); - MSM_BUS_DBG("NOC: Update bw for: %d: %lld\n", + MSM_BUS_DBG("NOC: Update bw for: %d: %ld\n", info->node_info->priv_id, add_bw); for (i = 0; i < ports; i++) { sel_cd->mas[info->node_info->masterp[i]].bw += bw; diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_of.c b/arch/arm/mach-msm/msm_bus/msm_bus_of.c deleted file mode 100644 index 24b0ce27..00000000 --- a/arch/arm/mach-msm/msm_bus/msm_bus_of.c +++ /dev/null @@ -1,150 +0,0 @@ -/* Copyright (c) 2012, The Linux Foundation. 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 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#define pr_fmt(fmt) "AXI: %s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include - -/** - * msm_bus_cl_get_pdata() - Generate bus client data from device tree - * provided by clients. - * - * of_node: Device tree node to extract information from - * - * The function returns a valid pointer to the allocated bus-scale-pdata - * if the vectors were correctly read from the client's device node. - * Any error in reading or parsing the device node will return NULL - * to the caller. - */ -struct msm_bus_scale_pdata *msm_bus_cl_get_pdata(struct platform_device *pdev) -{ - struct device_node *of_node; - struct msm_bus_scale_pdata *pdata = NULL; - struct msm_bus_paths *usecase = NULL; - int i = 0, j, ret, num_usecases = 0, num_paths, len; - const uint32_t *vec_arr = NULL; - bool mem_err = false; - - if (!pdev) { - pr_err("Error: Null Platform device\n"); - return NULL; - } - - of_node = pdev->dev.of_node; - pdata = devm_kzalloc(&pdev->dev, sizeof(struct msm_bus_scale_pdata), - GFP_KERNEL); - if (!pdata) { - pr_err("Error: Memory allocation for pdata failed\n"); - mem_err = true; - goto err; - } - - ret = of_property_read_string(of_node, "qcom,msm_bus,name", - &pdata->name); - if (ret) { - pr_err("Error: Client name not found\n"); - goto err; - } - - ret = of_property_read_u32(of_node, "qcom,msm_bus,num_cases", - &num_usecases); - if (ret) { - pr_err("Error: num_usecases not found\n"); - goto err; - } - - pdata->num_usecases = num_usecases; - ret = of_property_read_u32(of_node, "qcom,msm_bus,active_only", - &pdata->active_only); - if (ret) { - pr_info("active_only flag absent.\n"); - pr_info("Using dual context by default\n"); - } - - usecase = devm_kzalloc(&pdev->dev, (sizeof(struct msm_bus_paths) * - pdata->num_usecases), GFP_KERNEL); - if (!usecase) { - pr_err("Error: Memory allocation for paths failed\n"); - mem_err = true; - goto err; - } - - ret = of_property_read_u32(of_node, "qcom,msm_bus,num_paths", - &num_paths); - if (ret) { - pr_err("Error: num_paths not found\n"); - goto err; - } - - vec_arr = of_get_property(of_node, "qcom,msm_bus,vectors", &len); - if (len != num_usecases * num_paths * sizeof(struct msm_bus_vectors)) { - pr_err("Error: Length-error on getting vectors\n"); - goto err; - } - - for (i = 0; i < num_usecases; i++) { - usecase[i].num_paths = num_paths; - usecase[i].vectors = devm_kzalloc(&pdev->dev, num_paths * - sizeof(struct msm_bus_vectors), GFP_KERNEL); - if (!usecase[i].vectors) { - mem_err = true; - pr_err("Error: Mem alloc failure in vectors\n"); - goto err; - } - - for (j = 0; j < num_paths; j++) { - int index = ((i * num_paths) + j) * 4; - usecase[i].vectors[j].src = be32_to_cpu(vec_arr[index]); - usecase[i].vectors[j].dst = - be32_to_cpu(vec_arr[index + 1]); - usecase[i].vectors[j].ab = - be32_to_cpu(vec_arr[index + 2]); - usecase[i].vectors[j].ib = - be32_to_cpu(vec_arr[index + 3]); - } - } - - pdata->usecase = usecase; - return pdata; -err: - if (mem_err) { - for (; i > 0; i--) - kfree(usecase[i-1].vectors); - - kfree(usecase); - kfree(pdata); - } - - return NULL; -} -EXPORT_SYMBOL(msm_bus_cl_get_pdata); - -/** - * msm_bus_cl_clear_pdata() - Clear pdata allocated from device-tree - * of_node: Device tree node to extract information from - */ -void msm_bus_cl_clear_pdata(struct msm_bus_scale_pdata *pdata) -{ - int i; - - for (i = 0; i < pdata->num_usecases; i++) - kfree(pdata->usecase[i].vectors); - - kfree(pdata->usecase); - kfree(pdata); -} -EXPORT_SYMBOL(msm_bus_cl_clear_pdata); diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c b/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c index 57b7f72d..00edd0a0 100644 --- a/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c +++ b/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c @@ -239,7 +239,7 @@ static void msm_bus_rpm_update_bw(struct msm_bus_inode_info *hop, struct msm_bus_inode_info *info, struct msm_bus_fabric_registration *fab_pdata, void *sel_cdata, int *master_tiers, - int64_t add_bw) + long int add_bw) { int index, i, j, tiers, ports; struct commit_data *sel_cd = (struct commit_data *)sel_cdata; @@ -302,9 +302,9 @@ static void msm_bus_rpm_update_bw(struct msm_bus_inode_info *hop, msm_bus_create_bw_tier_pair_bytes(tier, tieredbw); sel_cd->actarb[index] = tieredbw; - MSM_BUS_DBG("tr:%d mpor:%d tbw:%ld bws: %lld\n", - hop_tier, info->node_info->masterp[i], - tieredbw, *hop->link_info.sel_bw); + MSM_BUS_DBG("tier:%d mport: %d tiered_bw:%ld " + "bwsum: %ld\n", hop_tier, info->node_info-> + masterp[i], tieredbw, *hop->link_info.sel_bw); } } } @@ -314,12 +314,10 @@ static void msm_bus_rpm_update_bw(struct msm_bus_inode_info *hop, for (i = 0; i < ports; i++) { sel_cd->bwsum[hop->node_info->slavep[i]] = (uint16_t)msm_bus_create_bw_tier_pair_bytes(0, - (uint32_t)msm_bus_div64(hop->node_info->num_sports, - *hop->link_info.sel_bw)); - MSM_BUS_DBG("slavep:%d, link_bw: %u\n", - hop->node_info->slavep[i], (uint32_t) - msm_bus_div64(hop->node_info->num_sports, - *hop->link_info.sel_bw)); + (*hop->link_info.sel_bw/hop->node_info->num_sports)); + MSM_BUS_DBG("slavep:%d, link_bw: %ld\n", + hop->node_info->slavep[i], (*hop->link_info.sel_bw/ + hop->node_info->num_sports)); } } @@ -803,7 +801,7 @@ static void msm_bus_rpm_update_bw(struct msm_bus_inode_info *hop, struct msm_bus_inode_info *info, struct msm_bus_fabric_registration *fab_pdata, void *sel_cdata, int *master_tiers, - int64_t add_bw) + long int add_bw) { int index, i, j, tiers, ports; struct commit_data *sel_cd = (struct commit_data *)sel_cdata; @@ -855,9 +853,9 @@ static void msm_bus_rpm_update_bw(struct msm_bus_inode_info *hop, sel_cd->arb[tier][index] = msm_bus_create_bw_tier_pair_bytes(0, tieredbw); sel_cd->actarb[tier][index] = tieredbw; - MSM_BUS_DBG("tr:%d mpor:%d tbw:%lu bws: %lld\n", - hop_tier, info->node_info->masterp[i], tieredbw, - *hop->link_info.sel_bw); + MSM_BUS_DBG("tier:%d mport: %d tiered_bw:%lu " + "bwsum: %ld\n", hop_tier, info->node_info-> + masterp[i], tieredbw, *hop->link_info.sel_bw); } } } @@ -867,13 +865,11 @@ static void msm_bus_rpm_update_bw(struct msm_bus_inode_info *hop, ports = INTERLEAVED_VAL(fab_pdata, hop->node_info->num_sports); for (i = 0; i < ports; i++) { sel_cd->bwsum[hop->node_info->slavep[i]] - = msm_bus_pack_bwsum_bytes((uint32_t) - msm_bus_div64(hop->node_info->num_sports, - *hop->link_info.sel_bw)); - MSM_BUS_DBG("slavep:%d, link_bw: %lld\n", - hop->node_info->slavep[i], - msm_bus_div64(hop->node_info->num_sports, - *hop->link_info.sel_bw)); + = msm_bus_pack_bwsum_bytes((*hop->link_info. + sel_bw/hop->node_info->num_sports)); + MSM_BUS_DBG("slavep:%d, link_bw: %ld\n", + hop->node_info->slavep[i], (*hop->link_info.sel_bw/ + hop->node_info->num_sports)); } } diff --git a/arch/arm/mach-msm/msm_dcvs_scm.c b/arch/arm/mach-msm/msm_dcvs_scm.c index df6c44f8..4364fccb 100644 --- a/arch/arm/mach-msm/msm_dcvs_scm.c +++ b/arch/arm/mach-msm/msm_dcvs_scm.c @@ -32,16 +32,6 @@ struct scm_register_core { phys_addr_t core_param_phy; }; -struct scm_algo { - uint32_t core_id; - phys_addr_t algo_phy; -}; - -struct scm_init { - uint32_t phy; - uint32_t size; -}; - struct scm_pwr_param { uint32_t core_id; phys_addr_t pwr_param_phy; @@ -57,6 +47,16 @@ struct msm_algo_param { } u; }; +struct scm_algo { + uint32_t core_id; + phys_addr_t algo_phy; +}; + +struct scm_init { + uint32_t phy; + uint32_t size; +}; + int msm_dcvs_scm_init(size_t size) { int ret = 0; diff --git a/arch/arm/mach-msm/msm_rq_stats.c b/arch/arm/mach-msm/msm_rq_stats.c index 6ace92a9..27c2bb21 100644 --- a/arch/arm/mach-msm/msm_rq_stats.c +++ b/arch/arm/mach-msm/msm_rq_stats.c @@ -38,7 +38,7 @@ struct notifier_block freq_transition; struct notifier_block cpu_hotplug; -#ifdef CONFIG_MSM_MPDEC +#if defined(CONFIG_MSM_MPDEC) || defined(CONFIG_BRICKED_HOTPLUG) unsigned int get_rq_info(void) { unsigned long flags = 0; diff --git a/arch/arm/mach-msm/qdsp5/Makefile b/arch/arm/mach-msm/qdsp5/Makefile index f4fe052c..b13e1b55 100644 --- a/arch/arm/mach-msm/qdsp5/Makefile +++ b/arch/arm/mach-msm/qdsp5/Makefile @@ -1,3 +1,4 @@ + obj-y += adsp.o adsp_driver.o adsp_info.o adsp_rm.o dsp_debug.o adsp_debug.o obj-y += adsp_video_verify_cmd.o obj-y += adsp_videoenc_verify_cmd.o diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c index 8b8703b2..57da251f 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.c +++ b/arch/arm/mach-msm/qdsp5/adsp.c @@ -1094,9 +1094,9 @@ int msm_adsp_enable(struct msm_adsp_module *module) MM_INFO("enable '%s'state[%d] id[%d]\n", module->name, module->state, module->id); - if (!strncmp(module->name, "JPEGTASK", sizeof(module->name))) + if (!strncmp(module->name, "JPEGTASK", sizeof(*module->name))) module_en = find_adsp_module_by_name(&adsp_info, "VIDEOTASK"); - else if (!strncmp(module->name, "VIDEOTASK", sizeof(module->name))) + else if (!strncmp(module->name, "VIDEOTASK", sizeof(*module->name))) module_en = find_adsp_module_by_name(&adsp_info, "JPEGTASK"); if (module_en) { mutex_lock(&module_en->lock); diff --git a/arch/arm/mach-msm/qdsp5/adsp.h b/arch/arm/mach-msm/qdsp5/adsp.h index d2c67444..0922d883 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.h +++ b/arch/arm/mach-msm/qdsp5/adsp.h @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include diff --git a/arch/arm/mach-msm/qdsp5/adsp_driver.c b/arch/arm/mach-msm/qdsp5/adsp_driver.c index e7784323..eb9c3887 100644 --- a/arch/arm/mach-msm/qdsp5/adsp_driver.c +++ b/arch/arm/mach-msm/qdsp5/adsp_driver.c @@ -25,7 +25,6 @@ #include #include "adsp.h" #include -#include #include struct adsp_ion_info { @@ -139,7 +138,7 @@ static int get_ion_region_info(int fd, struct adsp_ion_region *region) pr_err("%s: could not get flags for the handle\n", __func__); goto flag_error; } - temp_ptr = ion_map_kernel(region->client, region->handle, ionflag); + temp_ptr = ion_map_kernel(region->client, region->handle); if (IS_ERR_OR_NULL(temp_ptr)) { pr_err("%s: could not get virtual address\n", __func__); goto map_error; @@ -267,7 +266,7 @@ int adsp_ion_do_cache_op(struct msm_adsp_module *module, module->name, vaddr, len); return ret; } - if ((region->ion_flag == CACHED) && region->handle) { + if ((region->ion_flag == ION_FLAG_CACHED) && region->handle) { len = ((((len) + 31) & (~31)) + 32); ret = msm_ion_do_cache_op(region->client, region->handle, (void *)paddr, len, cmd); diff --git a/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c index 4d03dca2..d03f9a5a 100644 --- a/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c +++ b/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c @@ -17,11 +17,11 @@ */ #include -#include #include #include "adsp.h" #include +#include #define MAX_FLUSH_SIZE 160 diff --git a/arch/arm/mach-msm/qdsp5/audio_aac.c b/arch/arm/mach-msm/qdsp5/audio_aac.c index 18138391..45091629 100644 --- a/arch/arm/mach-msm/qdsp5/audio_aac.c +++ b/arch/arm/mach-msm/qdsp5/audio_aac.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include @@ -1373,7 +1373,6 @@ static int audaac_process_eos(struct audio *audio, rc = -EINVAL; goto done; } - if (copy_from_user(frame->data, buf_start, mfield_size)) { rc = -EFAULT; goto done; @@ -1734,7 +1733,7 @@ static int audio_open(struct inode *inode, struct file *file) MM_DBG("allocating mem sz = %d\n", mem_sz); handle = ion_alloc(client, mem_sz, SZ_4K, - ION_HEAP(ION_AUDIO_HEAP_ID)); + ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate O/P buffers\n"); rc = -ENOMEM; @@ -1760,7 +1759,7 @@ static int audio_open(struct inode *inode, struct file *file) goto output_buff_get_flags_error; } - audio->map_v_write = ion_map_kernel(client, handle, ionflag); + audio->map_v_write = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_write)) { MM_ERR("could not map write buffers,freeing instance 0x%08x\n", (int)audio); @@ -1776,7 +1775,7 @@ static int audio_open(struct inode *inode, struct file *file) mem_sz = (PCM_BUFSZ_MIN * PCM_BUF_MAX_COUNT); MM_DBG("allocating mem sz = %d\n", mem_sz); handle = ion_alloc(client, mem_sz, - SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID)); + SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate I/P buffers\n"); rc = -ENOMEM; @@ -1803,8 +1802,7 @@ static int audio_open(struct inode *inode, struct file *file) goto input_buff_get_flags_error; } - audio->map_v_read = ion_map_kernel(client, - handle, ionflag); + audio->map_v_read = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_read)) { MM_ERR("could not map read buffers, freeing instance \ 0x%08x\n", (int)audio); diff --git a/arch/arm/mach-msm/qdsp5/audio_aac_in.c b/arch/arm/mach-msm/qdsp5/audio_aac_in.c index 61b26a86..4c13a3c1 100644 --- a/arch/arm/mach-msm/qdsp5/audio_aac_in.c +++ b/arch/arm/mach-msm/qdsp5/audio_aac_in.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include "audmgr.h" @@ -1371,7 +1371,7 @@ static int audaac_in_open(struct inode *inode, struct file *file) MM_DBG("allocating mem sz = %d\n", dma_size); handle = ion_alloc(client, dma_size, SZ_4K, - ION_HEAP(ION_AUDIO_HEAP_ID)); + ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate O/P buffers\n"); rc = -ENOMEM; @@ -1399,7 +1399,7 @@ static int audaac_in_open(struct inode *inode, struct file *file) goto output_buff_get_flags_error; } - audio->map_v_read = ion_map_kernel(client, handle, ionflag); + audio->map_v_read = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_read)) { MM_ERR("could not map read buffers,freeing instance 0x%08x\n", (int)audio); @@ -1415,7 +1415,7 @@ static int audaac_in_open(struct inode *inode, struct file *file) MM_DBG("allocating BUFFER_SIZE %d\n", BUFFER_SIZE); handle = ion_alloc(client, BUFFER_SIZE, - SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID)); + SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate I/P buffers\n"); rc = -ENOMEM; @@ -1445,8 +1445,7 @@ static int audaac_in_open(struct inode *inode, struct file *file) goto input_buff_get_flags_error; } - audio->map_v_write = ion_map_kernel(client, - handle, ionflag); + audio->map_v_write = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_write)) { MM_ERR("could not map write buffers\n"); rc = -ENOMEM; diff --git a/arch/arm/mach-msm/qdsp5/audio_ac3.c b/arch/arm/mach-msm/qdsp5/audio_ac3.c index 21cf6cca..ba3fe941 100644 --- a/arch/arm/mach-msm/qdsp5/audio_ac3.c +++ b/arch/arm/mach-msm/qdsp5/audio_ac3.c @@ -1,6 +1,6 @@ /* arch/arm/mach-msm/audio_ac3.c * - * Copyright (c) 2008-2009, 2011-2012 The Linux Foundation. All rights reserved. + * Copyright (c) 2008-2009, 2011-2013 The Linux Foundation. All rights reserved. * * This code also borrows from audio_aac.c, which is * Copyright (C) 2008 Google, Inc. @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include @@ -1034,7 +1034,7 @@ static long audac3_ioctl(struct file *file, unsigned int cmd, handle = ion_alloc(audio->client, (config.buffer_size * config.buffer_count), - SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID)); + SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to alloc I/P buffs\n"); audio->input_buff_handle = NULL; @@ -1072,8 +1072,7 @@ static long audac3_ioctl(struct file *file, unsigned int cmd, } audio->map_v_read = ion_map_kernel( - audio->client, - handle, ionflag); + audio->client, handle); if (IS_ERR(audio->map_v_read)) { MM_ERR("map of read buf failed\n"); ion_free(audio->client, handle); @@ -1603,7 +1602,7 @@ static int audac3_open(struct inode *inode, struct file *file) audio->client = client; handle = ion_alloc(client, DMASZ, SZ_4K, - ION_HEAP(ION_AUDIO_HEAP_ID)); + ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate O/P buffers\n"); rc = -ENOMEM; @@ -1629,7 +1628,7 @@ static int audac3_open(struct inode *inode, struct file *file) goto output_buff_get_flags_error; } - audio->map_v_write = ion_map_kernel(client, handle, ionflag); + audio->map_v_write = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_write)) { MM_ERR("could not map write buffers,freeing instance 0x%08x\n", (int)audio); diff --git a/arch/arm/mach-msm/qdsp5/audio_acdb.c b/arch/arm/mach-msm/qdsp5/audio_acdb.c index 7f276748..2dc7a62b 100644 --- a/arch/arm/mach-msm/qdsp5/audio_acdb.c +++ b/arch/arm/mach-msm/qdsp5/audio_acdb.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -110,7 +109,6 @@ struct acdb_data { audpp_cmd_cfg_object_params_eqalizer eq; struct audrec_session_info session_info; /*pmem info*/ - int pmem_fd; unsigned long paddr; unsigned long kvaddr; unsigned long pmem_len; @@ -1136,7 +1134,6 @@ static long audio_acdb_ioctl(struct file *file, unsigned int cmd, { int rc = 0; unsigned long flags = 0; - struct msm_audio_pmem_info info; MM_DBG("%s\n", __func__); @@ -1156,23 +1153,6 @@ static long audio_acdb_ioctl(struct file *file, unsigned int cmd, MM_ERR("AUDPP returned err =%d\n", rc); spin_unlock_irqrestore(&acdb_data.dsp_lock, flags); break; - case AUDIO_REGISTER_PMEM: - MM_DBG("AUDIO_REGISTER_PMEM\n"); - if (copy_from_user(&info, (void *) arg, sizeof(info))) { - MM_ERR("Cannot copy from user\n"); - return -EFAULT; - } - rc = get_pmem_file(info.fd, &acdb_data.paddr, - &acdb_data.kvaddr, - &acdb_data.pmem_len, - &acdb_data.file); - if (rc == 0) - acdb_data.pmem_fd = info.fd; - break; - case AUDIO_DEREGISTER_PMEM: - if (acdb_data.pmem_fd) - put_pmem_file(acdb_data.file); - break; case AUDIO_SET_ACDB_BLK: MM_DBG("IOCTL AUDIO_SET_ACDB_BLK\n"); rc = acdb_set_calibration_blk(arg); diff --git a/arch/arm/mach-msm/qdsp5/audio_amrnb.c b/arch/arm/mach-msm/qdsp5/audio_amrnb.c index fad01db3..4faffd24 100644 --- a/arch/arm/mach-msm/qdsp5/audio_amrnb.c +++ b/arch/arm/mach-msm/qdsp5/audio_amrnb.c @@ -2,7 +2,7 @@ * * amrnb audio decoder device * - * Copyright (c) 2008-2009, 2011-2012 The Linux Foundation. All rights reserved. + * Copyright (c) 2008-2009, 2011-2013 The Linux Foundation. All rights reserved. * * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5/audio_mp3.c * @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include @@ -986,7 +986,7 @@ static long audamrnb_ioctl(struct file *file, unsigned int cmd, handle = ion_alloc(audio->client, (config.buffer_size * config.buffer_count), - SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID)); + SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to alloc I/P buffs\n"); audio->input_buff_handle = NULL; @@ -1023,8 +1023,7 @@ static long audamrnb_ioctl(struct file *file, unsigned int cmd, break; } audio->map_v_read = ion_map_kernel( - audio->client, - handle, ionflag); + audio->client, handle); if (IS_ERR(audio->map_v_read)) { MM_ERR("failed to map read buf\n"); ion_free(audio->client, handle); @@ -1551,7 +1550,7 @@ static int audamrnb_open(struct inode *inode, struct file *file) audio->client = client; handle = ion_alloc(client, mem_sz, SZ_4K, - ION_HEAP(ION_AUDIO_HEAP_ID)); + ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate O/P buffers\n"); rc = -ENOMEM; @@ -1577,7 +1576,7 @@ static int audamrnb_open(struct inode *inode, struct file *file) goto output_buff_get_flags_error; } - audio->map_v_write = ion_map_kernel(client, handle, ionflag); + audio->map_v_write = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_write)) { MM_ERR("could not map write buffers\n"); rc = -ENOMEM; diff --git a/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c b/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c index 9f85b51f..fbee2f93 100644 --- a/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c +++ b/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include "audmgr.h" @@ -742,6 +742,7 @@ static long audamrnb_in_ioctl(struct file *file, MM_DBG("\n"); if (cmd == AUDIO_GET_STATS) { struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); stats.byte_count = atomic_read(&audio->in_bytes); stats.sample_count = atomic_read(&audio->in_samples); if (copy_to_user((void *) arg, &stats, sizeof(stats))) @@ -896,7 +897,7 @@ static ssize_t audamrnb_in_read(struct file *file, struct amrnb_encoded_meta_out meta_field; struct audio_frame_nt *nt_frame; MM_DBG("count = %d\n", count); - memset(&meta_field, 0, sizeof(meta_field)); + memset(&meta_field, 0, sizeof(meta_field)); mutex_lock(&audio->read_lock); while (count > 0) { rc = wait_event_interruptible( @@ -1337,7 +1338,7 @@ static int audamrnb_in_open(struct inode *inode, struct file *file) if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { MM_DBG("allocating BUFFER_SIZE %d\n", BUFFER_SIZE); handle = ion_alloc(client, BUFFER_SIZE, - SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID)); + SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate write buffers\n"); rc = -ENOMEM; @@ -1367,8 +1368,7 @@ static int audamrnb_in_open(struct inode *inode, struct file *file) goto input_buff_get_flags_error; } - audio->map_v_write = ion_map_kernel(client, - handle, ionflag); + audio->map_v_write = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_write)) { MM_ERR("could not map write buffers\n"); rc = -ENOMEM; diff --git a/arch/arm/mach-msm/qdsp5/audio_amrwb.c b/arch/arm/mach-msm/qdsp5/audio_amrwb.c index 148a31c4..2b69e9fc 100644 --- a/arch/arm/mach-msm/qdsp5/audio_amrwb.c +++ b/arch/arm/mach-msm/qdsp5/audio_amrwb.c @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include #include @@ -982,7 +982,7 @@ static long audamrwb_ioctl(struct file *file, unsigned int cmd, handle = ion_alloc(audio->client, (config.buffer_size * config.buffer_count), - SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID)); + SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to alloc I/P buffs\n"); audio->input_buff_handle = NULL; @@ -1019,8 +1019,7 @@ static long audamrwb_ioctl(struct file *file, unsigned int cmd, break; } audio->map_v_read = ion_map_kernel( - audio->client, - handle, ionflag); + audio->client, handle); if (IS_ERR(audio->map_v_read)) { MM_ERR("failed to map mem for read buf\n"); ion_free(audio->client, handle); @@ -1616,7 +1615,7 @@ static int audamrwb_open(struct inode *inode, struct file *file) audio->client = client; handle = ion_alloc(client, mem_sz, SZ_4K, - ION_HEAP(ION_AUDIO_HEAP_ID)); + ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate O/P buffers\n"); goto output_buff_alloc_error; @@ -1641,7 +1640,7 @@ static int audamrwb_open(struct inode *inode, struct file *file) goto output_buff_get_flags_error; } - audio->map_v_write = ion_map_kernel(client, handle, ionflag); + audio->map_v_write = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_write)) { MM_ERR("could not map write buffers\n"); rc = -ENOMEM; diff --git a/arch/arm/mach-msm/qdsp5/audio_evrc.c b/arch/arm/mach-msm/qdsp5/audio_evrc.c index 42f33db6..2ac4aea6 100644 --- a/arch/arm/mach-msm/qdsp5/audio_evrc.c +++ b/arch/arm/mach-msm/qdsp5/audio_evrc.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include @@ -972,7 +972,7 @@ static long audevrc_ioctl(struct file *file, unsigned int cmd, handle = ion_alloc(audio->client, (config.buffer_size * config.buffer_count), - SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID)); + SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to alloc I/P buffs\n"); audio->input_buff_handle = NULL; @@ -1009,8 +1009,7 @@ static long audevrc_ioctl(struct file *file, unsigned int cmd, break; } audio->map_v_read = ion_map_kernel( - audio->client, - handle, ionflag); + audio->client, handle); if (IS_ERR(audio->map_v_read)) { MM_ERR("failed to map mem" " for read buf\n"); @@ -1543,7 +1542,7 @@ static int audevrc_open(struct inode *inode, struct file *file) audio->client = client; handle = ion_alloc(client, mem_sz, SZ_4K, - ION_HEAP(ION_AUDIO_HEAP_ID)); + ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate O/P buffers\n"); rc = -ENOMEM; @@ -1569,7 +1568,7 @@ static int audevrc_open(struct inode *inode, struct file *file) goto output_buff_get_flags_error; } - audio->map_v_write = ion_map_kernel(client, handle, ionflag); + audio->map_v_write = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_write)) { MM_ERR("could not map write buffers\n"); rc = -ENOMEM; diff --git a/arch/arm/mach-msm/qdsp5/audio_evrc_in.c b/arch/arm/mach-msm/qdsp5/audio_evrc_in.c index 03b105e1..18ea0eaa 100644 --- a/arch/arm/mach-msm/qdsp5/audio_evrc_in.c +++ b/arch/arm/mach-msm/qdsp5/audio_evrc_in.c @@ -33,7 +33,7 @@ #include -#include +#include #include #include @@ -732,7 +732,7 @@ static long audevrc_in_ioctl(struct file *file, MM_DBG("\n"); if (cmd == AUDIO_GET_STATS) { struct msm_audio_stats stats; - memset(&stats, 0, sizeof(stats)); + memset(&stats, 0, sizeof(stats)); stats.byte_count = atomic_read(&audio->in_bytes); stats.sample_count = atomic_read(&audio->in_samples); if (copy_to_user((void *) arg, &stats, sizeof(stats))) @@ -885,7 +885,7 @@ static ssize_t audevrc_in_read(struct file *file, int rc = 0; struct evrc_encoded_meta_out meta_field; struct audio_frame_nt *nt_frame; - memset(&meta_field, 0, sizeof(meta_field)); + memset(&meta_field, 0, sizeof(meta_field)); MM_DBG("count = %d\n", count); mutex_lock(&audio->read_lock); while (count > 0) { @@ -1321,7 +1321,7 @@ static int audevrc_in_open(struct inode *inode, struct file *file) MM_DBG("allocating mem sz = %d\n", dma_size); handle = ion_alloc(client, dma_size, SZ_4K, - ION_HEAP(ION_AUDIO_HEAP_ID)); + ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate O/P buffers\n"); rc = -ENOMEM; @@ -1349,7 +1349,7 @@ static int audevrc_in_open(struct inode *inode, struct file *file) goto output_buff_get_flags_error; } - audio->map_v_read = ion_map_kernel(client, handle, ionflag); + audio->map_v_read = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_read)) { MM_ERR("could not map read buffers,freeing instance 0x%08x\n", (int)audio); @@ -1364,7 +1364,7 @@ static int audevrc_in_open(struct inode *inode, struct file *file) if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { MM_DBG("allocating BUFFER_SIZE %d\n", BUFFER_SIZE); handle = ion_alloc(client, BUFFER_SIZE, - SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID)); + SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate I/P buffers\n"); rc = -ENOMEM; @@ -1394,8 +1394,7 @@ static int audevrc_in_open(struct inode *inode, struct file *file) goto input_buff_alloc_error; } - audio->map_v_write = ion_map_kernel(client, - handle, ionflag); + audio->map_v_write = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_write)) { MM_ERR("could not map write buffers\n"); rc = -ENOMEM; diff --git a/arch/arm/mach-msm/qdsp5/audio_lpa.c b/arch/arm/mach-msm/qdsp5/audio_lpa.c index a07acf08..707a9eba 100644 --- a/arch/arm/mach-msm/qdsp5/audio_lpa.c +++ b/arch/arm/mach-msm/qdsp5/audio_lpa.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include #include @@ -744,7 +744,7 @@ static int audlpa_ion_add(struct audio *audio, pr_err("%s: could not get flags for the handle\n", __func__); goto flag_error; } - kvaddr = (unsigned long)ion_map_kernel(audio->client, handle, ionflag); + kvaddr = (unsigned long)ion_map_kernel(audio->client, handle); if (IS_ERR_OR_NULL((void *)kvaddr)) { pr_err("%s: could not get virtual address\n", __func__); goto map_error; diff --git a/arch/arm/mach-msm/qdsp5/audio_mp3.c b/arch/arm/mach-msm/qdsp5/audio_mp3.c index 41d26856..3950a2a6 100644 --- a/arch/arm/mach-msm/qdsp5/audio_mp3.c +++ b/arch/arm/mach-msm/qdsp5/audio_mp3.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include @@ -1100,7 +1100,7 @@ static int audmp3_ion_add(struct audio *audio, goto flag_error; } - temp_ptr = ion_map_kernel(audio->client, handle, ionflag); + temp_ptr = ion_map_kernel(audio->client, handle); if (IS_ERR_OR_NULL(temp_ptr)) { pr_err("%s: could not get virtual address\n", __func__); goto map_error; @@ -1326,7 +1326,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (cmd == AUDIO_GET_STATS) { struct msm_audio_stats stats; - memset(&stats, 0, sizeof(stats)); + memset(&stats, 0, sizeof(stats)); stats.byte_count = audpp_avsync_byte_count(audio->dec_id); stats.sample_count = audpp_avsync_sample_count(audio->dec_id); if (copy_to_user((void *) arg, &stats, sizeof(stats))) @@ -1531,7 +1531,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) handle = ion_alloc(audio->client, (config.buffer_size * config.buffer_count), - SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID)); + SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to alloc I/P buffs\n"); rc = -ENOMEM; @@ -1564,8 +1564,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } audio->map_v_read = ion_map_kernel( - audio->client, - handle, ionflag); + audio->client, handle); if (IS_ERR(audio->map_v_read)) { MM_ERR("map of read buf failed\n"); @@ -2271,7 +2270,7 @@ static int audio_open(struct inode *inode, struct file *file) MM_DBG("memsz = %d\n", mem_sz); handle = ion_alloc(client, mem_sz, SZ_4K, - ION_HEAP(ION_AUDIO_HEAP_ID)); + ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate O/P buffers\n"); rc = -ENOMEM; @@ -2297,7 +2296,7 @@ static int audio_open(struct inode *inode, struct file *file) goto output_buff_get_flags_error; } - audio->map_v_write = ion_map_kernel(client, handle, ionflag); + audio->map_v_write = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_write)) { MM_ERR("could not map write buffers\n"); rc = -ENOMEM; diff --git a/arch/arm/mach-msm/qdsp5/audio_pcm.c b/arch/arm/mach-msm/qdsp5/audio_pcm.c index beee4a4c..4ffc2bee 100644 --- a/arch/arm/mach-msm/qdsp5/audio_pcm.c +++ b/arch/arm/mach-msm/qdsp5/audio_pcm.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include @@ -826,7 +826,7 @@ static int audpcm_ion_add(struct audio *audio, pr_err("%s: could not get flags for the handle\n", __func__); goto flag_error; } - kvaddr = (unsigned long)ion_map_kernel(audio->client, handle, ionflag); + kvaddr = (unsigned long)ion_map_kernel(audio->client, handle); if (IS_ERR_OR_NULL((void *)kvaddr)) { pr_err("%s: could not get virtual address\n", __func__); goto map_error; @@ -1579,7 +1579,7 @@ static int audio_open(struct inode *inode, struct file *file) MM_DBG("memsz = %d\n", mem_sz); handle = ion_alloc(client, mem_sz, SZ_4K, - ION_HEAP(ION_AUDIO_HEAP_ID)); + ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate O/P buffers\n"); rc = -ENOMEM; @@ -1605,7 +1605,7 @@ static int audio_open(struct inode *inode, struct file *file) goto output_buff_get_flags_error; } - audio->map_v_write = ion_map_kernel(client, handle, ionflag); + audio->map_v_write = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_write)) { MM_ERR("could not map write buffers\n"); rc = -ENOMEM; diff --git a/arch/arm/mach-msm/qdsp5/audio_pcm_in.c b/arch/arm/mach-msm/qdsp5/audio_pcm_in.c index 9e59030f..9cbb4a3b 100644 --- a/arch/arm/mach-msm/qdsp5/audio_pcm_in.c +++ b/arch/arm/mach-msm/qdsp5/audio_pcm_in.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include @@ -851,7 +851,7 @@ static int audpcm_in_open(struct inode *inode, struct file *file) MM_DBG("allocating mem sz = %d\n", DMASZ); handle = ion_alloc(client, DMASZ, SZ_4K, - ION_HEAP(ION_AUDIO_HEAP_ID)); + ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate O/P buffers\n"); rc = -ENOMEM; @@ -879,7 +879,7 @@ static int audpcm_in_open(struct inode *inode, struct file *file) goto output_buff_get_flags_error; } - audio->data = ion_map_kernel(client, handle, ionflag); + audio->data = ion_map_kernel(client, handle); if (IS_ERR(audio->data)) { MM_ERR("could not map read buffers,freeing instance 0x%08x\n", (int)audio); diff --git a/arch/arm/mach-msm/qdsp5/audio_qcelp.c b/arch/arm/mach-msm/qdsp5/audio_qcelp.c index 4669a739..bb4ffeff 100644 --- a/arch/arm/mach-msm/qdsp5/audio_qcelp.c +++ b/arch/arm/mach-msm/qdsp5/audio_qcelp.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include @@ -785,7 +785,7 @@ static long audqcelp_ioctl(struct file *file, unsigned int cmd, if (cmd == AUDIO_GET_STATS) { struct msm_audio_stats stats; - memset(&stats, 0, sizeof(stats)); + memset(&stats, 0, sizeof(stats)); stats.byte_count = audpp_avsync_byte_count(audio->dec_id); stats.sample_count = audpp_avsync_sample_count(audio->dec_id); if (copy_to_user((void *)arg, &stats, sizeof(stats))) @@ -973,7 +973,7 @@ static long audqcelp_ioctl(struct file *file, unsigned int cmd, handle = ion_alloc(audio->client, (config.buffer_size * config.buffer_count), - SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID)); + SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to alloc I/P buffs\n"); audio->input_buff_handle = NULL; @@ -1010,8 +1010,7 @@ static long audqcelp_ioctl(struct file *file, unsigned int cmd, break; } audio->map_v_read = ion_map_kernel( - audio->client, - handle, ionflag); + audio->client, handle); if (IS_ERR(audio->map_v_read)) { MM_ERR("failed to map read buf\n"); ion_free(audio->client, handle); @@ -1541,7 +1540,7 @@ static int audqcelp_open(struct inode *inode, struct file *file) audio->client = client; handle = ion_alloc(client, mem_sz, SZ_4K, - ION_HEAP(ION_AUDIO_HEAP_ID)); + ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate O/P buffers\n"); rc = -ENOMEM; @@ -1567,7 +1566,7 @@ static int audqcelp_open(struct inode *inode, struct file *file) goto output_buff_get_flags_error; } - audio->map_v_write = ion_map_kernel(client, handle, ionflag); + audio->map_v_write = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_write)) { MM_ERR("could not map write buffers\n"); rc = -ENOMEM; diff --git a/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c b/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c index 6619c44b..e38cd9b1 100644 --- a/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c +++ b/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c @@ -33,7 +33,7 @@ #include -#include +#include #include #include @@ -732,6 +732,7 @@ static long audqcelp_in_ioctl(struct file *file, MM_DBG("\n"); if (cmd == AUDIO_GET_STATS) { struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); stats.byte_count = atomic_read(&audio->in_bytes); stats.sample_count = atomic_read(&audio->in_samples); if (copy_to_user((void *) arg, &stats, sizeof(stats))) @@ -884,7 +885,7 @@ static ssize_t audqcelp_in_read(struct file *file, int rc = 0; struct qcelp_encoded_meta_out meta_field; struct audio_frame_nt *nt_frame; - memset(&meta_field, 0, sizeof(meta_field)); + memset(&meta_field, 0, sizeof(meta_field)); MM_DBG("count = %d\n", count); mutex_lock(&audio->read_lock); while (count > 0) { @@ -1313,7 +1314,7 @@ static int audqcelp_in_open(struct inode *inode, struct file *file) MM_DBG("allocating mem sz = %d\n", dma_size); handle = ion_alloc(client, dma_size, SZ_4K, - ION_HEAP(ION_AUDIO_HEAP_ID)); + ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate O/P buffers\n"); rc = -ENOMEM; @@ -1341,7 +1342,7 @@ static int audqcelp_in_open(struct inode *inode, struct file *file) goto output_buff_get_flags_error; } - audio->map_v_read = ion_map_kernel(client, handle, ionflag); + audio->map_v_read = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_read)) { MM_ERR("could not map read buffers,freeing instance 0x%08x\n", (int)audio); @@ -1356,7 +1357,7 @@ static int audqcelp_in_open(struct inode *inode, struct file *file) if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { MM_DBG("allocating BUFFER_SIZE %d\n", BUFFER_SIZE); handle = ion_alloc(client, BUFFER_SIZE, - SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID)); + SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate I/P buffers\n"); rc = -ENOMEM; @@ -1386,8 +1387,7 @@ static int audqcelp_in_open(struct inode *inode, struct file *file) goto input_buff_get_flags_error; } - audio->map_v_write = ion_map_kernel(client, - handle, ionflag); + audio->map_v_write = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_write)) { MM_ERR("could not map write buffers\n"); rc = -ENOMEM; diff --git a/arch/arm/mach-msm/qdsp5/audio_wma.c b/arch/arm/mach-msm/qdsp5/audio_wma.c index 163e30da..00ebe20b 100644 --- a/arch/arm/mach-msm/qdsp5/audio_wma.c +++ b/arch/arm/mach-msm/qdsp5/audio_wma.c @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include #include @@ -833,7 +833,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (cmd == AUDIO_GET_STATS) { struct msm_audio_stats stats; - memset(&stats, 0, sizeof(stats)); + memset(&stats, 0, sizeof(stats)); stats.byte_count = audpp_avsync_byte_count(audio->dec_id); stats.sample_count = audpp_avsync_sample_count(audio->dec_id); if (copy_to_user((void *)arg, &stats, sizeof(stats))) @@ -1051,7 +1051,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) handle = ion_alloc(audio->client, (config.buffer_size * config.buffer_count), - SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID)); + SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to alloc I/P buffs\n"); audio->input_buff_handle = NULL; @@ -1089,8 +1089,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } audio->map_v_read = ion_map_kernel( - audio->client, - handle, ionflag); + audio->client, handle); if (IS_ERR(audio->map_v_read)) { MM_ERR("map of read buf failed\n"); ion_free(audio->client, handle); @@ -1695,7 +1694,7 @@ static int audio_open(struct inode *inode, struct file *file) audio->client = client; handle = ion_alloc(client, mem_sz, SZ_4K, - ION_HEAP(ION_AUDIO_HEAP_ID)); + ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate O/P buffers\n"); rc = -ENOMEM; @@ -1721,7 +1720,7 @@ static int audio_open(struct inode *inode, struct file *file) goto output_buff_get_flags_error; } - audio->map_v_write = ion_map_kernel(client, handle, ionflag); + audio->map_v_write = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_write)) { MM_ERR("could not map write buffers\n"); rc = -ENOMEM; diff --git a/arch/arm/mach-msm/qdsp5/audio_wmapro.c b/arch/arm/mach-msm/qdsp5/audio_wmapro.c index 6a7a1d28..fa51f646 100644 --- a/arch/arm/mach-msm/qdsp5/audio_wmapro.c +++ b/arch/arm/mach-msm/qdsp5/audio_wmapro.c @@ -40,7 +40,7 @@ #include #include #include -#include +#include #include #include @@ -821,7 +821,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (cmd == AUDIO_GET_STATS) { struct msm_audio_stats stats; - memset(&stats, 0, sizeof(stats)); + memset(&stats, 0, sizeof(stats)); stats.byte_count = audpp_avsync_byte_count(audio->dec_id); stats.sample_count = audpp_avsync_sample_count(audio->dec_id); if (copy_to_user((void *)arg, &stats, sizeof(stats))) @@ -1048,7 +1048,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) handle = ion_alloc(audio->client, (config.buffer_size * config.buffer_count), - SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID)); + SZ_4K, ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to alloc I/P buffs\n"); audio->input_buff_handle = NULL; @@ -1086,8 +1086,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } audio->map_v_read = ion_map_kernel( - audio->client, - handle, ionflag); + audio->client, handle); if (IS_ERR(audio->map_v_read)) { MM_ERR("map of read buf failed\n"); ion_free(audio->client, handle); @@ -1690,7 +1689,7 @@ static int audio_open(struct inode *inode, struct file *file) audio->client = client; handle = ion_alloc(client, mem_sz, SZ_4K, - ION_HEAP(ION_AUDIO_HEAP_ID)); + ION_HEAP(ION_AUDIO_HEAP_ID), 0); if (IS_ERR_OR_NULL(handle)) { MM_ERR("Unable to create allocate O/P buffers\n"); rc = -ENOMEM; @@ -1716,7 +1715,7 @@ static int audio_open(struct inode *inode, struct file *file) goto output_buff_get_flags_error; } - audio->map_v_write = ion_map_kernel(client, handle, ionflag); + audio->map_v_write = ion_map_kernel(client, handle); if (IS_ERR(audio->map_v_write)) { MM_ERR("could not map write buffers\n"); rc = -ENOMEM; diff --git a/arch/arm/mach-msm/rpcrouter_sdio_xprt.c b/arch/arm/mach-msm/rpcrouter_sdio_xprt.c new file mode 100644 index 00000000..e9818e51 --- /dev/null +++ b/arch/arm/mach-msm/rpcrouter_sdio_xprt.c @@ -0,0 +1,655 @@ +/* Copyright (c) 2010-2011, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * RPCROUTER SDIO XPRT module. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "smd_rpcrouter.h" + +enum { + MSM_SDIO_XPRT_DEBUG = 1U << 0, + MSM_SDIO_XPRT_INFO = 1U << 1, +}; + +static int msm_sdio_xprt_debug_mask; +module_param_named(debug_mask, msm_sdio_xprt_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +#if defined(CONFIG_MSM_RPC_SDIO_DEBUG) +#define SDIO_XPRT_DBG(x...) do { \ + if (msm_sdio_xprt_debug_mask & MSM_SDIO_XPRT_DEBUG) \ + printk(KERN_DEBUG x); \ + } while (0) + +#define SDIO_XPRT_INFO(x...) do { \ + if (msm_sdio_xprt_debug_mask & MSM_SDIO_XPRT_INFO) \ + printk(KERN_INFO x); \ + } while (0) +#else +#define SDIO_XPRT_DBG(x...) do { } while (0) +#define SDIO_XPRT_INFO(x...) do { } while (0) +#endif + +#define MAX_SDIO_WRITE_RETRY 5 +#define SDIO_BUF_SIZE (RPCROUTER_MSGSIZE_MAX + sizeof(struct rr_header) - 8) +#define NUM_SDIO_BUFS 20 +#define MAX_TX_BUFS 10 +#define MAX_RX_BUFS 10 + +struct sdio_xprt { + struct sdio_channel *handle; + + struct list_head write_list; + spinlock_t write_list_lock; + + struct list_head read_list; + spinlock_t read_list_lock; + + struct list_head free_list; + spinlock_t free_list_lock; + + struct wake_lock read_wakelock; +}; + +struct rpcrouter_sdio_xprt { + struct rpcrouter_xprt xprt; + struct sdio_xprt *channel; +}; + +static struct rpcrouter_sdio_xprt sdio_remote_xprt; + +static void sdio_xprt_read_data(struct work_struct *work); +static DECLARE_DELAYED_WORK(work_read_data, sdio_xprt_read_data); +static struct workqueue_struct *sdio_xprt_read_workqueue; + +struct sdio_buf_struct { + struct list_head list; + uint32_t size; + uint32_t read_index; + uint32_t write_index; + unsigned char data[SDIO_BUF_SIZE]; +}; + +static void sdio_xprt_write_data(struct work_struct *work); +static DECLARE_WORK(work_write_data, sdio_xprt_write_data); +static wait_queue_head_t write_avail_wait_q; +static uint32_t num_free_bufs; +static uint32_t num_tx_bufs; +static uint32_t num_rx_bufs; + +static DEFINE_MUTEX(modem_reset_lock); +static uint32_t modem_reset; + +static void free_sdio_xprt(struct sdio_xprt *chnl) +{ + struct sdio_buf_struct *buf; + unsigned long flags; + + if (!chnl) { + printk(KERN_ERR "Invalid chnl to free\n"); + return; + } + + spin_lock_irqsave(&chnl->free_list_lock, flags); + while (!list_empty(&chnl->free_list)) { + buf = list_first_entry(&chnl->free_list, + struct sdio_buf_struct, list); + list_del(&buf->list); + kfree(buf); + } + num_free_bufs = 0; + spin_unlock_irqrestore(&chnl->free_list_lock, flags); + + spin_lock_irqsave(&chnl->write_list_lock, flags); + while (!list_empty(&chnl->write_list)) { + buf = list_first_entry(&chnl->write_list, + struct sdio_buf_struct, list); + list_del(&buf->list); + kfree(buf); + } + num_tx_bufs = 0; + spin_unlock_irqrestore(&chnl->write_list_lock, flags); + + spin_lock_irqsave(&chnl->read_list_lock, flags); + while (!list_empty(&chnl->read_list)) { + buf = list_first_entry(&chnl->read_list, + struct sdio_buf_struct, list); + list_del(&buf->list); + kfree(buf); + } + num_rx_bufs = 0; + spin_unlock_irqrestore(&chnl->read_list_lock, flags); + wake_unlock(&chnl->read_wakelock); +} + +static struct sdio_buf_struct *alloc_from_free_list(struct sdio_xprt *chnl) +{ + struct sdio_buf_struct *buf; + unsigned long flags; + + spin_lock_irqsave(&chnl->free_list_lock, flags); + if (list_empty(&chnl->free_list)) { + spin_unlock_irqrestore(&chnl->free_list_lock, flags); + SDIO_XPRT_DBG("%s: Free list empty\n", __func__); + return NULL; + } + buf = list_first_entry(&chnl->free_list, struct sdio_buf_struct, list); + list_del(&buf->list); + num_free_bufs--; + spin_unlock_irqrestore(&chnl->free_list_lock, flags); + + buf->size = 0; + buf->read_index = 0; + buf->write_index = 0; + + return buf; +} + +static void return_to_free_list(struct sdio_xprt *chnl, + struct sdio_buf_struct *buf) +{ + unsigned long flags; + + if (!chnl || !buf) { + pr_err("%s: Invalid chnl or buf\n", __func__); + return; + } + + buf->size = 0; + buf->read_index = 0; + buf->write_index = 0; + + spin_lock_irqsave(&chnl->free_list_lock, flags); + list_add_tail(&buf->list, &chnl->free_list); + num_free_bufs++; + spin_unlock_irqrestore(&chnl->free_list_lock, flags); + +} + +static int rpcrouter_sdio_remote_read_avail(void) +{ + int read_avail = 0; + unsigned long flags; + struct sdio_buf_struct *buf; + + spin_lock_irqsave(&sdio_remote_xprt.channel->read_list_lock, flags); + list_for_each_entry(buf, &sdio_remote_xprt.channel->read_list, list) { + read_avail += buf->size; + } + spin_unlock_irqrestore(&sdio_remote_xprt.channel->read_list_lock, + flags); + return read_avail; +} + +static int rpcrouter_sdio_remote_read(void *data, uint32_t len) +{ + struct sdio_buf_struct *buf; + unsigned char *buf_data; + unsigned long flags; + + SDIO_XPRT_DBG("sdio_xprt Called %s\n", __func__); + if (len < 0 || !data) + return -EINVAL; + else if (len == 0) + return 0; + + spin_lock_irqsave(&sdio_remote_xprt.channel->read_list_lock, flags); + if (list_empty(&sdio_remote_xprt.channel->read_list)) { + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->read_list_lock, flags); + return -EINVAL; + } + + buf = list_first_entry(&sdio_remote_xprt.channel->read_list, + struct sdio_buf_struct, list); + if (buf->size < len) { + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->read_list_lock, flags); + return -EINVAL; + } + + buf_data = buf->data + buf->read_index; + memcpy(data, buf_data, len); + buf->read_index += len; + buf->size -= len; + if (buf->size == 0) { + list_del(&buf->list); + num_rx_bufs--; + return_to_free_list(sdio_remote_xprt.channel, buf); + } + + if (list_empty(&sdio_remote_xprt.channel->read_list)) + wake_unlock(&sdio_remote_xprt.channel->read_wakelock); + spin_unlock_irqrestore(&sdio_remote_xprt.channel->read_list_lock, + flags); + return len; +} + +static int rpcrouter_sdio_remote_write_avail(void) +{ + uint32_t write_avail = 0; + unsigned long flags; + + SDIO_XPRT_DBG("sdio_xprt Called %s\n", __func__); + spin_lock_irqsave(&sdio_remote_xprt.channel->write_list_lock, flags); + write_avail = (MAX_TX_BUFS - num_tx_bufs) * SDIO_BUF_SIZE; + spin_unlock_irqrestore(&sdio_remote_xprt.channel->write_list_lock, + flags); + return write_avail; +} + +static int rpcrouter_sdio_remote_write(void *data, uint32_t len, + enum write_data_type type) +{ + unsigned long flags; + static struct sdio_buf_struct *buf; + unsigned char *buf_data; + + switch (type) { + case HEADER: + spin_lock_irqsave(&sdio_remote_xprt.channel->write_list_lock, + flags); + if (num_tx_bufs == MAX_TX_BUFS) { + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->write_list_lock, + flags); + return -ENOMEM; + } + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->write_list_lock, flags); + + SDIO_XPRT_DBG("sdio_xprt WRITE HEADER %s\n", __func__); + buf = alloc_from_free_list(sdio_remote_xprt.channel); + if (!buf) { + pr_err("%s: alloc_from_free_list failed\n", __func__); + return -ENOMEM; + } + buf_data = buf->data + buf->write_index; + memcpy(buf_data, data, len); + buf->write_index += len; + buf->size += len; + return len; + case PACKMARK: + SDIO_XPRT_DBG("sdio_xprt WRITE PACKMARK %s\n", __func__); + if (!buf) { + pr_err("%s: HEADER not written or alloc failed\n", + __func__); + return -ENOMEM; + } + buf_data = buf->data + buf->write_index; + memcpy(buf_data, data, len); + buf->write_index += len; + buf->size += len; + return len; + case PAYLOAD: + SDIO_XPRT_DBG("sdio_xprt WRITE PAYLOAD %s\n", __func__); + if (!buf) { + pr_err("%s: HEADER not written or alloc failed\n", + __func__); + return -ENOMEM; + } + buf_data = buf->data + buf->write_index; + memcpy(buf_data, data, len); + buf->write_index += len; + buf->size += len; + + SDIO_XPRT_DBG("sdio_xprt flush %d bytes\n", buf->size); + spin_lock_irqsave(&sdio_remote_xprt.channel->write_list_lock, + flags); + list_add_tail(&buf->list, + &sdio_remote_xprt.channel->write_list); + num_tx_bufs++; + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->write_list_lock, flags); + queue_work(sdio_xprt_read_workqueue, &work_write_data); + buf = NULL; + return len; + default: + return -EINVAL; + } +} + +static void sdio_xprt_write_data(struct work_struct *work) +{ + int rc = 0, sdio_write_retry = 0; + unsigned long flags; + struct sdio_buf_struct *buf; + + mutex_lock(&modem_reset_lock); + if (modem_reset) { + mutex_unlock(&modem_reset_lock); + return; + } + + spin_lock_irqsave(&sdio_remote_xprt.channel->write_list_lock, flags); + while (!list_empty(&sdio_remote_xprt.channel->write_list)) { + buf = list_first_entry(&sdio_remote_xprt.channel->write_list, + struct sdio_buf_struct, list); + list_del(&buf->list); + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->write_list_lock, flags); + mutex_unlock(&modem_reset_lock); + + wait_event(write_avail_wait_q, + (!(modem_reset) && (sdio_write_avail( + sdio_remote_xprt.channel->handle) >= + buf->size))); + + mutex_lock(&modem_reset_lock); + while (!(modem_reset) && + ((rc = sdio_write(sdio_remote_xprt.channel->handle, + buf->data, buf->size)) < 0) && + (sdio_write_retry++ < MAX_SDIO_WRITE_RETRY)) { + printk(KERN_ERR "sdio_write failed with RC %d\n", rc); + mutex_unlock(&modem_reset_lock); + msleep(250); + mutex_lock(&modem_reset_lock); + } + if (modem_reset) { + mutex_unlock(&modem_reset_lock); + kfree(buf); + return; + } else { + return_to_free_list(sdio_remote_xprt.channel, buf); + } + + if (!rc) + SDIO_XPRT_DBG("sdio_write %d bytes completed\n", + buf->size); + + spin_lock_irqsave(&sdio_remote_xprt.channel->write_list_lock, + flags); + num_tx_bufs--; + } + spin_unlock_irqrestore(&sdio_remote_xprt.channel->write_list_lock, + flags); + mutex_unlock(&modem_reset_lock); +} + +static int rpcrouter_sdio_remote_close(void) +{ + SDIO_XPRT_DBG("sdio_xprt Called %s\n", __func__); + flush_workqueue(sdio_xprt_read_workqueue); + sdio_close(sdio_remote_xprt.channel->handle); + free_sdio_xprt(sdio_remote_xprt.channel); + return 0; +} + +static void sdio_xprt_read_data(struct work_struct *work) +{ + int size = 0, read_avail; + unsigned long flags; + struct sdio_buf_struct *buf; + SDIO_XPRT_DBG("sdio_xprt Called %s\n", __func__); + + mutex_lock(&modem_reset_lock); + while (!(modem_reset) && + ((read_avail = + sdio_read_avail(sdio_remote_xprt.channel->handle)) > 0)) { + spin_lock_irqsave(&sdio_remote_xprt.channel->read_list_lock, + flags); + if (num_rx_bufs == MAX_RX_BUFS) { + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->read_list_lock, + flags); + queue_delayed_work(sdio_xprt_read_workqueue, + &work_read_data, + msecs_to_jiffies(100)); + break; + } + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->read_list_lock, flags); + + buf = alloc_from_free_list(sdio_remote_xprt.channel); + if (!buf) { + SDIO_XPRT_DBG("%s: Failed to alloc_from_free_list" + " Try again later\n", __func__); + queue_delayed_work(sdio_xprt_read_workqueue, + &work_read_data, + msecs_to_jiffies(100)); + break; + } + + size = sdio_read(sdio_remote_xprt.channel->handle, + buf->data, read_avail); + if (size < 0) { + printk(KERN_ERR "sdio_read failed," + " read %d bytes, expected %d\n", + size, read_avail); + return_to_free_list(sdio_remote_xprt.channel, buf); + queue_delayed_work(sdio_xprt_read_workqueue, + &work_read_data, + msecs_to_jiffies(100)); + break; + } + + if (size == 0) + size = read_avail; + + buf->size = size; + buf->write_index = size; + spin_lock_irqsave(&sdio_remote_xprt.channel->read_list_lock, + flags); + list_add_tail(&buf->list, + &sdio_remote_xprt.channel->read_list); + num_rx_bufs++; + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->read_list_lock, flags); + wake_lock(&sdio_remote_xprt.channel->read_wakelock); + } + + if (!modem_reset && !list_empty(&sdio_remote_xprt.channel->read_list)) + msm_rpcrouter_xprt_notify(&sdio_remote_xprt.xprt, + RPCROUTER_XPRT_EVENT_DATA); + mutex_unlock(&modem_reset_lock); +} + +static void rpcrouter_sdio_remote_notify(void *_dev, unsigned event) +{ + if (event == SDIO_EVENT_DATA_READ_AVAIL) { + SDIO_XPRT_DBG("%s Received Notify" + "SDIO_EVENT_DATA_READ_AVAIL\n", __func__); + queue_delayed_work(sdio_xprt_read_workqueue, + &work_read_data, 0); + } + if (event == SDIO_EVENT_DATA_WRITE_AVAIL) { + SDIO_XPRT_DBG("%s Received Notify" + "SDIO_EVENT_DATA_WRITE_AVAIL\n", __func__); + wake_up(&write_avail_wait_q); + } +} + +static int allocate_sdio_xprt(struct sdio_xprt **sdio_xprt_chnl) +{ + struct sdio_buf_struct *buf; + struct sdio_xprt *chnl; + int i; + unsigned long flags; + int rc = -ENOMEM; + + if (!(*sdio_xprt_chnl)) { + chnl = kmalloc(sizeof(struct sdio_xprt), GFP_KERNEL); + if (!chnl) { + printk(KERN_ERR "sdio_xprt channel" + " allocation failed\n"); + return rc; + } + + spin_lock_init(&chnl->write_list_lock); + spin_lock_init(&chnl->read_list_lock); + spin_lock_init(&chnl->free_list_lock); + + INIT_LIST_HEAD(&chnl->write_list); + INIT_LIST_HEAD(&chnl->read_list); + INIT_LIST_HEAD(&chnl->free_list); + wake_lock_init(&chnl->read_wakelock, + WAKE_LOCK_SUSPEND, "rpc_sdio_xprt_read"); + } else { + chnl = *sdio_xprt_chnl; + } + + for (i = 0; i < NUM_SDIO_BUFS; i++) { + buf = kzalloc(sizeof(struct sdio_buf_struct), GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "sdio_buf_struct alloc failed\n"); + goto alloc_failure; + } + spin_lock_irqsave(&chnl->free_list_lock, flags); + list_add_tail(&buf->list, &chnl->free_list); + spin_unlock_irqrestore(&chnl->free_list_lock, flags); + } + num_free_bufs = NUM_SDIO_BUFS; + + *sdio_xprt_chnl = chnl; + return 0; + +alloc_failure: + spin_lock_irqsave(&chnl->free_list_lock, flags); + while (!list_empty(&chnl->free_list)) { + buf = list_first_entry(&chnl->free_list, + struct sdio_buf_struct, + list); + list_del(&buf->list); + kfree(buf); + } + spin_unlock_irqrestore(&chnl->free_list_lock, flags); + wake_lock_destroy(&chnl->read_wakelock); + + kfree(chnl); + *sdio_xprt_chnl = NULL; + return rc; +} + +static int rpcrouter_sdio_remote_probe(struct platform_device *pdev) +{ + int rc; + + SDIO_XPRT_INFO("%s Called\n", __func__); + + mutex_lock(&modem_reset_lock); + if (!modem_reset) { + sdio_xprt_read_workqueue = + create_singlethread_workqueue("sdio_xprt"); + if (!sdio_xprt_read_workqueue) { + mutex_unlock(&modem_reset_lock); + return -ENOMEM; + } + + sdio_remote_xprt.xprt.name = "rpcrotuer_sdio_xprt"; + sdio_remote_xprt.xprt.read_avail = + rpcrouter_sdio_remote_read_avail; + sdio_remote_xprt.xprt.read = rpcrouter_sdio_remote_read; + sdio_remote_xprt.xprt.write_avail = + rpcrouter_sdio_remote_write_avail; + sdio_remote_xprt.xprt.write = rpcrouter_sdio_remote_write; + sdio_remote_xprt.xprt.close = rpcrouter_sdio_remote_close; + sdio_remote_xprt.xprt.priv = NULL; + + init_waitqueue_head(&write_avail_wait_q); + } + modem_reset = 0; + + rc = allocate_sdio_xprt(&sdio_remote_xprt.channel); + if (rc) { + destroy_workqueue(sdio_xprt_read_workqueue); + mutex_unlock(&modem_reset_lock); + return rc; + } + + /* Open up SDIO channel */ + rc = sdio_open("SDIO_RPC", &sdio_remote_xprt.channel->handle, NULL, + rpcrouter_sdio_remote_notify); + + if (rc < 0) { + free_sdio_xprt(sdio_remote_xprt.channel); + destroy_workqueue(sdio_xprt_read_workqueue); + mutex_unlock(&modem_reset_lock); + return rc; + } + mutex_unlock(&modem_reset_lock); + + msm_rpcrouter_xprt_notify(&sdio_remote_xprt.xprt, + RPCROUTER_XPRT_EVENT_OPEN); + + SDIO_XPRT_INFO("%s Completed\n", __func__); + + return 0; +} + +static int rpcrouter_sdio_remote_remove(struct platform_device *pdev) +{ + SDIO_XPRT_INFO("%s Called\n", __func__); + + mutex_lock(&modem_reset_lock); + modem_reset = 1; + wake_up(&write_avail_wait_q); + free_sdio_xprt(sdio_remote_xprt.channel); + mutex_unlock(&modem_reset_lock); + + msm_rpcrouter_xprt_notify(&sdio_remote_xprt.xprt, + RPCROUTER_XPRT_EVENT_CLOSE); + + SDIO_XPRT_INFO("%s Completed\n", __func__); + + return 0; +} + +/*Remove this platform driver after mainline of SDIO_AL update*/ +static struct platform_driver rpcrouter_sdio_remote_driver = { + .probe = rpcrouter_sdio_remote_probe, + .driver = { + .name = "SDIO_AL", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver rpcrouter_sdio_driver = { + .probe = rpcrouter_sdio_remote_probe, + .remove = rpcrouter_sdio_remote_remove, + .driver = { + .name = "SDIO_RPC", + .owner = THIS_MODULE, + }, +}; + +static int __init rpcrouter_sdio_init(void) +{ + int rc; + msm_sdio_xprt_debug_mask = 0x2; + rc = platform_driver_register(&rpcrouter_sdio_remote_driver); + if (rc < 0) + return rc; + return platform_driver_register(&rpcrouter_sdio_driver); +} + +module_init(rpcrouter_sdio_init); +MODULE_DESCRIPTION("RPC Router SDIO XPRT"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/sdio_al.c b/arch/arm/mach-msm/sdio_al.c new file mode 100644 index 00000000..bcfc556c --- /dev/null +++ b/arch/arm/mach-msm/sdio_al.c @@ -0,0 +1,4365 @@ +/* Copyright (c) 2010-2011, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * SDIO-Abstraction-Layer Module. + * + * To be used with Qualcomm's SDIO-Client connected to this host. + */ +#include "sdio_al_private.h" + +#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 "../../../drivers/mmc/host/msm_sdcc.h" + +/** + * Func#0 has SDIO standard registers + * Func#1 is for Mailbox. + * Functions 2..7 are for channels. + * Currently only functions 2..5 are active due to SDIO-Client + * number of pipes. + * + */ +#define SDIO_AL_MAX_CHANNELS 6 + +/** Func 1..5 */ +#define SDIO_AL_MAX_FUNCS (SDIO_AL_MAX_CHANNELS+1) +#define SDIO_AL_WAKEUP_FUNC 6 + +/** Number of SDIO-Client pipes */ +#define SDIO_AL_MAX_PIPES 16 +#define SDIO_AL_ACTIVE_PIPES 8 + +/** CMD53/CMD54 Block size */ +#define SDIO_AL_BLOCK_SIZE 256 + +/** Func#1 hardware Mailbox base address */ +#define HW_MAILBOX_ADDR 0x1000 + +/** Func#1 peer sdioc software version. + * The header is duplicated also to the mailbox of the other + * functions. It can be used before other functions are enabled. */ +#define SDIOC_SW_HEADER_ADDR 0x0400 + +/** Func#2..7 software Mailbox base address at 16K */ +#define SDIOC_SW_MAILBOX_ADDR 0x4000 + +/** Some Mailbox registers address, written by host for + control */ +#define PIPES_THRESHOLD_ADDR 0x01000 + +#define PIPES_0_7_IRQ_MASK_ADDR 0x01048 + +#define PIPES_8_15_IRQ_MASK_ADDR 0x0104C + +#define FUNC_1_4_MASK_IRQ_ADDR 0x01040 +#define FUNC_5_7_MASK_IRQ_ADDR 0x01044 +#define FUNC_1_4_USER_IRQ_ADDR 0x01050 +#define FUNC_5_7_USER_IRQ_ADDR 0x01054 + +#define EOT_PIPES_ENABLE 0x00 + +/** Maximum read/write data available is SDIO-Client limitation */ +#define MAX_DATA_AVAILABLE (16*1024) +#define INVALID_DATA_AVAILABLE (0x8000) + +/** SDIO-Client HW threshold to generate interrupt to the + * SDIO-Host on write available bytes. + */ +#define DEFAULT_WRITE_THRESHOLD (1024) + +/** SDIO-Client HW threshold to generate interrupt to the + * SDIO-Host on read available bytes, for streaming (non + * packet) rx data. + */ +#define DEFAULT_READ_THRESHOLD (1024) +#define LOW_LATENCY_THRESHOLD (1) + +/* Extra bytes to ensure getting the rx threshold interrupt on stream channels + when restoring the threshold after sleep */ +#define THRESHOLD_CHANGE_EXTRA_BYTES (100) + +/** SW threshold to trigger reading the mailbox. */ +#define DEFAULT_MIN_WRITE_THRESHOLD (1024) +#define DEFAULT_MIN_WRITE_THRESHOLD_STREAMING (1600) + +#define THRESHOLD_DISABLE_VAL (0xFFFFFFFF) + +/** Mailbox polling time for packet channels */ +#define DEFAULT_POLL_DELAY_MSEC 10 +/** Mailbox polling time for streaming channels */ +#define DEFAULT_POLL_DELAY_NOPACKET_MSEC 30 + +/** The SDIO-Client prepares N buffers of size X per Tx pipe. + * Even when the transfer fills a partial buffer, + * that buffer becomes unusable for the next transfer. */ +#define DEFAULT_PEER_TX_BUF_SIZE (128) + +#define ROUND_UP(x, n) (((x + n - 1) / n) * n) + +/** Func#2..7 FIFOs are r/w via + sdio_readsb() & sdio_writesb(),when inc_addr=0 */ +#define PIPE_RX_FIFO_ADDR 0x00 +#define PIPE_TX_FIFO_ADDR 0x00 + +/** Inactivity time to go to sleep in mseconds */ +#define INACTIVITY_TIME_MSEC 30 +#define INITIAL_INACTIVITY_TIME_MSEC 5000 + +/** Context validity check */ +#define SDIO_AL_SIGNATURE 0xAABBCCDD + +/* Vendor Specific Command */ +#define SD_IO_RW_EXTENDED_QCOM 54 + +#define TIME_TO_WAIT_US 500 +#define SDIO_CLOSE_FLUSH_TIMEOUT_MSEC (10000) +#define RX_FLUSH_BUFFER_SIZE (16*1024) + +#define SDIO_TEST_POSTFIX "_TEST" + +#define DATA_DEBUG(x, y...) \ + do { \ + if (sdio_al->debug.debug_data_on) \ + pr_info(y); \ + sdio_al_log(x, y); \ + } while (0) + +#define LPM_DEBUG(x, y...) \ + do { \ + if (sdio_al->debug.debug_lpm_on) \ + pr_info(y); \ + sdio_al_log(x, y); \ + } while (0) + +#define sdio_al_loge(x, y...) \ + do { \ + pr_err(y); \ + sdio_al_log(x, y); \ + } while (0) + +#define sdio_al_logi(x, y...) \ + do { \ + pr_info(y); \ + sdio_al_log(x, y); \ + } while (0) + +#define CLOSE_DEBUG(x, y...) \ + do { \ + if (sdio_al->debug.debug_close_on) \ + pr_info(y); \ + sdio_al_log(x, y); \ + } while (0) + +/* The index of the SDIO card used for the sdio_al_dloader */ +#define SDIO_BOOTLOADER_CARD_INDEX 1 + + +/* SDIO card state machine */ +enum sdio_al_device_state { + CARD_INSERTED, + CARD_REMOVED, + MODEM_RESTART +}; + +struct sdio_al_debug { + u8 debug_lpm_on; + u8 debug_data_on; + u8 debug_close_on; + struct dentry *sdio_al_debug_root; + struct dentry *sdio_al_debug_lpm_on; + struct dentry *sdio_al_debug_data_on; + struct dentry *sdio_al_debug_close_on; + struct dentry *sdio_al_debug_info; + struct dentry *sdio_al_debug_log_buffers[MAX_NUM_OF_SDIO_DEVICES + 1]; +}; + +/* Polling time for the inactivity timer for devices that doesn't have + * a streaming channel + */ +#define SDIO_AL_POLL_TIME_NO_STREAMING 30 + +#define CHAN_TO_FUNC(x) ((x) + 2 - 1) + +/** + * Mailbox structure. + * The Mailbox is located on the SDIO-Client Function#1. + * The mailbox size is 128 bytes, which is one block. + * The mailbox allows the host ton: + * 1. Get the number of available bytes on the pipes. + * 2. Enable/Disable SDIO-Client interrupt, related to pipes. + * 3. Set the Threshold for generating interrupt. + * + */ +struct sdio_mailbox { + u32 pipe_bytes_threshold[SDIO_AL_MAX_PIPES]; /* Addr 0x1000 */ + + /* Mask USER interrupts generated towards host - Addr 0x1040 */ + u32 mask_irq_func_1:8; /* LSB */ + u32 mask_irq_func_2:8; + u32 mask_irq_func_3:8; + u32 mask_irq_func_4:8; + + u32 mask_irq_func_5:8; + u32 mask_irq_func_6:8; + u32 mask_irq_func_7:8; + u32 mask_mutex_irq:8; + + /* Mask PIPE interrupts generated towards host - Addr 0x1048 */ + u32 mask_eot_pipe_0_7:8; + u32 mask_thresh_above_limit_pipe_0_7:8; + u32 mask_overflow_pipe_0_7:8; + u32 mask_underflow_pipe_0_7:8; + + u32 mask_eot_pipe_8_15:8; + u32 mask_thresh_above_limit_pipe_8_15:8; + u32 mask_overflow_pipe_8_15:8; + u32 mask_underflow_pipe_8_15:8; + + /* Status of User interrupts generated towards host - Addr 0x1050 */ + u32 user_irq_func_1:8; + u32 user_irq_func_2:8; + u32 user_irq_func_3:8; + u32 user_irq_func_4:8; + + u32 user_irq_func_5:8; + u32 user_irq_func_6:8; + u32 user_irq_func_7:8; + u32 user_mutex_irq:8; + + /* Status of PIPE interrupts generated towards host */ + /* Note: All sources are cleared once they read. - Addr 0x1058 */ + u32 eot_pipe_0_7:8; + u32 thresh_above_limit_pipe_0_7:8; + u32 overflow_pipe_0_7:8; + u32 underflow_pipe_0_7:8; + + u32 eot_pipe_8_15:8; + u32 thresh_above_limit_pipe_8_15:8; + u32 overflow_pipe_8_15:8; + u32 underflow_pipe_8_15:8; + + u16 pipe_bytes_avail[SDIO_AL_MAX_PIPES]; +}; + +/** Track pending Rx Packet size */ +struct rx_packet_size { + u32 size; /* in bytes */ + struct list_head list; +}; + +#define PEER_SDIOC_SW_MAILBOX_SIGNATURE 0xFACECAFE +#define PEER_SDIOC_SW_MAILBOX_UT_SIGNATURE 0x5D107E57 +#define PEER_SDIOC_SW_MAILBOX_BOOT_SIGNATURE 0xDEADBEEF + +/* Allow support in old sdio version */ +#define PEER_SDIOC_OLD_VERSION_MAJOR 0x0002 +#define INVALID_SDIO_CHAN 0xFF + +/** + * Peer SDIO-Client software header. + */ +struct peer_sdioc_sw_header { + u32 signature; + u32 version; + u32 max_channels; + char channel_names[SDIO_AL_MAX_CHANNELS][PEER_CHANNEL_NAME_SIZE]; + u32 reserved[23]; +}; + +struct peer_sdioc_boot_sw_header { + u32 signature; + u32 version; + u32 boot_ch_num; + u32 reserved[29]; /* 32 - previous fields */ +}; + +/** + * Peer SDIO-Client software mailbox. + */ +struct peer_sdioc_sw_mailbox { + struct peer_sdioc_sw_header sw_header; + struct peer_sdioc_channel_config ch_config[SDIO_AL_MAX_CHANNELS]; +}; + +#define SDIO_AL_DEBUG_LOG_SIZE 3000 +struct sdio_al_local_log { + char buffer[SDIO_AL_DEBUG_LOG_SIZE]; + unsigned int buf_cur_pos; + spinlock_t log_lock; +}; + +#define SDIO_AL_DEBUG_TMP_LOG_SIZE 250 +static int sdio_al_log(struct sdio_al_local_log *, const char *fmt, ...); + +/** + * SDIO Abstraction Layer driver context. + * + * @pdata - + * @debug - + * @devices - an array of the the devices claimed by sdio_al + * @unittest_mode - a flag to indicate if sdio_al is in + * unittest mode + * @bootloader_dev - the device which is used for the + * bootloader + * @subsys_notif_handle - handle for modem restart + * notifications + * + */ +struct sdio_al { + struct sdio_al_local_log gen_log; + struct sdio_al_local_log device_log[MAX_NUM_OF_SDIO_DEVICES]; + struct sdio_al_platform_data *pdata; + struct sdio_al_debug debug; + struct sdio_al_device *devices[MAX_NUM_OF_SDIO_DEVICES]; + int unittest_mode; + struct sdio_al_device *bootloader_dev; + void *subsys_notif_handle; + int sdioc_major; + int skip_print_info; +}; + +struct sdio_al_work { + struct work_struct work; + struct sdio_al_device *sdio_al_dev; +}; + + +/** + * SDIO Abstraction Layer device context. + * + * @card - card claimed. + * + * @mailbox - A shadow of the SDIO-Client mailbox. + * + * @channel - Channels context. + * + * @workqueue - workqueue to read the mailbox and handle + * pending requests. Reading the mailbox should not happen + * in interrupt context. + * + * @work - work to submit to workqueue. + * + * @is_ready - driver is ready. + * + * @ask_mbox - Flag to request reading the mailbox, + * for different reasons. + * + * @wake_lock - Lock when can't sleep. + * + * @lpm_chan - Channel to use for LPM (low power mode) + * communication. + * + * @is_ok_to_sleep - Mark if driver is OK with going to sleep + * (no pending transactions). + * + * @inactivity_time - time allowed to be in inactivity before + * going to sleep + * + * @timer - timer to use for polling the mailbox. + * + * @poll_delay_msec - timer delay for polling the mailbox. + * + * @is_err - error detected. + * + * @signature - Context Validity Check. + * + * @flashless_boot_on - flag to indicate if sdio_al is in + * flshless boot mode + * + */ +struct sdio_al_device { + struct sdio_al_local_log *dev_log; + struct mmc_card *card; + struct mmc_host *host; + struct sdio_mailbox *mailbox; + struct sdio_channel channel[SDIO_AL_MAX_CHANNELS]; + + struct peer_sdioc_sw_header *sdioc_sw_header; + struct peer_sdioc_boot_sw_header *sdioc_boot_sw_header; + + struct workqueue_struct *workqueue; + struct sdio_al_work sdio_al_work; + struct sdio_al_work boot_work; + + int is_ready; + + wait_queue_head_t wait_mbox; + int ask_mbox; + int bootloader_done; + + struct wake_lock wake_lock; + int lpm_chan; + int is_ok_to_sleep; + unsigned long inactivity_time; + + struct timer_list timer; + u32 poll_delay_msec; + int is_timer_initialized; + + int is_err; + + u32 signature; + + unsigned int is_suspended; + + int flashless_boot_on; + int ch_close_supported; + int state; + int (*lpm_callback)(void *, int); + + int print_after_interrupt; + + u8 *rx_flush_buf; +}; + +/* + * Host operation: + * lower 16bits are operation code + * upper 16bits are operation state + */ +#define PEER_OPERATION(op_code , op_state) ((op_code) | ((op_state) << 16)) +#define GET_PEER_OPERATION_CODE(op) ((op) & 0xffff) +#define GET_PEER_OPERATION_STATE(op) ((op) >> 16) + +enum peer_op_code { + PEER_OP_CODE_CLOSE = 1 +}; + +enum peer_op_state { + PEER_OP_STATE_INIT = 0, + PEER_OP_STATE_START = 1 +}; + + +/* + * On the kernel command line specify + * sdio_al.debug_lpm_on=1 to enable the LPM debug messages + * By default the LPM debug messages are turned off + */ +static int debug_lpm_on; +module_param(debug_lpm_on, int, 0); + +/* + * On the kernel command line specify + * sdio_al.debug_data_on=1 to enable the DATA debug messages + * By default the DATA debug messages are turned off + */ +static int debug_data_on; +module_param(debug_data_on, int, 0); + +/* + * Enables / disables open close debug messages + */ +static int debug_close_on = 1; +module_param(debug_close_on, int, 0); + +/** The driver context */ +static struct sdio_al *sdio_al; + +/* Static functions declaration */ +static int enable_eot_interrupt(struct sdio_al_device *sdio_al_dev, + int pipe_index, int enable); +static int enable_threshold_interrupt(struct sdio_al_device *sdio_al_dev, + int pipe_index, int enable); +static void sdio_func_irq(struct sdio_func *func); +static void sdio_al_timer_handler(unsigned long data); +static int get_min_poll_time_msec(struct sdio_al_device *sdio_al_dev); +static u32 check_pending_rx_packet(struct sdio_channel *ch, u32 eot); +static u32 remove_handled_rx_packet(struct sdio_channel *ch); +static int set_pipe_threshold(struct sdio_al_device *sdio_al_dev, + int pipe_index, int threshold); +static int sdio_al_wake_up(struct sdio_al_device *sdio_al_dev, + u32 not_from_int, struct sdio_channel *ch); +static int sdio_al_client_setup(struct sdio_al_device *sdio_al_dev); +static int enable_mask_irq(struct sdio_al_device *sdio_al_dev, + int func_num, int enable, u8 bit_offset); +static int sdio_al_enable_func_retry(struct sdio_func *func, const char *name); +static void sdio_al_print_info(void); +static int sdio_read_internal(struct sdio_channel *ch, void *data, int len); +static int sdio_read_from_closed_ch(struct sdio_channel *ch, int len); +static void stop_and_del_timer(struct sdio_al_device *sdio_al_dev); + +#define SDIO_AL_ERR(func) \ + do { \ + printk_once(KERN_ERR MODULE_NAME \ + ":In Error state, ignore %s\n", \ + func); \ + sdio_al_print_info(); \ + } while (0) + +#ifdef CONFIG_DEBUG_FS +static int debug_info_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t debug_info_write(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + sdio_al_print_info(); + return 1; +} + +const struct file_operations debug_info_ops = { + .open = debug_info_open, + .write = debug_info_write, +}; + +struct debugfs_blob_wrapper sdio_al_dbgfs_log[MAX_NUM_OF_SDIO_DEVICES + 1]; + +/* +* +* Trigger on/off for debug messages +* for trigger off the data messages debug level use: +* echo 0 > /sys/kernel/debugfs/sdio_al/debug_data_on +* for trigger on the data messages debug level use: +* echo 1 > /sys/kernel/debugfs/sdio_al/debug_data_on +* for trigger off the lpm messages debug level use: +* echo 0 > /sys/kernel/debugfs/sdio_al/debug_lpm_on +* for trigger on the lpm messages debug level use: +* echo 1 > /sys/kernel/debugfs/sdio_al/debug_lpm_on +*/ +static int sdio_al_debugfs_init(void) +{ + int i, blob_errs = 0; + + sdio_al->debug.sdio_al_debug_root = debugfs_create_dir("sdio_al", NULL); + if (!sdio_al->debug.sdio_al_debug_root) + return -ENOENT; + + sdio_al->debug.sdio_al_debug_lpm_on = debugfs_create_u8("debug_lpm_on", + S_IRUGO | S_IWUGO, + sdio_al->debug.sdio_al_debug_root, + &sdio_al->debug.debug_lpm_on); + + sdio_al->debug.sdio_al_debug_data_on = debugfs_create_u8( + "debug_data_on", + S_IRUGO | S_IWUGO, + sdio_al->debug.sdio_al_debug_root, + &sdio_al->debug.debug_data_on); + + sdio_al->debug.sdio_al_debug_close_on = debugfs_create_u8( + "debug_close_on", + S_IRUGO | S_IWUGO, + sdio_al->debug.sdio_al_debug_root, + &sdio_al->debug.debug_close_on); + + sdio_al->debug.sdio_al_debug_info = debugfs_create_file( + "sdio_debug_info", + S_IRUGO | S_IWUGO, + sdio_al->debug.sdio_al_debug_root, + NULL, + &debug_info_ops); + + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; ++i) { + char temp[18]; + + scnprintf(temp, 18, "sdio_al_log_dev_%d", i + 1); + sdio_al->debug.sdio_al_debug_log_buffers[i] = + debugfs_create_blob(temp, + S_IRUGO | S_IWUGO, + sdio_al->debug.sdio_al_debug_root, + &sdio_al_dbgfs_log[i]); + } + + sdio_al->debug.sdio_al_debug_log_buffers[MAX_NUM_OF_SDIO_DEVICES] = + debugfs_create_blob("sdio_al_gen_log", + S_IRUGO | S_IWUGO, + sdio_al->debug.sdio_al_debug_root, + &sdio_al_dbgfs_log[MAX_NUM_OF_SDIO_DEVICES]); + + for (i = 0; i < (MAX_NUM_OF_SDIO_DEVICES + 1); ++i) { + if (!sdio_al->debug.sdio_al_debug_log_buffers[i]) { + pr_err(MODULE_NAME ": Failed to create debugfs buffer" + " entry for " + "sdio_al->debug.sdio_al_debug_log_buffers[%d]", + i); + blob_errs = 1; + } + } + + if (blob_errs) { + for (i = 0; i < (MAX_NUM_OF_SDIO_DEVICES + 1); ++i) + if (sdio_al->debug.sdio_al_debug_log_buffers[i]) + debugfs_remove( + sdio_al-> + debug.sdio_al_debug_log_buffers[i]); + } + + + if ((!sdio_al->debug.sdio_al_debug_data_on) && + (!sdio_al->debug.sdio_al_debug_lpm_on) && + (!sdio_al->debug.sdio_al_debug_close_on) && + (!sdio_al->debug.sdio_al_debug_info) && + blob_errs) { + debugfs_remove(sdio_al->debug.sdio_al_debug_root); + sdio_al->debug.sdio_al_debug_root = NULL; + return -ENOENT; + } + + sdio_al_dbgfs_log[MAX_NUM_OF_SDIO_DEVICES].data = + sdio_al->gen_log.buffer; + sdio_al_dbgfs_log[MAX_NUM_OF_SDIO_DEVICES].size = + SDIO_AL_DEBUG_LOG_SIZE; + + return 0; +} + +static void sdio_al_debugfs_cleanup(void) +{ + int i; + + debugfs_remove(sdio_al->debug.sdio_al_debug_lpm_on); + debugfs_remove(sdio_al->debug.sdio_al_debug_data_on); + debugfs_remove(sdio_al->debug.sdio_al_debug_close_on); + debugfs_remove(sdio_al->debug.sdio_al_debug_info); + + for (i = 0; i < (MAX_NUM_OF_SDIO_DEVICES + 1); ++i) + debugfs_remove(sdio_al->debug.sdio_al_debug_log_buffers[i]); + + debugfs_remove(sdio_al->debug.sdio_al_debug_root); +} +#endif + +static int sdio_al_log(struct sdio_al_local_log *log, const char *fmt, ...) +{ + va_list args; + int r; + char *tp, *log_buf; + unsigned int *log_cur_pos; + struct timeval kt; + unsigned long flags; + static char sdio_al_log_tmp[SDIO_AL_DEBUG_TMP_LOG_SIZE]; + + spin_lock_irqsave(&log->log_lock, flags); + + kt = ktime_to_timeval(ktime_get()); + r = scnprintf(sdio_al_log_tmp, SDIO_AL_DEBUG_TMP_LOG_SIZE, + "[%8ld.%6ld] ", kt.tv_sec, kt.tv_usec); + + va_start(args, fmt); + r += vscnprintf(&sdio_al_log_tmp[r], (SDIO_AL_DEBUG_TMP_LOG_SIZE - r), + fmt, args); + va_end(args); + + log_buf = log->buffer; + log_cur_pos = &(log->buf_cur_pos); + + for (tp = sdio_al_log_tmp; tp < (sdio_al_log_tmp + r); tp++) { + log_buf[(*log_cur_pos)++] = *tp; + if ((*log_cur_pos) == SDIO_AL_DEBUG_LOG_SIZE) + *log_cur_pos = 0; + } + + spin_unlock_irqrestore(&log->log_lock, flags); + + return r; +} + +static int sdio_al_verify_func1(struct sdio_al_device *sdio_al_dev, + char const *func) +{ + if (sdio_al_dev == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "sdio_al_dev\n", func); + return -ENODEV; + } + + if (sdio_al_dev->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: Invalid " + "signature\n", func); + return -ENODEV; + } + + if (!sdio_al_dev->card) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: NULL " + "card\n", func); + return -ENODEV; + } + if (!sdio_al_dev->card->sdio_func[0]) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: NULL " + "func1\n", func); + return -ENODEV; + } + return 0; +} + +static int sdio_al_claim_mutex(struct sdio_al_device *sdio_al_dev, + char const *func) +{ + if (!sdio_al_dev) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "device\n", func); + return -ENODEV; + } + + if (sdio_al_dev->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: Invalid " + "device signature\n", func); + return -ENODEV; + } + + if (!sdio_al_dev->host) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: NULL " + "host\n", func); + return -ENODEV; + } + + mmc_claim_host(sdio_al_dev->host); + + return 0; +} + +static int sdio_al_release_mutex(struct sdio_al_device *sdio_al_dev, + char const *func) +{ + if (!sdio_al_dev) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "device\n", func); + return -ENODEV; + } + + if (sdio_al_dev->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: Invalid " + "device signature\n", func); + return -ENODEV; + } + + if (!sdio_al_dev->host) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: NULL " + "host\n", func); + return -ENODEV; + } + + mmc_release_host(sdio_al_dev->host); + + return 0; +} + +static int sdio_al_claim_mutex_and_verify_dev( + struct sdio_al_device *sdio_al_dev, + char const *func) +{ + if (sdio_al_claim_mutex(sdio_al_dev, __func__)) + return -ENODEV; + + if (sdio_al_dev->state != CARD_INSERTED) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: Invalid " + "device state %d\n", func, sdio_al_dev->state); + sdio_al_release_mutex(sdio_al_dev, __func__); + return -ENODEV; + } + + return 0; +} + +static void sdio_al_get_into_err_state(struct sdio_al_device *sdio_al_dev) +{ + if ((!sdio_al) || (!sdio_al_dev)) + return; + + sdio_al_dev->is_err = true; + sdio_al->debug.debug_data_on = 0; + sdio_al->debug.debug_lpm_on = 0; + sdio_al_print_info(); +} + +void sdio_al_register_lpm_cb(void *device_handle, + int(*lpm_callback)(void *, int)) +{ + struct sdio_al_device *sdio_al_dev = + (struct sdio_al_device *) device_handle; + + if (!sdio_al_dev) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s - " + "device_handle is NULL\n", __func__); + return; + } + + if (lpm_callback) { + sdio_al_dev->lpm_callback = lpm_callback; + lpm_callback((void *)sdio_al_dev, + sdio_al_dev->is_ok_to_sleep); + } + + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ": %s - device %d " + "registered for wakeup callback\n", __func__, + sdio_al_dev->host->index); +} + +void sdio_al_unregister_lpm_cb(void *device_handle) +{ + struct sdio_al_device *sdio_al_dev = + (struct sdio_al_device *) device_handle; + + if (!sdio_al_dev) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s - " + "device_handle is NULL\n", __func__); + return; + } + + sdio_al_dev->lpm_callback = NULL; + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ": %s - device %d " + "unregister for wakeup callback\n", __func__, + sdio_al_dev->host->index); +} + +static void sdio_al_vote_for_sleep(struct sdio_al_device *sdio_al_dev, + int is_vote_for_sleep) +{ + pr_debug(MODULE_NAME ": %s()", __func__); + + if (!sdio_al_dev) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s - sdio_al_dev" + " is NULL\n", __func__); + return; + } + + if (is_vote_for_sleep) { + pr_debug(MODULE_NAME ": %s - sdio vote for Sleep", __func__); + wake_unlock(&sdio_al_dev->wake_lock); + } else { + pr_debug(MODULE_NAME ": %s - sdio vote against sleep", + __func__); + wake_lock(&sdio_al_dev->wake_lock); + } + + if (sdio_al_dev->lpm_callback != NULL) { + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ": %s - " + "is_vote_for_sleep=%d for card#%d, " + "calling callback...", __func__, + is_vote_for_sleep, + sdio_al_dev->host->index); + sdio_al_dev->lpm_callback((void *)sdio_al_dev, + is_vote_for_sleep); + } +} + +/** + * Write SDIO-Client lpm information + * Should only be called with host claimed. + */ +static int write_lpm_info(struct sdio_al_device *sdio_al_dev) +{ + struct sdio_func *lpm_func = NULL; + int offset = offsetof(struct peer_sdioc_sw_mailbox, ch_config)+ + sizeof(struct peer_sdioc_channel_config) * + sdio_al_dev->lpm_chan+ + offsetof(struct peer_sdioc_channel_config, is_host_ok_to_sleep); + int ret; + + if (sdio_al_dev->lpm_chan == INVALID_SDIO_CHAN) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Invalid " + "lpm_chan for card %d\n", + sdio_al_dev->host->index); + return -EINVAL; + } + + if (!sdio_al_dev->card || + !sdio_al_dev->card->sdio_func[sdio_al_dev->lpm_chan+1]) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": NULL card or lpm_func\n"); + return -ENODEV; + } + lpm_func = sdio_al_dev->card->sdio_func[sdio_al_dev->lpm_chan+1]; + + pr_debug(MODULE_NAME ":write_lpm_info is_ok_to_sleep=%d, device %d\n", + sdio_al_dev->is_ok_to_sleep, + sdio_al_dev->host->index); + + ret = sdio_memcpy_toio(lpm_func, SDIOC_SW_MAILBOX_ADDR+offset, + &sdio_al_dev->is_ok_to_sleep, sizeof(u32)); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":failed to " + "write lpm info for card %d\n", + sdio_al_dev->host->index); + return ret; + } + + return 0; +} + +/* Set inactivity counter to intial value to allow clients come up */ +static inline void start_inactive_time(struct sdio_al_device *sdio_al_dev) +{ + sdio_al_dev->inactivity_time = jiffies + + msecs_to_jiffies(INITIAL_INACTIVITY_TIME_MSEC); +} + +static inline void restart_inactive_time(struct sdio_al_device *sdio_al_dev) +{ + sdio_al_dev->inactivity_time = jiffies + + msecs_to_jiffies(INACTIVITY_TIME_MSEC); +} + +static inline int is_inactive_time_expired(struct sdio_al_device *sdio_al_dev) +{ + return time_after(jiffies, sdio_al_dev->inactivity_time); +} + + +static int is_user_irq_enabled(struct sdio_al_device *sdio_al_dev, + int func_num) +{ + int ret = 0; + struct sdio_func *func1; + u32 user_irq = 0; + u32 addr = 0; + u32 offset = 0; + u32 masked_user_irq = 0; + + if (sdio_al_verify_func1(sdio_al_dev, __func__)) + return 0; + func1 = sdio_al_dev->card->sdio_func[0]; + + if (func_num < 4) { + addr = FUNC_1_4_USER_IRQ_ADDR; + offset = func_num * 8; + } else { + addr = FUNC_5_7_USER_IRQ_ADDR; + offset = (func_num - 4) * 8; + } + + user_irq = sdio_readl(func1, addr, &ret); + if (ret) { + pr_debug(MODULE_NAME ":read_user_irq fail\n"); + return 0; + } + + masked_user_irq = (user_irq >> offset) && 0xFF; + if (masked_user_irq == 0x1) { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":user_irq " + "enabled\n"); + return 1; + } + + return 0; +} + +static void sdio_al_sleep(struct sdio_al_device *sdio_al_dev, + struct mmc_host *host) +{ + int i; + + /* Go to sleep */ + pr_debug(MODULE_NAME ":Inactivity timer expired." + " Going to sleep\n"); + /* Stop mailbox timer */ + stop_and_del_timer(sdio_al_dev); + /* Make sure we get interrupt for non-packet-mode right away */ + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + struct sdio_channel *ch = &sdio_al_dev->channel[i]; + if ((ch->state != SDIO_CHANNEL_STATE_OPEN) && + (ch->state != SDIO_CHANNEL_STATE_CLOSED)) { + pr_debug(MODULE_NAME ":continue for channel %s in" + " state %d\n", ch->name, ch->state); + continue; + } + if (ch->is_packet_mode == false) { + ch->read_threshold = LOW_LATENCY_THRESHOLD; + set_pipe_threshold(sdio_al_dev, + ch->rx_pipe_index, + ch->read_threshold); + } + } + /* Prevent modem to go to sleep until we get the PROG_DONE on + the dummy CMD52 */ + msmsdcc_set_pwrsave(sdio_al_dev->host, 0); + /* Mark HOST_OK_TOSLEEP */ + sdio_al_dev->is_ok_to_sleep = 1; + write_lpm_info(sdio_al_dev); + + msmsdcc_lpm_enable(host); + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":Finished sleep sequence" + " for card %d. Sleep now.\n", + sdio_al_dev->host->index); + /* Release wakelock */ + sdio_al_vote_for_sleep(sdio_al_dev, 1); +} + + +/** + * Read SDIO-Client Mailbox from Function#1.thresh_pipe + * + * The mailbox contain the bytes available per pipe, + * and the End-Of-Transfer indication per pipe (if available). + * + * WARNING: Each time the Mailbox is read from the client, the + * read_bytes_avail is incremented with another pending + * transfer. Therefore, a pending rx-packet should be added to a + * list before the next read of the mailbox. + * + * This function should run from a workqueue context since it + * notifies the clients. + * + * This function assumes that sdio_al_claim_mutex was called before + * calling it. + * + */ +static int read_mailbox(struct sdio_al_device *sdio_al_dev, int from_isr) +{ + int ret; + struct sdio_func *func1 = NULL; + struct sdio_mailbox *mailbox = sdio_al_dev->mailbox; + struct mmc_host *host = sdio_al_dev->host; + u32 new_write_avail = 0; + u32 old_write_avail = 0; + u32 any_read_avail = 0; + u32 any_write_pending = 0; + int i; + u32 rx_notify_bitmask = 0; + u32 tx_notify_bitmask = 0; + u32 eot_pipe = 0; + u32 thresh_pipe = 0; + u32 overflow_pipe = 0; + u32 underflow_pipe = 0; + u32 thresh_intr_mask = 0; + int is_closing = 0; + + if (sdio_al_dev->is_err) { + SDIO_AL_ERR(__func__); + return 0; + } + + if (sdio_al_verify_func1(sdio_al_dev, __func__)) + return -ENODEV; + func1 = sdio_al_dev->card->sdio_func[0]; + + pr_debug(MODULE_NAME ":start %s from_isr = %d for card %d.\n" + , __func__, from_isr, sdio_al_dev->host->index); + + pr_debug(MODULE_NAME ":before sdio_memcpy_fromio.\n"); + memset(mailbox, 0, sizeof(struct sdio_mailbox)); + ret = sdio_memcpy_fromio(func1, mailbox, + HW_MAILBOX_ADDR, sizeof(*mailbox)); + pr_debug(MODULE_NAME ":after sdio_memcpy_fromio.\n"); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to read " + "Mailbox for card %d, goto error state\n", + sdio_al_dev->host->index); + sdio_al_get_into_err_state(sdio_al_dev); + goto exit_err; + } + + eot_pipe = (mailbox->eot_pipe_0_7) | + (mailbox->eot_pipe_8_15<<8); + thresh_pipe = (mailbox->thresh_above_limit_pipe_0_7) | + (mailbox->thresh_above_limit_pipe_8_15<<8); + + overflow_pipe = (mailbox->overflow_pipe_0_7) | + (mailbox->overflow_pipe_8_15<<8); + underflow_pipe = mailbox->underflow_pipe_0_7 | + (mailbox->underflow_pipe_8_15<<8); + thresh_intr_mask = + (mailbox->mask_thresh_above_limit_pipe_0_7) | + (mailbox->mask_thresh_above_limit_pipe_8_15<<8); + + if (overflow_pipe || underflow_pipe) + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Mailbox ERROR " + "overflow=0x%x, underflow=0x%x\n", + overflow_pipe, underflow_pipe); + + /* In case of modem reset we would like to read the daya from the modem + to clear the interrupts but do not process it */ + if (sdio_al_dev->state != CARD_INSERTED) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":sdio_al_device" + " (card %d) is in invalid state %d\n", + sdio_al_dev->host->index, + sdio_al_dev->state); + return -ENODEV; + } + + pr_debug(MODULE_NAME ":card %d: eot=0x%x, thresh=0x%x\n", + sdio_al_dev->host->index, + eot_pipe, thresh_pipe); + + /* Scan for Rx Packets available and update read available bytes */ + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + struct sdio_channel *ch = &sdio_al_dev->channel[i]; + u32 old_read_avail; + u32 read_avail; + u32 new_packet_size = 0; + + if (ch->state == SDIO_CHANNEL_STATE_CLOSING) + is_closing = true; /* used to prevent sleep */ + + old_read_avail = ch->read_avail; + read_avail = mailbox->pipe_bytes_avail[ch->rx_pipe_index]; + + if ((ch->state == SDIO_CHANNEL_STATE_CLOSED) && + (read_avail > 0)) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ":%s: Invalid read_avail 0x%x, for CLOSED ch %s\n", + __func__, read_avail, ch->name); + sdio_read_from_closed_ch(ch, read_avail); + } + if ((ch->state != SDIO_CHANNEL_STATE_OPEN) && + (ch->state != SDIO_CHANNEL_STATE_CLOSING)) + continue; + + if (read_avail > INVALID_DATA_AVAILABLE) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ":Invalid read_avail 0x%x for pipe %d\n", + read_avail, ch->rx_pipe_index); + continue; + } + any_read_avail |= read_avail | old_read_avail; + ch->statistics.last_any_read_avail = any_read_avail; + ch->statistics.last_read_avail = read_avail; + ch->statistics.last_old_read_avail = old_read_avail; + + if (ch->is_packet_mode) { + if ((eot_pipe & (1<rx_pipe_index)) && + sdio_al_dev->print_after_interrupt) { + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME + ":Interrupt on ch %s, " + "card %d", ch->name, + sdio_al_dev->host->index); + } + new_packet_size = check_pending_rx_packet(ch, eot_pipe); + } else { + if ((thresh_pipe & (1<rx_pipe_index)) && + sdio_al_dev->print_after_interrupt) { + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME + ":Interrupt on ch %s, " + "card %d", ch->name, + sdio_al_dev->host->index); + } + ch->read_avail = read_avail; + + /* + * Restore default thresh for non packet channels. + * in case it IS low latency channel then read_threshold + * and def_read_threshold are both + * LOW_LATENCY_THRESHOLD + */ + if ((ch->read_threshold != ch->def_read_threshold) && + (read_avail >= ch->threshold_change_cnt)) { + if (!ch->is_low_latency_ch) { + ch->read_threshold = + ch->def_read_threshold; + set_pipe_threshold(sdio_al_dev, + ch->rx_pipe_index, + ch->read_threshold); + } + } + } + + if ((ch->is_packet_mode) && (new_packet_size > 0)) { + rx_notify_bitmask |= (1<num); + ch->statistics.total_notifs++; + } + + if ((!ch->is_packet_mode) && (ch->read_avail > 0) && + (old_read_avail == 0)) { + rx_notify_bitmask |= (1<num); + ch->statistics.total_notifs++; + } + } + sdio_al_dev->print_after_interrupt = 0; + + /* Update Write available */ + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + struct sdio_channel *ch = &sdio_al_dev->channel[i]; + + if ((ch->state != SDIO_CHANNEL_STATE_OPEN) && + (ch->state != SDIO_CHANNEL_STATE_CLOSING)) + continue; + + new_write_avail = mailbox->pipe_bytes_avail[ch->tx_pipe_index]; + + if (new_write_avail > INVALID_DATA_AVAILABLE) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ":Invalid write_avail 0x%x for pipe %d\n", + new_write_avail, ch->tx_pipe_index); + continue; + } + + old_write_avail = ch->write_avail; + ch->write_avail = new_write_avail; + + if ((old_write_avail <= ch->min_write_avail) && + (new_write_avail >= ch->min_write_avail)) + tx_notify_bitmask |= (1<num); + + /* There is not enough write avail for this channel. + We need to keep reading mailbox to wait for the appropriate + write avail and cannot sleep. Ignore SMEM channel that has + only one direction. */ + if (strncmp(ch->name, "SDIO_SMEM", CHANNEL_NAME_SIZE)) + any_write_pending |= + (new_write_avail < ch->ch_config.max_tx_threshold); + } + /* notify clients */ + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + struct sdio_channel *ch = &sdio_al_dev->channel[i]; + + if ((ch->state != SDIO_CHANNEL_STATE_OPEN) || + (ch->notify == NULL)) + continue; + + if (rx_notify_bitmask & (1<num)) + ch->notify(ch->priv, + SDIO_EVENT_DATA_READ_AVAIL); + + if (tx_notify_bitmask & (1<num)) + ch->notify(ch->priv, + SDIO_EVENT_DATA_WRITE_AVAIL); + } + + + if ((rx_notify_bitmask == 0) && (tx_notify_bitmask == 0) && + !any_read_avail && !any_write_pending) { + DATA_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":Nothing to " + "Notify for card %d, is_closing=%d\n", + sdio_al_dev->host->index, is_closing); + if (is_closing) + restart_inactive_time(sdio_al_dev); + else if (is_inactive_time_expired(sdio_al_dev)) + sdio_al_sleep(sdio_al_dev, host); + } else { + DATA_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":Notify bitmask" + " for card %d rx=0x%x, tx=0x%x.\n", + sdio_al_dev->host->index, + rx_notify_bitmask, tx_notify_bitmask); + /* Restart inactivity timer if any activity on the channel */ + restart_inactive_time(sdio_al_dev); + } + + pr_debug(MODULE_NAME ":end %s.\n", __func__); + +exit_err: + return ret; +} + +/** + * Check pending rx packet when reading the mailbox. + */ +static u32 check_pending_rx_packet(struct sdio_channel *ch, u32 eot) +{ + u32 rx_pending; + u32 rx_avail; + u32 new_packet_size = 0; + struct sdio_al_device *sdio_al_dev = ch->sdio_al_dev; + + + if (sdio_al_dev == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": NULL sdio_al_dev" + " for channel %s\n", ch->name); + return -EINVAL; + } + + mutex_lock(&ch->ch_lock); + + rx_pending = ch->rx_pending_bytes; + rx_avail = sdio_al_dev->mailbox->pipe_bytes_avail[ch->rx_pipe_index]; + + pr_debug(MODULE_NAME ":pipe %d of card %d rx_avail=0x%x, " + "rx_pending=0x%x\n", + ch->rx_pipe_index, sdio_al_dev->host->index, rx_avail, + rx_pending); + + + /* new packet detected */ + if (eot & (1<rx_pipe_index)) { + struct rx_packet_size *p = NULL; + new_packet_size = rx_avail - rx_pending; + + if ((rx_avail <= rx_pending)) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": Invalid new packet size." + " rx_avail=%d.\n", rx_avail); + new_packet_size = 0; + goto exit_err; + } + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (p == NULL) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": failed to allocate item for " + "rx_pending list. rx_avail=%d, " + "rx_pending=%d.\n", + rx_avail, rx_pending); + new_packet_size = 0; + goto exit_err; + } + p->size = new_packet_size; + /* Add new packet as last */ + list_add_tail(&p->list, &ch->rx_size_list_head); + ch->rx_pending_bytes += new_packet_size; + + if (ch->read_avail == 0) + ch->read_avail = new_packet_size; + } + +exit_err: + mutex_unlock(&ch->ch_lock); + + return new_packet_size; +} + + + +/** + * Remove first pending packet from the list. + */ +static u32 remove_handled_rx_packet(struct sdio_channel *ch) +{ + struct rx_packet_size *p = NULL; + + mutex_lock(&ch->ch_lock); + + ch->rx_pending_bytes -= ch->read_avail; + + if (!list_empty(&ch->rx_size_list_head)) { + p = list_first_entry(&ch->rx_size_list_head, + struct rx_packet_size, list); + list_del(&p->list); + kfree(p); + } else { + sdio_al_loge(ch->sdio_al_dev->dev_log, MODULE_NAME ":%s: ch " + "%s: unexpected empty list!!\n", + __func__, ch->name); + } + + if (list_empty(&ch->rx_size_list_head)) { + ch->read_avail = 0; + } else { + p = list_first_entry(&ch->rx_size_list_head, + struct rx_packet_size, list); + ch->read_avail = p->size; + } + + mutex_unlock(&ch->ch_lock); + + return ch->read_avail; +} + + +/** + * Bootloader worker function. + * + * @note: clear the bootloader_done flag only after reading the + * mailbox, to ignore more requests while reading the mailbox. + */ +static void boot_worker(struct work_struct *work) +{ + int ret = 0; + int func_num = 0; + int i; + struct sdio_al_device *sdio_al_dev = NULL; + struct sdio_al_work *sdio_al_work = container_of(work, + struct sdio_al_work, + work); + + if (sdio_al_work == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "sdio_al_work\n", __func__); + return; + } + + sdio_al_dev = sdio_al_work->sdio_al_dev; + if (sdio_al_dev == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "sdio_al_dev\n", __func__); + return; + } + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":Bootloader Worker Started" + ", wait for bootloader_done event..\n"); + wait_event(sdio_al_dev->wait_mbox, + sdio_al_dev->bootloader_done); + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":Got bootloader_done " + "event..\n"); + /* Do polling until MDM is up */ + for (i = 0; i < 5000; ++i) { + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return; + if (is_user_irq_enabled(sdio_al_dev, func_num)) { + sdio_al_release_mutex(sdio_al_dev, __func__); + sdio_al_dev->bootloader_done = 0; + ret = sdio_al_client_setup(sdio_al_dev); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": sdio_al_client_setup failed, " + "for card %d ret=%d\n", + sdio_al_dev->host->index, ret); + sdio_al_get_into_err_state(sdio_al_dev); + } + goto done; + } + sdio_al_release_mutex(sdio_al_dev, __func__); + msleep(100); + } + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Timeout waiting for " + "user_irq for card %d\n", + sdio_al_dev->host->index); + sdio_al_get_into_err_state(sdio_al_dev); + +done: + pr_debug(MODULE_NAME ":Boot Worker for card %d Exit!\n", + sdio_al_dev->host->index); +} + +/** + * Worker function. + * + * @note: clear the ask_mbox flag only after + * reading the mailbox, to ignore more requests while + * reading the mailbox. + */ +static void worker(struct work_struct *work) +{ + int ret = 0; + struct sdio_al_device *sdio_al_dev = NULL; + struct sdio_al_work *sdio_al_work = container_of(work, + struct sdio_al_work, + work); + if (sdio_al_work == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": worker: NULL " + "sdio_al_work\n"); + return; + } + + sdio_al_dev = sdio_al_work->sdio_al_dev; + if (sdio_al_dev == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": worker: NULL " + "sdio_al_dev\n"); + return; + } + pr_debug(MODULE_NAME ":Worker Started..\n"); + while ((sdio_al_dev->is_ready) && (ret == 0)) { + pr_debug(MODULE_NAME ":Wait for read mailbox request..\n"); + wait_event(sdio_al_dev->wait_mbox, sdio_al_dev->ask_mbox); + if (!sdio_al_dev->is_ready) + break; + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + break; + if (sdio_al_dev->is_ok_to_sleep) { + ret = sdio_al_wake_up(sdio_al_dev, 1, NULL); + if (ret) { + sdio_al_release_mutex(sdio_al_dev, __func__); + return; + } + } + ret = read_mailbox(sdio_al_dev, false); + sdio_al_release_mutex(sdio_al_dev, __func__); + sdio_al_dev->ask_mbox = false; + } + + pr_debug(MODULE_NAME ":Worker Exit!\n"); +} + +/** + * Write command using CMD54 rather than CMD53. + * Writing with CMD54 generate EOT interrupt at the + * SDIO-Client. + * Based on mmc_io_rw_extended() + */ +static int sdio_write_cmd54(struct mmc_card *card, unsigned fn, + unsigned addr, const u8 *buf, + unsigned blocks, unsigned blksz) +{ + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg; + int incr_addr = 1; /* MUST */ + int write = 1; + + BUG_ON(!card); + BUG_ON(fn > 7); + BUG_ON(blocks == 1 && blksz > 512); + WARN_ON(blocks == 0); + WARN_ON(blksz == 0); + + write = true; + pr_debug(MODULE_NAME ":sdio_write_cmd54()" + "fn=%d,buf=0x%x,blocks=%d,blksz=%d\n", + fn, (u32) buf, blocks, blksz); + + memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = SD_IO_RW_EXTENDED_QCOM; + + cmd.arg = write ? 0x80000000 : 0x00000000; + cmd.arg |= fn << 28; + cmd.arg |= incr_addr ? 0x04000000 : 0x00000000; + cmd.arg |= addr << 9; + if (blocks == 1 && blksz <= 512) + cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */ + else + cmd.arg |= 0x08000000 | blocks; /* block mode */ + cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + + data.blksz = blksz; + data.blocks = blocks; + data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, buf, blksz * blocks); + + mmc_set_data_timeout(&data, card); + + mmc_wait_for_req(card->host, &mrq); + + if (cmd.error) + return cmd.error; + if (data.error) + return data.error; + + if (mmc_host_is_spi(card->host)) { + /* host driver already reported errors */ + } else { + if (cmd.resp[0] & R5_ERROR) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME + ":%s: R5_ERROR for card %d", + __func__, card->host->index); + return -EIO; + } + if (cmd.resp[0] & R5_FUNCTION_NUMBER) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME + ":%s: R5_FUNCTION_NUMBER for card %d", + __func__, card->host->index); + return -EINVAL; + } + if (cmd.resp[0] & R5_OUT_OF_RANGE) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME + ":%s: R5_OUT_OF_RANGE for card %d", + __func__, card->host->index); + return -ERANGE; + } + } + + return 0; +} + + +/** + * Write data to channel. + * Handle different data size types. + * + */ +static int sdio_ch_write(struct sdio_channel *ch, const u8 *buf, u32 len) +{ + int ret = 0; + unsigned blksz = ch->func->cur_blksize; + int blocks = len / blksz; + int remain_bytes = len % blksz; + struct mmc_card *card = NULL; + u32 fn = ch->func->num; + + if (!ch) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "channel\n", __func__); + return -ENODEV; + } + + if (!ch->sdio_al_dev) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "sdio_al_dev\n", __func__); + return -ENODEV; + } + + if (len == 0) { + sdio_al_loge(ch->sdio_al_dev->dev_log, MODULE_NAME ":channel " + "%s trying to write 0 bytes\n", ch->name); + return -EINVAL; + } + + card = ch->func->card; + + if (remain_bytes) { + /* CMD53 */ + if (blocks) { + ret = sdio_memcpy_toio(ch->func, PIPE_TX_FIFO_ADDR, + (void *) buf, blocks*blksz); + if (ret != 0) { + sdio_al_loge(ch->sdio_al_dev->dev_log, + MODULE_NAME ":%s: sdio_memcpy_toio " + "failed for channel %s\n", + __func__, ch->name); + sdio_al_get_into_err_state(ch->sdio_al_dev); + return ret; + } + } + + buf += (blocks*blksz); + + ret = sdio_write_cmd54(card, fn, PIPE_TX_FIFO_ADDR, + buf, 1, remain_bytes); + } else { + ret = sdio_write_cmd54(card, fn, PIPE_TX_FIFO_ADDR, + buf, blocks, blksz); + } + + if (ret != 0) { + sdio_al_loge(ch->sdio_al_dev->dev_log, MODULE_NAME ":%s: " + "sdio_write_cmd54 failed for channel %s\n", + __func__, ch->name); + ch->sdio_al_dev->is_err = true; + return ret; + } + + return ret; +} + +static int sdio_al_bootloader_completed(void) +{ + int i; + + pr_debug(MODULE_NAME ":sdio_al_bootloader_completed was called\n"); + + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; ++i) { + struct sdio_al_device *dev = NULL; + if (sdio_al->devices[i] == NULL) + continue; + dev = sdio_al->devices[i]; + dev->bootloader_done = 1; + wake_up(&dev->wait_mbox); + } + + return 0; +} + +static int sdio_al_wait_for_bootloader_comp(struct sdio_al_device *sdio_al_dev) +{ + int ret = 0; + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + /* + * Enable function 0 interrupt mask to allow 9k to raise this interrupt + * in power-up. When sdio_downloader will notify its completion + * we will poll on this interrupt to wait for 9k power-up + */ + ret = enable_mask_irq(sdio_al_dev, 0, 1, 0); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": Enable_mask_irq for card %d failed, " + "ret=%d\n", + sdio_al_dev->host->index, ret); + sdio_al_release_mutex(sdio_al_dev, __func__); + return ret; + } + + sdio_al_release_mutex(sdio_al_dev, __func__); + + /* + * Start bootloader worker that will wait for the bootloader + * completion + */ + sdio_al_dev->boot_work.sdio_al_dev = sdio_al_dev; + INIT_WORK(&sdio_al_dev->boot_work.work, boot_worker); + sdio_al_dev->bootloader_done = 0; + queue_work(sdio_al_dev->workqueue, &sdio_al_dev->boot_work.work); + + return 0; +} + +static int sdio_al_bootloader_setup(void) +{ + int ret = 0; + struct sdio_al_device *bootloader_dev = sdio_al->bootloader_dev; + struct sdio_func *func1 = NULL; + + if (sdio_al_claim_mutex_and_verify_dev(bootloader_dev, __func__)) + return -ENODEV; + + if (bootloader_dev->flashless_boot_on) { + sdio_al_loge(bootloader_dev->dev_log, MODULE_NAME ":Already " + "in boot process.\n"); + sdio_al_release_mutex(bootloader_dev, __func__); + return 0; + } + + bootloader_dev->sdioc_boot_sw_header + = kzalloc(sizeof(*bootloader_dev->sdioc_boot_sw_header), + GFP_KERNEL); + if (bootloader_dev->sdioc_boot_sw_header == NULL) { + sdio_al_loge(bootloader_dev->dev_log, MODULE_NAME ":fail to " + "allocate sdioc boot sw header.\n"); + sdio_al_release_mutex(bootloader_dev, __func__); + return -ENOMEM; + } + + if (sdio_al_verify_func1(bootloader_dev, __func__)) { + sdio_al_release_mutex(bootloader_dev, __func__); + goto exit_err; + } + func1 = bootloader_dev->card->sdio_func[0]; + + ret = sdio_memcpy_fromio(func1, + bootloader_dev->sdioc_boot_sw_header, + SDIOC_SW_HEADER_ADDR, + sizeof(struct peer_sdioc_boot_sw_header)); + if (ret) { + sdio_al_loge(bootloader_dev->dev_log, MODULE_NAME ":fail to " + "read sdioc boot sw header.\n"); + sdio_al_release_mutex(bootloader_dev, __func__); + goto exit_err; + } + + if (bootloader_dev->sdioc_boot_sw_header->signature != + (u32) PEER_SDIOC_SW_MAILBOX_BOOT_SIGNATURE) { + sdio_al_loge(bootloader_dev->dev_log, MODULE_NAME ":invalid " + "mailbox signature 0x%x.\n", + bootloader_dev->sdioc_boot_sw_header->signature); + sdio_al_release_mutex(bootloader_dev, __func__); + ret = -EINVAL; + goto exit_err; + } + + /* Upper byte has to be equal - no backward compatibility for unequal */ + if ((bootloader_dev->sdioc_boot_sw_header->version >> 16) != + (sdio_al->pdata->peer_sdioc_boot_version_major)) { + sdio_al_loge(bootloader_dev->dev_log, MODULE_NAME ": HOST(0x%x)" + " and CLIENT(0x%x) SDIO_AL BOOT VERSION don't match\n", + ((sdio_al->pdata->peer_sdioc_boot_version_major<<16)+ + sdio_al->pdata->peer_sdioc_boot_version_minor), + bootloader_dev->sdioc_boot_sw_header->version); + sdio_al_release_mutex(bootloader_dev, __func__); + ret = -EIO; + goto exit_err; + } + + sdio_al_logi(bootloader_dev->dev_log, MODULE_NAME ": SDIOC BOOT SW " + "version 0x%x\n", + bootloader_dev->sdioc_boot_sw_header->version); + + bootloader_dev->flashless_boot_on = true; + + sdio_al_release_mutex(bootloader_dev, __func__); + + ret = sdio_al_wait_for_bootloader_comp(bootloader_dev); + if (ret) { + sdio_al_loge(bootloader_dev->dev_log, MODULE_NAME + ": sdio_al_wait_for_bootloader_comp failed, " + "err=%d\n", ret); + goto exit_err; + } + + ret = sdio_downloader_setup(bootloader_dev->card, 1, + bootloader_dev->sdioc_boot_sw_header->boot_ch_num, + sdio_al_bootloader_completed); + + if (ret) { + sdio_al_loge(bootloader_dev->dev_log, MODULE_NAME + ": sdio_downloader_setup failed, err=%d\n", ret); + goto exit_err; + } + + sdio_al_logi(bootloader_dev->dev_log, MODULE_NAME ":In Flashless boot," + " waiting for its completion\n"); + + +exit_err: + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":free " + "sdioc_boot_sw_header.\n"); + kfree(bootloader_dev->sdioc_boot_sw_header); + bootloader_dev->sdioc_boot_sw_header = NULL; + bootloader_dev = NULL; + + return ret; +} + + +/** + * Read SDIO-Client software header + * + */ +static int read_sdioc_software_header(struct sdio_al_device *sdio_al_dev, + struct peer_sdioc_sw_header *header) +{ + int ret; + int i; + int test_version = 0; + int sdioc_test_version = 0; + struct sdio_func *func1 = NULL; + + pr_debug(MODULE_NAME ":reading sdioc sw header.\n"); + + if (sdio_al_verify_func1(sdio_al_dev, __func__)) + return -ENODEV; + + func1 = sdio_al_dev->card->sdio_func[0]; + + ret = sdio_memcpy_fromio(func1, header, + SDIOC_SW_HEADER_ADDR, sizeof(*header)); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":fail to read " + "sdioc sw header.\n"); + goto exit_err; + } + + if (header->signature == (u32)PEER_SDIOC_SW_MAILBOX_UT_SIGNATURE) { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":SDIOC SW " + "unittest signature. 0x%x\n", + header->signature); + sdio_al->unittest_mode = true; + /* Verify test code compatibility with the modem */ + sdioc_test_version = (header->version & 0xFF00) >> 8; + test_version = sdio_al->pdata->peer_sdioc_version_minor >> 8; + if (test_version != sdioc_test_version) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": HOST(0x%x) and CLIENT(0x%x) " + "testing VERSION don't match\n", + test_version, + sdioc_test_version); + msleep(500); + BUG(); + } + } + + if ((header->signature != (u32) PEER_SDIOC_SW_MAILBOX_SIGNATURE) && + (header->signature != (u32) PEER_SDIOC_SW_MAILBOX_UT_SIGNATURE)) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":SDIOC SW " + "invalid signature. 0x%x\n", header->signature); + goto exit_err; + } + /* Upper byte has to be equal - no backward compatibility for unequal */ + sdio_al->sdioc_major = header->version >> 16; + if (sdio_al->pdata->allow_sdioc_version_major_2) { + if ((sdio_al->sdioc_major != + sdio_al->pdata->peer_sdioc_version_major) && + (sdio_al->sdioc_major != PEER_SDIOC_OLD_VERSION_MAJOR)) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": HOST(0x%x) and CLIENT(0x%x) " + "SDIO_AL VERSION don't match\n", + ((sdio_al->pdata->peer_sdioc_version_major<<16)+ + sdio_al->pdata->peer_sdioc_version_minor), + header->version); + goto exit_err; + } + } else { + if (sdio_al->sdioc_major != + sdio_al->pdata->peer_sdioc_version_major) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": HOST(0x%x) and CLIENT(0x%x) " + "SDIO_AL VERSION don't match\n", + ((sdio_al->pdata->peer_sdioc_version_major<<16)+ + sdio_al->pdata->peer_sdioc_version_minor), + header->version); + goto exit_err; + } + } + sdio_al_dev->ch_close_supported = (header->version & 0x000F) >= + (sdio_al->pdata->peer_sdioc_version_minor & 0xF); + + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":SDIOC SW version 0x%x," + " sdio_al major 0x%x minor 0x%x\n", header->version, + sdio_al->sdioc_major, + sdio_al->pdata->peer_sdioc_version_minor); + + sdio_al_dev->flashless_boot_on = false; + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + struct sdio_channel *ch = &sdio_al_dev->channel[i]; + + /* Set default values */ + ch->read_threshold = DEFAULT_READ_THRESHOLD; + ch->write_threshold = DEFAULT_WRITE_THRESHOLD; + ch->min_write_avail = DEFAULT_MIN_WRITE_THRESHOLD; + ch->is_packet_mode = true; + ch->peer_tx_buf_size = DEFAULT_PEER_TX_BUF_SIZE; + ch->poll_delay_msec = 0; + + ch->num = i; + ch->func = NULL; + ch->rx_pipe_index = ch->num*2; + ch->tx_pipe_index = ch->num*2+1; + + memset(ch->name, 0, sizeof(ch->name)); + + if (header->channel_names[i][0]) { + memcpy(ch->name, SDIO_PREFIX, + strlen(SDIO_PREFIX)); + memcpy(ch->name + strlen(SDIO_PREFIX), + header->channel_names[i], + PEER_CHANNEL_NAME_SIZE); + + ch->state = SDIO_CHANNEL_STATE_IDLE; + ch->sdio_al_dev = sdio_al_dev; + if (sdio_al_dev->card->sdio_func[ch->num+1]) { + ch->func = + sdio_al_dev->card->sdio_func[ch->num+1]; + } else { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": NULL func for channel %s\n", + ch->name); + goto exit_err; + } + } else { + ch->state = SDIO_CHANNEL_STATE_INVALID; + } + + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":Channel=%s, " + "state=%d\n", ch->name, ch->state); + } + + return 0; + +exit_err: + sdio_al_get_into_err_state(sdio_al_dev); + memset(header, 0, sizeof(*header)); + + return -EIO; +} + +/** + * Read SDIO-Client channel configuration + * + */ +static int read_sdioc_channel_config(struct sdio_channel *ch) +{ + int ret; + struct peer_sdioc_sw_mailbox *sw_mailbox = NULL; + struct peer_sdioc_channel_config *ch_config = NULL; + struct sdio_al_device *sdio_al_dev = ch->sdio_al_dev; + + if (sdio_al_dev == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": NULL sdio_al_dev" + " for channel %s\n", ch->name); + return -EINVAL; + } + + if (sdio_al_dev->sdioc_sw_header->version == 0) + return -1; + + pr_debug(MODULE_NAME ":reading sw mailbox %s channel.\n", ch->name); + + sw_mailbox = kzalloc(sizeof(*sw_mailbox), GFP_KERNEL); + if (sw_mailbox == NULL) + return -ENOMEM; + + ret = sdio_memcpy_fromio(ch->func, sw_mailbox, + SDIOC_SW_MAILBOX_ADDR, sizeof(*sw_mailbox)); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":fail to read " + "sw mailbox.\n"); + goto exit_err; + } + + ch_config = &sw_mailbox->ch_config[ch->num]; + memcpy(&ch->ch_config, ch_config, + sizeof(struct peer_sdioc_channel_config)); + + if (!ch_config->is_ready) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":sw mailbox " + "channel not ready.\n"); + goto exit_err; + } + + ch->read_threshold = LOW_LATENCY_THRESHOLD; + ch->is_low_latency_ch = ch_config->is_low_latency_ch; + /* Threshold on 50% of the maximum size , sdioc uses double-buffer */ + ch->write_threshold = (ch_config->max_tx_threshold * 5) / 10; + ch->threshold_change_cnt = ch->ch_config.max_rx_threshold - + ch->read_threshold + THRESHOLD_CHANGE_EXTRA_BYTES; + + if (ch->is_low_latency_ch) + ch->def_read_threshold = LOW_LATENCY_THRESHOLD; + else /* Aggregation up to 90% of the maximum size */ + ch->def_read_threshold = (ch_config->max_rx_threshold * 9) / 10; + + ch->is_packet_mode = ch_config->is_packet_mode; + if (!ch->is_packet_mode) { + ch->poll_delay_msec = DEFAULT_POLL_DELAY_NOPACKET_MSEC; + ch->min_write_avail = DEFAULT_MIN_WRITE_THRESHOLD_STREAMING; + } + /* The max_packet_size is set by the modem in version 3 and on */ + if (sdio_al->sdioc_major > PEER_SDIOC_OLD_VERSION_MAJOR) + ch->min_write_avail = ch_config->max_packet_size; + + if (ch->min_write_avail > ch->write_threshold) + ch->min_write_avail = ch->write_threshold; + + CLOSE_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":ch %s " + "read_threshold=%d, write_threshold=%d," + " min_write_avail=%d, max_rx_threshold=%d," + " max_tx_threshold=%d\n", ch->name, ch->read_threshold, + ch->write_threshold, ch->min_write_avail, + ch_config->max_rx_threshold, + ch_config->max_tx_threshold); + + ch->peer_tx_buf_size = ch_config->tx_buf_size; + + kfree(sw_mailbox); + + return 0; + +exit_err: + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":Reading SW Mailbox " + "error.\n"); + kfree(sw_mailbox); + + return -1; +} + + +/** + * Enable/Disable EOT interrupt of a pipe. + * + */ +static int enable_eot_interrupt(struct sdio_al_device *sdio_al_dev, + int pipe_index, int enable) +{ + int ret = 0; + struct sdio_func *func1; + u32 mask; + u32 pipe_mask; + u32 addr; + + if (sdio_al_verify_func1(sdio_al_dev, __func__)) + return -ENODEV; + func1 = sdio_al_dev->card->sdio_func[0]; + + if (pipe_index < 8) { + addr = PIPES_0_7_IRQ_MASK_ADDR; + pipe_mask = (1<card->sdio_func[0]; + + if (func_num < 4) { + addr = FUNC_1_4_MASK_IRQ_ADDR; + offset = func_num * 8 + bit_offset; + } else { + addr = FUNC_5_7_MASK_IRQ_ADDR; + offset = (func_num - 4) * 8 + bit_offset; + } + + func_mask = 1<dev_log, MODULE_NAME ": " + "enable_mask_irq fail\n"); + goto exit_err; + } + + if (enable) + mask &= (~func_mask); /* 0 = enable */ + else + mask |= (func_mask); /* 1 = disable */ + + pr_debug(MODULE_NAME ":enable_mask_irq, writing mask = 0x%x\n", mask); + + sdio_writel(func1, mask, addr, &ret); + +exit_err: + return ret; +} + +/** + * Enable/Disable Threshold interrupt of a pipe. + * + */ +static int enable_threshold_interrupt(struct sdio_al_device *sdio_al_dev, + int pipe_index, int enable) +{ + int ret = 0; + struct sdio_func *func1; + u32 mask; + u32 pipe_mask; + u32 addr; + + if (sdio_al_verify_func1(sdio_al_dev, __func__)) + return -ENODEV; + func1 = sdio_al_dev->card->sdio_func[0]; + + if (pipe_index < 8) { + addr = PIPES_0_7_IRQ_MASK_ADDR; + pipe_mask = (1<card->sdio_func[0]; + + sdio_writel(func1, threshold, + PIPES_THRESHOLD_ADDR+pipe_index*4, &ret); + if (ret) + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": " + "set_pipe_threshold err=%d\n", -ret); + + return ret; +} + +/** + * Enable func w/ retries + * + */ +static int sdio_al_enable_func_retry(struct sdio_func *func, const char *name) +{ + int ret, i; + for (i = 0; i < 200; i++) { + ret = sdio_enable_func(func); + if (ret) { + pr_debug(MODULE_NAME ":retry enable %s func#%d " + "ret=%d\n", + name, func->num, ret); + msleep(10); + } else + break; + } + + return ret; +} + +/** + * Open Channel + * + * 1. Init Channel Context. + * 2. Init the Channel SDIO-Function. + * 3. Init the Channel Pipes on Mailbox. + */ +static int open_channel(struct sdio_channel *ch) +{ + int ret = 0; + struct sdio_al_device *sdio_al_dev = ch->sdio_al_dev; + + if (sdio_al_dev == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": NULL " + "sdio_al_dev for channel %s\n", ch->name); + return -EINVAL; + } + + /* Init channel Context */ + /** Func#1 is reserved for mailbox */ + ch->func = sdio_al_dev->card->sdio_func[ch->num+1]; + ch->rx_pipe_index = ch->num*2; + ch->tx_pipe_index = ch->num*2+1; + ch->signature = SDIO_AL_SIGNATURE; + + ch->total_rx_bytes = 0; + ch->total_tx_bytes = 0; + + ch->write_avail = 0; + ch->read_avail = 0; + ch->rx_pending_bytes = 0; + + mutex_init(&ch->ch_lock); + + pr_debug(MODULE_NAME ":open_channel %s func#%d\n", + ch->name, ch->func->num); + + INIT_LIST_HEAD(&(ch->rx_size_list_head)); + + /* Init SDIO Function */ + ret = sdio_al_enable_func_retry(ch->func, ch->name); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": " + "sdio_enable_func() err=%d\n", -ret); + goto exit_err; + } + + /* Note: Patch Func CIS tuple issue */ + ret = sdio_set_block_size(ch->func, SDIO_AL_BLOCK_SIZE); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": " + "sdio_set_block_size()failed, err=%d\n", -ret); + goto exit_err; + } + + ch->func->max_blksize = SDIO_AL_BLOCK_SIZE; + + sdio_set_drvdata(ch->func, ch); + + /* Get channel parameters from the peer SDIO-Client */ + read_sdioc_channel_config(ch); + + /* Set Pipes Threshold on Mailbox */ + ret = set_pipe_threshold(sdio_al_dev, + ch->rx_pipe_index, ch->read_threshold); + if (ret) + goto exit_err; + ret = set_pipe_threshold(sdio_al_dev, + ch->tx_pipe_index, ch->write_threshold); + if (ret) + goto exit_err; + + /* Set flag before interrupts are enabled to allow notify */ + ch->state = SDIO_CHANNEL_STATE_OPEN; + pr_debug(MODULE_NAME ":channel %s is in OPEN state now\n", ch->name); + + sdio_al_dev->poll_delay_msec = get_min_poll_time_msec(sdio_al_dev); + + /* lpm mechanism lives under the assumption there is always a timer */ + /* Check if need to start the timer */ + if ((sdio_al_dev->poll_delay_msec) && + (sdio_al_dev->is_timer_initialized == false)) { + + init_timer(&sdio_al_dev->timer); + sdio_al_dev->timer.data = (unsigned long) sdio_al_dev; + sdio_al_dev->timer.function = sdio_al_timer_handler; + sdio_al_dev->timer.expires = jiffies + + msecs_to_jiffies(sdio_al_dev->poll_delay_msec); + add_timer(&sdio_al_dev->timer); + sdio_al_dev->is_timer_initialized = true; + } + + /* Enable Pipes Interrupts */ + enable_eot_interrupt(sdio_al_dev, ch->rx_pipe_index, true); + enable_eot_interrupt(sdio_al_dev, ch->tx_pipe_index, true); + + enable_threshold_interrupt(sdio_al_dev, ch->rx_pipe_index, true); + enable_threshold_interrupt(sdio_al_dev, ch->tx_pipe_index, true); + +exit_err: + + return ret; +} + +/** + * Ask the worker to read the mailbox. + */ +static void ask_reading_mailbox(struct sdio_al_device *sdio_al_dev) +{ + if (!sdio_al_dev->ask_mbox) { + pr_debug(MODULE_NAME ":ask_reading_mailbox for card %d\n", + sdio_al_dev->host->index); + sdio_al_dev->ask_mbox = true; + wake_up(&sdio_al_dev->wait_mbox); + } +} + +/** + * Start the timer + */ +static void start_timer(struct sdio_al_device *sdio_al_dev) +{ + if ((sdio_al_dev->poll_delay_msec) && + (sdio_al_dev->state == CARD_INSERTED)) { + sdio_al_dev->timer.expires = jiffies + + msecs_to_jiffies(sdio_al_dev->poll_delay_msec); + add_timer(&sdio_al_dev->timer); + } +} + +/** + * Restart(postpone) the already working timer + */ +static void restart_timer(struct sdio_al_device *sdio_al_dev) +{ + if ((sdio_al_dev->poll_delay_msec) && + (sdio_al_dev->state == CARD_INSERTED)) { + ulong expires = jiffies + + msecs_to_jiffies(sdio_al_dev->poll_delay_msec); + mod_timer(&sdio_al_dev->timer, expires); + } +} + +/** + * Stop and delete the timer + */ +static void stop_and_del_timer(struct sdio_al_device *sdio_al_dev) +{ + if (sdio_al_dev->is_timer_initialized) { + sdio_al_dev->poll_delay_msec = 0; + del_timer_sync(&sdio_al_dev->timer); + } +} + +/** + * Do the wakup sequence. + * This function should be called after claiming the host! + * The caller is responsible for releasing the host. + * + * Wake up sequence + * 1. Get lock + * 2. Enable wake up function if needed + * 3. Mark NOT OK to sleep and write it + * 4. Restore default thresholds + * 5. Start the mailbox and inactivity timer again + */ +static int sdio_al_wake_up(struct sdio_al_device *sdio_al_dev, + u32 not_from_int, struct sdio_channel *ch) +{ + int ret = 0; + struct sdio_func *wk_func = NULL; + unsigned long time_to_wait; + struct mmc_host *host = sdio_al_dev->host; + + if (sdio_al_dev->is_err) { + SDIO_AL_ERR(__func__); + return -ENODEV; + } + + if (!sdio_al_dev->is_ok_to_sleep) { + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":card %d " + "already awake, no need to wake up\n", + sdio_al_dev->host->index); + return 0; + } + + /* Wake up sequence */ + if (not_from_int) { + if (ch) { + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ": Wake up" + " card %d (not by interrupt), ch %s", + sdio_al_dev->host->index, + ch->name); + } else { + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ": Wake up" + " card %d (not by interrupt)", + sdio_al_dev->host->index); + } + } else { + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ": Wake up card " + "%d by interrupt", + sdio_al_dev->host->index); + sdio_al_dev->print_after_interrupt = 1; + } + + sdio_al_vote_for_sleep(sdio_al_dev, 0); + + msmsdcc_lpm_disable(host); + msmsdcc_set_pwrsave(host, 0); + /* Poll the GPIO */ + time_to_wait = jiffies + msecs_to_jiffies(1000); + while (time_before(jiffies, time_to_wait)) { + if (sdio_al->pdata->get_mdm2ap_status()) + break; + udelay(TIME_TO_WAIT_US); + } + + pr_debug(MODULE_NAME ":GPIO mdm2ap_status=%d\n", + sdio_al->pdata->get_mdm2ap_status()); + + /* Here get_mdm2ap_status() returning 0 is not an error condition */ + if (sdio_al->pdata->get_mdm2ap_status() == 0) + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ": " + "get_mdm2ap_status() is 0\n"); + + /* Enable Wake up Function */ + if (!sdio_al_dev->card || + !sdio_al_dev->card->sdio_func[SDIO_AL_WAKEUP_FUNC-1]) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": NULL card or wk_func\n"); + return -ENODEV; + } + wk_func = sdio_al_dev->card->sdio_func[SDIO_AL_WAKEUP_FUNC-1]; + ret = sdio_al_enable_func_retry(wk_func, "wakeup func"); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": " + "sdio_enable_func() err=%d\n", -ret); + goto error_exit; + } + /* Mark NOT OK_TOSLEEP */ + sdio_al_dev->is_ok_to_sleep = 0; + ret = write_lpm_info(sdio_al_dev); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": " + "write_lpm_info() failed, err=%d\n", -ret); + sdio_al_dev->is_ok_to_sleep = 1; + sdio_disable_func(wk_func); + goto error_exit; + } + sdio_disable_func(wk_func); + + /* Start the timer again*/ + restart_inactive_time(sdio_al_dev); + sdio_al_dev->poll_delay_msec = get_min_poll_time_msec(sdio_al_dev); + start_timer(sdio_al_dev); + + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME "Finished Wake up sequence" + " for card %d", sdio_al_dev->host->index); + + msmsdcc_set_pwrsave(host, 1); + pr_debug(MODULE_NAME ":Turn clock off\n"); + + return ret; +error_exit: + sdio_al_vote_for_sleep(sdio_al_dev, 1); + msmsdcc_set_pwrsave(host, 1); + WARN_ON(ret); + sdio_al_get_into_err_state(sdio_al_dev); + return ret; +} + + +/** + * SDIO Function Interrupt handler. + * + * Interrupt shall be triggered by SDIO-Client when: + * 1. End-Of-Transfer (EOT) detected in packet mode. + * 2. Bytes-available reached the threshold. + * + * Reading the mailbox clears the EOT/Threshold interrupt + * source. + * The interrupt source should be cleared before this ISR + * returns. This ISR is called from IRQ Thread and not + * interrupt, so it may sleep. + * + */ +static void sdio_func_irq(struct sdio_func *func) +{ + struct sdio_al_device *sdio_al_dev = sdio_get_drvdata(func); + + pr_debug(MODULE_NAME ":start %s.\n", __func__); + + if (sdio_al_dev == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": NULL device"); + return; + } + + if (sdio_al_dev->is_ok_to_sleep) + sdio_al_wake_up(sdio_al_dev, 0, NULL); + else + restart_timer(sdio_al_dev); + + read_mailbox(sdio_al_dev, true); + + pr_debug(MODULE_NAME ":end %s.\n", __func__); +} + +/** + * Timer Expire Handler + * + */ +static void sdio_al_timer_handler(unsigned long data) +{ + struct sdio_al_device *sdio_al_dev = (struct sdio_al_device *)data; + if (sdio_al_dev == NULL) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": NULL " + "sdio_al_dev for data %lu\n", data); + return; + } + if (sdio_al_dev->state != CARD_INSERTED) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": sdio_al_dev " + "is in invalid state %d\n", sdio_al_dev->state); + return; + } + pr_debug(MODULE_NAME " Timer Expired\n"); + + ask_reading_mailbox(sdio_al_dev); + + restart_timer(sdio_al_dev); +} + +/** + * Driver Setup. + * + */ +static int sdio_al_setup(struct sdio_al_device *sdio_al_dev) +{ + int ret = 0; + struct mmc_card *card = sdio_al_dev->card; + struct sdio_func *func1 = NULL; + int i = 0; + int fn = 0; + + if (sdio_al_verify_func1(sdio_al_dev, __func__)) + return -ENODEV; + func1 = card->sdio_func[0]; + + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":sdio_al_setup for " + "card %d\n", sdio_al_dev->host->index); + + ret = sdio_al->pdata->config_mdm2ap_status(1); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME "Could not " + "request GPIO\n"); + return ret; + } + + INIT_WORK(&sdio_al_dev->sdio_al_work.work, worker); + /* disable all pipes interrupts before claim irq. + since all are enabled by default. */ + for (i = 0 ; i < SDIO_AL_MAX_PIPES; i++) { + enable_eot_interrupt(sdio_al_dev, i, false); + enable_threshold_interrupt(sdio_al_dev, i, false); + } + + /* Disable all SDIO Functions before claim irq. */ + for (fn = 1 ; fn <= card->sdio_funcs; fn++) + sdio_disable_func(card->sdio_func[fn-1]); + + sdio_set_drvdata(func1, sdio_al_dev); + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":claim IRQ for card " + "%d\n", sdio_al_dev->host->index); + + ret = sdio_claim_irq(func1, sdio_func_irq); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to claim" + " IRQ for card %d\n", + sdio_al_dev->host->index); + return ret; + } + + sdio_al_dev->is_ready = true; + + /* Start worker before interrupt might happen */ + queue_work(sdio_al_dev->workqueue, &sdio_al_dev->sdio_al_work.work); + + start_inactive_time(sdio_al_dev); + + pr_debug(MODULE_NAME ":Ready.\n"); + + return 0; +} + +/** + * Driver Tear-Down. + * + */ +static void sdio_al_tear_down(void) +{ + int i, j; + struct sdio_al_device *sdio_al_dev = NULL; + struct sdio_func *func1; + + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; ++i) { + if (sdio_al->devices[i] == NULL) + continue; + sdio_al_dev = sdio_al->devices[i]; + + if (sdio_al_dev->is_ready) { + sdio_al_dev->is_ready = false; /* Flag worker to exit */ + sdio_al_dev->ask_mbox = false; + ask_reading_mailbox(sdio_al_dev); /* Wakeup worker */ + /* allow gracefully exit of the worker thread */ + msleep(100); + + flush_workqueue(sdio_al_dev->workqueue); + destroy_workqueue(sdio_al_dev->workqueue); + + sdio_al_vote_for_sleep(sdio_al_dev, 1); + + if (!sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, + __func__)) { + if (!sdio_al_dev->card || + !sdio_al_dev->card->sdio_func[0]) { + sdio_al_loge(sdio_al_dev->dev_log, + MODULE_NAME + ": %s: Invalid func1", + __func__); + return; + } + func1 = sdio_al_dev->card->sdio_func[0]; + sdio_release_irq(func1); + sdio_disable_func(func1); + sdio_al_release_mutex(sdio_al_dev, __func__); + } + } + + for (j = 0; j < SDIO_AL_MAX_CHANNELS; j++) + sdio_al_dev->channel[j].signature = 0x0; + sdio_al_dev->signature = 0; + + kfree(sdio_al_dev->sdioc_sw_header); + kfree(sdio_al_dev->mailbox); + kfree(sdio_al_dev->rx_flush_buf); + kfree(sdio_al_dev); + } + + sdio_al->pdata->config_mdm2ap_status(0); +} + +/** + * Find channel by name. + * + */ +static struct sdio_channel *find_channel_by_name(const char *name) +{ + struct sdio_channel *ch = NULL; + int i, j; + struct sdio_al_device *sdio_al_dev = NULL; + + for (j = 0; j < MAX_NUM_OF_SDIO_DEVICES; ++j) { + if (sdio_al->devices[j] == NULL) + continue; + sdio_al_dev = sdio_al->devices[j]; + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + if (sdio_al_dev->channel[i].state == + SDIO_CHANNEL_STATE_INVALID) + continue; + if (strncmp(sdio_al_dev->channel[i].name, name, + CHANNEL_NAME_SIZE) == 0) { + ch = &sdio_al_dev->channel[i]; + break; + } + } + if (ch != NULL) + break; + } + + return ch; +} + +/** + * Find the minimal poll time. + * + */ +static int get_min_poll_time_msec(struct sdio_al_device *sdio_sl_dev) +{ + int i; + int poll_delay_msec = 0x0FFFFFFF; + + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) + if ((sdio_sl_dev->channel[i].state == + SDIO_CHANNEL_STATE_OPEN) && + (sdio_sl_dev->channel[i].poll_delay_msec > 0) && + (sdio_sl_dev->channel[i].poll_delay_msec < poll_delay_msec)) + poll_delay_msec = + sdio_sl_dev->channel[i].poll_delay_msec; + + if (poll_delay_msec == 0x0FFFFFFF) + poll_delay_msec = SDIO_AL_POLL_TIME_NO_STREAMING; + + pr_debug(MODULE_NAME ":poll delay time is %d msec\n", poll_delay_msec); + + return poll_delay_msec; +} + +/** + * Open SDIO Channel. + * + * Enable the channel. + * Set the channel context. + * Trigger reading the mailbox to check available bytes. + * + */ +int sdio_open(const char *name, struct sdio_channel **ret_ch, void *priv, + void (*notify)(void *priv, unsigned ch_event)) +{ + int ret = 0; + struct sdio_channel *ch = NULL; + struct sdio_al_device *sdio_al_dev = NULL; + + *ret_ch = NULL; /* default */ + + ch = find_channel_by_name(name); + if (ch == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":Can't find " + "channel name %s\n", name); + return -EINVAL; + } + + sdio_al_dev = ch->sdio_al_dev; + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + if ((ch->state != SDIO_CHANNEL_STATE_IDLE) && + (ch->state != SDIO_CHANNEL_STATE_CLOSED)) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Wrong ch %s " + "state %d\n", name, ch->state); + ret = -EPERM; + goto exit_err; + } + + if (sdio_al_dev->is_err) { + SDIO_AL_ERR(__func__); + ret = -ENODEV; + goto exit_err; + } + + ret = sdio_al_wake_up(sdio_al_dev, 1, ch); + if (ret) + goto exit_err; + + ch->notify = notify; + ch->priv = priv; + + /* Note: Set caller returned context before interrupts are enabled */ + *ret_ch = ch; + + ret = open_channel(ch); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":sdio_open %s " + "err=%d\n", name, -ret); + goto exit_err; + } + + CLOSE_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":sdio_open %s " + "completed OK\n", name); + if (sdio_al_dev->lpm_chan == INVALID_SDIO_CHAN) { + if (sdio_al->sdioc_major == PEER_SDIOC_OLD_VERSION_MAJOR) { + if (!ch->is_packet_mode) { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME + ":setting channel %s as " + "lpm_chan\n", name); + sdio_al_dev->lpm_chan = ch->num; + } + } else { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ": " + "setting channel %s as lpm_chan\n", + name); + sdio_al_dev->lpm_chan = ch->num; + } + } + +exit_err: + sdio_al_release_mutex(sdio_al_dev, __func__); + return ret; +} +EXPORT_SYMBOL(sdio_open); + +/** + * Request peer operation + * note: sanity checks of parameters done by caller + * called under bus locked + */ +static int peer_set_operation(u32 opcode, + struct sdio_al_device *sdio_al_dev, + struct sdio_channel *ch) +{ + int ret; + int offset; + struct sdio_func *wk_func = NULL; + u32 peer_operation; + int loop_count = 0; + + if (!sdio_al_dev->card || + !sdio_al_dev->card->sdio_func[SDIO_AL_WAKEUP_FUNC-1]) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": NULL card or wk_func\n"); + ret = -ENODEV; + goto exit; + } + wk_func = sdio_al_dev->card->sdio_func[SDIO_AL_WAKEUP_FUNC-1]; + + /* calculate offset of peer_operation field in sw mailbox struct */ + offset = offsetof(struct peer_sdioc_sw_mailbox, ch_config) + + sizeof(struct peer_sdioc_channel_config) * ch->num + + offsetof(struct peer_sdioc_channel_config, peer_operation); + + ret = sdio_al_wake_up(sdio_al_dev, 1, ch); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to " + "wake up\n"); + goto exit; + } + /* request operation from MDM peer */ + peer_operation = PEER_OPERATION(opcode, PEER_OP_STATE_INIT); + ret = sdio_memcpy_toio(ch->func, SDIOC_SW_MAILBOX_ADDR+offset, + &peer_operation, sizeof(u32)); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":failed to " + "request close operation\n"); + goto exit; + } + ret = sdio_al_enable_func_retry(wk_func, "wk_func"); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to enable" + " Func#%d\n", wk_func->num); + goto exit; + } + pr_debug(MODULE_NAME ":%s: wk_func enabled on ch %s\n", + __func__, ch->name); + /* send "start" operation to MDM */ + peer_operation = PEER_OPERATION(opcode, PEER_OP_STATE_START); + ret = sdio_memcpy_toio(ch->func, SDIOC_SW_MAILBOX_ADDR+offset, + &peer_operation, sizeof(u32)); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":failed to " + "send start close operation\n"); + goto exit; + } + ret = sdio_disable_func(wk_func); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to " + "disable Func#%d\n", wk_func->num); + goto exit; + } + /* poll for peer operation ack */ + while (peer_operation != 0) { + ret = sdio_memcpy_fromio(ch->func, + &peer_operation, + SDIOC_SW_MAILBOX_ADDR+offset, + sizeof(u32)); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ":failed to request ack on close" + " operation, loop_count = %d\n", + loop_count); + goto exit; + } + loop_count++; + if (loop_count > 10) { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":%s: " + "peer_operation=0x%x wait loop" + " %d on ch %s\n", __func__, + peer_operation, loop_count, ch->name); + } + } +exit: + return ret; +} + +static int channel_close(struct sdio_channel *ch, int flush_flag) +{ + int ret; + struct sdio_al_device *sdio_al_dev = NULL; + int flush_len; + ulong flush_expires; + + if (!ch) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL " + "channel\n", __func__); + return -ENODEV; + } + + if (!ch->func) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":%s: NULL func" + " on channel:%d\n", __func__, ch->num); + return -ENODEV; + } + + sdio_al_dev = ch->sdio_al_dev; + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + if (!sdio_al_dev->ch_close_supported) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":%s: Not " + "supported by mdm, ch %s\n", + __func__, ch->name); + ret = -ENOTSUPP; + goto error_exit; + } + + if (sdio_al_dev->is_err) { + SDIO_AL_ERR(__func__); + ret = -ENODEV; + goto error_exit; + } + if (ch->state != SDIO_CHANNEL_STATE_OPEN) { + sdio_al_loge(sdio_al_dev->dev_log, + MODULE_NAME ":%s: ch %s is not in " + "open state (%d)\n", + __func__, ch->name, ch->state); + ret = -ENODEV; + goto error_exit; + } + ch->state = SDIO_CHANNEL_STATE_CLOSING; + ret = peer_set_operation(PEER_OP_CODE_CLOSE, sdio_al_dev, ch); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":%s: " + "peer_set_operation() failed: %d\n", + __func__, ret); + ret = -ENODEV; + goto error_exit; + } + /* udate poll time for opened channels */ + if (ch->poll_delay_msec > 0) { + sdio_al_dev->poll_delay_msec = + get_min_poll_time_msec(sdio_al_dev); + } + sdio_al_release_mutex(ch->sdio_al_dev, __func__); + + flush_expires = jiffies + + msecs_to_jiffies(SDIO_CLOSE_FLUSH_TIMEOUT_MSEC); + /* flush rx packets of the channel */ + if (flush_flag) { + do { + while (ch->read_avail > 0) { + flush_len = ch->read_avail; + ret = sdio_read_internal(ch, + sdio_al_dev->rx_flush_buf, + flush_len); + if (ret) { + sdio_al_loge(&sdio_al->gen_log, + MODULE_NAME ":%s sdio_read" + " failed: %d, ch %s\n", + __func__, ret, + ch->name); + return ret; + } + + if (time_after(jiffies, flush_expires) != 0) { + sdio_al_loge(&sdio_al->gen_log, + MODULE_NAME ":%s flush rx " + "packets timeout: ch %s\n", + __func__, ch->name); + sdio_al_get_into_err_state(sdio_al_dev); + return -EBUSY; + } + } + msleep(100); + if (ch->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(&sdio_al->gen_log, + MODULE_NAME ":%s: after sleep," + " invalid signature" + " 0x%x\n", __func__, + ch->signature); + return -ENODEV; + } + if (sdio_al_claim_mutex_and_verify_dev(ch->sdio_al_dev, + __func__)) + return -ENODEV; + + ret = read_mailbox(sdio_al_dev, false); + if (ret) { + sdio_al_loge(&sdio_al->gen_log, + MODULE_NAME ":%s: failed to" + " read mailbox", __func__); + goto error_exit; + } + sdio_al_release_mutex(ch->sdio_al_dev, __func__); + } while (ch->read_avail > 0); + } + if (sdio_al_claim_mutex_and_verify_dev(ch->sdio_al_dev, + __func__)) + return -ENODEV; + /* disable function to be able to open the channel again */ + ret = sdio_disable_func(ch->func); + if (ret) { + sdio_al_loge(&sdio_al->gen_log, + MODULE_NAME ":Fail to disable Func#%d\n", + ch->func->num); + goto error_exit; + } + ch->state = SDIO_CHANNEL_STATE_CLOSED; + CLOSE_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":%s: Ch %s closed " + "successfully\n", __func__, ch->name); + +error_exit: + sdio_al_release_mutex(ch->sdio_al_dev, __func__); + + return ret; +} + +/** + * Close SDIO Channel. + * + */ +int sdio_close(struct sdio_channel *ch) +{ + return channel_close(ch, true); +} +EXPORT_SYMBOL(sdio_close); + +/** + * Get the number of available bytes to write. + * + */ +int sdio_write_avail(struct sdio_channel *ch) +{ + if (!ch) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL " + "channel\n", __func__); + return -ENODEV; + } + if (ch->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: " + "Invalid signature 0x%x\n", __func__, + ch->signature); + return -ENODEV; + } + if (ch->state != SDIO_CHANNEL_STATE_OPEN) { + sdio_al_loge(ch->sdio_al_dev->dev_log, MODULE_NAME ":%s: " + "channel %s state is not open (%d)\n", + __func__, ch->name, ch->state); + return -ENODEV; + } + pr_debug(MODULE_NAME ":sdio_write_avail %s 0x%x\n", + ch->name, ch->write_avail); + + return ch->write_avail; +} +EXPORT_SYMBOL(sdio_write_avail); + +/** + * Get the number of available bytes to read. + * + */ +int sdio_read_avail(struct sdio_channel *ch) +{ + if (!ch) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL " + "channel\n", __func__); + return -ENODEV; + } + if (ch->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: " + "Invalid signature 0x%x\n", __func__, + ch->signature); + return -ENODEV; + } + if (ch->state != SDIO_CHANNEL_STATE_OPEN) { + sdio_al_loge(ch->sdio_al_dev->dev_log, MODULE_NAME ":%s: " + "channel %s state is not open (%d)\n", + __func__, ch->name, ch->state); + return -ENODEV; + } + pr_debug(MODULE_NAME ":sdio_read_avail %s 0x%x\n", + ch->name, ch->read_avail); + + return ch->read_avail; +} +EXPORT_SYMBOL(sdio_read_avail); + +static int sdio_read_from_closed_ch(struct sdio_channel *ch, int len) +{ + int ret = 0; + struct sdio_al_device *sdio_al_dev = NULL; + + if (!ch) { + sdio_al_loge(ch->sdio_al_dev->dev_log, + MODULE_NAME ":%s: NULL channel\n", __func__); + return -ENODEV; + } + + sdio_al_dev = ch->sdio_al_dev; + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + ret = sdio_memcpy_fromio(ch->func, sdio_al_dev->rx_flush_buf, + PIPE_RX_FIFO_ADDR, len); + + if (ret) { + sdio_al_loge(ch->sdio_al_dev->dev_log, + MODULE_NAME ":ch %s: %s err=%d, len=%d\n", + ch->name, __func__, -ret, len); + sdio_al_dev->is_err = true; + sdio_al_release_mutex(sdio_al_dev, __func__); + return ret; + } + + restart_inactive_time(sdio_al_dev); + + sdio_al_release_mutex(sdio_al_dev, __func__); + + return 0; +} + +/** + * Internal read from SDIO Channel. + * + * Reading from the pipe will trigger interrupt if there are + * other pending packets on the SDIO-Client. + * + */ +static int sdio_read_internal(struct sdio_channel *ch, void *data, int len) +{ + int ret = 0; + struct sdio_al_device *sdio_al_dev = NULL; + + if (!ch) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL " + "channel\n", __func__); + return -ENODEV; + } + if (!data) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL data\n", + __func__); + return -ENODEV; + } + if (len == 0) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":channel %s trying" + " to read 0 bytes\n", ch->name); + return -EINVAL; + } + + if (ch->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: Invalid " + "signature 0x%x\n", __func__, ch->signature); + return -ENODEV; + } + + sdio_al_dev = ch->sdio_al_dev; + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + if (sdio_al_dev->is_err) { + SDIO_AL_ERR(__func__); + ret = -ENODEV; + goto exit; + } + + /* lpm policy says we can't go to sleep when we have pending rx data, + so either we had rx interrupt and woken up, or we never went to + sleep */ + if (sdio_al_dev->is_ok_to_sleep) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":%s: called " + "when is_ok_to_sleep is set for ch %s, len=%d," + " last_any_read_avail=%d, last_read_avail=%d, " + "last_old_read_avail=%d", __func__, ch->name, + len, ch->statistics.last_any_read_avail, + ch->statistics.last_read_avail, + ch->statistics.last_old_read_avail); + } + BUG_ON(sdio_al_dev->is_ok_to_sleep); + + if ((ch->state != SDIO_CHANNEL_STATE_OPEN) && + (ch->state != SDIO_CHANNEL_STATE_CLOSING)) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":%s wrong " + "channel %s state %d\n", + __func__, ch->name, ch->state); + ret = -EINVAL; + goto exit; + } + + DATA_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":start ch %s read %d " + "avail %d.\n", ch->name, len, ch->read_avail); + + restart_inactive_time(sdio_al_dev); + + if ((ch->is_packet_mode) && (len != ch->read_avail)) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":sdio_read ch " + "%s len != read_avail\n", ch->name); + ret = -EINVAL; + goto exit; + } + + if (len > ch->read_avail) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":ERR ch %s: " + "reading more bytes (%d) than the avail(%d).\n", + ch->name, len, ch->read_avail); + ret = -ENOMEM; + goto exit; + } + + ret = sdio_memcpy_fromio(ch->func, data, PIPE_RX_FIFO_ADDR, len); + + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":ch %s: " + "sdio_read err=%d, len=%d, read_avail=%d, " + "last_read_avail=%d, last_old_read_avail=%d\n", + ch->name, -ret, len, ch->read_avail, + ch->statistics.last_read_avail, + ch->statistics.last_old_read_avail); + sdio_al_get_into_err_state(sdio_al_dev); + goto exit; + } + + ch->statistics.total_read_times++; + + /* Remove handled packet from the list regardless if ret is ok */ + if (ch->is_packet_mode) + remove_handled_rx_packet(ch); + else + ch->read_avail -= len; + + ch->total_rx_bytes += len; + DATA_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":end ch %s read %d " + "avail %d total %d.\n", ch->name, len, + ch->read_avail, ch->total_rx_bytes); + + if ((ch->read_avail == 0) && !(ch->is_packet_mode)) + ask_reading_mailbox(sdio_al_dev); + +exit: + sdio_al_release_mutex(sdio_al_dev, __func__); + + return ret; +} + +/** + * Read from SDIO Channel. + * + * Reading from the pipe will trigger interrupt if there are + * other pending packets on the SDIO-Client. + * + */ +int sdio_read(struct sdio_channel *ch, void *data, int len) +{ + if (!ch) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL " + "channel\n", __func__); + return -ENODEV; + } + if (ch->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: " + "Invalid signature 0x%x\n", __func__, ch->signature); + return -ENODEV; + } + if (ch->state == SDIO_CHANNEL_STATE_OPEN) { + return sdio_read_internal(ch, data, len); + } else { + sdio_al_loge(ch->sdio_al_dev->dev_log, MODULE_NAME + ":%s: Invalid channel %s state %d\n", + __func__, ch->name, ch->state); + } + return -ENODEV; +} +EXPORT_SYMBOL(sdio_read); + +/** + * Write to SDIO Channel. + * + */ +int sdio_write(struct sdio_channel *ch, const void *data, int len) +{ + int ret = 0; + struct sdio_al_device *sdio_al_dev = NULL; + + if (!ch) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL " + "channel\n", __func__); + return -ENODEV; + } + if (!data) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL data\n", + __func__); + return -ENODEV; + } + if (len == 0) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":channel %s trying" + " to write 0 bytes\n", ch->name); + return -EINVAL; + } + + if (ch->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: Invalid " + "signature 0x%x\n", __func__, ch->signature); + return -ENODEV; + } + + sdio_al_dev = ch->sdio_al_dev; + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + WARN_ON(len > ch->write_avail); + + if (sdio_al_dev->is_err) { + SDIO_AL_ERR(__func__); + ret = -ENODEV; + goto exit; + } + + if (ch->state != SDIO_CHANNEL_STATE_OPEN) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":writing to " + "closed channel %s\n", ch->name); + ret = -EINVAL; + goto exit; + } + + if (sdio_al_dev->is_ok_to_sleep) { + ret = sdio_al_wake_up(sdio_al_dev, 1, ch); + if (ret) + goto exit; + } else { + restart_inactive_time(sdio_al_dev); + } + + DATA_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":start ch %s write %d " + "avail %d.\n", ch->name, len, ch->write_avail); + + if (len > ch->write_avail) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":ERR ch %s: " + "write more bytes (%d) than available %d.\n", + ch->name, len, ch->write_avail); + ret = -ENOMEM; + goto exit; + } + + ret = sdio_ch_write(ch, data, len); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":sdio_write " + "on channel %s err=%d\n", ch->name, -ret); + goto exit; + } + + ch->total_tx_bytes += len; + DATA_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":end ch %s write %d " + "avail %d total %d.\n", ch->name, len, + ch->write_avail, ch->total_tx_bytes); + + /* Round up to whole buffer size */ + len = ROUND_UP(len, ch->peer_tx_buf_size); + /* Protect from wraparound */ + len = min(len, (int) ch->write_avail); + ch->write_avail -= len; + + if (ch->write_avail < ch->min_write_avail) + ask_reading_mailbox(sdio_al_dev); + +exit: + sdio_al_release_mutex(sdio_al_dev, __func__); + + return ret; +} +EXPORT_SYMBOL(sdio_write); + +static int __devinit msm_sdio_al_probe(struct platform_device *pdev) +{ + if (!sdio_al) { + pr_err(MODULE_NAME ": %s: NULL sdio_al\n", __func__); + return -ENODEV; + } + + sdio_al->pdata = pdev->dev.platform_data; + return 0; +} + +static int __devexit msm_sdio_al_remove(struct platform_device *pdev) +{ + return 0; +} + +static void sdio_al_close_all_channels(struct sdio_al_device *sdio_al_dev) +{ + int j; + int ret; + struct sdio_channel *ch = NULL; + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s", __func__); + + if (!sdio_al_dev) { + sdio_al_loge(sdio_al_dev->dev_log, + MODULE_NAME ": %s: NULL device", __func__); + return; + } + for (j = 0; j < SDIO_AL_MAX_CHANNELS; j++) { + ch = &sdio_al_dev->channel[j]; + + if (ch->state == SDIO_CHANNEL_STATE_OPEN) { + sdio_al_loge(sdio_al_dev->dev_log, + MODULE_NAME ": %s: Call to sdio_close() for" + " ch %s\n", __func__, ch->name); + ret = channel_close(ch, false); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, + MODULE_NAME ": %s: failed sdio_close()" + " for ch %s (%d)\n", + __func__, ch->name, ret); + } + } else { + pr_debug(MODULE_NAME ": %s: skip sdio_close() ch %s" + " (state=%d)\n", __func__, + ch->name, ch->state); + } + } +} + +static void sdio_al_invalidate_sdio_clients(struct sdio_al_device *sdio_al_dev, + struct platform_device **pdev_arr) +{ + int j; + + pr_debug(MODULE_NAME ": %s: Notifying SDIO clients for card %d", + __func__, sdio_al_dev->host->index); + for (j = 0; j < SDIO_AL_MAX_CHANNELS; ++j) { + if (sdio_al_dev->channel[j].state == + SDIO_CHANNEL_STATE_INVALID) + continue; + pdev_arr[j] = sdio_al_dev->channel[j].pdev; + sdio_al_dev->channel[j].signature = 0x0; + sdio_al_dev->channel[j].state = + SDIO_CHANNEL_STATE_INVALID; + } +} + +static void sdio_al_modem_reset_operations(struct sdio_al_device + *sdio_al_dev) +{ + int ret = 0; + struct platform_device *pdev_arr[SDIO_AL_MAX_CHANNELS]; + int j; + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s", __func__); + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return; + + if (sdio_al_dev->state == CARD_REMOVED) { + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: " + "card %d is already removed", __func__, + sdio_al_dev->host->index); + goto exit_err; + } + + if (sdio_al_dev->state == MODEM_RESTART) { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ": %s: " + "card %d was already notified for modem reset", + __func__, sdio_al_dev->host->index); + goto exit_err; + } + + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ": %s: Set the " + "state to MODEM_RESTART for card %d", + __func__, sdio_al_dev->host->index); + sdio_al_dev->state = MODEM_RESTART; + sdio_al_dev->is_ready = false; + + /* Stop mailbox timer */ + stop_and_del_timer(sdio_al_dev); + + if ((sdio_al_dev->is_ok_to_sleep) && + (!sdio_al_dev->is_err)) { + pr_debug(MODULE_NAME ": %s: wakeup modem for " + "card %d", __func__, + sdio_al_dev->host->index); + ret = sdio_al_wake_up(sdio_al_dev, 1, NULL); + } + + if (!ret && (!sdio_al_dev->is_err) && sdio_al_dev->card && + sdio_al_dev->card->sdio_func[0]) { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME + ": %s: sdio_release_irq for card %d", + __func__, + sdio_al_dev->host->index); + sdio_release_irq(sdio_al_dev->card->sdio_func[0]); + } + + memset(pdev_arr, 0, sizeof(pdev_arr)); + sdio_al_invalidate_sdio_clients(sdio_al_dev, pdev_arr); + + sdio_al_release_mutex(sdio_al_dev, __func__); + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: Notifying SDIO " + "clients for card %d", + __func__, sdio_al_dev->host->index); + for (j = 0; j < SDIO_AL_MAX_CHANNELS; j++) { + if (!pdev_arr[j]) + continue; + platform_device_unregister(pdev_arr[j]); + } + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: Finished Notifying " + "SDIO clients for card %d", + __func__, sdio_al_dev->host->index); + + return; + +exit_err: + sdio_al_release_mutex(sdio_al_dev, __func__); + return; +} + +#ifdef CONFIG_MSM_SUBSYSTEM_RESTART +static void sdio_al_reset(void) +{ + int i; + struct sdio_al_device *sdio_al_dev; + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s", __func__); + + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; i++) { + if (sdio_al->devices[i] == NULL) { + pr_debug(MODULE_NAME ": %s: NULL device in index %d", + __func__, i); + continue; + } + sdio_al_dev = sdio_al->devices[i]; + sdio_al_modem_reset_operations(sdio_al->devices[i]); + } + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s completed", __func__); +} +#endif + +static void msm_sdio_al_shutdown(struct platform_device *pdev) +{ + int i; + struct sdio_al_device *sdio_al_dev; + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME + "Initiating msm_sdio_al_shutdown..."); + + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; i++) { + if (sdio_al->devices[i] == NULL) { + pr_debug(MODULE_NAME ": %s: NULL device in index %d", + __func__, i); + continue; + } + sdio_al_dev = sdio_al->devices[i]; + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return; + + if (sdio_al_dev->ch_close_supported) + sdio_al_close_all_channels(sdio_al_dev); + + sdio_al_release_mutex(sdio_al_dev, __func__); + + sdio_al_modem_reset_operations(sdio_al_dev); + } + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: " + "msm_sdio_al_shutdown complete.", __func__); +} + +static struct platform_driver msm_sdio_al_driver = { + .probe = msm_sdio_al_probe, + .remove = __exit_p(msm_sdio_al_remove), + .shutdown = msm_sdio_al_shutdown, + .driver = { + .name = "msm_sdio_al", + }, +}; + +/** + * Initialize SDIO_AL channels. + * + */ +static int init_channels(struct sdio_al_device *sdio_al_dev) +{ + int ret = 0; + int i; + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + ret = read_sdioc_software_header(sdio_al_dev, + sdio_al_dev->sdioc_sw_header); + if (ret) + goto exit; + + ret = sdio_al_setup(sdio_al_dev); + if (ret) + goto exit; + + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + int ch_name_size; + if (sdio_al_dev->channel[i].state == SDIO_CHANNEL_STATE_INVALID) + continue; + if (sdio_al->unittest_mode) { + memset(sdio_al_dev->channel[i].ch_test_name, 0, + sizeof(sdio_al_dev->channel[i].ch_test_name)); + ch_name_size = strnlen(sdio_al_dev->channel[i].name, + CHANNEL_NAME_SIZE); + strncpy(sdio_al_dev->channel[i].ch_test_name, + sdio_al_dev->channel[i].name, + ch_name_size); + strncat(sdio_al_dev->channel[i].ch_test_name + + ch_name_size, + SDIO_TEST_POSTFIX, + SDIO_TEST_POSTFIX_SIZE); + pr_debug(MODULE_NAME ":pdev.name = %s\n", + sdio_al_dev->channel[i].ch_test_name); + sdio_al_dev->channel[i].pdev = platform_device_alloc( + sdio_al_dev->channel[i].ch_test_name, -1); + } else { + pr_debug(MODULE_NAME ":pdev.name = %s\n", + sdio_al_dev->channel[i].name); + sdio_al_dev->channel[i].pdev = platform_device_alloc( + sdio_al_dev->channel[i].name, -1); + } + if (!sdio_al_dev->channel[i].pdev) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ":NULL platform device for ch %s", + sdio_al_dev->channel[i].name); + sdio_al_dev->channel[i].state = + SDIO_CHANNEL_STATE_INVALID; + continue; + } + ret = platform_device_add(sdio_al_dev->channel[i].pdev); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ":platform_device_add failed, " + "ret=%d\n", ret); + sdio_al_dev->channel[i].state = + SDIO_CHANNEL_STATE_INVALID; + } + } + +exit: + sdio_al_release_mutex(sdio_al_dev, __func__); + return ret; +} + +/** + * Initialize SDIO_AL channels according to the client setup. + * This function also check if the client is in boot mode and + * flashless boot is required to be activated or the client is + * up and running. + * + */ +static int sdio_al_client_setup(struct sdio_al_device *sdio_al_dev) +{ + int ret = 0; + struct sdio_func *func1; + int signature = 0; + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + if (!sdio_al_dev->card || !sdio_al_dev->card->sdio_func[0]) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":NULL card or " + "func1\n"); + sdio_al_release_mutex(sdio_al_dev, __func__); + return -ENODEV; + } + func1 = sdio_al_dev->card->sdio_func[0]; + + /* Read the header signature to determine the status of the MDM + * SDIO Client + */ + signature = sdio_readl(func1, SDIOC_SW_HEADER_ADDR, &ret); + sdio_al_release_mutex(sdio_al_dev, __func__); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":fail to read " + "signature from sw header.\n"); + return ret; + } + + switch (signature) { + case PEER_SDIOC_SW_MAILBOX_BOOT_SIGNATURE: + if (sdio_al_dev == sdio_al->bootloader_dev) { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":setup " + "bootloader on card %d\n", + sdio_al_dev->host->index); + return sdio_al_bootloader_setup(); + } else { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":wait " + "for bootloader completion " + "on card %d\n", + sdio_al_dev->host->index); + return sdio_al_wait_for_bootloader_comp(sdio_al_dev); + } + case PEER_SDIOC_SW_MAILBOX_SIGNATURE: + case PEER_SDIOC_SW_MAILBOX_UT_SIGNATURE: + return init_channels(sdio_al_dev); + default: + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Invalid " + "signature 0x%x\n", signature); + return -EINVAL; + } + + return 0; +} + +static void clean_sdio_al_device_data(struct sdio_al_device *sdio_al_dev) +{ + sdio_al_dev->is_ready = 0; + sdio_al_dev->bootloader_done = 0; + sdio_al_dev->lpm_chan = 0; + sdio_al_dev->is_ok_to_sleep = 0; + sdio_al_dev->inactivity_time = 0; + sdio_al_dev->poll_delay_msec = 0; + sdio_al_dev->is_timer_initialized = 0; + sdio_al_dev->is_err = 0; + sdio_al_dev->is_suspended = 0; + sdio_al_dev->flashless_boot_on = 0; + sdio_al_dev->ch_close_supported = 0; + sdio_al_dev->print_after_interrupt = 0; + memset(sdio_al_dev->sdioc_sw_header, 0, + sizeof(*sdio_al_dev->sdioc_sw_header)); + memset(sdio_al_dev->mailbox, 0, sizeof(*sdio_al_dev->mailbox)); + memset(sdio_al_dev->rx_flush_buf, 0, + sizeof(*sdio_al_dev->rx_flush_buf)); +} + +/* + * SDIO driver functions + */ +static int sdio_al_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *sdio_dev_id) +{ + int ret = 0; + struct sdio_al_device *sdio_al_dev = NULL; + int i; + struct mmc_card *card = NULL; + + if (!func) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL func\n", + __func__); + return -ENODEV; + } + card = func->card; + + if (!card) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL card\n", + __func__); + return -ENODEV; + } + + if (!card->sdio_func[0]) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "func1\n", + __func__); + return -ENODEV; + } + + if (card->sdio_funcs < SDIO_AL_MAX_FUNCS) { + dev_info(&card->dev, + "SDIO-functions# %d less than expected.\n", + card->sdio_funcs); + return -ENODEV; + } + + /* Check if there is already a device for this card */ + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; ++i) { + if (sdio_al->devices[i] == NULL) + continue; + if (sdio_al->devices[i]->host == card->host) { + sdio_al_dev = sdio_al->devices[i]; + if (sdio_al_dev->state == CARD_INSERTED) + return 0; + clean_sdio_al_device_data(sdio_al_dev); + break; + } + } + + if (!sdio_al_dev) { + sdio_al_dev = kzalloc(sizeof(struct sdio_al_device), + GFP_KERNEL); + if (sdio_al_dev == NULL) + return -ENOMEM; + + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES ; ++i) + if (sdio_al->devices[i] == NULL) { + sdio_al->devices[i] = sdio_al_dev; + sdio_al_dev->dev_log = &sdio_al->device_log[i]; + spin_lock_init(&sdio_al_dev->dev_log->log_lock); + #ifdef CONFIG_DEBUG_FS + sdio_al_dbgfs_log[i].data = + sdio_al_dev->dev_log->buffer; + sdio_al_dbgfs_log[i].size = + SDIO_AL_DEBUG_LOG_SIZE; + #endif + break; + } + if (i == MAX_NUM_OF_SDIO_DEVICES) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":No space " + "in devices array for the device\n"); + return -ENOMEM; + } + } + + dev_info(&card->dev, "SDIO Card claimed.\n"); + sdio_al->skip_print_info = 0; + + sdio_al_dev->state = CARD_INSERTED; + + if (card->host->index == SDIO_BOOTLOADER_CARD_INDEX) + sdio_al->bootloader_dev = sdio_al_dev; + + sdio_al_dev->is_ready = false; + + sdio_al_dev->signature = SDIO_AL_SIGNATURE; + + sdio_al_dev->is_suspended = 0; + sdio_al_dev->is_timer_initialized = false; + + sdio_al_dev->lpm_chan = INVALID_SDIO_CHAN; + + sdio_al_dev->card = card; + sdio_al_dev->host = card->host; + + if (!sdio_al_dev->mailbox) { + sdio_al_dev->mailbox = kzalloc(sizeof(struct sdio_mailbox), + GFP_KERNEL); + if (sdio_al_dev->mailbox == NULL) + return -ENOMEM; + } + + if (!sdio_al_dev->sdioc_sw_header) { + sdio_al_dev->sdioc_sw_header + = kzalloc(sizeof(*sdio_al_dev->sdioc_sw_header), + GFP_KERNEL); + if (sdio_al_dev->sdioc_sw_header == NULL) + return -ENOMEM; + } + + if (!sdio_al_dev->rx_flush_buf) { + sdio_al_dev->rx_flush_buf = kzalloc(RX_FLUSH_BUFFER_SIZE, + GFP_KERNEL); + if (sdio_al_dev->rx_flush_buf == NULL) { + sdio_al_loge(&sdio_al->gen_log, + MODULE_NAME ":Fail to allocate " + "rx_flush_buf for card %d\n", + card->host->index); + return -ENOMEM; + } + } + + sdio_al_dev->timer.data = (unsigned long)sdio_al_dev; + + wake_lock_init(&sdio_al_dev->wake_lock, WAKE_LOCK_SUSPEND, MODULE_NAME); + /* Don't allow sleep until all required clients register */ + sdio_al_vote_for_sleep(sdio_al_dev, 0); + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + /* Init Func#1 */ + ret = sdio_al_enable_func_retry(card->sdio_func[0], "Init Func#1"); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to " + "enable Func#%d\n", card->sdio_func[0]->num); + goto exit; + } + + /* Patch Func CIS tuple issue */ + ret = sdio_set_block_size(card->sdio_func[0], SDIO_AL_BLOCK_SIZE); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to set " + "block size, Func#%d\n", card->sdio_func[0]->num); + goto exit; + } + sdio_al_dev->card->sdio_func[0]->max_blksize = SDIO_AL_BLOCK_SIZE; + + sdio_al_dev->workqueue = create_singlethread_workqueue("sdio_al_wq"); + sdio_al_dev->sdio_al_work.sdio_al_dev = sdio_al_dev; + init_waitqueue_head(&sdio_al_dev->wait_mbox); + + ret = sdio_al_client_setup(sdio_al_dev); + +exit: + sdio_al_release_mutex(sdio_al_dev, __func__); + return ret; +} + +static void sdio_al_sdio_remove(struct sdio_func *func) +{ + struct sdio_al_device *sdio_al_dev = NULL; + int i; + struct mmc_card *card = NULL; + struct platform_device *pdev_arr[SDIO_AL_MAX_CHANNELS]; + + if (!func) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL func\n", + __func__); + return; + } + card = func->card; + + if (!card) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL card\n", + __func__); + return; + } + + /* Find the sdio_al_device of this card */ + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; ++i) { + if (sdio_al->devices[i] == NULL) + continue; + if (sdio_al->devices[i]->card == card) { + sdio_al_dev = sdio_al->devices[i]; + break; + } + } + if (sdio_al_dev == NULL) { + pr_debug(MODULE_NAME ":%s :NULL sdio_al_dev for card %d\n", + __func__, card->host->index); + return; + } + + if (sdio_al_claim_mutex(sdio_al_dev, __func__)) + return; + + if (sdio_al_dev->state == CARD_REMOVED) { + sdio_al_release_mutex(sdio_al_dev, __func__); + return; + } + + if (!card->sdio_func[0]) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "func1\n", __func__); + sdio_al_release_mutex(sdio_al_dev, __func__); + return; + } + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":%s for card %d\n", + __func__, card->host->index); + + sdio_al_dev->state = CARD_REMOVED; + + memset(pdev_arr, 0, sizeof(pdev_arr)); + sdio_al_invalidate_sdio_clients(sdio_al_dev, pdev_arr); + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":%s: ask_reading_mailbox " + "for card %d\n", __func__, card->host->index); + sdio_al_dev->is_ready = false; /* Flag worker to exit */ + sdio_al_dev->ask_mbox = false; + ask_reading_mailbox(sdio_al_dev); /* Wakeup worker */ + + stop_and_del_timer(sdio_al_dev); + + sdio_al_release_mutex(sdio_al_dev, __func__); + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: Notifying SDIO " + "clients for card %d", + __func__, sdio_al_dev->host->index); + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + if (!pdev_arr[i]) + continue; + platform_device_unregister(pdev_arr[i]); + } + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: Finished Notifying " + "SDIO clients for card %d", + __func__, sdio_al_dev->host->index); + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":%s: vote for sleep for " + "card %d\n", __func__, card->host->index); + sdio_al_vote_for_sleep(sdio_al_dev, 1); + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":%s: flush_workqueue for " + "card %d\n", __func__, card->host->index); + flush_workqueue(sdio_al_dev->workqueue); + destroy_workqueue(sdio_al_dev->workqueue); + wake_lock_destroy(&sdio_al_dev->wake_lock); + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":%s: sdio card %d removed." + "\n", __func__, card->host->index); +} + +static void sdio_print_mailbox(char *prefix_str, struct sdio_mailbox *mailbox) +{ + int k = 0; + char buf[256]; + char buf1[10]; + + if (!mailbox) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": mailbox is " + "NULL\n"); + return; + } + + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: pipes 0_7: eot=0x%x," + " thresh=0x%x, overflow=0x%x, " + "underflow=0x%x, mask_thresh=0x%x\n", + prefix_str, mailbox->eot_pipe_0_7, + mailbox->thresh_above_limit_pipe_0_7, + mailbox->overflow_pipe_0_7, + mailbox->underflow_pipe_0_7, + mailbox->mask_thresh_above_limit_pipe_0_7); + + memset(buf, 0, sizeof(buf)); + strncat(buf, ": bytes_avail:", sizeof(buf)); + + for (k = 0 ; k < SDIO_AL_ACTIVE_PIPES ; ++k) { + snprintf(buf1, sizeof(buf1), "%d, ", + mailbox->pipe_bytes_avail[k]); + strncat(buf, buf1, sizeof(buf)); + } + + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME "%s", buf); +} + +static void sdio_al_print_info(void) +{ + int i = 0; + int j = 0; + int ret = 0; + struct sdio_mailbox *mailbox = NULL; + struct sdio_mailbox *hw_mailbox = NULL; + struct peer_sdioc_channel_config *ch_config = NULL; + struct sdio_func *func1 = NULL; + struct sdio_func *lpm_func = NULL; + int offset = 0; + int is_ok_to_sleep = 0; + char buf[50]; + + if (sdio_al->skip_print_info == 1) + return; + + sdio_al->skip_print_info = 1; + + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s - SDIO DEBUG INFO\n", + __func__); + + if (!sdio_al) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s - ERROR - " + "sdio_al is NULL\n", __func__); + return; + } + + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": GPIO mdm2ap_status=%d\n", + sdio_al->pdata->get_mdm2ap_status()); + + for (j = 0 ; j < MAX_NUM_OF_SDIO_DEVICES ; ++j) { + struct sdio_al_device *sdio_al_dev = sdio_al->devices[j]; + + if (sdio_al_dev == NULL) { + continue; + } + + if (!sdio_al_dev->host) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": Host" + " is NULL\n);"); + continue; + } + + snprintf(buf, sizeof(buf), "Card#%d: Shadow HW MB", + sdio_al_dev->host->index); + + /* printing Shadowing HW Mailbox*/ + mailbox = sdio_al_dev->mailbox; + sdio_print_mailbox(buf, mailbox); + + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": Card#%d: " + "is_ok_to_sleep=%d\n", + sdio_al_dev->host->index, + sdio_al_dev->is_ok_to_sleep); + + + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": Card#%d: " + "Shadow channels SW MB:", + sdio_al_dev->host->index); + + /* printing Shadowing SW Mailbox per channel*/ + for (i = 0 ; i < SDIO_AL_MAX_CHANNELS ; ++i) { + struct sdio_channel *ch = &sdio_al_dev->channel[i]; + + if (ch == NULL) { + continue; + } + + if (ch->state == SDIO_CHANNEL_STATE_INVALID) + continue; + + ch_config = &sdio_al_dev->channel[i].ch_config; + + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": Ch %s: max_rx_thres=0x%x, " + "max_tx_thres=0x%x, tx_buf=0x%x, " + "is_packet_mode=%d, " + "max_packet=0x%x, min_write=0x%x", + ch->name, ch_config->max_rx_threshold, + ch_config->max_tx_threshold, + ch_config->tx_buf_size, + ch_config->is_packet_mode, + ch_config->max_packet_size, + ch->min_write_avail); + + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": total_rx=0x%x, total_tx=0x%x, " + "read_avail=0x%x, write_avail=0x%x, " + "rx_pending=0x%x, num_reads=0x%x, " + "num_notifs=0x%x", ch->total_rx_bytes, + ch->total_tx_bytes, ch->read_avail, + ch->write_avail, ch->rx_pending_bytes, + ch->statistics.total_read_times, + ch->statistics.total_notifs); + } /* end loop over all channels */ + + } /* end loop over all devices */ + + /* reading from client and printing is_host_ok_to_sleep per device */ + for (j = 0 ; j < MAX_NUM_OF_SDIO_DEVICES ; ++j) { + struct sdio_al_device *sdio_al_dev = sdio_al->devices[j]; + + if (sdio_al_verify_func1(sdio_al_dev, __func__)) + continue; + + if (!sdio_al_dev->host) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": Host is NULL"); + continue; + } + + if (sdio_al_dev->lpm_chan == INVALID_SDIO_CHAN) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": %s - for Card#%d, is lpm_chan==" + "INVALID_SDIO_CHAN. continuing...", + __func__, sdio_al_dev->host->index); + continue; + } + + offset = offsetof(struct peer_sdioc_sw_mailbox, ch_config)+ + sizeof(struct peer_sdioc_channel_config) * + sdio_al_dev->lpm_chan+ + offsetof(struct peer_sdioc_channel_config, is_host_ok_to_sleep); + + lpm_func = sdio_al_dev->card->sdio_func[sdio_al_dev-> + lpm_chan+1]; + if (!lpm_func) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": %s - lpm_func is NULL for card#%d" + " continuing...\n", __func__, + sdio_al_dev->host->index); + continue; + } + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return; + ret = sdio_memcpy_fromio(lpm_func, + &is_ok_to_sleep, + SDIOC_SW_MAILBOX_ADDR+offset, + sizeof(int)); + sdio_al_release_mutex(sdio_al_dev, __func__); + + if (ret) + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": %s - fail to read " + "is_HOST_ok_to_sleep from mailbox for card %d", + __func__, sdio_al_dev->host->index); + else + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": Card#%d: " + "is_HOST_ok_to_sleep=%d\n", + sdio_al_dev->host->index, + is_ok_to_sleep); + } + + for (j = 0 ; j < MAX_NUM_OF_SDIO_DEVICES ; ++j) { + struct sdio_al_device *sdio_al_dev = sdio_al->devices[j]; + + if (!sdio_al_dev) + continue; + + /* Reading HW Mailbox */ + hw_mailbox = sdio_al_dev->mailbox; + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return; + + if (!sdio_al_dev->card || !sdio_al_dev->card->sdio_func[0]) { + sdio_al_release_mutex(sdio_al_dev, __func__); + return; + } + func1 = sdio_al_dev->card->sdio_func[0]; + ret = sdio_memcpy_fromio(func1, hw_mailbox, + HW_MAILBOX_ADDR, sizeof(*hw_mailbox)); + sdio_al_release_mutex(sdio_al_dev, __func__); + + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": fail to read " + "mailbox for card#%d. " + "continuing...\n", + sdio_al_dev->host->index); + continue; + } + + snprintf(buf, sizeof(buf), "Card#%d: Current HW MB", + sdio_al_dev->host->index); + + /* Printing HW Mailbox */ + sdio_print_mailbox(buf, hw_mailbox); + } +} + +static struct sdio_device_id sdio_al_sdioid[] = { + {.class = 0, .vendor = 0x70, .device = 0x2460}, + {.class = 0, .vendor = 0x70, .device = 0x0460}, + {.class = 0, .vendor = 0x70, .device = 0x23F1}, + {.class = 0, .vendor = 0x70, .device = 0x23F0}, + {} +}; + +static struct sdio_driver sdio_al_sdiofn_driver = { + .name = "sdio_al_sdiofn", + .id_table = sdio_al_sdioid, + .probe = sdio_al_sdio_probe, + .remove = sdio_al_sdio_remove, +}; + +#ifdef CONFIG_MSM_SUBSYSTEM_RESTART +/* + * Callback for notifications from restart mudule. + * This function handles only the BEFORE_RESTART notification. + * Stop all the activity on the card and notify our clients. + */ +static int sdio_al_subsys_notifier_cb(struct notifier_block *this, + unsigned long notif_type, + void *data) +{ + if (notif_type != SUBSYS_BEFORE_SHUTDOWN) { + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: got " + "notification %ld", __func__, notif_type); + return NOTIFY_DONE; + } + + sdio_al_reset(); + return NOTIFY_OK; +} + +static struct notifier_block sdio_al_nb = { + .notifier_call = sdio_al_subsys_notifier_cb, +}; +#endif + +/** + * Module Init. + * + * @warn: allocate sdio_al context before registering driver. + * + */ +static int __init sdio_al_init(void) +{ + int ret = 0; + int i; + + pr_debug(MODULE_NAME ":sdio_al_init\n"); + + pr_info(MODULE_NAME ":SDIO-AL SW version %s\n", + DRV_VERSION); + + sdio_al = kzalloc(sizeof(struct sdio_al), GFP_KERNEL); + if (sdio_al == NULL) + return -ENOMEM; + + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES ; ++i) + sdio_al->devices[i] = NULL; + + sdio_al->unittest_mode = false; + + sdio_al->debug.debug_lpm_on = debug_lpm_on; + sdio_al->debug.debug_data_on = debug_data_on; + sdio_al->debug.debug_close_on = debug_close_on; + +#ifdef CONFIG_DEBUG_FS + sdio_al_debugfs_init(); +#endif + + +#ifdef CONFIG_MSM_SUBSYSTEM_RESTART + sdio_al->subsys_notif_handle = subsys_notif_register_notifier( + "external_modem", &sdio_al_nb); +#endif + + ret = platform_driver_register(&msm_sdio_al_driver); + if (ret) { + pr_err(MODULE_NAME ": platform_driver_register failed: %d\n", + ret); + goto exit; + } + + sdio_register_driver(&sdio_al_sdiofn_driver); + + spin_lock_init(&sdio_al->gen_log.log_lock); + +exit: + if (ret) + kfree(sdio_al); + return ret; +} + +/** + * Module Exit. + * + * Free allocated memory. + * Disable SDIO-Card. + * Unregister driver. + * + */ +static void __exit sdio_al_exit(void) +{ + if (sdio_al == NULL) + return; + + pr_debug(MODULE_NAME ":sdio_al_exit\n"); + +#ifdef CONFIG_MSM_SUBSYSTEM_RESTART + subsys_notif_unregister_notifier( + sdio_al->subsys_notif_handle, &sdio_al_nb); +#endif + + sdio_al_tear_down(); + + sdio_unregister_driver(&sdio_al_sdiofn_driver); + + kfree(sdio_al); + +#ifdef CONFIG_DEBUG_FS + sdio_al_debugfs_cleanup(); +#endif + + platform_driver_unregister(&msm_sdio_al_driver); + + pr_debug(MODULE_NAME ":sdio_al_exit complete\n"); +} + +module_init(sdio_al_init); +module_exit(sdio_al_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SDIO Abstraction Layer"); +MODULE_AUTHOR("Amir Samuelov "); +MODULE_VERSION(DRV_VERSION); + diff --git a/arch/arm/mach-msm/sdio_al_dloader.c b/arch/arm/mach-msm/sdio_al_dloader.c new file mode 100644 index 00000000..167c163c --- /dev/null +++ b/arch/arm/mach-msm/sdio_al_dloader.c @@ -0,0 +1,2541 @@ +/* Copyright (c) 2011-2012, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * SDIO-Downloader + * + * To be used with Qualcomm's SDIO-Client connected to this host. + */ + +/* INCLUDES */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdio_al_private.h" +#include +#include +#include +#include +#include +#include + +/* DEFINES AND MACROS */ +#define MAX_NUM_DEVICES 1 +#define TTY_SDIO_DEV "tty_sdio_0" +#define TTY_SDIO_DEV_TEST "tty_sdio_test_0" +#define SDIOC_MAILBOX_ADDRESS 0 +#define SDIO_DL_BLOCK_SIZE 512 +#define SDIO_DL_MAIN_THREAD_NAME "sdio_tty_main_thread" +#define SDIOC_DL_BUFF_ADDRESS 0 +#define SDIOC_UP_BUFF_ADDRESS 0x4 +#define SDIOC_DL_BUFF_SIZE_OFFSET 0x8 +#define SDIOC_UP_BUFF_SIZE_OFFSET 0xC +#define SDIOC_DL_WR_PTR 0x10 +#define SDIOC_DL_RD_PTR 0x14 +#define SDIOC_UL_WR_PTR 0x18 +#define SDIOC_UL_RD_PTR 0x1C +#define SDIOC_EXIT_PTR 0x20 +#define SDIOC_OP_MODE_PTR 0x24 +#define SDIOC_PTRS_OFFSET 0x10 +#define SDIOC_PTR_REGS_SIZE 0x10 +#define SDIOC_CFG_REGS_SIZE 0x10 +#define WRITE_RETRIES 0xFFFFFFFF +#define INPUT_SPEED 4800 +#define OUTPUT_SPEED 4800 +#define SDIOC_EXIT_CODE 0xDEADDEAD +#define SLEEP_MS 10 +#define PRINTING_GAP 200 +#define TIMER_DURATION 10 +#define PUSH_TIMER_DURATION 5000 +#define MULTIPLE_RATIO 1 +#define MS_IN_SEC 1000 +#define BITS_IN_BYTE 8 +#define BYTES_IN_KB 1024 +#define WRITE_TILL_END_RETRIES 5 +#define SDIO_DLD_NORMAL_MODE_NAME "SDIO DLD NORMAL MODE" +#define SDIO_DLD_BOOT_TEST_MODE_NAME "SDIO DLD BOOT TEST MODE" +#define SDIO_DLD_AMSS_TEST_MODE_NAME "SDIO DLD AMSS TEST MODE" +#define TEST_NAME_MAX_SIZE 30 +#define PUSH_STRING +#define SDIO_DLD_OUTGOING_BUFFER_SIZE (48*1024*MULTIPLE_RATIO) + +/* FORWARD DECLARATIONS */ +static int sdio_dld_open(struct tty_struct *tty, struct file *file); +static void sdio_dld_close(struct tty_struct *tty, struct file *file); +static int sdio_dld_write_callback(struct tty_struct *tty, + const unsigned char *buf, int count); +static int sdio_dld_write_room(struct tty_struct *tty); +static int sdio_dld_main_task(void *card); +static void sdio_dld_print_info(void); +#ifdef CONFIG_DEBUG_FS +static int sdio_dld_debug_info_open(struct inode *inode, struct file *file); +static ssize_t sdio_dld_debug_info_write(struct file *file, + const char __user *buf, size_t count, loff_t *ppos); +#endif + + +/* STRUCTURES AND TYPES */ +enum sdio_dld_op_mode { + SDIO_DLD_NO_MODE = 0, + SDIO_DLD_NORMAL_MODE = 1, + SDIO_DLD_BOOT_TEST_MODE = 2, + SDIO_DLD_AMSS_TEST_MODE = 3, + SDIO_DLD_NUM_OF_MODES, +}; + +struct sdioc_reg_sequential_chunk_ptrs { + unsigned int dl_wr_ptr; + unsigned int dl_rd_ptr; + unsigned int up_wr_ptr; + unsigned int up_rd_ptr; +}; + +struct sdioc_reg_sequential_chunk_cfg { + unsigned int dl_buff_address; + unsigned int up_buff_address; + unsigned int dl_buff_size; + unsigned int ul_buff_size; +}; + +struct sdioc_reg { + unsigned int reg_val; + unsigned int reg_offset; +}; + +struct sdioc_reg_chunk { + struct sdioc_reg dl_buff_address; + struct sdioc_reg up_buff_address; + struct sdioc_reg dl_buff_size; + struct sdioc_reg ul_buff_size; + struct sdioc_reg dl_wr_ptr; + struct sdioc_reg dl_rd_ptr; + struct sdioc_reg up_wr_ptr; + struct sdioc_reg up_rd_ptr; + struct sdioc_reg good_to_exit_ptr; +}; + +struct sdio_data { + char *data; + int offset_read_p; + int offset_write_p; + int buffer_size; + int num_of_bytes_in_use; +}; + +struct sdio_dld_data { + struct sdioc_reg_chunk sdioc_reg; + struct sdio_data incoming_data; + struct sdio_data outgoing_data; +}; + +struct sdio_dld_wait_event { + wait_queue_head_t wait_event; + int wake_up_signal; +}; + +struct sdio_dld_task { + struct task_struct *dld_task; + const char *task_name; + struct sdio_dld_wait_event exit_wait; + atomic_t please_close; +}; + +#ifdef CONFIG_DEBUG_FS +struct sdio_dloader_debug { + struct dentry *sdio_dld_debug_root; + struct dentry *sdio_al_dloader; +}; + +const struct file_operations sdio_dld_debug_info_ops = { + .open = sdio_dld_debug_info_open, + .write = sdio_dld_debug_info_write, +}; +#endif + +struct sdio_downloader { + int sdioc_boot_func; + struct sdio_dld_wait_event write_callback_event; + struct sdio_dld_task dld_main_thread; + struct tty_driver *tty_drv; + struct tty_struct *tty_str; + struct sdio_dld_data sdio_dloader_data; + struct mmc_card *card; + int(*done_callback)(void); + struct sdio_dld_wait_event main_loop_event; + struct timer_list timer; + unsigned int poll_ms; + struct timer_list push_timer; + unsigned int push_timer_ms; + enum sdio_dld_op_mode op_mode; + char op_mode_name[TEST_NAME_MAX_SIZE]; +}; + +struct sdio_dld_global_info { + int global_bytes_write_toio; + int global_bytes_write_tty; + int global_bytes_read_fromio; + int global_bytes_push_tty; + u64 start_time; + u64 end_time; + u64 delta_jiffies; + unsigned int time_msec; + unsigned int throughput; + int cl_dl_wr_ptr; + int cl_dl_rd_ptr; + int cl_up_wr_ptr; + int cl_up_rd_ptr; + int host_read_ptr; + int host_write_ptr; + int cl_dl_buffer_size; + int cl_up_buffer_size; + int host_outgoing_buffer_size; + int cl_dl_buffer_address; + int cl_up_buffer_address; +}; + +static const struct tty_operations sdio_dloader_tty_ops = { + .open = sdio_dld_open, + .close = sdio_dld_close, + .write = sdio_dld_write_callback, + .write_room = sdio_dld_write_room, +}; + +/* GLOBAL VARIABLES */ +struct sdio_downloader *sdio_dld; +struct sdio_dld_global_info sdio_dld_info; +static char outgoing_data_buffer[SDIO_DLD_OUTGOING_BUFFER_SIZE]; + +static DEFINE_SPINLOCK(lock1); +static unsigned long lock_flags1; +static DEFINE_SPINLOCK(lock2); +static unsigned long lock_flags2; + +/* + * sdio_op_mode sets the operation mode of the sdio_dloader - + * it may be in NORMAL_MODE, BOOT_TEST_MODE or AMSS_TEST_MODE + */ +static int sdio_op_mode = (int)SDIO_DLD_NORMAL_MODE; +module_param(sdio_op_mode, int, 0); + +#ifdef CONFIG_DEBUG_FS + +struct sdio_dloader_debug sdio_dld_debug; + +#define ARR_SIZE 30000 +#define SDIO_DLD_DEBUGFS_INIT_VALUE 87654321 +#define SDIO_DLD_DEBUGFS_CASE_1_CODE 11111111 +#define SDIO_DLD_DEBUGFS_CASE_2_CODE 22222222 +#define SDIO_DLD_DEBUGFS_CASE_3_CODE 33333333 +#define SDIO_DLD_DEBUGFS_CASE_4_CODE 44444444 +#define SDIO_DLD_DEBUGFS_CASE_5_CODE 55555555 +#define SDIO_DLD_DEBUGFS_CASE_6_CODE 66666666 +#define SDIO_DLD_DEBUGFS_CASE_7_CODE 77777777 +#define SDIO_DLD_DEBUGFS_CASE_8_CODE 88888888 +#define SDIO_DLD_DEBUGFS_CASE_9_CODE 99999999 +#define SDIO_DLD_DEBUGFS_CASE_10_CODE 10101010 +#define SDIO_DLD_DEBUGFS_CASE_11_CODE 11001100 +#define SDIO_DLD_DEBUGFS_CASE_12_CODE 12001200 +#define SDIO_DLD_DEBUGFS_LOOP_WAIT 7 +#define SDIO_DLD_DEBUGFS_LOOP_WAKEUP 8 +#define SDIO_DLD_DEBUGFS_CB_WAIT 3 +#define SDIO_DLD_DEBUGFS_CB_WAKEUP 4 + +static int curr_index; +struct ptrs { + int h_w_ptr; + int h_r_ptr; + int c_u_w_ptr; + int c_u_r_ptr; + int code; + int h_has_to_send; + int c_has_to_receive; + int min_of; + int reserve2; + int tty_count; + int write_tty; + int write_toio; + int loop_wait_wake; + int cb_wait_wake; + int c_d_w_ptr; + int c_d_r_ptr; + int to_read; + int push_to_tty; + int global_tty_send; + int global_sdio_send; + int global_tty_received; + int global_sdio_received; + int reserve22; + int reserve23; + int reserve24; + int reserve25; + int reserve26; + int reserve27; + int reserve28; + int reserve29; + int reserve30; + int reserve31; +}; + +struct global_data { + int curr_i; + int duration_ms; + int global_bytes_sent; + int throughput_Mbs; + int host_outgoing_buffer_size_KB; + int client_up_buffer_size_KB; + int client_dl_buffer_size_KB; + int client_dl_buffer_address; + int client_up_buffer_address; + int global_bytes_received; + int global_bytes_pushed; + int reserve11; + int reserve12; + int reserve13; + int reserve14; + int reserve15; + int reserve16; + int reserve17; + int reserve18; + int reserve19; + int reserve20; + int reserve21; + int reserve22; + int reserve23; + int reserve24; + int reserve25; + int reserve26; + int reserve27; + int reserve28; + int reserve29; + int reserve30; + int reserve31; + struct ptrs ptr_array[ARR_SIZE]; +}; + +static struct global_data gd; +static struct debugfs_blob_wrapper blob; +static struct dentry *root; +static struct dentry *dld; + +struct debugfs_global { + int global_8k_has; + int global_9k_has; + int global_min; + int global_count; + int global_write_tty; + int global_write_toio; + int global_bytes_cb_tty; + int global_to_read; + int global_push_to_tty; + int global_tty_send; + int global_sdio_send; + int global_sdio_received; + int global_tty_push; +}; + +static struct debugfs_global debugfs_glob; + +static void update_standard_fields(int index) +{ + + gd.ptr_array[index].global_tty_send = + sdio_dld_info.global_bytes_write_tty; + gd.ptr_array[index].global_sdio_send = + sdio_dld_info.global_bytes_write_toio; + gd.ptr_array[index].global_tty_received = + sdio_dld_info.global_bytes_push_tty; + gd.ptr_array[index].global_sdio_received = + sdio_dld_info.global_bytes_read_fromio; +} + +static void update_gd(int code) +{ + struct sdioc_reg_chunk *reg_str = + &sdio_dld->sdio_dloader_data.sdioc_reg; + struct sdio_data *outgoing = &sdio_dld->sdio_dloader_data.outgoing_data; + int index = curr_index%ARR_SIZE; + + gd.curr_i = curr_index; + gd.duration_ms = 0; + gd.global_bytes_sent = 0; + gd.throughput_Mbs = 0; + gd.host_outgoing_buffer_size_KB = 0; + gd.client_up_buffer_size_KB = 0; + gd.client_dl_buffer_size_KB = 0; + gd.client_dl_buffer_address = 0; + gd.client_up_buffer_address = 0; + gd.global_bytes_received = 0; + gd.global_bytes_pushed = 0; + gd.reserve11 = 0; + gd.reserve12 = 0; + gd.reserve13 = 0; + gd.reserve14 = 0; + gd.reserve15 = 0; + gd.reserve16 = 0; + gd.reserve17 = 0; + gd.reserve18 = 0; + gd.reserve19 = 0; + gd.reserve20 = 0; + gd.reserve21 = 0; + gd.reserve22 = 0; + gd.reserve23 = 0; + gd.reserve24 = 0; + gd.reserve25 = 0; + gd.reserve26 = 0; + gd.reserve27 = 0; + gd.reserve28 = 0; + gd.reserve29 = 0; + gd.reserve30 = 0; + gd.reserve31 = 0; + + gd.ptr_array[index].h_w_ptr = SDIO_DLD_DEBUGFS_INIT_VALUE; /*0*/ + gd.ptr_array[index].h_r_ptr = SDIO_DLD_DEBUGFS_INIT_VALUE; /*1*/ + gd.ptr_array[index].c_u_w_ptr = SDIO_DLD_DEBUGFS_INIT_VALUE; /*2*/ + gd.ptr_array[index].c_u_r_ptr = SDIO_DLD_DEBUGFS_INIT_VALUE; /*3*/ + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_INIT_VALUE; /*4*/ + gd.ptr_array[index].h_has_to_send = SDIO_DLD_DEBUGFS_INIT_VALUE;/*5*/ + gd.ptr_array[index].c_has_to_receive = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*6*/ + gd.ptr_array[index].min_of = SDIO_DLD_DEBUGFS_INIT_VALUE; /*7*/ + gd.ptr_array[index].reserve2 = SDIO_DLD_DEBUGFS_INIT_VALUE; /*8*/ + gd.ptr_array[index].tty_count = SDIO_DLD_DEBUGFS_INIT_VALUE; /*9*/ + gd.ptr_array[index].write_tty = SDIO_DLD_DEBUGFS_INIT_VALUE; /*A*/ + gd.ptr_array[index].write_toio = SDIO_DLD_DEBUGFS_INIT_VALUE; /*B*/ + gd.ptr_array[index].loop_wait_wake = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*C*/ + gd.ptr_array[index].cb_wait_wake = SDIO_DLD_DEBUGFS_INIT_VALUE; /*D*/ + gd.ptr_array[index].c_d_w_ptr = SDIO_DLD_DEBUGFS_INIT_VALUE; /*E*/ + gd.ptr_array[index].c_d_r_ptr = SDIO_DLD_DEBUGFS_INIT_VALUE; /*F*/ + gd.ptr_array[index].to_read = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*0x10*/ + gd.ptr_array[index].push_to_tty = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*0x11*/ + gd.ptr_array[index].global_tty_send = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*0x12*/ + gd.ptr_array[index].global_sdio_send = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*0x13*/ + gd.ptr_array[index].global_tty_received = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*0x14*/ + gd.ptr_array[index].global_sdio_received = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*0x15*/ + gd.ptr_array[index].reserve22 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve23 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve24 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve25 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve26 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve27 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve28 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve29 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve30 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve31 = SDIO_DLD_DEBUGFS_INIT_VALUE; + + switch (code) { + case SDIO_DLD_DEBUGFS_CASE_1_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_1_CODE; + gd.ptr_array[index].h_w_ptr = outgoing->offset_write_p; + gd.ptr_array[index].h_r_ptr = outgoing->offset_read_p; + gd.ptr_array[index].c_u_w_ptr = reg_str->up_wr_ptr.reg_val; + gd.ptr_array[index].c_u_r_ptr = reg_str->up_rd_ptr.reg_val; + gd.ptr_array[index].c_d_w_ptr = reg_str->dl_wr_ptr.reg_val; + gd.ptr_array[index].c_d_r_ptr = reg_str->dl_rd_ptr.reg_val; + break; + + case SDIO_DLD_DEBUGFS_CASE_2_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_2_CODE; + gd.ptr_array[index].c_u_r_ptr = reg_str->up_rd_ptr.reg_val; + gd.ptr_array[index].c_u_w_ptr = reg_str->up_wr_ptr.reg_val; + gd.ptr_array[index].h_has_to_send = debugfs_glob.global_8k_has; + gd.ptr_array[index].c_has_to_receive = + debugfs_glob.global_9k_has; + gd.ptr_array[index].min_of = debugfs_glob.global_min; + break; + + case SDIO_DLD_DEBUGFS_CASE_3_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_3_CODE; + gd.ptr_array[index].h_w_ptr = outgoing->offset_write_p; + gd.ptr_array[index].h_r_ptr = outgoing->offset_read_p; + gd.ptr_array[index].write_tty = debugfs_glob.global_write_tty; + break; + + case SDIO_DLD_DEBUGFS_CASE_4_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_4_CODE; + gd.ptr_array[index].h_w_ptr = outgoing->offset_write_p; + gd.ptr_array[index].h_r_ptr = outgoing->offset_read_p; + gd.ptr_array[index].c_u_r_ptr = reg_str->up_rd_ptr.reg_val; + gd.ptr_array[index].c_u_w_ptr = reg_str->up_wr_ptr.reg_val; + gd.ptr_array[index].write_toio = + debugfs_glob.global_write_toio; + break; + + case SDIO_DLD_DEBUGFS_CASE_5_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_5_CODE; + gd.ptr_array[index].tty_count = debugfs_glob.global_count; + break; + + case SDIO_DLD_DEBUGFS_CASE_6_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_6_CODE; + gd.ptr_array[index].loop_wait_wake = 7; + break; + + case SDIO_DLD_DEBUGFS_CASE_7_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_7_CODE; + gd.ptr_array[index].loop_wait_wake = 8; + break; + + case SDIO_DLD_DEBUGFS_CASE_8_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_8_CODE; + gd.ptr_array[index].cb_wait_wake = 3; + break; + + case SDIO_DLD_DEBUGFS_CASE_9_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_9_CODE; + gd.ptr_array[index].cb_wait_wake = 4; + break; + + case SDIO_DLD_DEBUGFS_CASE_10_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_10_CODE; + gd.ptr_array[index].cb_wait_wake = + debugfs_glob.global_bytes_cb_tty; + break; + + case SDIO_DLD_DEBUGFS_CASE_11_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_11_CODE; + gd.ptr_array[index].to_read = debugfs_glob.global_to_read; + break; + + case SDIO_DLD_DEBUGFS_CASE_12_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_12_CODE; + gd.ptr_array[index].push_to_tty = + debugfs_glob.global_push_to_tty; + break; + + default: + break; + } + update_standard_fields(index); + curr_index++; +} + +static int bootloader_debugfs_init(void) +{ + /* /sys/kernel/debug/bootloader there will be dld_arr file */ + root = debugfs_create_dir("bootloader", NULL); + if (!root) { + pr_info(MODULE_NAME ": %s - creating root dir " + "failed\n", __func__); + return -ENODEV; + } + + blob.data = &gd; + blob.size = sizeof(struct global_data); + dld = debugfs_create_blob("dld_arr", S_IRUGO, root, &blob); + if (!dld) { + debugfs_remove_recursive(root); + pr_err(MODULE_NAME ": %s, failed to create debugfs entry\n", + __func__); + return -ENODEV; + } + + return 0; +} + +/* +* for triggering the sdio_dld info use: +* echo 1 > /sys/kernel/debug/sdio_al_dld/sdio_al_dloader_info +*/ +static int sdio_dld_debug_init(void) +{ + sdio_dld_debug.sdio_dld_debug_root = + debugfs_create_dir("sdio_al_dld", NULL); + if (!sdio_dld_debug.sdio_dld_debug_root) { + pr_err(MODULE_NAME ": %s - Failed to create folder. " + "sdio_dld_debug_root is NULL", + __func__); + return -ENOENT; + } + + sdio_dld_debug.sdio_al_dloader = debugfs_create_file( + "sdio_al_dloader_info", + S_IRUGO | S_IWUGO, + sdio_dld_debug.sdio_dld_debug_root, + NULL, + &sdio_dld_debug_info_ops); + + if (!sdio_dld_debug.sdio_al_dloader) { + pr_err(MODULE_NAME ": %s - Failed to create a file. " + "sdio_al_dloader is NULL", + __func__); + debugfs_remove(sdio_dld_debug.sdio_dld_debug_root); + sdio_dld_debug.sdio_dld_debug_root = NULL; + return -ENOENT; + } + + return 0; +} + +static int sdio_dld_debug_info_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t sdio_dld_debug_info_write(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + sdio_dld_print_info(); + return count; +} +#endif /* CONFIG_DEBUG_FS */ + +static void sdio_dld_print_info(void) +{ + + sdio_dld_info.end_time = get_jiffies_64(); /* read the current time */ + sdio_dld_info.delta_jiffies = + sdio_dld_info.end_time - sdio_dld_info.start_time; + sdio_dld_info.time_msec = jiffies_to_msecs(sdio_dld_info.delta_jiffies); + + sdio_dld_info.throughput = sdio_dld_info.global_bytes_write_toio * + BITS_IN_BYTE / sdio_dld_info.time_msec; + sdio_dld_info.throughput /= MS_IN_SEC; + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - DURATION IN MSEC = %d\n", + __func__, + sdio_dld_info.time_msec); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - BYTES WRITTEN ON SDIO BUS " + "= %d...BYTES SENT BY TTY = %d", + __func__, + sdio_dld_info.global_bytes_write_toio, + sdio_dld_info.global_bytes_write_tty); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - BYTES RECEIVED ON SDIO BUS " + "= %d...BYTES SENT TO TTY = %d", + __func__, + sdio_dld_info.global_bytes_read_fromio, + sdio_dld_info.global_bytes_push_tty); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - THROUGHPUT=%d Mbit/Sec", + __func__, sdio_dld_info.throughput); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - CLIENT DL_BUFFER_SIZE=%d" + " KB..CLIENT UL_BUFFER=%d KB\n", + __func__, + sdio_dld_info.cl_dl_buffer_size/BYTES_IN_KB, + sdio_dld_info.cl_up_buffer_size/BYTES_IN_KB); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - HOST OUTGOING BUFFER_SIZE" + "=%d KB", + __func__, + sdio_dld_info.host_outgoing_buffer_size/BYTES_IN_KB); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - CLIENT DL BUFFER " + "ADDRESS = 0x%x", __func__, + sdio_dld_info.cl_dl_buffer_address); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - CLIENT UP BUFFER " + "ADDRESS = 0x%x", + __func__, + sdio_dld_info.cl_up_buffer_address); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - CLIENT - UPLINK BUFFER - " + "READ POINTER = %d", __func__, + sdio_dld_info.cl_up_rd_ptr); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - CLIENT - UPLINK BUFFER - " + "WRITE POINTER = %d", __func__, + sdio_dld_info.cl_up_wr_ptr); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - CLIENT - DOWNLINK BUFFER - " + "READ POINTER = %d", __func__, + sdio_dld_info.cl_dl_rd_ptr); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - CLIENT - DOWNLINK BUFFER - " + "WRITE POINTER = %d", __func__, + sdio_dld_info.cl_dl_wr_ptr); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - HOST - OUTGOING BUFFER - " + "READ POINTER = %d", __func__, + sdio_dld_info.host_read_ptr); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - HOST - OUTGOING BUFFER - " + "WRITE POINTER = %d", __func__, + sdio_dld_info.host_write_ptr); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - END DEBUG INFO", __func__); +} + +/** + * sdio_dld_set_op_mode + * sets the op_mode and the name of the op_mode. Also, in case + * it's invalid mode sets op_mode to SDIO_DLD_NORMAL_MODE + * + * @op_mode: the operation mode to be set + * @return NONE + */ +static void sdio_dld_set_op_mode(enum sdio_dld_op_mode op_mode) +{ + sdio_dld->op_mode = op_mode; + + switch (op_mode) { + case SDIO_DLD_NORMAL_MODE: + memcpy(sdio_dld->op_mode_name, + SDIO_DLD_NORMAL_MODE_NAME, TEST_NAME_MAX_SIZE); + break; + case SDIO_DLD_BOOT_TEST_MODE: + memcpy(sdio_dld->op_mode_name, + SDIO_DLD_BOOT_TEST_MODE_NAME, TEST_NAME_MAX_SIZE); + break; + case SDIO_DLD_AMSS_TEST_MODE: + memcpy(sdio_dld->op_mode_name, + SDIO_DLD_AMSS_TEST_MODE_NAME, TEST_NAME_MAX_SIZE); + break; + default: + sdio_dld->op_mode = SDIO_DLD_NORMAL_MODE; + pr_err(MODULE_NAME ": %s - Invalid Op_Mode = %d. Settings " + "Op_Mode to default - NORMAL_MODE\n", + __func__, op_mode); + memcpy(sdio_dld->op_mode_name, + SDIO_DLD_NORMAL_MODE_NAME, TEST_NAME_MAX_SIZE); + break; + } + + if (sdio_dld->op_mode_name != NULL) { + pr_info(MODULE_NAME ": %s - FLASHLESS BOOT - Op_Mode is set to " + "%s\n", __func__, sdio_dld->op_mode_name); + } else { + pr_info(MODULE_NAME ": %s - FLASHLESS BOOT - op_mode_name is " + "NULL\n", __func__); + } +} + +/** + * sdio_dld_allocate_local_buffers + * allocates local outgoing and incoming buffers and also sets + * threshold for outgoing data. + * + * @return 0 on success or negative value on error. + */ +static int sdio_dld_allocate_local_buffers(void) +{ + struct sdioc_reg_chunk *reg_str = &sdio_dld->sdio_dloader_data. + sdioc_reg; + struct sdio_data *outgoing = &sdio_dld->sdio_dloader_data.outgoing_data; + struct sdio_data *incoming = &sdio_dld->sdio_dloader_data.incoming_data; + + incoming->data = + kzalloc(reg_str->dl_buff_size.reg_val, GFP_KERNEL); + + if (!incoming->data) { + pr_err(MODULE_NAME ": %s - param ""incoming->data"" is NULL. " + "Couldn't allocate incoming_data local buffer\n", + __func__); + return -ENOMEM; + } + + incoming->buffer_size = reg_str->dl_buff_size.reg_val; + + outgoing->data = outgoing_data_buffer; + + outgoing->buffer_size = SDIO_DLD_OUTGOING_BUFFER_SIZE; + + if (outgoing->buffer_size != + reg_str->ul_buff_size.reg_val*MULTIPLE_RATIO) { + pr_err(MODULE_NAME ": %s - HOST outgoing buffer size (%d bytes)" + "must be a multiple of ClIENT uplink buffer size (%d " + "bytes). HOST_SIZE == n*CLIENT_SIZE.(n=1,2,3...)\n", + __func__, + SDIO_DLD_OUTGOING_BUFFER_SIZE, + reg_str->ul_buff_size.reg_val); + kfree(incoming->data); + return -EINVAL; + } + + /* keep sdio_dld_info up to date */ + sdio_dld_info.host_outgoing_buffer_size = outgoing->buffer_size; + + return 0; +} + +/** + * sdio_dld_dealloc_local_buffers frees incoming and outgoing + * buffers. + * + * @return None. + */ +static void sdio_dld_dealloc_local_buffers(void) +{ + kfree((void *)sdio_dld->sdio_dloader_data.incoming_data.data); +} + +/** + * mailbox_to_seq_chunk_read_cfg + * reads 4 configuration registers of mailbox from str_func, as + * a sequentail chunk in memory, and updates global struct + * accordingly. + * + * @str_func: a pointer to func struct. + * @return 0 on success or negative value on error. + */ +static int mailbox_to_seq_chunk_read_cfg(struct sdio_func *str_func) +{ + struct sdioc_reg_sequential_chunk_cfg seq_chunk; + struct sdioc_reg_chunk *reg = &sdio_dld->sdio_dloader_data.sdioc_reg; + int status = 0; + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + sdio_claim_host(str_func); + + /* reading SDIOC_MAILBOX_SIZE bytes from SDIOC_MAILBOX_ADDRESS */ + status = sdio_memcpy_fromio(str_func, + (void *)&seq_chunk, + SDIOC_MAILBOX_ADDRESS, + SDIOC_CFG_REGS_SIZE); + if (status) { + pr_err(MODULE_NAME ": %s - sdio_memcpy_fromio()" + " READING CFG MAILBOX failed. status=%d.\n", + __func__, status); + } + + sdio_release_host(str_func); + + reg->dl_buff_address.reg_val = seq_chunk.dl_buff_address; + reg->up_buff_address.reg_val = seq_chunk.up_buff_address; + reg->dl_buff_size.reg_val = seq_chunk.dl_buff_size; + reg->ul_buff_size.reg_val = seq_chunk.ul_buff_size; + + /* keep sdio_dld_info up to date */ + sdio_dld_info.cl_dl_buffer_size = seq_chunk.dl_buff_size; + sdio_dld_info.cl_up_buffer_size = seq_chunk.ul_buff_size; + sdio_dld_info.cl_dl_buffer_address = seq_chunk.dl_buff_address; + sdio_dld_info.cl_up_buffer_address = seq_chunk.up_buff_address; + + return status; +} + +/** + * mailbox_to_seq_chunk_read_ptrs + * reads 4 pointers registers of mailbox from str_func, as a + * sequentail chunk in memory, and updates global struct + * accordingly. + * + * @str_func: a pointer to func struct. + * @return 0 on success or negative value on error. + */ +static int mailbox_to_seq_chunk_read_ptrs(struct sdio_func *str_func) +{ + struct sdioc_reg_sequential_chunk_ptrs seq_chunk; + struct sdioc_reg_chunk *reg = &sdio_dld->sdio_dloader_data.sdioc_reg; + int status = 0; + + struct sdio_data *outgoing = &sdio_dld->sdio_dloader_data.outgoing_data; + static int counter = 1; + static int offset_write_p; + static int offset_read_p; + static int up_wr_ptr; + static int up_rd_ptr; + static int dl_wr_ptr; + static int dl_rd_ptr; + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + sdio_claim_host(str_func); + + /* reading SDIOC_MAILBOX_SIZE bytes from SDIOC_MAILBOX_ADDRESS */ + status = sdio_memcpy_fromio(str_func, + (void *)&seq_chunk, + SDIOC_PTRS_OFFSET, + SDIOC_PTR_REGS_SIZE); + if (status) { + pr_err(MODULE_NAME ": %s - sdio_memcpy_fromio()" + " READING PTRS MAILBOX failed. status=%d.\n", + __func__, status); + } + + sdio_release_host(str_func); + + reg->dl_rd_ptr.reg_val = seq_chunk.dl_rd_ptr; + reg->dl_wr_ptr.reg_val = seq_chunk.dl_wr_ptr; + reg->up_rd_ptr.reg_val = seq_chunk.up_rd_ptr; + reg->up_wr_ptr.reg_val = seq_chunk.up_wr_ptr; + + /* keeping sdio_dld_info up to date */ + sdio_dld_info.cl_dl_rd_ptr = seq_chunk.dl_rd_ptr; + sdio_dld_info.cl_dl_wr_ptr = seq_chunk.dl_wr_ptr; + sdio_dld_info.cl_up_rd_ptr = seq_chunk.up_rd_ptr; + sdio_dld_info.cl_up_wr_ptr = seq_chunk.up_wr_ptr; + + + /* DEBUG - if there was a change in value */ + if ((offset_write_p != outgoing->offset_write_p) || + (offset_read_p != outgoing->offset_read_p) || + (up_wr_ptr != reg->up_wr_ptr.reg_val) || + (up_rd_ptr != reg->up_rd_ptr.reg_val) || + (dl_wr_ptr != reg->dl_wr_ptr.reg_val) || + (dl_rd_ptr != reg->dl_rd_ptr.reg_val) || + (counter % PRINTING_GAP == 0)) { + counter = 1; + pr_debug(MODULE_NAME ": %s MailBox pointers: BLOCK_SIZE=%d, " + "hw=%d, hr=%d, cuw=%d, cur=%d, cdw=%d, cdr=%d\n", + __func__, + SDIO_DL_BLOCK_SIZE, + outgoing->offset_write_p, + outgoing->offset_read_p, + reg->up_wr_ptr.reg_val, + reg->up_rd_ptr.reg_val, + reg->dl_wr_ptr.reg_val, + reg->dl_rd_ptr.reg_val); + +#ifdef CONFIG_DEBUG_FS + update_gd(SDIO_DLD_DEBUGFS_CASE_1_CODE); +#endif + /* update static variables */ + offset_write_p = outgoing->offset_write_p; + offset_read_p = outgoing->offset_read_p; + up_wr_ptr = reg->up_wr_ptr.reg_val; + up_rd_ptr = reg->up_rd_ptr.reg_val; + dl_wr_ptr = reg->dl_wr_ptr.reg_val; + dl_rd_ptr = reg->dl_rd_ptr.reg_val; + } else { + counter++; + } + return status; +} + +/** + * sdio_dld_init_func + * enables the sdio func, and sets the func block size. + * + * @str_func: a pointer to func struct. + * @return 0 on success or negative value on error. + */ +static int sdio_dld_init_func(struct sdio_func *str_func) +{ + int status1 = 0; + int status2 = 0; + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + sdio_claim_host(str_func); + + status1 = sdio_enable_func(str_func); + if (status1) { + sdio_release_host(str_func); + pr_err(MODULE_NAME ": %s - sdio_enable_func() failed. " + "status=%d\n", __func__, status1); + return status1; + } + + status2 = sdio_set_block_size(str_func, SDIO_DL_BLOCK_SIZE); + if (status2) { + pr_err(MODULE_NAME ": %s - sdio_set_block_size() failed. " + "status=%d\n", __func__, status2); + status1 = sdio_disable_func(str_func); + if (status1) { + pr_err(MODULE_NAME ": %s - sdio_disable_func() " + "failed. status=%d\n", __func__, status1); + } + sdio_release_host(str_func); + return status2; + } + + sdio_release_host(str_func); + str_func->max_blksize = SDIO_DL_BLOCK_SIZE; + return 0; +} + +/** + * sdio_dld_allocate_buffers + * initializes the sdio func, and then reads the mailbox, in + * order to allocate incoming and outgoing buffers according to + * the size that was read from the mailbox. + * + * @str_func: a pointer to func struct. + * @return 0 on success or negative value on error. + */ +static int sdio_dld_allocate_buffers(struct sdio_func *str_func) +{ + int status = 0; + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + status = mailbox_to_seq_chunk_read_cfg(str_func); + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "mailbox_to_seq_chunk_read_cfg(). status=%d\n", + __func__, status); + return status; + } + + status = sdio_dld_allocate_local_buffers(); + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdio_dld_allocate_local_buffers(). status=%d\n", + __func__, status); + return status; + } + return 0; +} + +/** + * sdio_dld_create_thread + * creates thread and wakes it up. + * + * @return 0 on success or negative value on error. + */ +static int sdio_dld_create_thread(void) +{ + sdio_dld->dld_main_thread.task_name = SDIO_DL_MAIN_THREAD_NAME; + + sdio_dld->dld_main_thread.dld_task = + kthread_create(sdio_dld_main_task, + (void *)(sdio_dld->card), + sdio_dld->dld_main_thread.task_name); + + if (IS_ERR(sdio_dld->dld_main_thread.dld_task)) { + pr_err(MODULE_NAME ": %s - kthread_create() failed\n", + __func__); + return -ENOMEM; + } + wake_up_process(sdio_dld->dld_main_thread.dld_task); + return 0; +} + +/** + * start_timer + * sets the timer and starts. + * + * @timer: the timer to configure and add + * @ms: the ms until it expires + * @return None. + */ +static void start_timer(struct timer_list *timer, unsigned int ms) +{ + if ((ms == 0) || (timer == NULL)) { + pr_err(MODULE_NAME ": %s - invalid parameter", __func__); + } else { + timer->expires = jiffies + + msecs_to_jiffies(ms); + add_timer(timer); + } +} + +/** + * sdio_dld_timer_handler + * this is the timer handler. whenever it is invoked, it wakes + * up the main loop task, and the write callback, and starts + * the timer again. + * + * @data: a pointer to the tty device driver structure. + * @return None. + */ + +static void sdio_dld_timer_handler(unsigned long data) +{ + pr_debug(MODULE_NAME " Timer Expired\n"); + spin_lock_irqsave(&lock2, lock_flags2); + if (sdio_dld->main_loop_event.wake_up_signal == 0) { + sdio_dld->main_loop_event.wake_up_signal = 1; + wake_up(&sdio_dld->main_loop_event.wait_event); + } + spin_unlock_irqrestore(&lock2, lock_flags2); + + sdio_dld->write_callback_event.wake_up_signal = 1; + wake_up(&sdio_dld->write_callback_event.wait_event); + + start_timer(&sdio_dld->timer, sdio_dld->poll_ms); +} + +/** + * sdio_dld_push_timer_handler + * this is a timer handler of the push_timer. + * + * @data: a pointer to the tty device driver structure. + * @return None. + */ +static void sdio_dld_push_timer_handler(unsigned long data) +{ + pr_err(MODULE_NAME " %s - Push Timer Expired... Trying to " + "push data to TTY Core for over then %d ms.\n", + __func__, sdio_dld->push_timer_ms); +} + +/** + * sdio_dld_open + * this is the open callback of the tty driver. + * it initializes the sdio func, allocates the buffers, and + * creates the main thread. + * + * @tty: a pointer to the tty struct. + * @file: file descriptor. + * @return 0 on success or negative value on error. + */ +static int sdio_dld_open(struct tty_struct *tty, struct file *file) +{ + int status = 0; + int func_in_array = + REAL_FUNC_TO_FUNC_IN_ARRAY(sdio_dld->sdioc_boot_func); + struct sdio_func *str_func = sdio_dld->card->sdio_func[func_in_array]; + + sdio_dld->tty_str = tty; + sdio_dld->tty_str->low_latency = 1; + sdio_dld->tty_str->icanon = 0; + set_bit(TTY_NO_WRITE_SPLIT, &sdio_dld->tty_str->flags); + + pr_info(MODULE_NAME ": %s, TTY DEVICE FOR FLASHLESS BOOT OPENED\n", + __func__); + sdio_dld_info.start_time = get_jiffies_64(); /* read the current time */ + + if (!tty) { + pr_err(MODULE_NAME ": %s - param ""tty"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + atomic_set(&sdio_dld->dld_main_thread.please_close, 0); + sdio_dld->dld_main_thread.exit_wait.wake_up_signal = 0; + + status = sdio_dld_allocate_buffers(str_func); + if (status) { + pr_err(MODULE_NAME ": %s, failed in " + "sdio_dld_allocate_buffers(). status=%d\n", + __func__, status); + return status; + } + + /* init waiting event of the write callback */ + init_waitqueue_head(&sdio_dld->write_callback_event.wait_event); + + /* init waiting event of the main loop */ + init_waitqueue_head(&sdio_dld->main_loop_event.wait_event); + + /* configure and init the timer */ + sdio_dld->poll_ms = TIMER_DURATION; + init_timer(&sdio_dld->timer); + sdio_dld->timer.data = (unsigned long) sdio_dld; + sdio_dld->timer.function = sdio_dld_timer_handler; + sdio_dld->timer.expires = jiffies + + msecs_to_jiffies(sdio_dld->poll_ms); + add_timer(&sdio_dld->timer); + + sdio_dld->push_timer_ms = PUSH_TIMER_DURATION; + init_timer(&sdio_dld->push_timer); + sdio_dld->push_timer.data = (unsigned long) sdio_dld; + sdio_dld->push_timer.function = sdio_dld_push_timer_handler; + + status = sdio_dld_create_thread(); + if (status) { + del_timer_sync(&sdio_dld->timer); + del_timer_sync(&sdio_dld->push_timer); + sdio_dld_dealloc_local_buffers(); + pr_err(MODULE_NAME ": %s, failed in sdio_dld_create_thread()." + "status=%d\n", __func__, status); + return status; + } + return 0; +} + +/** + * sdio_dld_close + * this is the close callback of the tty driver. it requests + * the main thread to exit, and waits for notification of it. + * it also de-allocates the buffers, and unregisters the tty + * driver and device. + * + * @tty: a pointer to the tty struct. + * @file: file descriptor. + * @return None. + */ +static void sdio_dld_close(struct tty_struct *tty, struct file *file) +{ + int status = 0; + struct sdioc_reg_chunk *reg = &sdio_dld->sdio_dloader_data.sdioc_reg; + + /* informing the SDIOC that it can exit boot phase */ + sdio_dld->sdio_dloader_data.sdioc_reg.good_to_exit_ptr.reg_val = + SDIOC_EXIT_CODE; + + atomic_set(&sdio_dld->dld_main_thread.please_close, 1); + + pr_debug(MODULE_NAME ": %s - CLOSING - WAITING...", __func__); + + wait_event(sdio_dld->dld_main_thread.exit_wait.wait_event, + sdio_dld->dld_main_thread.exit_wait.wake_up_signal); + pr_debug(MODULE_NAME ": %s - CLOSING - WOKE UP...", __func__); + + del_timer_sync(&sdio_dld->timer); + del_timer_sync(&sdio_dld->push_timer); + + sdio_dld_dealloc_local_buffers(); + + tty_unregister_device(sdio_dld->tty_drv, 0); + + status = tty_unregister_driver(sdio_dld->tty_drv); + + if (status) { + pr_err(MODULE_NAME ": %s - tty_unregister_driver() failed\n", + __func__); + } + +#ifdef CONFIG_DEBUG_FS + gd.curr_i = curr_index; + gd.duration_ms = sdio_dld_info.time_msec; + gd.global_bytes_sent = sdio_dld_info.global_bytes_write_toio; + gd.global_bytes_received = 0; + gd.throughput_Mbs = sdio_dld_info.throughput; + gd.host_outgoing_buffer_size_KB = sdio_dld->sdio_dloader_data. + outgoing_data.buffer_size/BYTES_IN_KB; + gd.client_up_buffer_size_KB = reg->ul_buff_size.reg_val/BYTES_IN_KB; + gd.client_dl_buffer_size_KB = reg->dl_buff_size.reg_val/BYTES_IN_KB; + gd.client_dl_buffer_address = reg->dl_buff_address.reg_val; + gd.client_up_buffer_address = reg->up_buff_address.reg_val; + gd.global_bytes_received = sdio_dld_info.global_bytes_read_fromio; + gd.global_bytes_pushed = sdio_dld_info.global_bytes_push_tty; +#endif + + /* saving register values before deallocating sdio_dld + in order to use it in sdio_dld_print_info() through shell command */ + sdio_dld_info.cl_dl_rd_ptr = reg->dl_rd_ptr.reg_val; + sdio_dld_info.cl_dl_wr_ptr = reg->dl_wr_ptr.reg_val; + sdio_dld_info.cl_up_rd_ptr = reg->up_rd_ptr.reg_val; + sdio_dld_info.cl_up_wr_ptr = reg->up_wr_ptr.reg_val; + + sdio_dld_info.host_read_ptr = + sdio_dld->sdio_dloader_data.outgoing_data.offset_read_p; + + sdio_dld_info.host_write_ptr = + sdio_dld->sdio_dloader_data.outgoing_data.offset_write_p; + + sdio_dld_info.cl_dl_buffer_size = + sdio_dld->sdio_dloader_data.sdioc_reg.dl_buff_size.reg_val; + + sdio_dld_info.cl_up_buffer_size = + sdio_dld->sdio_dloader_data.sdioc_reg.ul_buff_size.reg_val; + + sdio_dld_info.host_outgoing_buffer_size = + sdio_dld->sdio_dloader_data.outgoing_data.buffer_size; + + sdio_dld_info.cl_dl_buffer_address = + sdio_dld->sdio_dloader_data.sdioc_reg.dl_buff_address.reg_val; + + sdio_dld_info.cl_up_buffer_address = + sdio_dld->sdio_dloader_data.sdioc_reg.up_buff_address.reg_val; + + sdio_dld_print_info(); + + if (sdio_dld->done_callback) + sdio_dld->done_callback(); + + pr_info(MODULE_NAME ": %s - Freeing sdio_dld data structure, and " + " returning...", __func__); + kfree(sdio_dld); +} + +/** + * writing_size_to_buf + * writes from src buffer into dest buffer. if dest buffer + * reaches its end, rollover happens. + * + * @dest: destination buffer. + * @src: source buffer. + * @dest_wr_ptr: writing pointer in destination buffer. + * @dest_size: destination buffer size. + * @dest_rd_ptr: reading pointer in destination buffer. + * @size_to_write: size of bytes to write. + * @return -how many bytes actually written to destination + * buffer. + * + * ONLY destination buffer is treated as cyclic buffer. + */ +static int writing_size_to_buf(char *dest, + const unsigned char *src, + int *dest_wr_ptr, + int dest_size, + int dest_rd_ptr, + int size_to_write) +{ + int actually_written = 0; + int size_to_add = *dest_wr_ptr; + + if (!dest) { + pr_err(MODULE_NAME ": %s - param ""dest"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!src) { + pr_err(MODULE_NAME ": %s - param ""src"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!dest_wr_ptr) { + pr_err(MODULE_NAME ": %s - param ""dest_wr_ptr"" is NULL.\n", + __func__); + return -EINVAL; + } + + for (actually_written = 0 ; + actually_written < size_to_write ; ++actually_written) { + /* checking if buffer is full */ + if (((size_to_add + 1) % dest_size) == dest_rd_ptr) { + *dest_wr_ptr = size_to_add; + return actually_written; + } + + dest[size_to_add] = src[actually_written]; + size_to_add = (size_to_add+1)%dest_size; + } + + *dest_wr_ptr = size_to_add; + + return actually_written; +} + +/** + * sdioc_bytes_till_end_of_buffer - this routine calculates how many bytes are + * empty/in use. if calculation requires rap around - it will ignore the rap + * around and will do the calculation untill the end of the buffer + * + * @write_ptr: writing pointer. + * @read_ptr: reading pointer. + * @total_size: buffer size. + * @free_bytes: return value-how many free bytes. + * @bytes_in_use: return value-how many bytes in use. + * @return 0 on success or negative value on error. + * + * buffer is treated as a cyclic buffer. + */ +static int sdioc_bytes_till_end_of_buffer(int write_ptr, + int read_ptr, + int total_size, + int *free_bytes, + int *bytes_in_use) +{ + if (!free_bytes) { + pr_err(MODULE_NAME ": %s - param ""free_bytes"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!bytes_in_use) { + pr_err(MODULE_NAME ": %s - param ""bytes_in_use"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (write_ptr >= read_ptr) { + if (read_ptr == 0) + *free_bytes = total_size - write_ptr - 1; + else + *free_bytes = total_size - write_ptr; + *bytes_in_use = write_ptr - read_ptr; + } else { + *bytes_in_use = total_size - read_ptr; + *free_bytes = read_ptr - write_ptr - 1; + } + + return 0; +} + +/** + * sdioc_bytes_free_in_buffer + * this routine calculates how many bytes are free in a buffer + * and how many are in use, according to its reading and + * writing pointer offsets. + * + * @write_ptr: writing pointer. + * @read_ptr: reading pointer. + * @total_size: buffer size. + * @free_bytes: return value-how many free bytes in buffer. + * @bytes_in_use: return value-how many bytes in use in buffer. + * @return 0 on success or negative value on error. + * + * buffer is treated as a cyclic buffer. + */ +static int sdioc_bytes_free_in_buffer(int write_ptr, + int read_ptr, + int total_size, + int *free_bytes, + int *bytes_in_use) +{ + if (!free_bytes) { + pr_err(MODULE_NAME ": %s - param ""free_bytes"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!bytes_in_use) { + pr_err(MODULE_NAME ": %s - param ""bytes_in_use"" is NULL.\n", + __func__); + return -EINVAL; + } + + /* if pointers equel - buffers are empty. nothing to read/write */ + + if (write_ptr >= read_ptr) + *bytes_in_use = write_ptr - read_ptr; + else + *bytes_in_use = total_size - (read_ptr - write_ptr); + + *free_bytes = total_size - *bytes_in_use - 1; + + return 0; +} + +/* +* sdio_dld_write_room +* +* This is the write_room function of the tty driver. +* +* @tty: pointer to tty struct. +* @return free bytes for write. +* +*/ +static int sdio_dld_write_room(struct tty_struct *tty) +{ + return sdio_dld->sdio_dloader_data.outgoing_data.buffer_size; +} + +/** + * sdio_dld_write_callback + * this is the write callback of the tty driver. + * + * @tty: pointer to tty struct. + * @buf: buffer to write from. + * @count: number of bytes to write. + * @return bytes written or negative value on error. + * + * if destination buffer has not enough room for the incoming + * data, returns an error. + */ +static int sdio_dld_write_callback(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct sdio_data *outgoing = &sdio_dld->sdio_dloader_data.outgoing_data; + int dst_free_bytes = 0; + int dummy = 0; + int status = 0; + int bytes_written = 0; + int total_written = 0; + static int write_retry; + int pending_to_write = count; + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_count = count; + update_gd(SDIO_DLD_DEBUGFS_CASE_5_CODE); +#endif + + pr_debug(MODULE_NAME ": %s - WRITING CALLBACK CALLED WITH %d bytes\n", + __func__, count); + + if (!outgoing->data) { + pr_err(MODULE_NAME ": %s - param ""outgoing->data"" is NULL.\n", + __func__); + return -EINVAL; + } + + pr_debug(MODULE_NAME ": %s - WRITE CALLBACK size to write to outgoing" + " buffer %d\n", __func__, count); + + /* as long as there is something to write to outgoing buffer */ + do { + int bytes_to_write = 0; + status = sdioc_bytes_free_in_buffer( + outgoing->offset_write_p, + outgoing->offset_read_p, + outgoing->buffer_size, + &dst_free_bytes, + &dummy); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdioc_bytes_free_in_buffer(). status=%d\n", + __func__, status); + return status; + } + + /* + * if there is free room in outgoing buffer + * lock mutex and request trigger notification from the main + * task. unlock mutex, and wait for sinal + */ + if (dst_free_bytes > 0) { + write_retry = 0; + /* + * if there is more data to write to outgoing buffer + * than it can receive, wait for signal from main task + */ + if (pending_to_write > dst_free_bytes) { + + /* sampling updated dst_free_bytes */ + status = sdioc_bytes_free_in_buffer( + outgoing->offset_write_p, + outgoing->offset_read_p, + outgoing->buffer_size, + &dst_free_bytes, + &dummy); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in " + "Function " + "sdioc_bytes_free_in_buffer(). " + "status=%d\n", __func__, status); + return status; + } + } + + bytes_to_write = min(pending_to_write, dst_free_bytes); + bytes_written = + writing_size_to_buf(outgoing->data, + buf+total_written, + &outgoing->offset_write_p, + outgoing->buffer_size, + outgoing->offset_read_p, + bytes_to_write); + + /* keeping sdio_dld_info up to date */ + sdio_dld_info.host_write_ptr = + sdio_dld->sdio_dloader_data. + outgoing_data.offset_write_p; + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_write_tty = bytes_written; + update_gd(SDIO_DLD_DEBUGFS_CASE_3_CODE); +#endif + sdio_dld_info.global_bytes_write_tty += bytes_written; + + spin_lock_irqsave(&lock2, lock_flags2); + if (sdio_dld->main_loop_event.wake_up_signal == 0) { + sdio_dld->main_loop_event.wake_up_signal = 1; + wake_up(&sdio_dld->main_loop_event.wait_event); + } + spin_unlock_irqrestore(&lock2, lock_flags2); + + /* + * although outgoing buffer has enough room, writing + * failed + */ + if (bytes_written != bytes_to_write) { + pr_err(MODULE_NAME ": %s - couldn't write " + "%d bytes to " "outgoing buffer." + "bytes_written=%d\n", + __func__, bytes_to_write, + bytes_written); + return -EIO; + } + + total_written += bytes_written; + pending_to_write -= bytes_written; + outgoing->num_of_bytes_in_use += bytes_written; + + pr_debug(MODULE_NAME ": %s - WRITE CHUNK to outgoing " + "buffer. pending_to_write=%d, " + "outgoing_free_bytes=%d, " + "bytes_written=%d\n", + __func__, + pending_to_write, + dst_free_bytes, + bytes_written); + + } else { + write_retry++; + + pr_debug(MODULE_NAME ": %s - WRITE CALLBACK - NO ROOM." + " pending_to_write=%d, write_retry=%d\n", + __func__, + pending_to_write, + write_retry); + + spin_lock_irqsave(&lock1, lock_flags1); + sdio_dld->write_callback_event.wake_up_signal = 0; + spin_unlock_irqrestore(&lock1, lock_flags1); + + pr_debug(MODULE_NAME ": %s - WRITE CALLBACK - " + "WAITING...", __func__); +#ifdef CONFIG_DEBUG_FS + update_gd(SDIO_DLD_DEBUGFS_CASE_8_CODE); +#endif + wait_event(sdio_dld->write_callback_event.wait_event, + sdio_dld->write_callback_event. + wake_up_signal); +#ifdef CONFIG_DEBUG_FS + update_gd(SDIO_DLD_DEBUGFS_CASE_9_CODE); +#endif + pr_debug(MODULE_NAME ": %s - WRITE CALLBACK - " + "WOKE UP...", __func__); + } + } while (pending_to_write > 0 && write_retry < WRITE_RETRIES); + + if (pending_to_write > 0) { + + pr_err(MODULE_NAME ": %s - WRITE CALLBACK - pending data is " + "%d out of %d > 0. total written in this " + "callback = %d\n", + __func__, pending_to_write, count, total_written); + } + + if (write_retry == WRITE_RETRIES) { + pr_err(MODULE_NAME ": %s, write_retry=%d= max\n", + __func__, write_retry); + } + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_bytes_cb_tty = total_written; + update_gd(SDIO_DLD_DEBUGFS_CASE_10_CODE); +#endif + + return total_written; +} + +/** + * sdio_memcpy_fromio_wrapper - + * reads from sdioc, and updats the sdioc registers according + * to how many bytes were actually read. + * + * @str_func: a pointer to func struct. + * @client_rd_ptr: sdioc value of downlink read ptr. + * @client_wr_ptr: sdioc value of downlink write ptr. + * @buffer_to_store: buffer to store incoming data. + * @address_to_read: address to start reading from in sdioc. + * @size_to_read: size of bytes to read. + * @client_buffer_size: sdioc downlink buffer size. + * @return 0 on success or negative value on error. + */ +static int sdio_memcpy_fromio_wrapper(struct sdio_func *str_func, + unsigned int client_rd_ptr, + unsigned int client_wr_ptr, + void *buffer_to_store, + unsigned int address_to_read_from, + int size_to_read, + int client_buffer_size) +{ + int status = 0; + struct sdioc_reg_chunk *reg_str = + &sdio_dld->sdio_dloader_data.sdioc_reg; + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!buffer_to_store) { + pr_err(MODULE_NAME ": %s - param ""buffer_to_store"" is " + "NULL.\n", + __func__); + return -EINVAL; + } + + if (size_to_read < 0) { + pr_err(MODULE_NAME ": %s - invalid size to read=%d\n", + __func__, size_to_read); + return -EINVAL; + } + + sdio_claim_host(str_func); + + pr_debug(MODULE_NAME ": %s, READING DATA - from add %d, " + "size_to_read=%d\n", + __func__, address_to_read_from, size_to_read); + + status = sdio_memcpy_fromio(str_func, + (void *)buffer_to_store, + address_to_read_from, + size_to_read); + if (status) { + pr_err(MODULE_NAME ": %s - sdio_memcpy_fromio()" + " DATA failed. status=%d.\n", + __func__, status); + sdio_release_host(str_func); + return status; + } + + /* updating an offset according to cyclic buffer size */ + reg_str->dl_rd_ptr.reg_val = + (reg_str->dl_rd_ptr.reg_val + size_to_read) % + client_buffer_size; + /* keeping sdio_dld_info up to date */ + sdio_dld_info.cl_dl_rd_ptr = reg_str->dl_rd_ptr.reg_val; + + status = sdio_memcpy_toio(str_func, + reg_str->dl_rd_ptr.reg_offset, + (void *)®_str->dl_rd_ptr.reg_val, + sizeof(reg_str->dl_rd_ptr.reg_val)); + + if (status) { + pr_err(MODULE_NAME ": %s - sdio_memcpy_toio() " + "UPDATE PTR failed. status=%d.\n", + __func__, status); + } + + sdio_release_host(str_func); + return status; +} + +/** + * sdio_memcpy_toio_wrapper + * writes to sdioc, and updats the sdioc registers according + * to how many bytes were actually read. + * + * @str_func: a pointer to func struct. + * @client_wr_ptr: sdioc downlink write ptr. + * @h_read_ptr: host incoming read ptrs + * @buf_write_from: buffer to write from. + * @bytes_to_write: number of bytes to write. + * @return 0 on success or negative value on error. + */ +static int sdio_memcpy_toio_wrapper(struct sdio_func *str_func, + unsigned int client_wr_ptr, + unsigned int h_read_ptr, + void *buf_write_from, + int bytes_to_write) +{ + int status = 0; + struct sdioc_reg_chunk *reg_str = + &sdio_dld->sdio_dloader_data.sdioc_reg; + struct sdio_data *outgoing = &sdio_dld->sdio_dloader_data.outgoing_data; + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!buf_write_from) { + pr_err(MODULE_NAME ": %s - param ""buf_write_from"" is NULL.\n", + __func__); + return -EINVAL; + } + + sdio_claim_host(str_func); + + pr_debug(MODULE_NAME ": %s, WRITING DATA TOIO to address 0x%x, " + "bytes_to_write=%d\n", + __func__, + reg_str->up_buff_address.reg_val + reg_str->up_wr_ptr.reg_val, + bytes_to_write); + + status = sdio_memcpy_toio(str_func, + reg_str->up_buff_address.reg_val + + reg_str->up_wr_ptr.reg_val, + (void *) (outgoing->data + h_read_ptr), + bytes_to_write); + + if (status) { + pr_err(MODULE_NAME ": %s - sdio_memcpy_toio() " + "DATA failed. status=%d.\n", __func__, status); + sdio_release_host(str_func); + return status; + } + + sdio_dld_info.global_bytes_write_toio += bytes_to_write; + outgoing->num_of_bytes_in_use -= bytes_to_write; + + /* + * if writing to client succeeded, then + * 1. update the client up_wr_ptr + * 2. update the host outgoing rd ptr + **/ + reg_str->up_wr_ptr.reg_val = + ((reg_str->up_wr_ptr.reg_val + bytes_to_write) % + reg_str->ul_buff_size.reg_val); + + /* keeping sdio_dld_info up to date */ + sdio_dld_info.cl_up_wr_ptr = reg_str->up_wr_ptr.reg_val; + + outgoing->offset_read_p = + ((outgoing->offset_read_p + bytes_to_write) % + outgoing->buffer_size); + + /* keeping sdio_dld_info up to date*/ + sdio_dld_info.host_read_ptr = outgoing->offset_read_p; + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_write_toio = bytes_to_write; + update_gd(SDIO_DLD_DEBUGFS_CASE_4_CODE); +#endif + + /* updating uplink write pointer according to size that was written */ + status = sdio_memcpy_toio(str_func, + reg_str->up_wr_ptr.reg_offset, + (void *)(®_str->up_wr_ptr.reg_val), + sizeof(reg_str->up_wr_ptr.reg_val)); + if (status) { + pr_err(MODULE_NAME ": %s - sdio_memcpy_toio() " + "UPDATE PTR failed. status=%d.\n", + __func__, status); + } + + sdio_release_host(str_func); + return status; +} + +/** + * sdio_dld_read + * reads from sdioc + * + * @client_rd_ptr: sdioc downlink read ptr. + * @client_wr_ptr: sdioc downlink write ptr. + * @reg_str: sdioc register shadowing struct. + * @str_func: a pointer to func struct. + * @bytes_read:how many bytes read. + * @return 0 on success or negative value on error. + */ +static int sdio_dld_read(unsigned int client_rd_ptr, + unsigned int client_wr_ptr, + struct sdioc_reg_chunk *reg_str, + struct sdio_func *str_func, + int *bytes_read) +{ + int status = 0; + struct sdio_data *incoming = &sdio_dld->sdio_dloader_data.incoming_data; + + if (!reg_str) { + pr_err(MODULE_NAME ": %s - param ""reg_str"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!bytes_read) { + pr_err(MODULE_NAME ": %s - param ""bytes_read"" is NULL.\n", + __func__); + return -EINVAL; + } + + /* there is data to read in ONE chunk */ + if (client_wr_ptr > client_rd_ptr) { + status = sdio_memcpy_fromio_wrapper( + str_func, + client_rd_ptr, + client_wr_ptr, + (void *)incoming->data, + reg_str->dl_buff_address.reg_val + client_rd_ptr, + client_wr_ptr - client_rd_ptr, + reg_str->dl_buff_size.reg_val); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdio_memcpy_fromio_wrapper(). " + "SINGLE CHUNK READ. status=%d\n", + __func__, status); + return status; + } + + incoming->num_of_bytes_in_use += client_wr_ptr - client_rd_ptr; + *bytes_read = client_wr_ptr - client_rd_ptr; + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_to_read = + client_wr_ptr - client_rd_ptr; + update_gd(SDIO_DLD_DEBUGFS_CASE_11_CODE); +#endif + } + + /* there is data to read in TWO chunks */ + else { + int dl_buf_size = reg_str->dl_buff_size.reg_val; + int tail_size = dl_buf_size - client_rd_ptr; + + /* reading chunk#1: from rd_ptr to the end of the buffer */ + status = sdio_memcpy_fromio_wrapper( + str_func, + client_rd_ptr, + dl_buf_size, + (void *)incoming->data, + reg_str->dl_buff_address.reg_val + client_rd_ptr, + tail_size, + dl_buf_size); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdio_memcpy_fromio_wrapper(). " + "1 of 2 CHUNKS READ. status=%d\n", + __func__, status); + return status; + } + + incoming->num_of_bytes_in_use += tail_size; + *bytes_read = tail_size; + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_to_read = tail_size; + update_gd(SDIO_DLD_DEBUGFS_CASE_11_CODE); +#endif + + /* reading chunk#2: reading from beginning buffer */ + status = sdio_memcpy_fromio_wrapper( + str_func, + client_rd_ptr, + client_wr_ptr, + (void *)(incoming->data + tail_size), + reg_str->dl_buff_address.reg_val, + client_wr_ptr, + reg_str->dl_buff_size.reg_val); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdio_memcpy_fromio_wrapper(). " + "2 of 2 CHUNKS READ. status=%d\n", + __func__, status); + return status; + } + + incoming->num_of_bytes_in_use += client_wr_ptr; + *bytes_read += client_wr_ptr; + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_to_read = client_wr_ptr; + update_gd(SDIO_DLD_DEBUGFS_CASE_11_CODE); +#endif + } + return 0; +} + +/** + * sdio_dld_main_task + * sdio downloader main task. reads mailboxf checks if there is + * anything to read, checks if host has anything to + * write. + * + * @card: a pointer to mmc_card. + * @return 0 on success or negative value on error. + */ +static int sdio_dld_main_task(void *card) +{ + int status = 0; + struct tty_struct *tty = sdio_dld->tty_str; + struct sdioc_reg_chunk *reg_str = + &sdio_dld->sdio_dloader_data.sdioc_reg; + int func = sdio_dld->sdioc_boot_func; + struct sdio_func *str_func = NULL; + struct sdio_data *outgoing = &sdio_dld->sdio_dloader_data.outgoing_data; + struct sdio_data *incoming = &sdio_dld->sdio_dloader_data.incoming_data; + struct sdio_dld_task *task = &sdio_dld->dld_main_thread; + int retries = 0; +#ifdef PUSH_STRING + int bytes_pushed = 0; +#endif + + msleep(SLEEP_MS); + + if (!card) { + pr_err(MODULE_NAME ": %s - param ""card"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!tty) { + pr_err(MODULE_NAME ": %s - param ""tty"" is NULL.\n", + __func__); + return -EINVAL; + } + + str_func = ((struct mmc_card *)card)-> + sdio_func[REAL_FUNC_TO_FUNC_IN_ARRAY(func)]; + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + while (true) { + /* client pointers for both buffers */ + int client_ul_wr_ptr = 0; + int client_ul_rd_ptr = 0; + int client_dl_wr_ptr = 0; + int client_dl_rd_ptr = 0; + + /* host pointer for outgoing buffer */ + int h_out_wr_ptr = 0; + int h_out_rd_ptr = 0; + + int h_bytes_rdy_wr = 0; + int c_bytes_rdy_rcve = 0; + + int need_to_write = 0; + int need_to_read = 0; + + /* + * forever, checking for signal to die, then read MailBox. + * if nothing to read or nothing to write to client, sleep, + * and again read MailBox + */ + do { + int dummy = 0; + + /* checking if a signal to die was sent */ + if (atomic_read(&task->please_close) == 1) { + + pr_debug(MODULE_NAME ": %s - 0x%x was written " + "to 9K\n", __func__, SDIOC_EXIT_CODE); + + sdio_claim_host(str_func); + + /* returned value is not checked on purpose */ + sdio_memcpy_toio( + str_func, + reg_str->good_to_exit_ptr.reg_offset, + (void *)®_str->good_to_exit_ptr. + reg_val, + sizeof(reg_str->good_to_exit_ptr. + reg_val)); + + sdio_release_host(str_func); + + task->exit_wait.wake_up_signal = 1; + wake_up(&task->exit_wait.wait_event); + return 0; + } + + status = mailbox_to_seq_chunk_read_ptrs(str_func); + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "mailbox_to_seq_chunk_read_ptrs(). " + "status=%d\n", __func__, status); + return status; + } + + /* calculate how many bytes the host has send */ + h_out_wr_ptr = outgoing->offset_write_p; + h_out_rd_ptr = outgoing->offset_read_p; + + status = sdioc_bytes_till_end_of_buffer( + h_out_wr_ptr, + h_out_rd_ptr, + outgoing->buffer_size, + &dummy, + &h_bytes_rdy_wr); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdioc_bytes_till_end_of_buffer(). " + "status=%d\n", __func__, status); + return status; + } + + /* is there something to read from client */ + client_dl_wr_ptr = reg_str->dl_wr_ptr.reg_val; + client_dl_rd_ptr = reg_str->dl_rd_ptr.reg_val; + + if (client_dl_rd_ptr != client_dl_wr_ptr) + need_to_read = 1; + + /* + * calculate how many bytes the client can receive + * from host + */ + client_ul_wr_ptr = reg_str->up_wr_ptr.reg_val; + client_ul_rd_ptr = reg_str->up_rd_ptr.reg_val; + + status = sdioc_bytes_till_end_of_buffer( + client_ul_wr_ptr, + client_ul_rd_ptr, + reg_str->ul_buff_size.reg_val, + &c_bytes_rdy_rcve, + &dummy); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdioc_bytes_till_end_of_buffer(). " + "status=%d\n", __func__, status); + return status; + } + + /* if host has anything to write */ + if (h_bytes_rdy_wr > 0) + need_to_write = 1; + + if (need_to_write || need_to_read) + break; + + spin_lock_irqsave(&lock2, lock_flags2); + sdio_dld->main_loop_event.wake_up_signal = 0; + spin_unlock_irqrestore(&lock2, lock_flags2); + + pr_debug(MODULE_NAME ": %s - MAIN LOOP - WAITING...\n", + __func__); +#ifdef CONFIG_DEBUG_FS + update_gd(SDIO_DLD_DEBUGFS_CASE_6_CODE); +#endif + wait_event(sdio_dld->main_loop_event.wait_event, + sdio_dld->main_loop_event.wake_up_signal); +#ifdef CONFIG_DEBUG_FS + update_gd(SDIO_DLD_DEBUGFS_CASE_7_CODE); +#endif + + pr_debug(MODULE_NAME ": %s - MAIN LOOP - WOKE UP...\n", + __func__); + + } while (1); + + /* CHECK IF THERE IS ANYTHING TO READ IN CLIENT */ + if (need_to_read) { +#ifdef PUSH_STRING + int num_push = 0; + int left = 0; + int bytes_read; +#else + int i; +#endif + need_to_read = 0; + + status = sdio_dld_read(client_dl_rd_ptr, + client_dl_wr_ptr, + reg_str, + str_func, + &bytes_read); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdio_dld_read(). status=%d\n", + __func__, status); + return status; + } + + sdio_dld_info.global_bytes_read_fromio += + bytes_read; + + bytes_pushed = 0; +#ifdef PUSH_STRING + left = incoming->num_of_bytes_in_use; + start_timer(&sdio_dld->push_timer, + sdio_dld->push_timer_ms); + do { + num_push = tty_insert_flip_string( + tty, + incoming->data+bytes_pushed, + left); + + bytes_pushed += num_push; + left -= num_push; + tty_flip_buffer_push(tty); + } while (left != 0); + + del_timer(&sdio_dld->push_timer); + + if (bytes_pushed != incoming->num_of_bytes_in_use) { + pr_err(MODULE_NAME ": %s - failed\n", + __func__); + } +#else + pr_debug(MODULE_NAME ": %s - NEED TO READ %d\n", + __func__, incoming->num_of_bytes_in_use); + + for (i = 0 ; i < incoming->num_of_bytes_in_use ; ++i) { + int err = 0; + err = tty_insert_flip_char(tty, + incoming->data[i], + TTY_NORMAL); + tty_flip_buffer_push(tty); + } + + pr_debug(MODULE_NAME ": %s - JUST READ\n", __func__); +#endif /*PUSH_STRING*/ + sdio_dld_info.global_bytes_push_tty += + incoming->num_of_bytes_in_use; +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_push_to_tty = bytes_read; + update_gd(SDIO_DLD_DEBUGFS_CASE_12_CODE); +#endif + incoming->num_of_bytes_in_use = 0; + tty_flip_buffer_push(tty); + } + + /* CHECK IF THERE IS ANYTHING TO WRITE IN HOST AND HOW MUCH */ + if (need_to_write) { + int dummy = 0; + + do { + int bytes_to_write = min(c_bytes_rdy_rcve, + h_bytes_rdy_wr); + + /* + * in case nothing to send or no room to + * receive + */ + if (bytes_to_write == 0) + break; + + if (client_ul_rd_ptr == 0 && + (client_ul_rd_ptr != client_ul_wr_ptr)) + break; + + /* + * if client_rd_ptr points to start, but there + * is data to read wait until WRITE_TILL_END + * before writing a chunk of data, to avoid + * writing until (BUF_SIZE - 1), because it will + * yield an extra write of "1" bytes + */ + if (client_ul_rd_ptr == 0 && + (client_ul_rd_ptr != client_ul_wr_ptr) && + retries < WRITE_TILL_END_RETRIES) { + retries++; + break; + } + retries = 0; + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_8k_has = h_bytes_rdy_wr; + debugfs_glob.global_9k_has = c_bytes_rdy_rcve; + debugfs_glob.global_min = bytes_to_write; + update_gd(SDIO_DLD_DEBUGFS_CASE_2_CODE); +#endif + need_to_write = 0; + + pr_debug(MODULE_NAME ": %s - NEED TO WRITE " + "TOIO %d\n", + __func__, bytes_to_write); + + status = sdio_memcpy_toio_wrapper( + str_func, + reg_str->up_wr_ptr.reg_val, + outgoing->offset_read_p, + (void *)((char *)outgoing->data + + outgoing->offset_read_p), + bytes_to_write); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in " + "Function " + "sdio_memcpy_toio_wrapper(). " + "SINGLE CHUNK WRITE. " + "status=%d\n", + __func__, status); + return status; + } + + sdio_claim_host(str_func); + + status = sdio_memcpy_fromio( + str_func, + (void *)®_str->up_rd_ptr.reg_val, + SDIOC_UL_RD_PTR, + sizeof(reg_str->up_rd_ptr.reg_val)); + + if (status) { + pr_err(MODULE_NAME ": %s - " + "sdio_memcpy_fromio() " + "failed. status=%d\n", + __func__, status); + sdio_release_host(str_func); + + return status; + } + + sdio_release_host(str_func); + + spin_lock_irqsave(&lock1, lock_flags1); + if (sdio_dld->write_callback_event. + wake_up_signal == 0) { + sdio_dld->write_callback_event. + wake_up_signal = 1; + wake_up(&sdio_dld-> + write_callback_event. + wait_event); + } + + spin_unlock_irqrestore(&lock1, lock_flags1); + client_ul_wr_ptr = reg_str->up_wr_ptr.reg_val; + client_ul_rd_ptr = reg_str->up_rd_ptr.reg_val; + + status = sdioc_bytes_till_end_of_buffer( + client_ul_wr_ptr, + client_ul_rd_ptr, + reg_str->ul_buff_size.reg_val, + &c_bytes_rdy_rcve, + &dummy); + + /* calculate how many bytes host has to send */ + h_out_wr_ptr = outgoing->offset_write_p; + h_out_rd_ptr = outgoing->offset_read_p; + + status = sdioc_bytes_till_end_of_buffer( + h_out_wr_ptr, + h_out_rd_ptr, + outgoing->buffer_size, + &dummy, + &h_bytes_rdy_wr); + + } while (h_out_wr_ptr != h_out_rd_ptr); + } + } + return 0; +} + +/** + * sdio_dld_init_global + * initialization of sdio_dld global struct + * + * @card: a pointer to mmc_card. + * @return 0 on success or negative value on error. + */ +static int sdio_dld_init_global(struct mmc_card *card, + int(*done)(void)) +{ + if (!card) { + pr_err(MODULE_NAME ": %s - param ""card"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!done) { + pr_err(MODULE_NAME ": %s - param ""done"" is NULL.\n", + __func__); + return -EINVAL; + } + + sdio_dld->done_callback = done; + sdio_dld->card = card; + init_waitqueue_head(&sdio_dld->dld_main_thread.exit_wait.wait_event); + sdio_dld->write_callback_event.wake_up_signal = 1; + sdio_dld->main_loop_event.wake_up_signal = 1; + + sdio_dld->sdio_dloader_data.sdioc_reg.dl_buff_size.reg_offset = + SDIOC_DL_BUFF_SIZE_OFFSET; + + sdio_dld->sdio_dloader_data.sdioc_reg.dl_rd_ptr.reg_offset = + SDIOC_DL_RD_PTR; + + sdio_dld->sdio_dloader_data.sdioc_reg.dl_wr_ptr.reg_offset = + SDIOC_DL_WR_PTR; + + sdio_dld->sdio_dloader_data.sdioc_reg.ul_buff_size.reg_offset = + SDIOC_UP_BUFF_SIZE_OFFSET; + + sdio_dld->sdio_dloader_data.sdioc_reg.up_rd_ptr.reg_offset = + SDIOC_UL_RD_PTR; + + sdio_dld->sdio_dloader_data.sdioc_reg.up_wr_ptr.reg_offset = + SDIOC_UL_WR_PTR; + + sdio_dld->sdio_dloader_data.sdioc_reg.good_to_exit_ptr.reg_offset = + SDIOC_EXIT_PTR; + + sdio_dld->sdio_dloader_data.sdioc_reg.dl_buff_address.reg_offset = + SDIOC_DL_BUFF_ADDRESS; + + sdio_dld->sdio_dloader_data.sdioc_reg.up_buff_address.reg_offset = + SDIOC_UP_BUFF_ADDRESS; + + sdio_dld_set_op_mode(SDIO_DLD_NORMAL_MODE); + + return 0; +} + +/** + * sdio_downloader_setup + * initializes the TTY driver + * + * @card: a pointer to mmc_card. + * @num_of_devices: number of devices. + * @channel_number: channel number. + * @return 0 on success or negative value on error. + * + * The TTY stack needs to know in advance how many devices it should + * plan to manage. Use this call to set up the ports that will + * be exported through SDIO. + */ +int sdio_downloader_setup(struct mmc_card *card, + unsigned int num_of_devices, + int channel_number, + int(*done)(void)) +{ + int status = 0; + int result = 0; + int func_in_array = 0; + struct sdio_func *str_func = NULL; + struct device *tty_dev; + + if (num_of_devices == 0 || num_of_devices > MAX_NUM_DEVICES) { + pr_err(MODULE_NAME ": %s - invalid number of devices\n", + __func__); + return -EINVAL; + } + + if (!card) { + pr_err(MODULE_NAME ": %s - param ""card"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!done) { + pr_err(MODULE_NAME ": %s - param ""done"" is NULL.\n", + __func__); + return -EINVAL; + } + + sdio_dld = kzalloc(sizeof(struct sdio_downloader), GFP_KERNEL); + if (!sdio_dld) { + pr_err(MODULE_NAME ": %s - couldn't allocate sdio_dld data " + "structure.", __func__); + return -ENOMEM; + } + +#ifdef CONFIG_DEBUG_FS + bootloader_debugfs_init(); +#endif /* CONFIG_DEBUG_FS */ + + status = sdio_dld_init_global(card, done); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdio_dld_init_global(). status=%d\n", + __func__, status); + kfree(sdio_dld); + return status; + } + + sdio_dld->tty_drv = alloc_tty_driver(num_of_devices); + + if (!sdio_dld->tty_drv) { + pr_err(MODULE_NAME ": %s - param ""sdio_dld->tty_drv"" is " + "NULL.\n", __func__); + kfree(sdio_dld); + return -EINVAL; + } + + sdio_dld_set_op_mode((enum sdio_dld_op_mode)sdio_op_mode); + + /* according to op_mode, a different tty device is created */ + if (sdio_dld->op_mode == SDIO_DLD_BOOT_TEST_MODE) + sdio_dld->tty_drv->name = TTY_SDIO_DEV_TEST; + else + sdio_dld->tty_drv->name = TTY_SDIO_DEV; + + sdio_dld->tty_drv->owner = THIS_MODULE; + sdio_dld->tty_drv->driver_name = "SDIO_Dloader"; + + /* uses dynamically assigned dev_t values */ + sdio_dld->tty_drv->type = TTY_DRIVER_TYPE_SERIAL; + sdio_dld->tty_drv->subtype = SERIAL_TYPE_NORMAL; + sdio_dld->tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV + | TTY_DRIVER_RESET_TERMIOS; + + /* initializing the tty driver */ + sdio_dld->tty_drv->init_termios = tty_std_termios; + sdio_dld->tty_drv->init_termios.c_cflag = + B4800 | CS8 | CREAD | HUPCL | CLOCAL; + sdio_dld->tty_drv->init_termios.c_ispeed = INPUT_SPEED; + sdio_dld->tty_drv->init_termios.c_ospeed = OUTPUT_SPEED; + + tty_set_operations(sdio_dld->tty_drv, &sdio_dloader_tty_ops); + + status = tty_register_driver(sdio_dld->tty_drv); + if (status) { + put_tty_driver(sdio_dld->tty_drv); + pr_err(MODULE_NAME ": %s - tty_register_driver() failed\n", + __func__); + + sdio_dld->tty_drv = NULL; + kfree(sdio_dld); + return status; + } + + tty_dev = tty_register_device(sdio_dld->tty_drv, 0, NULL); + if (IS_ERR(tty_dev)) { + pr_err(MODULE_NAME ": %s - tty_register_device() " + "failed\n", __func__); + tty_unregister_driver(sdio_dld->tty_drv); + kfree(sdio_dld); + return PTR_ERR(tty_dev); + } + + sdio_dld->sdioc_boot_func = SDIOC_CHAN_TO_FUNC_NUM(channel_number); + func_in_array = REAL_FUNC_TO_FUNC_IN_ARRAY(sdio_dld->sdioc_boot_func); + str_func = sdio_dld->card->sdio_func[func_in_array]; + status = sdio_dld_init_func(str_func); + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdio_dld_init_func(). status=%d\n", + __func__, status); + goto exit_err; + } + +#ifdef CONFIG_DEBUG_FS + sdio_dld_debug_init(); +#endif + + sdio_claim_host(str_func); + + /* + * notifing the client by writing what mode we are by writing + * to a special register + */ + status = sdio_memcpy_toio(str_func, + SDIOC_OP_MODE_PTR, + (void *)&sdio_dld->op_mode, + sizeof(sdio_dld->op_mode)); + + sdio_release_host(str_func); + + if (status) { + pr_err(MODULE_NAME ": %s - sdio_memcpy_toio() " + "writing to OP_MODE_REGISTER failed. " + "status=%d.\n", + __func__, status); + goto exit_err; + } + + return 0; + +exit_err: + tty_unregister_device(sdio_dld->tty_drv, 0); + result = tty_unregister_driver(sdio_dld->tty_drv); + if (result) + pr_err(MODULE_NAME ": %s - tty_unregister_driver() " + "failed. result=%d\n", __func__, -result); + kfree(sdio_dld); + return status; +} + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SDIO Downloader"); +MODULE_AUTHOR("Yaniv Gardi "); +MODULE_VERSION(DRV_VERSION); + diff --git a/arch/arm/mach-msm/sdio_al_private.h b/arch/arm/mach-msm/sdio_al_private.h new file mode 100644 index 00000000..3a5ab79b --- /dev/null +++ b/arch/arm/mach-msm/sdio_al_private.h @@ -0,0 +1,267 @@ +/* Copyright (c) 2011, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * SDIO-Abstraction-Layer internal interface. + */ + +#ifndef __SDIO_AL_PRIVATE__ +#define __SDIO_AL_PRIVATE__ + +#include +#include +#include + +#define DRV_VERSION "1.30" +#define MODULE_NAME "sdio_al" +#define SDIOC_CHAN_TO_FUNC_NUM(x) ((x)+2) +#define REAL_FUNC_TO_FUNC_IN_ARRAY(x) ((x)-1) +#define SDIO_PREFIX "SDIO_" +#define PEER_CHANNEL_NAME_SIZE 4 +#define CHANNEL_NAME_SIZE (sizeof(SDIO_PREFIX) + PEER_CHANNEL_NAME_SIZE) +#define SDIO_TEST_POSTFIX_SIZE 5 +#define MAX_NUM_OF_SDIO_DEVICES 2 +#define TEST_CH_NAME_SIZE (CHANNEL_NAME_SIZE + SDIO_TEST_POSTFIX_SIZE) + +struct sdio_al_device; /* Forward Declaration */ + +enum sdio_channel_state { + SDIO_CHANNEL_STATE_INVALID, /* before reading software header */ + SDIO_CHANNEL_STATE_IDLE, /* channel valid, not opened */ + SDIO_CHANNEL_STATE_CLOSED, /* was closed */ + SDIO_CHANNEL_STATE_OPEN, /* opened */ + SDIO_CHANNEL_STATE_CLOSING, /* during flush, when closing */ +}; +/** + * Peer SDIO-Client channel configuration. + * + * @is_ready - channel is ready and the data is valid. + * + * @max_rx_threshold - maximum rx threshold, according to the + * total buffers size on the peer pipe. + * @max_tx_threshold - maximum tx threshold, according to the + * total buffers size on the peer pipe. + * @tx_buf_size - size of a single buffer on the peer pipe; a + * transfer smaller than the buffer size still + * make the buffer unusable for the next transfer. + * @max_packet_size + * @is_host_ok_to_sleep - Host marks this bit when it's okay to + * sleep (no pending transactions) + */ +struct peer_sdioc_channel_config { + u32 is_ready; + u32 max_rx_threshold; /* Downlink */ + u32 max_tx_threshold; /* Uplink */ + u32 tx_buf_size; + u32 max_packet_size; + u32 is_host_ok_to_sleep; + u32 is_packet_mode; + u32 peer_operation; + u32 is_low_latency_ch; + u32 reserved[23]; +}; + + +/** + * Peer SDIO-Client channel statsitics. + * + * @last_any_read_avail - the last read avail in all the + * channels including this channel. + * @last_read_avail - the last read_avail that was read from HW + * mailbox. + * @last_old_read_avail - the last read_avail channel shadow. + * @total_notifs - the total number of read notifications sent + * to this channel client + * @total_read_times - the total number of successful sdio_read + * calls for this channel + */ +struct sdio_channel_statistics { + int last_any_read_avail; + int last_read_avail; + int last_old_read_avail; + int total_notifs; + int total_read_times; +}; + +/** + * SDIO Channel context. + * + * @name - channel name. Used by the caller to open the + * channel. + * + * @read_threshold - Threshold on SDIO-Client mailbox for Rx + * Data available bytes. When the limit exceed + * the SDIO-Client generates an interrupt to the + * host. + * + * @write_threshold - Threshold on SDIO-Client mailbox for Tx + * Data available bytes. When the limit exceed + * the SDIO-Client generates an interrupt to the + * host. + * + * @def_read_threshold - Default theshold on SDIO-Client for Rx + * + * @min_write_avail - Threshold of minimal available bytes + * to write. Below that threshold the host + * will initiate reading the mailbox. + * + * @poll_delay_msec - Delay between polling the mailbox. When + * the SDIO-Client doesn't generates EOT + * interrupt for Rx Available bytes, the host + * should poll the SDIO-Client mailbox. + * + * @is_packet_mode - The host get interrupt when a packet is + * available at the SDIO-client (pipe EOT + * indication). + * + * @num - channel number. + * + * @notify - Client's callback. Should not call sdio read/write. + * + * @priv - Client's private context, provided to callback. + * + * @is_valid - Channel is used (we have a list of + * SDIO_AL_MAX_CHANNELS and not all of them are in + * use). + * + * @is_open - Channel is open. + * + * @func - SDIO Function handle. + * + * @rx_pipe_index - SDIO-Client Pipe Index for Rx Data. + * + * @tx_pipe_index - SDIO-Client Pipe Index for Tx Data. + * + * @ch_lock - Channel lock to protect channel specific Data + * + * @rx_pending_bytes - Total number of Rx pending bytes, at Rx + * packet list. Maximum of 16KB-1 limited by + * SDIO-Client specification. + * + * @read_avail - Available bytes to read. + * + * @write_avail - Available bytes to write. + * + * @rx_size_list_head - The head of Rx Pending Packets List. + * + * @pdev - platform device - clients to probe for the sdio-al. + * + * @signature - Context Validity check. + * + * @sdio_al_dev - a pointer to the sdio_al_device instance of + * this channel + * + * @statistics - channel statistics + * + */ +struct sdio_channel { + /* Channel Configuration Parameters*/ + char name[CHANNEL_NAME_SIZE]; + char ch_test_name[TEST_CH_NAME_SIZE]; + int read_threshold; + int write_threshold; + int def_read_threshold; + int threshold_change_cnt; + int min_write_avail; + int poll_delay_msec; + int is_packet_mode; + int is_low_latency_ch; + + struct peer_sdioc_channel_config ch_config; + + /* Channel Info */ + int num; + + void (*notify)(void *priv, unsigned channel_event); + void *priv; + + int state; + + struct sdio_func *func; + + int rx_pipe_index; + int tx_pipe_index; + + struct mutex ch_lock; + + u32 read_avail; + u32 write_avail; + + u32 peer_tx_buf_size; + + u16 rx_pending_bytes; + + struct list_head rx_size_list_head; + + struct platform_device *pdev; + + u32 total_rx_bytes; + u32 total_tx_bytes; + + u32 signature; + + struct sdio_al_device *sdio_al_dev; + + struct sdio_channel_statistics statistics; +}; + +/** + * sdio_downloader_setup + * initializes the TTY driver + * + * @card: a pointer to mmc_card. + * @num_of_devices: number of devices. + * @channel_number: channel number. + * @return 0 on success or negative value on error. + * + * The TTY stack needs to know in advance how many devices it should + * plan to manage. Use this call to set up the ports that will + * be exported through SDIO. + */ +int sdio_downloader_setup(struct mmc_card *card, + unsigned int num_of_devices, + int func_number, + int(*func)(void)); + +/** + * test_channel_init + * initializes a test channel + * + * @name: the channel name. + * @return 0 on success or negative value on error. + * + */ +int test_channel_init(char *name); + +/** + * sdio_al_register_lpm_cb + * Allow the sdio_al test to register for lpm voting + * notifications + * + * @device_handle: the device handle. + * @wakeup_callback: callback function to be called when voting. + * + */ +void sdio_al_register_lpm_cb(void *device_handle, + int(*lpm_callback)(void *, int)); + +/** + * sdio_al_unregister_lpm_cb + * Allow the sdio_al test to unregister for lpm voting + * notifications + * + * @device_handle: the device handle. + * + */ +void sdio_al_unregister_lpm_cb(void *device_handle); + +#endif /* __SDIO_AL_PRIVATE__ */ diff --git a/arch/arm/mach-msm/sdio_al_test.c b/arch/arm/mach-msm/sdio_al_test.c new file mode 100644 index 00000000..2c9f675e --- /dev/null +++ b/arch/arm/mach-msm/sdio_al_test.c @@ -0,0 +1,6500 @@ +/* Copyright (c) 2010-2011, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * SDIO-Abstraction-Layer Test Module. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sdio_al_private.h" +#include + +#include +enum lpm_test_msg_type { + LPM_NO_MSG, /* 0 */ + LPM_MSG_SEND, /* 1 */ + LPM_MSG_REC, /* 2 */ + LPM_SLEEP, /* 3 */ + LPM_WAKEUP, /* 4 */ + LPM_NOTIFY /* 5 */ +}; + +#define LPM_NO_MSG_NAME "LPM No Event" +#define LPM_MSG_SEND_NAME "LPM Send Msg Event" +#define LPM_MSG_REC_NAME "LPM Receive Msg Event" +#define LPM_SLEEP_NAME "LPM Sleep Event" +#define LPM_WAKEUP_NAME "LPM Wakeup Event" + +/** Module name string */ +#define TEST_MODULE_NAME "sdio_al_test" + +#define TEST_SIGNATURE 0x12345678 +#define TEST_CONFIG_SIGNATURE 0xBEEFCAFE + +#define MAX_XFER_SIZE (16*1024) +#define SMEM_MAX_XFER_SIZE 0xBC000 +#define A2_MIN_PACKET_SIZE 5 +#define RMNT_PACKET_SIZE (4*1024) +#define DUN_PACKET_SIZE (2*1024) +#define CSVT_PACKET_SIZE 1700 + +#define TEST_DBG(x...) if (test_ctx->runtime_debug) pr_info(x) + +#define LPM_TEST_NUM_OF_PACKETS 100 +#define LPM_MAX_OPEN_CHAN_PER_DEV 4 +#define LPM_ARRAY_SIZE (7*LPM_TEST_NUM_OF_PACKETS*LPM_MAX_OPEN_CHAN_PER_DEV) +#define SDIO_LPM_TEST "sdio_lpm_test_reading_task" +#define LPM_TEST_CONFIG_SIGNATURE 0xDEADBABE +#define LPM_MSG_NAME_SIZE 20 +#define MAX_STR_SIZE 10 +#define MAX_AVG_RTT_TIME_USEC 2500 +#define SDIO_RMNT_RTT_PACKET_SIZE 32 +#define SDIO_CSVT_RTT_PACKET_SIZE 1900 + +#define A2_HEADER_OVERHEAD 8 + +enum rx_process_state { + RX_PROCESS_PACKET_INIT, + RX_PROCESS_A2_HEADER, + RX_PROCESS_PACKET_DATA, +}; + +enum sdio_test_case_type { + SDIO_TEST_LOOPBACK_HOST, + SDIO_TEST_LOOPBACK_CLIENT, + SDIO_TEST_LPM_HOST_WAKER, + SDIO_TEST_LPM_CLIENT_WAKER, + SDIO_TEST_LPM_RANDOM, + SDIO_TEST_HOST_SENDER_NO_LP, + SDIO_TEST_CLOSE_CHANNEL, + SDIO_TEST_A2_VALIDATION, + /* The following tests are not part of the 9k tests and should be + * kept last in case new tests are added + */ + SDIO_TEST_PERF, + SDIO_TEST_RTT, + SDIO_TEST_MODEM_RESET, +}; + +struct lpm_task { + struct task_struct *lpm_task; + const char *task_name; +}; + +struct lpm_entry_type { + enum lpm_test_msg_type msg_type; + char msg_name[LPM_MSG_NAME_SIZE]; + u32 counter; + u32 current_ms; + u32 read_avail_mask; + char chan_name[CHANNEL_NAME_SIZE]; +}; + +struct lpm_msg { + u32 signature; + u32 counter; + u32 reserve1; + u32 reserve2; +}; + +struct test_config_msg { + u32 signature; + u32 test_case; + u32 test_param; + u32 num_packets; + u32 num_iterations; +}; + +struct test_result_msg { + u32 signature; + u32 is_successful; +}; + +struct test_work { + struct work_struct work; + struct test_channel *test_ch; +}; + +enum sdio_channels_ids { + SDIO_RPC, + SDIO_QMI, + SDIO_RMNT, + SDIO_DIAG, + SDIO_DUN, + SDIO_SMEM, + SDIO_CSVT, + SDIO_MAX_CHANNELS +}; + +enum sdio_test_results { + TEST_NO_RESULT, + TEST_FAILED, + TEST_PASSED +}; + +enum sdio_lpm_vote_state { + SDIO_NO_VOTE, + SDIO_VOTE_FOR_SLEEP, + SDIO_VOTE_AGAINST_SLEEP +}; + +struct sdio_test_device { + int open_channels_counter_to_recv; + int open_channels_counter_to_send; + struct lpm_entry_type *lpm_arr; + int array_size; + void *sdio_al_device; + spinlock_t lpm_array_lock; + unsigned long lpm_array_lock_flags; + u32 next_avail_entry_in_array; + struct lpm_task lpm_test_task; + u32 next_mask_id; + u32 read_avail_mask; + int modem_result_per_dev; + int final_result_per_dev; +}; + +struct test_channel { + struct sdio_channel *ch; + + char name[CHANNEL_NAME_SIZE]; + int ch_id; + + struct sdio_test_device *test_device; + + u32 *buf; + u32 buf_size; + + struct workqueue_struct *workqueue; + struct test_work test_work; + + u32 rx_bytes; + u32 tx_bytes; + + wait_queue_head_t wait_q; + atomic_t rx_notify_count; + atomic_t tx_notify_count; + atomic_t any_notify_count; + atomic_t wakeup_client; + atomic_t card_detected_event; + + int wait_counter; + + int is_used; + int test_type; + int ch_ready; + + struct test_config_msg config_msg; + + int test_completed; + int test_result; + struct timer_list timer; + int timer_interval_ms; + + struct timer_list timeout_timer; + int timeout_ms; + void *sdio_al_device; + int is_ok_to_sleep; + unsigned int packet_length; + int random_packet_size; + int next_index_in_sent_msg_per_chan; + int channel_mask_id; + int modem_result_per_chan; + int notify_counter_per_chan; + int max_burst_size; /* number of writes before close/open */ + int card_removed; +}; + +struct sdio_al_test_debug { + u32 dun_throughput; + u32 rmnt_throughput; + struct dentry *debug_root; + struct dentry *debug_test_result; + struct dentry *debug_dun_throughput; + struct dentry *debug_rmnt_throughput; + struct dentry *rpc_sender_test; + struct dentry *rpc_qmi_diag_sender_test; + struct dentry *smem_test; + struct dentry *smem_rpc_test; + struct dentry *rmnet_a2_validation_test; + struct dentry *dun_a2_validation_test; + struct dentry *rmnet_a2_perf_test; + struct dentry *dun_a2_perf_test; + struct dentry *csvt_a2_perf_test; + struct dentry *rmnet_dun_a2_perf_test; + struct dentry *rpc_sender_rmnet_a2_perf_test; + struct dentry *all_channels_test; + struct dentry *host_sender_no_lp_diag_test; + struct dentry *host_sender_no_lp_diag_rpc_test; + struct dentry *rmnet_small_packets_test; + struct dentry *rmnet_rtt_test; + struct dentry *csvt_rtt_test; + struct dentry *modem_reset_rpc_test; + struct dentry *modem_reset_rmnet_test; + struct dentry *modem_reset_channels_4bit_dev_test; + struct dentry *modem_reset_channels_8bit_dev_test; + struct dentry *modem_reset_all_channels_test; + struct dentry *open_close_test; + struct dentry *open_close_dun_rmnet_test; + struct dentry *close_chan_lpm_test; + struct dentry *lpm_test_client_wakes_host_test; + struct dentry *lpm_test_host_wakes_client_test; + struct dentry *lpm_test_random_single_channel_test; + struct dentry *lpm_test_random_multi_channel_test; +}; + +struct test_context { + dev_t dev_num; + struct device *dev; + struct cdev *cdev; + int number_of_active_devices; + int max_number_of_devices; + + struct sdio_test_device test_dev_arr[MAX_NUM_OF_SDIO_DEVICES]; + + struct test_channel *test_ch; + + struct test_channel *test_ch_arr[SDIO_MAX_CHANNELS]; + + long testcase; + + const char *name; + + int exit_flag; + + u32 signature; + + int runtime_debug; + + struct platform_device *smem_pdev; + struct sdio_smem_client *sdio_smem; + int smem_was_init; + u8 *smem_buf; + uint32_t smem_counter; + + struct platform_device *csvt_app_pdev; + + wait_queue_head_t wait_q; + int test_completed; + int test_result; + struct sdio_al_test_debug debug; + + struct wake_lock wake_lock; + + unsigned int lpm_pseudo_random_seed; +}; + +/* FORWARD DECLARATIONS */ +static int set_params_loopback_9k(struct test_channel *tch); +static int set_params_smem_test(struct test_channel *tch); +static int set_params_a2_validation(struct test_channel *tch); +static int set_params_a2_perf(struct test_channel *tch); +static int set_params_8k_sender_no_lp(struct test_channel *tch); +static int set_params_a2_small_pkts(struct test_channel *tch); +static int set_params_rtt(struct test_channel *tch); +static int set_params_loopback_9k_close(struct test_channel *tch); +static int close_channel_lpm_test(int channel_num); +static int set_params_lpm_test(struct test_channel *tch, + enum sdio_test_case_type test, + int timer_interval_ms); +static void set_pseudo_random_seed(void); +static int set_params_modem_reset(struct test_channel *tch); +static int test_start(void); +static void rx_cleanup(struct test_channel *test_ch, int *rx_packet_count); +static void sdio_al_test_cleanup_channels(void); +static void notify(void *priv, unsigned channel_event); +#ifdef CONFIG_MSM_SDIO_SMEM +static int sdio_smem_open(struct sdio_smem_client *sdio_smem); +#endif + +/* + * Seed for pseudo random time sleeping in Random LPM test. + * If not set, current time in jiffies is used. + */ +static unsigned int seed; +module_param(seed, int, 0); +static struct test_context *test_ctx; + +static void sdio_al_test_initial_dev_and_chan(struct test_context *test_ctx) +{ + int i = 0; + + if (!test_ctx) { + pr_err(TEST_MODULE_NAME ":%s - test_ctx is NULL.\n", __func__); + return; + } + + for (i = 0 ; i < MAX_NUM_OF_SDIO_DEVICES ; ++i) + test_ctx->test_dev_arr[i].sdio_al_device = NULL; + + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + if (!tch) + continue; + tch->is_used = 0; + } + + sdio_al_test_cleanup_channels(); +} + +#ifdef CONFIG_DEBUG_FS + +static int message_repeat; + +static int sdio_al_test_extract_number(const char __user *buf, + size_t count) +{ + int ret = 0; + int number = -1; + char local_buf[MAX_STR_SIZE] = {0}; + char *start = NULL; + + if (count > MAX_STR_SIZE) { + pr_err(TEST_MODULE_NAME ": %s - MAX_STR_SIZE(%d) < count(%d). " + "Please choose smaller number\n", + __func__, MAX_STR_SIZE, (int)count); + return -EINVAL; + } + + if (copy_from_user(local_buf, buf, count)) { + pr_err(TEST_MODULE_NAME ": %s - copy_from_user() failed\n", + __func__); + return -EINVAL; + } + + /* adding null termination to the string */ + local_buf[count] = '\0'; + + /* stripping leading and trailing white spaces */ + start = strstrip(local_buf); + + ret = kstrtoint(start, 10, &number); + + if (ret) { + pr_err(TEST_MODULE_NAME " : %s - kstrtoint() failed\n", + __func__); + return ret; + } + + return number; +} + +static int sdio_al_test_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + message_repeat = 1; + return 0; +} + +static void sdio_al_test_cleanup_channels(void) +{ + int channel_num; + int dummy = 0; + + for (channel_num = 0 ; channel_num < SDIO_MAX_CHANNELS ; + ++channel_num) { + if (channel_num == SDIO_SMEM) + continue; + + rx_cleanup(test_ctx->test_ch_arr[channel_num], &dummy); + } + + return; +} + +/* RPC SENDER TEST */ +static ssize_t rpc_sender_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- RPC SENDER TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rpc_sender_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRPC_SENDER_TEST\n" + "===============\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rpc_sender_test_ops = { + .open = sdio_al_test_open, + .write = rpc_sender_test_write, + .read = rpc_sender_test_read, +}; + +/* RPC, QMI & DIAG SENDER TEST */ +static ssize_t rpc_qmi_diag_sender_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- RPC, QMI AND DIAG SENDER TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]); + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_QMI]); + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_DIAG]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rpc_qmi_diag_sender_test_read(struct file *file, + char __user + *buffer, size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRPC_QMI_DIAG_SENDER_TEST\n" + "========================\n" + "Description:\n" + "TBD\n"); + + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rpc_qmi_diag_sender_test_ops = { + .open = sdio_al_test_open, + .write = rpc_qmi_diag_sender_test_write, + .read = rpc_qmi_diag_sender_test_read, +}; + +/* SMEM TEST */ +static ssize_t smem_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- SMEM TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_smem_test(test_ctx->test_ch_arr[SDIO_SMEM]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t smem_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nSMEM_TEST\n" + "=========\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations smem_test_ops = { + .open = sdio_al_test_open, + .write = smem_test_write, + .read = smem_test_read, +}; + +/* SMEM & RPC TEST */ +static ssize_t smem_rpc_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- SMEM AND RPC TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]); + set_params_smem_test(test_ctx->test_ch_arr[SDIO_SMEM]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t smem_rpc_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nSMEM_RPC_TEST\n" + "=============\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations smem_rpc_test_ops = { + .open = sdio_al_test_open, + .write = smem_rpc_test_write, + .read = smem_rpc_test_read, +}; + +/* RMNET A2 VALIDATION TEST */ +static ssize_t rmnet_a2_validation_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- RMNET A2 VALIDATION TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_a2_validation(test_ctx->test_ch_arr[SDIO_RMNT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rmnet_a2_validation_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRMNET_A2_VALIDATION_TEST\n" + "=========================\n" + "Description:\n" + "In this test, the HOST sends multiple packets to the\n" + "CLIENT and validates the packets loop backed from A2\n" + "for the RMNET channel.\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rmnet_a2_validation_test_ops = { + .open = sdio_al_test_open, + .write = rmnet_a2_validation_test_write, + .read = rmnet_a2_validation_test_read, +}; + +/* DUN A2 VALIDATION TEST */ +static ssize_t dun_a2_validation_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- DUN A2 VALIDATION TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_a2_validation(test_ctx->test_ch_arr[SDIO_DUN]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t dun_a2_validation_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nDUN_A2_VALIDATION_TEST\n" + "=========================\n" + "Description:\n" + "In this test, the HOST sends multiple packets to the\n" + "CLIENT and validates the packets loop backed from A2\n" + "for the DUN channel.\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations dun_a2_validation_test_ops = { + .open = sdio_al_test_open, + .write = dun_a2_validation_test_write, + .read = dun_a2_validation_test_read, +}; + +/* RMNET A2 PERFORMANCE TEST */ +static ssize_t rmnet_a2_perf_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- RMNET A2 PERFORMANCE TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_RMNT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rmnet_a2_perf_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRMNET_A2_PERFORMANCE_TEST\n" + "=========================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rmnet_a2_perf_test_ops = { + .open = sdio_al_test_open, + .write = rmnet_a2_perf_test_write, + .read = rmnet_a2_perf_test_read, +}; + +/* DUN A2 PERFORMANCE TEST */ +static ssize_t dun_a2_perf_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- DUN A2 PERFORMANCE TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_DUN]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t dun_a2_perf_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nDUN_A2_PERFORMANCE_TEST\n" + "=======================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations dun_a2_perf_test_ops = { + .open = sdio_al_test_open, + .write = dun_a2_perf_test_write, + .read = dun_a2_perf_test_read, +}; + +/* CSVT A2 PERFORMANCE TEST */ +static ssize_t csvt_a2_perf_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- CSVT A2 PERFORMANCE TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_CSVT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t csvt_a2_perf_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nCSVT_A2_PERFORMANCE_TEST\n" + "========================\n" + "Description:\n" + "Loopback test on the CSVT Channel, in order to check " + "throughput performance.\n" + "Packet size that are sent on the CSVT channel in this " + "test is %d.bytes\n\n" + "END OF DESCRIPTION\n", CSVT_PACKET_SIZE); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations csvt_a2_perf_test_ops = { + .open = sdio_al_test_open, + .write = csvt_a2_perf_test_write, + .read = csvt_a2_perf_test_read, +}; + +/* RMNET DUN A2 PERFORMANCE TEST */ +static ssize_t rmnet_dun_a2_perf_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- RMNET AND DUN A2 PERFORMANCE TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_RMNT]); + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_DUN]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rmnet_dun_a2_perf_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRMNET_DUN_A2_PERFORMANCE_TEST\n" + "=============================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rmnet_dun_a2_perf_test_ops = { + .open = sdio_al_test_open, + .write = rmnet_dun_a2_perf_test_write, + .read = rmnet_dun_a2_perf_test_read, +}; + +/* RPC SENDER & RMNET A2 PERFORMANCE TEST */ +static ssize_t rpc_sender_rmnet_a2_perf_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "--RPC SENDER AND RMNET A2 " + "PERFORMANCE --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]); + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_RMNT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rpc_sender_rmnet_a2_perf_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRPC_SENDER_RMNET_A2_PERFORMANCE_TEST\n" + "====================================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rpc_sender_rmnet_a2_perf_test_ops = { + .open = sdio_al_test_open, + .write = rpc_sender_rmnet_a2_perf_test_write, + .read = rpc_sender_rmnet_a2_perf_test_read, +}; + +/* ALL CHANNELS TEST */ +static ssize_t all_channels_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- ALL THE CHANNELS TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]); + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_QMI]); + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_DIAG]); + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_RMNT]); + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_DUN]); + set_params_smem_test(test_ctx->test_ch_arr[SDIO_SMEM]); + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_CSVT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t all_channels_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nALL_CHANNELS_TEST\n" + "=================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations all_channels_test_ops = { + .open = sdio_al_test_open, + .write = all_channels_test_write, + .read = all_channels_test_read, +}; + +/* HOST SENDER NO LP DIAG TEST */ +static ssize_t host_sender_no_lp_diag_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- HOST SENDER NO LP FOR DIAG TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_8k_sender_no_lp(test_ctx->test_ch_arr[SDIO_DIAG]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t host_sender_no_lp_diag_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nHOST_SENDER_NO_LP_DIAG_TEST\n" + "===========================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations host_sender_no_lp_diag_test_ops = { + .open = sdio_al_test_open, + .write = host_sender_no_lp_diag_test_write, + .read = host_sender_no_lp_diag_test_read, +}; + +/* HOST SENDER NO LP DIAG, RPC TEST */ +static ssize_t host_sender_no_lp_diag_rpc_test_write( + struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- HOST SENDER NO LP FOR DIAG, RPC " + "TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_8k_sender_no_lp(test_ctx->test_ch_arr[SDIO_DIAG]); + set_params_8k_sender_no_lp(test_ctx->test_ch_arr[SDIO_RPC]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t host_sender_no_lp_diag_rpc_test_read( + struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nHOST_SENDER_NO_LP_DIAG_RPC_TEST\n" + "===================================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations host_sender_no_lp_diag_rpc_test_ops = { + .open = sdio_al_test_open, + .write = host_sender_no_lp_diag_rpc_test_write, + .read = host_sender_no_lp_diag_rpc_test_read, +}; + +/* RMNET SMALL PACKETS TEST */ +static ssize_t rmnet_small_packets_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- RMNET SMALL PACKETS (5-128) TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_a2_small_pkts(test_ctx->test_ch_arr[SDIO_RMNT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rmnet_small_packets_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRMNET_SMALL_PACKETS_TEST\n" + "========================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rmnet_small_packets_test_ops = { + .open = sdio_al_test_open, + .write = rmnet_small_packets_test_write, + .read = rmnet_small_packets_test_read, +}; + +/* RMNET RTT TEST */ +static ssize_t rmnet_rtt_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- RMNET RTT TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_rtt(test_ctx->test_ch_arr[SDIO_RMNT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rmnet_rtt_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRMNET_RTT_TEST\n" + "==============\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rmnet_rtt_test_ops = { + .open = sdio_al_test_open, + .write = rmnet_rtt_test_write, + .read = rmnet_rtt_test_read, +}; + +/* CSVT RTT TEST */ +static ssize_t csvt_rtt_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- CSVT RTT TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_rtt(test_ctx->test_ch_arr[SDIO_CSVT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t csvt_rtt_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nCSVT_RTT_TEST\n" + "==============\n" + "Description:\n" + "In this test the HOST send a message of %d bytes " + "to the CLIENT\n\n" + "END OF DESCRIPTION\n", SDIO_CSVT_RTT_PACKET_SIZE); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations csvt_rtt_test_ops = { + .open = sdio_al_test_open, + .write = csvt_rtt_test_write, + .read = csvt_rtt_test_read, +}; + +/* MODEM RESET RPC TEST */ +static ssize_t modem_reset_rpc_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- MODEM RESET - RPC CHANNEL TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RPC]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t modem_reset_rpc_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nMODEM_RESET_RPC_TEST\n" + "====================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations modem_reset_rpc_test_ops = { + .open = sdio_al_test_open, + .write = modem_reset_rpc_test_write, + .read = modem_reset_rpc_test_read, +}; + +/* MODEM RESET RMNET TEST */ +static ssize_t modem_reset_rmnet_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- MODEM RESET - RMNT CHANNEL TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RMNT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t modem_reset_rmnet_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nMODEM_RESET_RMNET_TEST\n" + "======================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations modem_reset_rmnet_test_ops = { + .open = sdio_al_test_open, + .write = modem_reset_rmnet_test_write, + .read = modem_reset_rmnet_test_read, +}; + +/* MODEM RESET - CHANNELS IN 4BIT DEVICE TEST */ +static ssize_t modem_reset_channels_4bit_dev_test_write( + struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- MODEM RESET - ALL CHANNELS IN " + "4BIT DEVICE TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RPC]); + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_QMI]); + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_DIAG]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t modem_reset_channels_4bit_dev_test_read( + struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nMODEM_RESET_CHANNELS_4BIT_DEV_TEST\n" + "==================================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations modem_reset_channels_4bit_dev_test_ops = { + .open = sdio_al_test_open, + .write = modem_reset_channels_4bit_dev_test_write, + .read = modem_reset_channels_4bit_dev_test_read, +}; + +/* MODEM RESET - CHANNELS IN 8BIT DEVICE TEST */ +static ssize_t modem_reset_channels_8bit_dev_test_write( + struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- MODEM RESET - ALL CHANNELS IN " + "8BIT DEVICE TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RMNT]); + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_DUN]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t modem_reset_channels_8bit_dev_test_read( + struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nMODEM_RESET_CHANNELS_8BIT_DEV_TEST\n" + "==================================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations modem_reset_channels_8bit_dev_test_ops = { + .open = sdio_al_test_open, + .write = modem_reset_channels_8bit_dev_test_write, + .read = modem_reset_channels_8bit_dev_test_read, +}; + +/* MODEM RESET - ALL CHANNELS TEST */ +static ssize_t modem_reset_all_channels_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- MODEM RESET - ALL CHANNELS TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RPC]); + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_QMI]); + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_DIAG]); + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RMNT]); + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_DUN]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t modem_reset_all_channels_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nMODEM_RESET_ALL_CHANNELS_TEST\n" + "=============================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations modem_reset_all_channels_test_ops = { + .open = sdio_al_test_open, + .write = modem_reset_all_channels_test_write, + .read = modem_reset_all_channels_test_read, +}; + +/* HOST SENDER WITH OPEN/CLOSE TEST */ +static ssize_t open_close_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + struct test_channel **ch_arr = test_ctx->test_ch_arr; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- HOST SENDER WITH OPEN/CLOSE TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_loopback_9k_close(ch_arr[SDIO_DIAG]); + set_params_loopback_9k_close(ch_arr[SDIO_RPC]); + set_params_loopback_9k_close(ch_arr[SDIO_SMEM]); + set_params_loopback_9k_close(ch_arr[SDIO_QMI]); + set_params_loopback_9k_close(ch_arr[SDIO_RMNT]); + set_params_loopback_9k_close(ch_arr[SDIO_DUN]); + set_params_loopback_9k_close(ch_arr[SDIO_CSVT]); + + ret = test_start(); + + if (ret) + break; + + pr_info(TEST_MODULE_NAME " -- correctness test for" + "DIAG "); + set_params_loopback_9k(ch_arr[SDIO_DIAG]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t open_close_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nOPEN_CLOSE_TEST\n" + "============================\n" + "Description:\n" + "In this test the host sends 5k packets to the modem in the " + "following sequence: Send a random burst of packets on " + "Diag and Rmnet channels, read 0 or a random number " + "of packets, close and re-open the channel. At the end of the " + "test, the channel is verified by running a loopback test\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations open_close_test_ops = { + .open = sdio_al_test_open, + .write = open_close_test_write, + .read = open_close_test_read, +}; + +/* HOST SENDER WITH OPEN/CLOSE FOR DUN & RMNET TEST */ +static ssize_t open_close_dun_rmnet_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + struct test_channel **ch_arr = test_ctx->test_ch_arr; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- HOST SENDER WITH OPEN/CLOSE FOR " + "DUN AND RMNET TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_loopback_9k_close(ch_arr[SDIO_DUN]); + set_params_loopback_9k_close(ch_arr[SDIO_RMNT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t open_close_dun_rmnet_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nOPEN_CLOSE_DUN_RMNET_TEST\n" + "============================\n" + "Description:\n" + "In this test the host sends 5k packets to the modem in the " + "following sequence: Send a random burst of packets on " + "DUN and Rmnet channels, read 0 or a random number " + "of packets, close and re-open the channel.\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations open_close_dun_rmnet_test_ops = { + .open = sdio_al_test_open, + .write = open_close_dun_rmnet_test_write, + .read = open_close_dun_rmnet_test_read, +}; + +/* CLOSE CHANNEL & LPM TEST HOST WAKES THE CLIENT TEST */ +static ssize_t close_chan_lpm_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int channel_num = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- CLOSE CHANNEL & LPM TEST " + "HOST WAKES THE CLIENT TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + for (channel_num = 0 ; channel_num < SDIO_MAX_CHANNELS ; + channel_num++) { + + ret = close_channel_lpm_test(channel_num); + + if (ret) + break; + + set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC], + SDIO_TEST_LPM_HOST_WAKER, 120); + + ret = test_start(); + + if (ret) + break; + } + + if (ret) { + pr_err(TEST_MODULE_NAME " -- Close channel & LPM Test " + "FAILED: %d --\n", ret); + } else { + pr_err(TEST_MODULE_NAME " -- Close channel & LPM Test " + "PASSED\n"); + } + } + + return count; +} + +static ssize_t close_chan_lpm_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nCLOSE_CHAN_LPM_TEST\n" + "===================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations close_chan_lpm_test_ops = { + .open = sdio_al_test_open, + .write = close_chan_lpm_test_write, + .read = close_chan_lpm_test_read, +}; + +/* LPM TEST FOR DEVICE 1. CLIENT WAKES THE HOST TEST */ +static ssize_t lpm_test_client_wakes_host_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- LPM TEST FOR DEVICE 1. CLIENT " + "WAKES THE HOST TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC], + SDIO_TEST_LPM_CLIENT_WAKER, 90); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t lpm_test_client_wakes_host_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nLPM_TEST_CLIENT_WAKES_HOST_TEST\n" + "===============================\n" + "Description:\n" + "In this test, the HOST is going into LPM mode,\n" + "and the CLIENT is responsible to send it a message\n" + "in order to wake it up\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations lpm_test_client_wakes_host_test_ops = { + .open = sdio_al_test_open, + .write = lpm_test_client_wakes_host_test_write, + .read = lpm_test_client_wakes_host_test_read, +}; + +/* LPM TEST FOR DEVICE 1. HOST WAKES THE CLIENT TEST */ +static ssize_t lpm_test_host_wakes_client_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- LPM TEST FOR DEVICE 1. HOST " + "WAKES THE CLIENT TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC], + SDIO_TEST_LPM_HOST_WAKER, 120); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t lpm_test_host_wakes_client_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nLPM_TEST_HOST_WAKES_CLIENT_TEST\n" + "===============================\n" + "Description:\n" + "In this test, the CLIENT goes into LPM mode, and the\n" + "HOST is responsible to send it a message\n" + "in order to wake it up\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations lpm_test_host_wakes_client_test_ops = { + .open = sdio_al_test_open, + .write = lpm_test_host_wakes_client_test_write, + .read = lpm_test_host_wakes_client_test_read, +}; + +/* LPM TEST RANDOM, SINGLE CHANNEL TEST */ +static ssize_t lpm_test_random_single_channel_test_write( + struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- LPM TEST RANDOM SINGLE " + "CHANNEL TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_pseudo_random_seed(); + set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC], + SDIO_TEST_LPM_RANDOM, 0); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t lpm_test_random_single_channel_test_read( + struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nLPM_TEST_RANDOM_SINGLE_CHANNEL_TEST\n" + "===================================\n" + "Description:\n" + "In this test, the HOST and CLIENT " + "send messages to each other,\n" + "random in time, over RPC channel only.\n" + "All events are being recorded, and later on,\n" + "they are being analysed by the HOST and by the CLIENT\n," + "in order to check if the LPM mechanism worked properly,\n" + "meaning:" + " When all the relevant conditions are met, a device should:\n" + "1. Go to sleep\n" + "2. Wake up\n" + "3. Stay awake\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations lpm_test_random_single_channel_test_ops = { + .open = sdio_al_test_open, + .write = lpm_test_random_single_channel_test_write, + .read = lpm_test_random_single_channel_test_read, +}; + +/* LPM TEST RANDOM, MULTI CHANNEL TEST */ +static ssize_t lpm_test_random_multi_channel_test_write( + struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- LPM TEST RANDOM MULTI CHANNEL TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_pseudo_random_seed(); + + set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC], + SDIO_TEST_LPM_RANDOM, 0); + set_params_lpm_test(test_ctx->test_ch_arr[SDIO_DIAG], + SDIO_TEST_LPM_RANDOM, 0); + set_params_lpm_test(test_ctx->test_ch_arr[SDIO_QMI], + SDIO_TEST_LPM_RANDOM, 0); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t lpm_test_random_multi_channel_test_read( + struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nLPM_TEST_RANDOM_MULTI_CHANNEL_TEST\n" + "==================================\n" + "Description:\n" + "In this test, the HOST and CLIENT " + "send messages to each other,\n" + "random in time, over RPC, QMI AND DIAG channels\n" + "(i.e, on both SDIO devices).\n" + "All events are being recorded, and later on,\n" + "they are being analysed by the HOST and by the CLIENT,\n" + "in order to check if the LPM mechanism worked properly,\n" + "meaning:" + " When all the relevant conditions are met, a device should:\n" + "1. Go to sleep\n" + "2. Wake up\n" + "3. Stay awake\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations lpm_test_random_multi_channel_test_ops = { + .open = sdio_al_test_open, + .write = lpm_test_random_multi_channel_test_write, + .read = lpm_test_random_multi_channel_test_read, +}; + +static int sdio_al_test_debugfs_init(void) +{ + test_ctx->debug.debug_root = debugfs_create_dir("sdio_al_test", + NULL); + if (!test_ctx->debug.debug_root) + return -ENOENT; + + test_ctx->debug.debug_test_result = debugfs_create_u32( + "test_result", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + &test_ctx->test_result); + + test_ctx->debug.debug_dun_throughput = debugfs_create_u32( + "dun_throughput", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + &test_ctx->debug.dun_throughput); + + test_ctx->debug.debug_rmnt_throughput = debugfs_create_u32( + "rmnt_throughput", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + &test_ctx->debug.rmnt_throughput); + + test_ctx->debug.rpc_sender_test = + debugfs_create_file("10_rpc_sender_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rpc_sender_test_ops); + + test_ctx->debug.rpc_qmi_diag_sender_test = + debugfs_create_file("20_rpc_qmi_diag_sender_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rpc_qmi_diag_sender_test_ops); + + test_ctx->debug.rmnet_a2_validation_test = + debugfs_create_file("30_rmnet_a2_validation_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rmnet_a2_validation_test_ops); + + test_ctx->debug.dun_a2_validation_test = + debugfs_create_file("40_dun_a2_validation_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &dun_a2_validation_test_ops); + + test_ctx->debug.rmnet_a2_perf_test = + debugfs_create_file("50_rmnet_a2_perf_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rmnet_a2_perf_test_ops); + + test_ctx->debug.dun_a2_perf_test = + debugfs_create_file("60_dun_a2_perf_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &dun_a2_perf_test_ops); + + test_ctx->debug.csvt_a2_perf_test = + debugfs_create_file("71_csvt_a2_perf_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &csvt_a2_perf_test_ops); + + test_ctx->debug.rmnet_dun_a2_perf_test = + debugfs_create_file("70_rmnet_dun_a2_perf_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rmnet_dun_a2_perf_test_ops); + + test_ctx->debug.rpc_sender_rmnet_a2_perf_test = + debugfs_create_file("80_rpc_sender_rmnet_a2_perf_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rpc_sender_rmnet_a2_perf_test_ops); + + test_ctx->debug.smem_test = + debugfs_create_file("90_smem_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &smem_test_ops); + + test_ctx->debug.smem_rpc_test = + debugfs_create_file("100_smem_rpc_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &smem_rpc_test_ops); + + test_ctx->debug.all_channels_test = + debugfs_create_file("150_all_channels_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &all_channels_test_ops); + + test_ctx->debug.host_sender_no_lp_diag_test = + debugfs_create_file("160_host_sender_no_lp_diag_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &host_sender_no_lp_diag_test_ops); + + test_ctx->debug.host_sender_no_lp_diag_rpc_test = + debugfs_create_file("170_host_sender_no_lp_diag_rpc_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &host_sender_no_lp_diag_rpc_test_ops); + + test_ctx->debug.rmnet_small_packets_test = + debugfs_create_file("180_rmnet_small_packets_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rmnet_small_packets_test_ops); + + test_ctx->debug.rmnet_rtt_test = + debugfs_create_file("190_rmnet_rtt_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rmnet_rtt_test_ops); + + test_ctx->debug.csvt_rtt_test = + debugfs_create_file("191_csvt_rtt_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &csvt_rtt_test_ops); + + test_ctx->debug.modem_reset_rpc_test = + debugfs_create_file("220_modem_reset_rpc_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &modem_reset_rpc_test_ops); + + test_ctx->debug.modem_reset_rmnet_test = + debugfs_create_file("230_modem_reset_rmnet_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &modem_reset_rmnet_test_ops); + + test_ctx->debug.modem_reset_channels_4bit_dev_test = + debugfs_create_file("240_modem_reset_channels_4bit_dev_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &modem_reset_channels_4bit_dev_test_ops); + + test_ctx->debug.modem_reset_channels_8bit_dev_test = + debugfs_create_file("250_modem_reset_channels_8bit_dev_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &modem_reset_channels_8bit_dev_test_ops); + + test_ctx->debug.modem_reset_all_channels_test = + debugfs_create_file("260_modem_reset_all_channels_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &modem_reset_all_channels_test_ops); + + test_ctx->debug.open_close_test = + debugfs_create_file("270_open_close_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &open_close_test_ops); + + test_ctx->debug.open_close_dun_rmnet_test = + debugfs_create_file("271_open_close_dun_rmnet_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &open_close_dun_rmnet_test_ops); + + test_ctx->debug.close_chan_lpm_test = + debugfs_create_file("280_close_chan_lpm_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &close_chan_lpm_test_ops); + + test_ctx->debug.lpm_test_client_wakes_host_test = + debugfs_create_file("600_lpm_test_client_wakes_host_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &lpm_test_client_wakes_host_test_ops); + + test_ctx->debug.lpm_test_host_wakes_client_test = + debugfs_create_file("610_lpm_test_host_wakes_client_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &lpm_test_host_wakes_client_test_ops); + + test_ctx->debug.lpm_test_random_single_channel_test = + debugfs_create_file("620_lpm_test_random_single_channel_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &lpm_test_random_single_channel_test_ops); + + test_ctx->debug.lpm_test_random_multi_channel_test = + debugfs_create_file("630_lpm_test_random_multi_channel_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &lpm_test_random_multi_channel_test_ops); + + if ((!test_ctx->debug.debug_dun_throughput) && + (!test_ctx->debug.debug_rmnt_throughput)) { + debugfs_remove_recursive(test_ctx->debug.debug_root); + test_ctx->debug.debug_root = NULL; + return -ENOENT; + } + return 0; +} + +static void sdio_al_test_debugfs_cleanup(void) +{ + debugfs_remove(test_ctx->debug.debug_dun_throughput); + debugfs_remove(test_ctx->debug.debug_rmnt_throughput); + debugfs_remove(test_ctx->debug.debug_root); +} +#endif + +static int channel_name_to_id(char *name) +{ + pr_info(TEST_MODULE_NAME "%s: channel name %s\n", + __func__, name); + + if (!strncmp(name, "SDIO_RPC_TEST", + strnlen("SDIO_RPC_TEST", CHANNEL_NAME_SIZE))) + return SDIO_RPC; + else if (!strncmp(name, "SDIO_QMI_TEST", + strnlen("SDIO_QMI_TEST", TEST_CH_NAME_SIZE))) + return SDIO_QMI; + else if (!strncmp(name, "SDIO_RMNT_TEST", + strnlen("SDIO_RMNT_TEST", TEST_CH_NAME_SIZE))) + return SDIO_RMNT; + else if (!strncmp(name, "SDIO_DIAG_TEST", + strnlen("SDIO_DIAG", TEST_CH_NAME_SIZE))) + return SDIO_DIAG; + else if (!strncmp(name, "SDIO_DUN_TEST", + strnlen("SDIO_DUN_TEST", TEST_CH_NAME_SIZE))) + return SDIO_DUN; + else if (!strncmp(name, "SDIO_SMEM_TEST", + strnlen("SDIO_SMEM_TEST", TEST_CH_NAME_SIZE))) + return SDIO_SMEM; + else if (!strncmp(name, "SDIO_CSVT_TEST", + strnlen("SDIO_CSVT_TEST", TEST_CH_NAME_SIZE))) + return SDIO_CSVT; + else + return SDIO_MAX_CHANNELS; + + return SDIO_MAX_CHANNELS; +} + +/** + * Allocate and add SDIO_SMEM platform device + */ +#ifdef CONFIG_MSM_SDIO_SMEM +static int add_sdio_smem(void) +{ + int ret = 0; + + test_ctx->smem_pdev = platform_device_alloc("SDIO_SMEM", -1); + ret = platform_device_add(test_ctx->smem_pdev); + if (ret) { + pr_err(TEST_MODULE_NAME ": platform_device_add failed, " + "ret=%d\n", ret); + return ret; + } + return 0; +} +#endif + +static int open_sdio_ch(struct test_channel *tch) +{ + int ret = 0; + + if (!tch) { + pr_err(TEST_MODULE_NAME ": %s NULL tch\n", __func__); + return -EINVAL; + } + + if (!tch->ch_ready) { + TEST_DBG(TEST_MODULE_NAME ":openning channel %s\n", + tch->name); + if (tch->ch_id == SDIO_SMEM) { +#ifdef CONFIG_MSM_SDIO_SMEM + if (!test_ctx->smem_pdev) + ret = add_sdio_smem(); + else + ret = sdio_smem_open(test_ctx->sdio_smem); + if (ret) { + pr_err(TEST_MODULE_NAME + ":openning channel %s failed\n", + tch->name); + tch->ch_ready = false; + return -EINVAL; + } +#endif + } else { + tch->ch_ready = true; + ret = sdio_open(tch->name , &tch->ch, tch, + notify); + if (ret) { + pr_err(TEST_MODULE_NAME + ":openning channel %s failed\n", + tch->name); + tch->ch_ready = false; + return -EINVAL; + } + } + } + return ret; +} + +static int close_sdio_ch(struct test_channel *tch) +{ + int ret = 0; + + if (!tch) { + pr_err(TEST_MODULE_NAME ": %s NULL tch\n", __func__); + return -EINVAL; + } + + if (tch->ch_id == SDIO_SMEM) { +#ifdef CONFIG_MSM_SDIO_SMEM + TEST_DBG(TEST_MODULE_NAME":%s closing channel %s", + __func__, tch->name); + ret = sdio_smem_unregister_client(); + test_ctx->smem_counter = 0; +#endif + } else { + ret = sdio_close(tch->ch); + } + + if (ret) { + pr_err(TEST_MODULE_NAME":%s close channel %s" + " failed\n", __func__, tch->name); + } else { + TEST_DBG(TEST_MODULE_NAME":%s close channel %s" + " success\n", __func__, tch->name); + tch->ch_ready = false; + } + return ret; +} + +/** + * Config message + */ + +static void send_config_msg(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 write_avail = 0; + int size = sizeof(test_ch->config_msg); + + pr_debug(TEST_MODULE_NAME "%s\n", __func__); + + memcpy(test_ch->buf, (void *)&test_ch->config_msg, size); + + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + + pr_info(TEST_MODULE_NAME ":Sending the config message.\n"); + + /* wait for data ready event */ + write_avail = sdio_write_avail(test_ch->ch); + pr_debug(TEST_MODULE_NAME ":write_avail=%d\n", write_avail); + if (write_avail < size) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->tx_notify_count)); + atomic_dec(&test_ch->tx_notify_count); + } + + write_avail = sdio_write_avail(test_ch->ch); + pr_debug(TEST_MODULE_NAME ":write_avail=%d\n", write_avail); + if (write_avail < size) { + pr_info(TEST_MODULE_NAME ":not enough write avail.\n"); + return; + } + + ret = sdio_write(test_ch->ch, test_ch->buf, size); + if (ret) + pr_err(TEST_MODULE_NAME ":%s sdio_write err=%d.\n", + __func__, -ret); + else + pr_info(TEST_MODULE_NAME ":%s sent config_msg successfully.\n", + __func__); +} + +/** + * Loopback Test + */ +static void loopback_test(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 read_avail = 0; + u32 write_avail = 0; + + while (1) { + + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + + TEST_DBG(TEST_MODULE_NAME "--LOOPBACK WAIT FOR EVENT--.\n"); + /* wait for data ready event */ + wait_event(test_ch->wait_q, + atomic_read(&test_ch->rx_notify_count)); + atomic_dec(&test_ch->rx_notify_count); + + read_avail = sdio_read_avail(test_ch->ch); + if (read_avail == 0) + continue; + + + write_avail = sdio_write_avail(test_ch->ch); + if (write_avail < read_avail) { + pr_info(TEST_MODULE_NAME + ":not enough write avail.\n"); + continue; + } + + ret = sdio_read(test_ch->ch, test_ch->buf, read_avail); + if (ret) { + pr_info(TEST_MODULE_NAME + ":worker, sdio_read err=%d.\n", -ret); + continue; + } + test_ch->rx_bytes += read_avail; + + TEST_DBG(TEST_MODULE_NAME ":worker total rx bytes = 0x%x.\n", + test_ch->rx_bytes); + + + ret = sdio_write(test_ch->ch, + test_ch->buf, read_avail); + if (ret) { + pr_info(TEST_MODULE_NAME + ":loopback sdio_write err=%d.\n", + -ret); + continue; + } + test_ch->tx_bytes += read_avail; + + TEST_DBG(TEST_MODULE_NAME + ":loopback total tx bytes = 0x%x.\n", + test_ch->tx_bytes); + } /* end of while */ +} + +/** + * Check if all tests completed + */ +static void check_test_completion(void) +{ + int i; + + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + + if ((!tch) || (!tch->is_used) || (!tch->ch_ready)) + continue; + if (!tch->test_completed) { + pr_info(TEST_MODULE_NAME ": %s - Channel %s test is " + "not completed", __func__, tch->name); + return; + } + } + pr_info(TEST_MODULE_NAME ": %s - Test is completed", __func__); + test_ctx->test_completed = 1; + wake_up(&test_ctx->wait_q); +} + +static int pseudo_random_seed(unsigned int *seed_number) +{ + if (!seed_number) + return 0; + + *seed_number = (unsigned int)(((unsigned long)*seed_number * + (unsigned long)1103515367) + 35757); + return (int)((*seed_number / (64*1024)) % 500); +} + +/* this function must be locked before accessing it */ +static void lpm_test_update_entry(struct test_channel *tch, + enum lpm_test_msg_type msg_type, + char *msg_name, + int counter) +{ + u32 index = 0; + static int print_full = 1; + struct sdio_test_device *test_device; + + if (!tch) { + pr_err(TEST_MODULE_NAME ": %s - NULL test channel\n", __func__); + return; + } + + test_device = tch->test_device; + + if (!test_device) { + pr_err(TEST_MODULE_NAME ": %s - NULL test device\n", __func__); + return; + } + + if (!test_device->lpm_arr) { + pr_err(TEST_MODULE_NAME ": %s - NULL lpm_arr\n", __func__); + return; + } + + if (test_device->next_avail_entry_in_array >= + test_device->array_size) { + pr_err(TEST_MODULE_NAME ": %s - lpm array is full", + __func__); + + if (print_full) { + print_hex_dump(KERN_INFO, TEST_MODULE_NAME ": lpm_arr:", + 0, 32, 2, + (void *)test_device->lpm_arr, + sizeof(test_device->lpm_arr), false); + print_full = 0; + } + return; + } + + index = test_device->next_avail_entry_in_array; + if ((msg_type == LPM_MSG_SEND) || (msg_type == LPM_MSG_REC)) + test_device->lpm_arr[index].counter = counter; + else + test_device->lpm_arr[index].counter = 0; + + test_device->lpm_arr[index].msg_type = msg_type; + memcpy(test_device->lpm_arr[index].msg_name, msg_name, + LPM_MSG_NAME_SIZE); + test_device->lpm_arr[index].current_ms = + jiffies_to_msecs(get_jiffies_64()); + + test_device->lpm_arr[index].read_avail_mask = + test_device->read_avail_mask; + + if ((msg_type == LPM_SLEEP) || (msg_type == LPM_WAKEUP)) + memcpy(test_device->lpm_arr[index].chan_name, "DEVICE ", + CHANNEL_NAME_SIZE); + else + memcpy(test_device->lpm_arr[index].chan_name, tch->name, + CHANNEL_NAME_SIZE); + + test_device->next_avail_entry_in_array++; +} + +static int wait_for_result_msg(struct test_channel *test_ch) +{ + u32 read_avail = 0; + int ret = 0; + + pr_info(TEST_MODULE_NAME ": %s - START, channel %s\n", + __func__, test_ch->name); + + while (1) { + read_avail = sdio_read_avail(test_ch->ch); + + if (read_avail == 0) { + pr_info(TEST_MODULE_NAME + ": read_avail is 0 for chan %s\n", + test_ch->name); + wait_event(test_ch->wait_q, + atomic_read(&test_ch->rx_notify_count)); + atomic_dec(&test_ch->rx_notify_count); + continue; + } + + memset(test_ch->buf, 0x00, test_ch->buf_size); + + ret = sdio_read(test_ch->ch, test_ch->buf, read_avail); + if (ret) { + pr_info(TEST_MODULE_NAME ": sdio_read for chan" + "%s failed, err=%d.\n", + test_ch->name, -ret); + goto exit_err; + } + + if (test_ch->buf[0] != TEST_CONFIG_SIGNATURE) { + pr_info(TEST_MODULE_NAME ": Not a test_result " + "signature. expected 0x%x. received 0x%x " + "for chan %s\n", + TEST_CONFIG_SIGNATURE, + test_ch->buf[0], + test_ch->name); + continue; + } else { + pr_info(TEST_MODULE_NAME ": Signature is " + "TEST_CONFIG_SIGNATURE as expected for" + "channel %s\n", test_ch->name); + break; + } + } + + return test_ch->buf[1]; + +exit_err: + return 0; +} + +static void print_random_lpm_test_array(struct sdio_test_device *test_dev) +{ + int i; + + if (!test_dev) { + pr_err(TEST_MODULE_NAME ": %s - NULL test device\n", __func__); + return; + } + + for (i = 0 ; i < test_dev->next_avail_entry_in_array ; ++i) { + if (i == 0) + pr_err(TEST_MODULE_NAME ": index %4d, chan=%2s, " + "code=%1d=%4s, msg#%1d, ms from before=-1, " + "read_mask=0x%d, ms=%2u", + i, + test_dev->lpm_arr[i].chan_name, + test_dev->lpm_arr[i].msg_type, + test_dev->lpm_arr[i].msg_name, + test_dev->lpm_arr[i].counter, + test_dev->lpm_arr[i].read_avail_mask, + test_dev->lpm_arr[i].current_ms); + else + pr_err(TEST_MODULE_NAME ": index " + "%4d, %2s, code=%1d=%4s, msg#%1d, ms from " + "before=%2u, read_mask=0x%d, ms=%2u", + i, + test_dev->lpm_arr[i].chan_name, + test_dev->lpm_arr[i].msg_type, + test_dev->lpm_arr[i].msg_name, + test_dev->lpm_arr[i].counter, + test_dev->lpm_arr[i].current_ms - + test_dev->lpm_arr[i-1].current_ms, + test_dev->lpm_arr[i].read_avail_mask, + test_dev->lpm_arr[i].current_ms); + + udelay(1000); + } +} + +static int check_random_lpm_test_array(struct sdio_test_device *test_dev) +{ + int i = 0, j = 0; + unsigned int delta_ms = 0; + int arr_ind = 0; + int ret = 0; + int notify_counter = 0; + int sleep_counter = 0; + int wakeup_counter = 0; + int lpm_activity_counter = 0; + + if (!test_dev) { + pr_err(TEST_MODULE_NAME ": %s - NULL test device\n", __func__); + return -ENODEV; + } + + for (i = 0; i < test_dev->next_avail_entry_in_array; i++) { + notify_counter = 0; + sleep_counter = 0; + wakeup_counter = 0; + + if ((test_dev->lpm_arr[i].msg_type == LPM_MSG_SEND) || + (test_dev->lpm_arr[i].msg_type == LPM_MSG_REC)) { + /* find the next message in the array */ + arr_ind = test_dev->next_avail_entry_in_array; + for (j = i+1; j < arr_ind; j++) { + if ((test_dev->lpm_arr[j].msg_type == + LPM_MSG_SEND) || + (test_dev->lpm_arr[j].msg_type == + LPM_MSG_REC) || + (test_dev->lpm_arr[j].msg_type == + LPM_NOTIFY)) + break; + if (test_dev->lpm_arr[j].msg_type == + LPM_SLEEP) + sleep_counter++; + if (test_dev->lpm_arr[j].msg_type == + LPM_WAKEUP) + wakeup_counter++; + } + if (j == arr_ind) { + ret = 0; + break; + } + + delta_ms = test_dev->lpm_arr[j].current_ms - + test_dev->lpm_arr[i].current_ms; + if (delta_ms < 30) { + if ((sleep_counter == 0) + && (wakeup_counter == 0)) { + continue; + } else { + pr_err(TEST_MODULE_NAME "%s: lpm " + "activity while delta is less " + "than 30, i=%d, j=%d, " + "sleep_counter=%d, " + "wakeup_counter=%d", + __func__, i, j, + sleep_counter, wakeup_counter); + ret = -ENODEV; + break; + } + } else { + if ((delta_ms > 90) && + (test_dev->lpm_arr[i]. + read_avail_mask == 0)) { + if (j != i+3) { + pr_err(TEST_MODULE_NAME + "%s: unexpected " + "lpm activity " + "while delta is " + "bigger than " + "90, i=%d, " + "j=%d, " + "notify_counter" + "=%d", + __func__, i, j, + notify_counter); + ret = -ENODEV; + break; + } + lpm_activity_counter++; + } + } + } + } + + pr_info(TEST_MODULE_NAME ": %s - lpm_activity_counter=%d", + __func__, lpm_activity_counter); + + return ret; +} + +static int lpm_test_main_task(void *ptr) +{ + u32 read_avail = 0; + int last_msg_index = 0; + struct test_channel *test_ch = (struct test_channel *)ptr; + struct sdio_test_device *test_dev; + struct lpm_msg lpm_msg; + int ret = 0; + int host_result = 0; + + if (!test_ch) { + pr_err(TEST_MODULE_NAME ": %s - NULL channel\n", __func__); + return -ENODEV; + } + + pr_err(TEST_MODULE_NAME ": %s - STARTED. channel %s\n", + __func__, test_ch->name); + + test_dev = test_ch->test_device; + + if (!test_dev) { + pr_err(TEST_MODULE_NAME ": %s - NULL Test Device\n", __func__); + return -ENODEV; + } + + while (last_msg_index < test_ch->config_msg.num_packets - 1) { + + TEST_DBG(TEST_MODULE_NAME ": %s - " + "IN LOOP last_msg_index=%d\n", + __func__, last_msg_index); + + read_avail = sdio_read_avail(test_ch->ch); + if (read_avail == 0) { + TEST_DBG(TEST_MODULE_NAME + ":read_avail 0 for chan %s, " + "wait for event\n", + test_ch->name); + wait_event(test_ch->wait_q, + atomic_read(&test_ch->rx_notify_count)); + atomic_dec(&test_ch->rx_notify_count); + + read_avail = sdio_read_avail(test_ch->ch); + if (read_avail == 0) { + pr_err(TEST_MODULE_NAME + ":read_avail size %d for chan %s not as" + " expected\n", + read_avail, test_ch->name); + continue; + } + } + + memset(test_ch->buf, 0x00, sizeof(test_ch->buf)); + + ret = sdio_read(test_ch->ch, test_ch->buf, read_avail); + if (ret) { + pr_info(TEST_MODULE_NAME ":sdio_read for chan %s" + " err=%d.\n", test_ch->name, -ret); + goto exit_err; + } + + memcpy((void *)&lpm_msg, test_ch->buf, sizeof(lpm_msg)); + + /* + * when reading from channel, we want to turn off the bit + * mask that implies that there is pending data on that channel + */ + if (test_ch->test_device != NULL) { + spin_lock_irqsave(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + + test_ch->notify_counter_per_chan--; + + /* + * if the channel has no pending data, turn off the + * pending data bit mask of the channel + */ + if (test_ch->notify_counter_per_chan == 0) { + test_ch->test_device->read_avail_mask = + test_ch->test_device->read_avail_mask & + ~test_ch->channel_mask_id; + } + + last_msg_index = lpm_msg.counter; + lpm_test_update_entry(test_ch, + LPM_MSG_REC, + "RECEIVE", + last_msg_index); + + spin_unlock_irqrestore(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + } + } + + pr_info(TEST_MODULE_NAME ":%s: Finished to recieve all (%d) " + "packets from the modem %s. Waiting for result_msg", + __func__, test_ch->config_msg.num_packets, test_ch->name); + + /* Wait for the resault message from the modem */ + test_ch->modem_result_per_chan = wait_for_result_msg(test_ch); + + /* + * the DEVICE modem result is a failure if one of the channels on + * that device, got modem_result = 0. this is why we bitwise "AND" each + * time another channel completes its task + */ + test_dev->modem_result_per_dev &= test_ch->modem_result_per_chan; + + /* + * when reading from channel, we want to turn off the bit + * mask that implies that there is pending data on that channel + */ + spin_lock_irqsave(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + + test_dev->open_channels_counter_to_recv--; + + /* turning off the read_avail bit of the channel */ + test_ch->test_device->read_avail_mask = + test_ch->test_device->read_avail_mask & + ~test_ch->channel_mask_id; + + spin_unlock_irqrestore(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + + /* Wait for all the packets to be sent to the modem */ + while (1) { + spin_lock_irqsave(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + + if (test_ch->next_index_in_sent_msg_per_chan >= + test_ch->config_msg.num_packets - 1) { + + spin_unlock_irqrestore(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + break; + } else { + pr_info(TEST_MODULE_NAME ":%s: Didn't finished to send " + "all packets, " + "next_index_in_sent_msg_per_chan = %d ", + __func__, + test_ch->next_index_in_sent_msg_per_chan); + } + spin_unlock_irqrestore(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + msleep(60); + } + + /* + * if device has still open channels to test, then the test on the + * device is still running but the test on current channel is completed + */ + if (test_dev->open_channels_counter_to_recv != 0 || + test_dev->open_channels_counter_to_send != 0) { + test_ch->test_completed = 1; + return 0; + } else { + test_ctx->number_of_active_devices--; + sdio_al_unregister_lpm_cb(test_ch->sdio_al_device); + + if (test_ch->test_type == SDIO_TEST_LPM_RANDOM) + host_result = check_random_lpm_test_array(test_dev); + + if (host_result || + !test_dev->modem_result_per_dev || + test_ctx->runtime_debug) + print_random_lpm_test_array(test_dev); + + pr_info(TEST_MODULE_NAME ": %s - host_result=%d.(0 for " + "SUCCESS) device_modem_result=%d (1 for SUCCESS)", + __func__, host_result, test_dev->modem_result_per_dev); + + test_ch->test_completed = 1; + if (test_dev->modem_result_per_dev && !host_result) { + pr_info(TEST_MODULE_NAME ": %s - Random LPM " + "TEST_PASSED for device %d of %d\n", + __func__, + (test_ctx->max_number_of_devices- + test_ctx->number_of_active_devices), + test_ctx->max_number_of_devices); + test_dev->final_result_per_dev = 1; /* PASSED */ + } else { + pr_info(TEST_MODULE_NAME ": %s - Random LPM " + "TEST_FAILED for device %d of %d\n", + __func__, + (test_ctx->max_number_of_devices- + test_ctx->number_of_active_devices), + test_ctx->max_number_of_devices); + test_dev->final_result_per_dev = 0; /* FAILED */ + } + + check_test_completion(); + + kfree(test_ch->test_device->lpm_arr); + + return 0; + } + +exit_err: + pr_info(TEST_MODULE_NAME ": TEST FAIL for chan %s.\n", + test_ch->name); + test_ch->test_completed = 1; + test_dev->open_channels_counter_to_recv--; + test_dev->next_avail_entry_in_array = 0; + test_ch->next_index_in_sent_msg_per_chan = 0; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return -ENODEV; +} + +static int lpm_test_create_read_thread(struct test_channel *test_ch) +{ + struct sdio_test_device *test_dev; + + pr_info(TEST_MODULE_NAME ": %s - STARTED channel %s\n", + __func__, test_ch->name); + + if (!test_ch) { + pr_err(TEST_MODULE_NAME ": %s - NULL test channel\n", __func__); + return -ENODEV; + } + + test_dev = test_ch->test_device; + + if (!test_dev) { + pr_err(TEST_MODULE_NAME ": %s - NULL test device\n", __func__); + return -ENODEV; + } + + test_dev->lpm_test_task.task_name = SDIO_LPM_TEST; + + test_dev->lpm_test_task.lpm_task = + kthread_create(lpm_test_main_task, + (void *)(test_ch), + test_dev->lpm_test_task.task_name); + + if (IS_ERR(test_dev->lpm_test_task.lpm_task)) { + pr_err(TEST_MODULE_NAME ": %s - kthread_create() failed\n", + __func__); + return -ENOMEM; + } + + wake_up_process(test_dev->lpm_test_task.lpm_task); + + return 0; +} + +static void lpm_continuous_rand_test(struct test_channel *test_ch) +{ + unsigned int local_ms = 0; + int ret = 0; + unsigned int write_avail = 0; + struct sdio_test_device *test_dev; + + pr_info(MODULE_NAME ": %s - STARTED\n", __func__); + + if (!test_ch) { + pr_err(TEST_MODULE_NAME ": %s - NULL channel\n", __func__); + return; + } + + test_dev = test_ch->test_device; + + if (!test_dev) { + pr_err(TEST_MODULE_NAME ": %s - NULL Test Device\n", __func__); + return; + } + + ret = lpm_test_create_read_thread(test_ch); + if (ret != 0) { + pr_err(TEST_MODULE_NAME ": %s - failed to create lpm reading " + "thread", __func__); + } + + while (1) { + + struct lpm_msg msg; + u32 ret = 0; + + /* sleeping period is dependent on number of open channels */ + test_ch->config_msg.test_param = + test_ctx->lpm_pseudo_random_seed; + + local_ms = test_dev->open_channels_counter_to_send * + test_ctx->lpm_pseudo_random_seed; + TEST_DBG(TEST_MODULE_NAME ":%s: SLEEPING for %d ms", + __func__, local_ms); + msleep(local_ms); + + msg.counter = test_ch->next_index_in_sent_msg_per_chan; + msg.signature = LPM_TEST_CONFIG_SIGNATURE; + msg.reserve1 = 0; + msg.reserve2 = 0; + + /* wait for data ready event */ + write_avail = sdio_write_avail(test_ch->ch); + pr_debug(TEST_MODULE_NAME ": %s: write_avail=%d\n", + __func__, write_avail); + if (write_avail < sizeof(msg)) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->tx_notify_count)); + atomic_dec(&test_ch->tx_notify_count); + } + + write_avail = sdio_write_avail(test_ch->ch); + if (write_avail < sizeof(msg)) { + pr_info(TEST_MODULE_NAME ": %s: not enough write " + "avail.\n", __func__); + break; + } + + ret = sdio_write(test_ch->ch, (u32 *)&msg, sizeof(msg)); + if (ret) + pr_err(TEST_MODULE_NAME ":%s: sdio_write err=%d.\n", + __func__, -ret); + + TEST_DBG(TEST_MODULE_NAME ": %s: for chan %s, write, " + "msg # %d\n", + __func__, + test_ch->name, + test_ch->next_index_in_sent_msg_per_chan); + + if (test_ch->test_type == SDIO_TEST_LPM_RANDOM) { + spin_lock_irqsave(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + lpm_test_update_entry(test_ch, LPM_MSG_SEND, + "SEND ", + test_ch-> + next_index_in_sent_msg_per_chan); + + test_ch->next_index_in_sent_msg_per_chan++; + + if (test_ch->next_index_in_sent_msg_per_chan == + test_ch->config_msg.num_packets) { + spin_unlock_irqrestore( + &test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + break; + } + + spin_unlock_irqrestore(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + } + } + + spin_lock_irqsave(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + test_dev->open_channels_counter_to_send--; + spin_unlock_irqrestore(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + + pr_info(TEST_MODULE_NAME ": %s: - Finished to send all (%d) " + "packets to the modem on channel %s", + __func__, test_ch->config_msg.num_packets, test_ch->name); + + return; +} + +static void lpm_test(struct test_channel *test_ch) +{ + pr_info(TEST_MODULE_NAME ": %s - START channel %s\n", __func__, + test_ch->name); + + if (!test_ch) { + pr_err(TEST_MODULE_NAME ": %s - NULL test channel\n", __func__); + return; + } + + test_ch->modem_result_per_chan = wait_for_result_msg(test_ch); + pr_debug(TEST_MODULE_NAME ": %s - delete the timeout timer\n", + __func__); + del_timer_sync(&test_ch->timeout_timer); + + if (test_ch->modem_result_per_chan == 0) { + pr_err(TEST_MODULE_NAME ": LPM TEST - Client didn't sleep. " + "Result Msg - is_successful=%d\n", test_ch->buf[1]); + goto exit_err; + } else { + pr_info(TEST_MODULE_NAME ": %s -" + "LPM 9K WAS SLEEPING - PASS\n", __func__); + if (test_ch->test_result == TEST_PASSED) { + pr_info(TEST_MODULE_NAME ": LPM TEST_PASSED\n"); + test_ch->test_completed = 1; + check_test_completion(); + } else { + pr_err(TEST_MODULE_NAME ": LPM TEST - Host didn't " + "sleep. Client slept\n"); + goto exit_err; + } + } + + return; + +exit_err: + pr_info(TEST_MODULE_NAME ": TEST FAIL for chan %s.\n", + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + + +/** + * LPM Test while the host wakes up the modem + */ +static void lpm_test_host_waker(struct test_channel *test_ch) +{ + pr_info(TEST_MODULE_NAME ": %s - START\n", __func__); + wait_event(test_ch->wait_q, atomic_read(&test_ch->wakeup_client)); + atomic_set(&test_ch->wakeup_client, 0); + + pr_info(TEST_MODULE_NAME ": %s - Sending the config_msg to wakeup " + " the client\n", __func__); + send_config_msg(test_ch); + + lpm_test(test_ch); +} + +/** + * Writes number of packets into test channel + * @test_ch: test channel control struct + * @burst_size: number of packets to send + */ +static int write_packet_burst(struct test_channel *test_ch, + int burst_size) +{ + int ret = 0; + int packet_count = 0; + unsigned int random_num = 0; + int size = test_ch->packet_length; /* first packet size */ + u32 write_avail = 0; + + while (packet_count < burst_size) { + /* wait for data ready event */ + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":%s write_avail=%d,size=%d on chan" + " %s\n", __func__, + write_avail, size, test_ch->name); + if (write_avail < size) { + TEST_DBG(TEST_MODULE_NAME ":%s wait for event on" + " chan %s\n", __func__, test_ch->name); + wait_event(test_ch->wait_q, + atomic_read(&test_ch->tx_notify_count)); + atomic_dec(&test_ch->tx_notify_count); + } + write_avail = sdio_write_avail(test_ch->ch); + if (write_avail < size) { + pr_info(TEST_MODULE_NAME ":%s not enough write" + " avail %d, need %d on chan %s\n", + __func__, write_avail, size, + test_ch->name); + continue; + } + ret = sdio_write(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s sdio_write " + "failed (%d) on chan %s\n", __func__, + ret, test_ch->name); + break; + } + udelay(1000); /*low bus usage while running number of channels*/ + TEST_DBG(TEST_MODULE_NAME ":%s() successfully write %d bytes" + ", packet_count=%d on chan %s\n", __func__, + size, packet_count, test_ch->name); + test_ch->tx_bytes += size; + packet_count++; + /* get next packet size */ + random_num = get_random_int(); + size = (random_num % test_ch->packet_length) + 1; + } + return ret; +} + +/** + * Reads packet from test channel and checks that packet number + * encoded into the packet is equal to packet_counter + * This function is applicable for packet mode channels only + * + * @test_ch: test channel + * @size: expected packet size + * @packet_counter: number to validate readed packet + */ +static int read_data_from_packet_ch(struct test_channel *test_ch, + unsigned int size, + int packet_counter) +{ + u32 read_avail = 0; + int ret = 0; + + if (!test_ch || !test_ch->ch) { + pr_err(TEST_MODULE_NAME + ":%s: NULL channel\n", __func__); + return -EINVAL; + } + + if (!test_ch->ch->is_packet_mode) { + pr_err(TEST_MODULE_NAME + ":%s:not packet mode ch %s\n", + __func__, test_ch->name); + return -EINVAL; + } + read_avail = sdio_read_avail(test_ch->ch); + /* wait for read data ready event */ + if (read_avail < size) { + TEST_DBG(TEST_MODULE_NAME ":%s() wait for rx data on " + "chan %s\n", __func__, test_ch->name); + wait_event(test_ch->wait_q, + atomic_read(&test_ch->rx_notify_count)); + atomic_dec(&test_ch->rx_notify_count); + } + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":%s read_avail=%d bytes on chan %s\n", + __func__, read_avail, test_ch->name); + + if (read_avail != size) { + pr_err(TEST_MODULE_NAME + ":read_avail size %d for chan %s not as " + "expected size %d\n", + read_avail, test_ch->name, size); + return -EINVAL; + } + + ret = sdio_read(test_ch->ch, test_ch->buf, read_avail); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s() sdio_read for chan %s (%d)\n", + __func__, test_ch->name, -ret); + return ret; + } + if ((test_ch->buf[0] != packet_counter) && (size != 1)) { + pr_err(TEST_MODULE_NAME ":Read WRONG DATA" + " for chan %s, size=%d\n", + test_ch->name, size); + return -EINVAL; + } + return 0; +} + + +/** + * Reads packet from test channel and checks that packet number + * encoded into the packet is equal to packet_counter + * This function is applicable for streaming mode channels only + * + * @test_ch: test channel + * @size: expected packet size + * @packet_counter: number to validate readed packet + */ +static int read_data_from_stream_ch(struct test_channel *test_ch, + unsigned int size, + int packet_counter) +{ + u32 read_avail = 0; + int ret = 0; + + if (!test_ch || !test_ch->ch) { + pr_err(TEST_MODULE_NAME + ":%s: NULL channel\n", __func__); + return -EINVAL; + } + + if (test_ch->ch->is_packet_mode) { + pr_err(TEST_MODULE_NAME + ":%s:not streaming mode ch %s\n", + __func__, test_ch->name); + return -EINVAL; + } + read_avail = sdio_read_avail(test_ch->ch); + /* wait for read data ready event */ + if (read_avail < size) { + TEST_DBG(TEST_MODULE_NAME ":%s() wait for rx data on " + "chan %s\n", __func__, test_ch->name); + wait_event(test_ch->wait_q, + atomic_read(&test_ch->rx_notify_count)); + atomic_dec(&test_ch->rx_notify_count); + } + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":%s read_avail=%d bytes on chan %s\n", + __func__, read_avail, test_ch->name); + + if (read_avail < size) { + pr_err(TEST_MODULE_NAME + ":read_avail size %d for chan %s not as " + "expected size %d\n", + read_avail, test_ch->name, size); + return -EINVAL; + } + + ret = sdio_read(test_ch->ch, test_ch->buf, size + A2_HEADER_OVERHEAD); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s() sdio_read for chan %s (%d)\n", + __func__, test_ch->name, -ret); + return ret; + } + if ((test_ch->buf[A2_HEADER_OVERHEAD/4] != packet_counter) && + (size != 1)) { + pr_err(TEST_MODULE_NAME ":Read WRONG DATA" + " for chan %s, size=%d, packet_counter=%d\n", + test_ch->name, size, packet_counter); + print_hex_dump(KERN_INFO, TEST_MODULE_NAME ": rmnet:", + 0, 32, 2, + (void *)test_ch->buf, + size + A2_HEADER_OVERHEAD, false); + return -EINVAL; + } + return 0; +} + +/** + * Test close channel feature for SDIO_SMEM channel: + * close && re-open the SDIO_SMEM channel. + */ +#ifdef CONFIG_MSM_SDIO_SMEM +static void open_close_smem_test(struct test_channel *test_ch) +{ + int i = 0; + int ret = 0; + + pr_info(TEST_MODULE_NAME ":%s\n", __func__); + + for (i = 0; i < 100 ; ++i) { + ret = close_sdio_ch(test_ch); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s close_sdio_ch for ch %s" + " failed\n", + __func__, test_ch->name); + goto exit_err; + } + ret = open_sdio_ch(test_ch); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s open_sdio_ch for ch %s " + " failed\n", + __func__, test_ch->name); + goto exit_err; + } + } + + pr_info(TEST_MODULE_NAME ":%s TEST PASS for chan %s.\n", __func__, + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_PASSED; + check_test_completion(); + return; +exit_err: + pr_info(TEST_MODULE_NAME ":%s TEST FAIL for chan %s.\n", __func__, + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} +#endif + +/** + * Test close channel feature: + * 1. write random packet number into channel + * 2. read some data from channel (do this only for second half of + * requested packets to send). + * 3. close && re-open then repeat 1. + * + * Total packets to send: test_ch->config_msg.num_packets. + * Burst size is random in [1..test_ch->max_burst_size] range + * Packet size is random in [1..test_ch->packet_length] + */ +static void open_close_test(struct test_channel *test_ch) +{ + int ret = 0; + u32 read_avail = 0; + int total_packet_count = 0; + int size = 0; + u16 *buf16 = NULL; + int i; + int max_packet_count = 0; + unsigned int random_num = 0; + int curr_burst_size = 0; + + if (!test_ch || !test_ch->ch) { + pr_err(TEST_MODULE_NAME ":%s NULL channel\n", + __func__); + return; + } + + curr_burst_size = test_ch->max_burst_size; + size = test_ch->packet_length; + buf16 = (u16 *) test_ch->buf; + + /* the test sends configured number of packets in + 2 portions: first without reading between write bursts, + second with it */ + max_packet_count = test_ch->config_msg.num_packets / 2; + + pr_info(TEST_MODULE_NAME ":%s channel %s, total packets:%d," + " max packet size %d, max burst size:%d\n", + __func__, test_ch->name, + test_ch->config_msg.num_packets, test_ch->packet_length, + test_ch->max_burst_size); + for (i = 0 ; i < size / 2 ; i++) + buf16[i] = (u16) (i & 0xFFFF); + + for (i = 0; i < 2 ; i++) { + total_packet_count = 0; + while (total_packet_count < max_packet_count) { + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":%s exit test\n", + __func__); + return; + } + test_ch->buf[0] = total_packet_count; + random_num = get_random_int(); + curr_burst_size = (random_num % + test_ch->max_burst_size) + 1; + + /* limit burst size to send + * no more than configured packets */ + if (curr_burst_size + total_packet_count > + max_packet_count) { + curr_burst_size = max_packet_count - + total_packet_count; + } + TEST_DBG(TEST_MODULE_NAME ":%s Current burst size:%d" + " on chan %s\n", __func__, + curr_burst_size, test_ch->name); + ret = write_packet_burst(test_ch, curr_burst_size); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s write burst failed (%d), ch %s\n", + __func__, ret, test_ch->name); + goto exit_err; + } + if (i > 0) { + /* read from channel */ + if (test_ch->ch->is_packet_mode) + ret = read_data_from_packet_ch(test_ch, + size, + total_packet_count); + else + ret = read_data_from_stream_ch(test_ch, + size, + total_packet_count); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s read" + " failed:%d, chan %s\n", + __func__, ret, + test_ch->name); + goto exit_err; + } + } + TEST_DBG(TEST_MODULE_NAME ":%s before close, ch %s\n", + __func__, test_ch->name); + ret = close_sdio_ch(test_ch); + if (ret) { + pr_err(TEST_MODULE_NAME":%s close channel %s" + " failed (%d)\n", + __func__, test_ch->name, ret); + goto exit_err; + } else { + TEST_DBG(TEST_MODULE_NAME":%s close channel %s" + " success\n", __func__, + test_ch->name); + total_packet_count += curr_burst_size; + atomic_set(&test_ch->rx_notify_count, 0); + atomic_set(&test_ch->tx_notify_count, 0); + atomic_set(&test_ch->any_notify_count, 0); + } + TEST_DBG(TEST_MODULE_NAME ":%s before open, ch %s\n", + __func__, test_ch->name); + ret = open_sdio_ch(test_ch); + if (ret) { + pr_err(TEST_MODULE_NAME":%s open channel %s" + " failed (%d)\n", + __func__, test_ch->name, ret); + goto exit_err; + } else { + read_avail = sdio_read_avail(test_ch->ch); + if (read_avail > 0) { + pr_err(TEST_MODULE_NAME": after open" + " ch %s read_availis not zero" + " (%d bytes)\n", + test_ch->name, read_avail); + goto exit_err; + } + } + TEST_DBG(TEST_MODULE_NAME ":%s total tx = %d," + " packet# = %d, size = %d for ch %s\n", + __func__, test_ch->tx_bytes, + total_packet_count, size, + test_ch->name); + } /* end of while */ + } + pr_info(TEST_MODULE_NAME ":%s Test end: total rx bytes = 0x%x," + " total tx bytes = 0x%x for chan %s\n", __func__, + test_ch->rx_bytes, test_ch->tx_bytes, test_ch->name); + pr_info(TEST_MODULE_NAME ":%s TEST PASS for chan %s.\n", __func__, + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_PASSED; + check_test_completion(); + return; +exit_err: + pr_info(TEST_MODULE_NAME ":%s TEST FAIL for chan %s.\n", __func__, + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + +/** + * sender Test + */ +static void sender_test(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 read_avail = 0; + u32 write_avail = 0; + int packet_count = 0; + int size = 512; + u16 *buf16 = (u16 *) test_ch->buf; + int i; + int max_packet_count = 10000; + int random_num = 0; + + max_packet_count = test_ch->config_msg.num_packets; + + for (i = 0 ; i < size / 2 ; i++) + buf16[i] = (u16) (i & 0xFFFF); + + + pr_info(TEST_MODULE_NAME + ":SENDER TEST START for chan %s\n", test_ch->name); + + while (packet_count < max_packet_count) { + + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + + random_num = get_random_int(); + size = (random_num % test_ch->packet_length) + 1; + + TEST_DBG(TEST_MODULE_NAME "SENDER WAIT FOR EVENT for chan %s\n", + test_ch->name); + + /* wait for data ready event */ + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":write_avail=%d\n", write_avail); + if (write_avail < size) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->tx_notify_count)); + atomic_dec(&test_ch->tx_notify_count); + } + + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":write_avail=%d\n", write_avail); + if (write_avail < size) { + pr_info(TEST_MODULE_NAME ":not enough write avail.\n"); + continue; + } + + test_ch->buf[0] = packet_count; + + ret = sdio_write(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_info(TEST_MODULE_NAME ":sender sdio_write err=%d.\n", + -ret); + goto exit_err; + } + + /* wait for read data ready event */ + TEST_DBG(TEST_MODULE_NAME ":sender wait for rx data for " + "chan %s\n", + test_ch->name); + read_avail = sdio_read_avail(test_ch->ch); + wait_event(test_ch->wait_q, + atomic_read(&test_ch->rx_notify_count)); + atomic_dec(&test_ch->rx_notify_count); + + read_avail = sdio_read_avail(test_ch->ch); + + if (read_avail != size) { + pr_info(TEST_MODULE_NAME + ":read_avail size %d for chan %s not as " + "expected size %d.\n", + read_avail, test_ch->name, size); + goto exit_err; + } + + memset(test_ch->buf, 0x00, size); + + ret = sdio_read(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_info(TEST_MODULE_NAME ":sender sdio_read for chan %s" + " err=%d.\n", + test_ch->name, -ret); + goto exit_err; + } + + + if ((test_ch->buf[0] != packet_count) && (size != 1)) { + pr_info(TEST_MODULE_NAME ":sender sdio_read WRONG DATA" + " for chan %s, size=%d\n", + test_ch->name, size); + goto exit_err; + } + + test_ch->tx_bytes += size; + test_ch->rx_bytes += size; + packet_count++; + + TEST_DBG(TEST_MODULE_NAME + ":sender total rx bytes = 0x%x , packet#=%d, size=%d" + " for chan %s\n", + test_ch->rx_bytes, packet_count, size, test_ch->name); + TEST_DBG(TEST_MODULE_NAME + ":sender total tx bytes = 0x%x , packet#=%d, size=%d" + " for chan %s\n", + test_ch->tx_bytes, packet_count, size, test_ch->name); + + } /* end of while */ + + pr_info(TEST_MODULE_NAME + ":SENDER TEST END: total rx bytes = 0x%x, " + " total tx bytes = 0x%x for chan %s\n", + test_ch->rx_bytes, test_ch->tx_bytes, test_ch->name); + + pr_info(TEST_MODULE_NAME ": TEST PASS for chan %s.\n", + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_PASSED; + check_test_completion(); + return; + +exit_err: + pr_info(TEST_MODULE_NAME ": TEST FAIL for chan %s.\n", + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + +/** + * A2 Perf Test + */ +static void a2_performance_test(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 read_avail = 0; + u32 write_avail = 0; + int tx_packet_count = 0; + int rx_packet_count = 0; + int size = 0; + u16 *buf16 = (u16 *) test_ch->buf; + int i; + int total_bytes = 0; + int max_packets = 10000; + u32 packet_size = test_ch->buf_size; + int rand_size = 0; + + u64 start_jiffy, end_jiffy, delta_jiffies; + unsigned int time_msec = 0; + u32 throughput = 0; + + max_packets = test_ch->config_msg.num_packets; + packet_size = test_ch->packet_length; + + for (i = 0; i < packet_size / 2; i++) + buf16[i] = (u16) (i & 0xFFFF); + + pr_info(TEST_MODULE_NAME ": A2 PERFORMANCE TEST START for chan %s\n", + test_ch->name); + + start_jiffy = get_jiffies_64(); /* read the current time */ + + while (tx_packet_count < max_packets) { + + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + + if (test_ch->random_packet_size) { + rand_size = get_random_int(); + packet_size = (rand_size % test_ch->packet_length) + 1; + if (packet_size < A2_MIN_PACKET_SIZE) + packet_size = A2_MIN_PACKET_SIZE; + } + + /* wait for data ready event */ + /* use a func to avoid compiler optimizations */ + write_avail = sdio_write_avail(test_ch->ch); + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d, " + "read_avail=%d for chan %s\n", + test_ch->name, write_avail, read_avail, + test_ch->name); + if ((write_avail == 0) && (read_avail == 0)) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->any_notify_count)); + atomic_set(&test_ch->any_notify_count, 0); + } + + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d\n", + test_ch->name, write_avail); + if (write_avail > 0) { + size = min(packet_size, write_avail) ; + TEST_DBG(TEST_MODULE_NAME ":tx size = %d for chan %s\n", + size, test_ch->name); + test_ch->buf[0] = tx_packet_count; + test_ch->buf[(size/4)-1] = tx_packet_count; + + ret = sdio_write(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_info(TEST_MODULE_NAME ":sdio_write err=%d" + " for chan %s\n", + -ret, test_ch->name); + goto exit_err; + } + tx_packet_count++; + test_ch->tx_bytes += size; + } + + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, read_avail=%d\n", + test_ch->name, read_avail); + if (read_avail > 0) { + size = min(packet_size, read_avail); + pr_debug(TEST_MODULE_NAME ":rx size = %d.\n", size); + ret = sdio_read(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_info(TEST_MODULE_NAME ": sdio_read size %d " + " err=%d" + " for chan %s\n", + size, -ret, test_ch->name); + goto exit_err; + } + rx_packet_count++; + test_ch->rx_bytes += size; + } + + TEST_DBG(TEST_MODULE_NAME + ":total rx bytes = %d , rx_packet#=%d" + " for chan %s\n", + test_ch->rx_bytes, rx_packet_count, test_ch->name); + TEST_DBG(TEST_MODULE_NAME + ":total tx bytes = %d , tx_packet#=%d" + " for chan %s\n", + test_ch->tx_bytes, tx_packet_count, test_ch->name); + + } /* while (tx_packet_count < max_packets ) */ + + end_jiffy = get_jiffies_64(); /* read the current time */ + + delta_jiffies = end_jiffy - start_jiffy; + time_msec = jiffies_to_msecs(delta_jiffies); + + pr_info(TEST_MODULE_NAME ":total rx bytes = 0x%x , rx_packet#=%d for" + " chan %s.\n", + test_ch->rx_bytes, rx_packet_count, test_ch->name); + pr_info(TEST_MODULE_NAME ":total tx bytes = 0x%x , tx_packet#=%d" + " for chan %s.\n", + test_ch->tx_bytes, tx_packet_count, test_ch->name); + + total_bytes = (test_ch->tx_bytes + test_ch->rx_bytes); + pr_err(TEST_MODULE_NAME ":total bytes = %d, time msec = %d" + " for chan %s\n", + total_bytes , (int) time_msec, test_ch->name); + + if (!test_ch->random_packet_size) { + if (time_msec) { + throughput = (total_bytes / time_msec) * 8 / 1000; + pr_err(TEST_MODULE_NAME ": %s - Performance = " + "%d Mbit/sec for chan %s\n", + __func__, throughput, test_ch->name); + } else { + pr_err(TEST_MODULE_NAME ": %s - time_msec = 0 Couldn't " + "calculate performence for chan %s\n", + __func__, test_ch->name); + } + + } + +#ifdef CONFIG_DEBUG_FS + switch (test_ch->ch_id) { + case SDIO_DUN: + test_ctx->debug.dun_throughput = throughput; + break; + case SDIO_RMNT: + test_ctx->debug.rmnt_throughput = throughput; + break; + default: + pr_err(TEST_MODULE_NAME "No debugfs for this channel " + "throughput"); + } +#endif + + pr_err(TEST_MODULE_NAME ": A2 PERFORMANCE TEST END for chan %s.\n", + test_ch->name); + + pr_err(TEST_MODULE_NAME ": TEST PASS for chan %s\n", test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_PASSED; + check_test_completion(); + return; + +exit_err: + pr_err(TEST_MODULE_NAME ": TEST FAIL for chan %s\n", test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + +/** + * rx_cleanup + * This function reads all the messages sent by the modem until + * the read_avail is 0 after 1 second of sleep. + * The function returns the number of packets that was received. + */ +static void rx_cleanup(struct test_channel *test_ch, int *rx_packet_count) +{ + int read_avail = 0; + int ret = 0; + int counter = 0; + + if (!test_ch || !test_ch->ch) { + pr_err(TEST_MODULE_NAME ":%s NULL channel\n", + __func__); + return; + } + + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, read_avail=%d\n", + test_ch->name, read_avail); + + /* If no pending messages, wait to see if the modem sends data */ + if (read_avail == 0) { + msleep(1000); + read_avail = sdio_read_avail(test_ch->ch); + } + + while ((read_avail > 0) && (counter < 10)) { + TEST_DBG(TEST_MODULE_NAME ": read_avail=%d for ch %s\n", + read_avail, test_ch->name); + + ret = sdio_read(test_ch->ch, test_ch->buf, read_avail); + if (ret) { + pr_info(TEST_MODULE_NAME ": sdio_read size %d " + " err=%d for chan %s\n", + read_avail, -ret, test_ch->name); + break; + } + (*rx_packet_count)++; + test_ch->rx_bytes += read_avail; + read_avail = sdio_read_avail(test_ch->ch); + if (read_avail == 0) { + msleep(1000); + counter++; + read_avail = sdio_read_avail(test_ch->ch); + } + } + pr_info(TEST_MODULE_NAME ": finished cleanup for ch %s, " + "rx_packet_count=%d, total rx bytes=%d\n", + test_ch->name, *rx_packet_count, test_ch->rx_bytes); +} + + +/** + * A2 RTT Test + * This function sends a packet and calculate the RTT time of + * this packet. + * The test also calculte Min, Max and Average RTT + */ +static void a2_rtt_test(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 read_avail = 0; + u32 write_avail = 0; + int tx_packet_count = 0; + int rx_packet_count = 0; + u16 *buf16 = NULL; + int i; + int max_packets = 0; + u32 packet_size = 0; + s64 start_time, end_time; + int delta_usec = 0; + int time_average = 0; + int min_delta_usec = 0xFFFF; + int max_delta_usec = 0; + int total_time = 0; + int expected_read_size = 0; + int delay_ms = 0; + int slow_rtt_counter = 0; + int read_avail_so_far = 0; + + if (test_ch) { + /* + * Cleanup the pending RX data (such as loopback of the + * config msg) + */ + rx_cleanup(test_ch, &rx_packet_count); + rx_packet_count = 0; + } else { + return; + } + + max_packets = test_ch->config_msg.num_packets; + packet_size = test_ch->packet_length; + buf16 = (u16 *) test_ch->buf; + + for (i = 0; i < packet_size / 2; i++) + buf16[i] = (u16) (i & 0xFFFF); + + pr_info(TEST_MODULE_NAME ": A2 RTT TEST START for chan %s\n", + test_ch->name); + + switch (test_ch->ch_id) { + case SDIO_RMNT: + delay_ms = 100; + break; + case SDIO_CSVT: + delay_ms = 0; + break; + default: + pr_err(TEST_MODULE_NAME ": %s - ch_id invalid.\n", + __func__); + return; + } + + while (tx_packet_count < max_packets) { + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + start_time = 0; + end_time = 0; + read_avail_so_far = 0; + + if (delay_ms) + msleep(delay_ms); + + /* wait for data ready event */ + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":ch %s: write_avail=%d\n", + test_ch->name, write_avail); + if (write_avail == 0) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->tx_notify_count)); + atomic_dec(&test_ch->tx_notify_count); + } + + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d\n", + test_ch->name, write_avail); + if (write_avail > 0) { + TEST_DBG(TEST_MODULE_NAME ":tx size = %d for chan %s\n", + packet_size, test_ch->name); + test_ch->buf[0] = tx_packet_count; + + start_time = ktime_to_us(ktime_get()); + ret = sdio_write(test_ch->ch, test_ch->buf, + packet_size); + if (ret) { + pr_err(TEST_MODULE_NAME ":sdio_write err=%d" + " for chan %s\n", + -ret, test_ch->name); + goto exit_err; + } + tx_packet_count++; + test_ch->tx_bytes += packet_size; + } else { + pr_err(TEST_MODULE_NAME ": Invalid write_avail" + " %d for chan %s\n", + write_avail, test_ch->name); + goto exit_err; + } + + expected_read_size = packet_size + A2_HEADER_OVERHEAD; + + while (read_avail_so_far < expected_read_size) { + + read_avail = sdio_read_avail(test_ch->ch); + + if (!read_avail) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch-> + rx_notify_count)); + + atomic_dec(&test_ch->rx_notify_count); + continue; + } + + read_avail_so_far += read_avail; + + if (read_avail_so_far > expected_read_size) { + pr_err(TEST_MODULE_NAME ": %s - Invalid " + "read_avail(%d) read_avail_so_far(%d) " + "can't be larger than " + "expected_read_size(%d).", + __func__, + read_avail, + read_avail_so_far, + expected_read_size); + goto exit_err; + } + + /* + * must read entire pending bytes, so later, we will + * get a notification when more data arrives + */ + ret = sdio_read(test_ch->ch, test_ch->buf, + read_avail); + + if (ret) { + pr_info(TEST_MODULE_NAME ": sdio_read size %d " + " err=%d for chan %s\n", + read_avail, -ret, + test_ch->name); + goto exit_err; + } + } + + end_time = ktime_to_us(ktime_get()); + rx_packet_count++; + test_ch->rx_bytes += expected_read_size; + + delta_usec = (int)(end_time - start_time); + total_time += delta_usec; + if (delta_usec < min_delta_usec) + min_delta_usec = delta_usec; + if (delta_usec > max_delta_usec) + max_delta_usec = delta_usec; + + /* checking the RTT per channel criteria */ + if (delta_usec > MAX_AVG_RTT_TIME_USEC) { + pr_err(TEST_MODULE_NAME ": %s - " + "msg # %d - rtt time (%d usec) is " + "longer than %d usec\n", + __func__, + tx_packet_count, + delta_usec, + MAX_AVG_RTT_TIME_USEC); + slow_rtt_counter++; + } + + TEST_DBG(TEST_MODULE_NAME + ":RTT time=%d for packet #%d for chan %s\n", + delta_usec, tx_packet_count, test_ch->name); + } /* while (tx_packet_count < max_packets ) */ + + pr_info(TEST_MODULE_NAME ": %s - tx_packet_count = %d\n", + __func__, tx_packet_count); + + pr_info(TEST_MODULE_NAME ": %s - total rx bytes = 0x%x, " + "rx_packet# = %d for chan %s.\n", + __func__, test_ch->rx_bytes, rx_packet_count, test_ch->name); + + pr_info(TEST_MODULE_NAME ": %s - total tx bytes = 0x%x, " + "tx_packet# = %d for chan %s.\n", + __func__, test_ch->tx_bytes, tx_packet_count, test_ch->name); + + pr_info(TEST_MODULE_NAME ": %s - slow_rtt_counter = %d for " + "chan %s.\n", + __func__, slow_rtt_counter, test_ch->name); + + if (tx_packet_count) { + time_average = total_time / tx_packet_count; + pr_info(TEST_MODULE_NAME ":Average RTT time = %d for chan %s\n", + time_average, test_ch->name); + } else { + pr_err(TEST_MODULE_NAME ": %s - tx_packet_count=0. couldn't " + "calculate average rtt time", __func__); + } + + pr_info(TEST_MODULE_NAME ":MIN RTT time = %d for chan %s\n", + min_delta_usec, test_ch->name); + pr_info(TEST_MODULE_NAME ":MAX RTT time = %d for chan %s\n", + max_delta_usec, test_ch->name); + + pr_info(TEST_MODULE_NAME ": A2 RTT TEST END for chan %s.\n", + test_ch->name); + + if (ret) + goto exit_err; + + if (time_average == 0 || time_average > MAX_AVG_RTT_TIME_USEC) { + pr_err(TEST_MODULE_NAME ": %s - average_time = %d. Invalid " + "value", + __func__, time_average); + goto exit_err; + + } + + pr_info(TEST_MODULE_NAME ": TEST PASS for chan %s\n", test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_PASSED; + check_test_completion(); + return; + +exit_err: + pr_err(TEST_MODULE_NAME ": TEST FAIL for chan %s\n", test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + +/** + * Process Rx Data - Helper for A2 Validation Test + * @test_ch(in/out) : Test channel that contains Rx data buffer to process. + * + * @rx_unprocessed_bytes(in) : Number of bytes to process in the buffer. + * + * @rx_process_packet_state(in/out) : + * Current processing state (used to identify what to process + * next in a partial packet) + * + * @rx_packet_size(in/out) : + * Number of bytes remaining in the packet to be processed. + * + * @rx_packet_count(in/out) : + * Number of packets processed. + */ +static int process_rx_data(struct test_channel *test_ch, + u32 rx_unprocessed_bytes, + int *rx_process_packet_state, + u16 *rx_packet_size, + int *rx_packet_count) +{ + u8 *buf = (u8 *)test_ch->buf; + int eop = 0; + int i = 0; + int ret = 0; + u32 *ptr = 0; + u16 size = 0; + + /* process rx data */ + while (rx_unprocessed_bytes) { + TEST_DBG(TEST_MODULE_NAME ": unprocessed bytes : %u\n", + rx_unprocessed_bytes); + + switch (*rx_process_packet_state) { + case RX_PROCESS_PACKET_INIT: + /* process the A2 header */ + TEST_DBG(TEST_MODULE_NAME ": " + "RX_PROCESS_PACKET_INIT\n"); + *rx_process_packet_state = RX_PROCESS_PACKET_INIT; + if (rx_unprocessed_bytes < 4) + break; + + i += 4; + rx_unprocessed_bytes -= 4; + + case RX_PROCESS_A2_HEADER: + /* process the rest of A2 header */ + TEST_DBG(TEST_MODULE_NAME ": RX_PROCESS_A2_HEADER\n"); + *rx_process_packet_state = RX_PROCESS_A2_HEADER; + if (rx_unprocessed_bytes < 4) + break; + + ptr = (u32 *)&buf[i]; + /* + * upper 2 bytes of the last 4 bytes of A2 header + * contains the size of the packet + */ + *rx_packet_size = *ptr >> 0x10; + + i += 4; + rx_unprocessed_bytes -= 4; + + case RX_PROCESS_PACKET_DATA: + /* process the2_2_ packet data */ + TEST_DBG(TEST_MODULE_NAME ": RX_PROCESS_PACKET_DATA " + "- packet size - %u\n", *rx_packet_size); + *rx_process_packet_state = RX_PROCESS_PACKET_DATA; + + size = *rx_packet_size; + if (*rx_packet_size <= rx_unprocessed_bytes) { + eop = *rx_packet_size; + *rx_packet_size = 0; + } else { + eop = rx_unprocessed_bytes; + *rx_packet_size = *rx_packet_size - + rx_unprocessed_bytes; + } + + /* no more bytes available to process */ + if (!eop) + break; + /* + * end of packet is starting from + * the current position + */ + eop = eop + i; + TEST_DBG(TEST_MODULE_NAME ": size - %u, " + "packet size - %u eop - %d\n", + size, *rx_packet_size, eop); + + /* validate the data */ + for (; i < eop; i++) { + if (buf[i] != (test_ch->rx_bytes % 256)) { + pr_err(TEST_MODULE_NAME ": " + "Corrupt data. buf:%u, " + "data:%u\n", buf[i], + test_ch->rx_bytes % 256); + ret = -EINVAL; + goto err; + } + rx_unprocessed_bytes--; + test_ch->rx_bytes++; + } + + /* have more data to be processed */ + if (*rx_packet_size) + break; + + /* + * A2 sends data in 4 byte alignment, + * skip the padding + */ + if (size % 4) { + i += 4 - (size % 4); + rx_unprocessed_bytes -= 4 - (size % 4); + } + *rx_packet_count = *rx_packet_count + 1; + + /* re init the state to process new packet */ + *rx_process_packet_state = RX_PROCESS_PACKET_INIT; + break; + default: + pr_err(TEST_MODULE_NAME ": Invalid case: %d\n", + *rx_process_packet_state); + ret = -EINVAL; + goto err; + } + TEST_DBG(TEST_MODULE_NAME ": Continue processing " + "if more data is available\n"); + } + +err: + return ret; +} + +/** + * A2 Validation Test + * Send packets and validate the returned packets. + * Transmit one packet at a time, while process multiple rx + * packets in a single transaction. + * A transaction is of size min(random number, write_avail). + * A packet consists of a min of 1 byte to channel supported max. + */ +static void a2_validation_test(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 read_avail = 0; + u32 write_avail = 0; + int tx_packet_count = 0; + int rx_packet_count = 0; + int initial_rx_packet_count = 0; + u32 size = 0; + u8 *buf8 = (u8 *)test_ch->buf; + int i = 0; + int max_packets = test_ch->config_msg.num_packets; + u16 tx_packet_size = 0; + u16 rx_packet_size = 0; + u32 random_num = 0; + int rx_process_packet_state = RX_PROCESS_PACKET_INIT; + + pr_info(TEST_MODULE_NAME ": A2 VALIDATION TEST START for chan %s\n", + test_ch->name); + + /* Wait for the initial rx messages before starting the test. */ + rx_cleanup(test_ch, &initial_rx_packet_count); + + test_ch->tx_bytes = 0; + test_ch->rx_bytes = 0; + + /* Continue till we have transmitted and received all packets */ + while ((tx_packet_count < max_packets) || + (rx_packet_count < max_packets)) { + + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + + random_num = get_random_int(); + size = (random_num % test_ch->packet_length) + 1; + TEST_DBG(TEST_MODULE_NAME ": Random tx packet size =%u", size); + + /* + * wait for data ready event + * use a func to avoid compiler optimizations + */ + write_avail = sdio_write_avail(test_ch->ch); + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ": write_avail=%d, " + "read_avail=%d for chan %s\n", + write_avail, read_avail, test_ch->name); + + if ((write_avail == 0) && (read_avail == 0)) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->any_notify_count)); + atomic_set(&test_ch->any_notify_count, 0); + } + + /* Transmit data */ + write_avail = sdio_write_avail(test_ch->ch); + if ((tx_packet_count < max_packets) && (write_avail > 0)) { + tx_packet_size = min(size, write_avail) ; + TEST_DBG(TEST_MODULE_NAME ": tx size = %u, " + "write_avail = %u tx_packet# = %d\n", + tx_packet_size, write_avail, + tx_packet_count); + memset(test_ch->buf, 0, test_ch->buf_size); + /* populate the buffer */ + for (i = 0; i < tx_packet_size; i++) { + buf8[i] = test_ch->tx_bytes % 256; + test_ch->tx_bytes++; + } + + ret = sdio_write(test_ch->ch, test_ch->buf, + tx_packet_size); + if (ret) { + pr_err(TEST_MODULE_NAME ":sdio_write err=%d" + " for chan %s\n", + -ret, test_ch->name); + goto exit_err; + } + tx_packet_count++; + } + + /* Receive data */ + read_avail = sdio_read_avail(test_ch->ch); + if (read_avail > 0) { + TEST_DBG(TEST_MODULE_NAME ": rx size = %u, " + "rx_packet#=%d.\n", + read_avail, rx_packet_count); + memset(test_ch->buf, 0, test_ch->buf_size); + + ret = sdio_read(test_ch->ch, test_ch->buf, + read_avail); + if (ret) { + pr_err(TEST_MODULE_NAME ": sdio_read " + "size %d err=%d for chan %s\n", + size, -ret, test_ch->name); + goto exit_err; + } + + /* Process data */ + ret = process_rx_data(test_ch, read_avail, + &rx_process_packet_state, + &rx_packet_size, + &rx_packet_count); + + if (ret != 0) + goto exit_err; + } + TEST_DBG(TEST_MODULE_NAME ": Continue loop ...\n"); + } + + if (test_ch->tx_bytes != test_ch->rx_bytes) { + pr_err(TEST_MODULE_NAME ": Total number of bytes " + "transmitted (%u) does not match the total " + "number of bytes received (%u).", test_ch->tx_bytes, + test_ch->rx_bytes); + goto exit_err; + } + + pr_info(TEST_MODULE_NAME ": A2 VALIDATION TEST END for chan %s.\n", + test_ch->name); + + pr_info(TEST_MODULE_NAME ": TEST PASS for chan %s\n", test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_PASSED; + check_test_completion(); + return; + +exit_err: + pr_info(TEST_MODULE_NAME ": TEST FAIL for chan %s\n", test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + +/** + * sender No loopback Test + */ +static void sender_no_loopback_test(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 write_avail = 0; + int packet_count = 0; + int size = 512; + u16 *buf16 = (u16 *) test_ch->buf; + int i; + int max_packet_count = 10000; + unsigned int random_num = 0; + + max_packet_count = test_ch->config_msg.num_packets; + + for (i = 0 ; i < size / 2 ; i++) + buf16[i] = (u16) (i & 0xFFFF); + + pr_info(TEST_MODULE_NAME + ":SENDER NO LP TEST START for chan %s\n", test_ch->name); + + while (packet_count < max_packet_count) { + + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + + random_num = get_random_int(); + size = (random_num % test_ch->packet_length) + 1; + + TEST_DBG(TEST_MODULE_NAME ":SENDER WAIT FOR EVENT " + "for chan %s\n", + test_ch->name); + + /* wait for data ready event */ + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":write_avail=%d\n", write_avail); + if (write_avail < size) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->tx_notify_count)); + atomic_dec(&test_ch->tx_notify_count); + } + + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":write_avail=%d\n", write_avail); + if (write_avail < size) { + pr_info(TEST_MODULE_NAME ":not enough write avail.\n"); + continue; + } + + test_ch->buf[0] = packet_count; + + ret = sdio_write(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_info(TEST_MODULE_NAME ":sender sdio_write err=%d.\n", + -ret); + goto exit_err; + } + + test_ch->tx_bytes += size; + packet_count++; + + TEST_DBG(TEST_MODULE_NAME + ":sender total tx bytes = 0x%x , packet#=%d, size=%d" + " for chan %s\n", + test_ch->tx_bytes, packet_count, size, test_ch->name); + + } /* end of while */ + + pr_info(TEST_MODULE_NAME + ":SENDER TEST END: total tx bytes = 0x%x, " + " for chan %s\n", + test_ch->tx_bytes, test_ch->name); + + test_ch->modem_result_per_chan = wait_for_result_msg(test_ch); + + if (test_ch->modem_result_per_chan) { + pr_info(TEST_MODULE_NAME ": TEST PASS for chan %s.\n", + test_ch->name); + test_ch->test_result = TEST_PASSED; + } else { + pr_info(TEST_MODULE_NAME ": TEST FAILURE for chan %s.\n", + test_ch->name); + test_ch->test_result = TEST_FAILED; + } + test_ch->test_completed = 1; + check_test_completion(); + return; + +exit_err: + pr_info(TEST_MODULE_NAME ": TEST FAIL for chan %s.\n", + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + + +/** + * Modem reset Test + * The test verifies that it finished sending all the packets + * while there might be modem reset in the middle + */ +static void modem_reset_test(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 read_avail = 0; + u32 write_avail = 0; + int tx_packet_count = 0; + int rx_packet_count = 0; + int size = 0; + u16 *buf16 = (u16 *) test_ch->buf; + int i; + int max_packets = 10000; + u32 packet_size = test_ch->buf_size; + int is_err = 0; + + max_packets = test_ch->config_msg.num_packets; + packet_size = test_ch->packet_length; + + for (i = 0; i < packet_size / 2; i++) + buf16[i] = (u16) (i & 0xFFFF); + + pr_info(TEST_MODULE_NAME ": Modem Reset TEST START for chan %s\n", + test_ch->name); + + while (tx_packet_count < max_packets) { + + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + + if (test_ch->card_removed) { + pr_info(TEST_MODULE_NAME ": card removal was detected " + "for chan %s, tx_total=0x%x\n", + test_ch->name, test_ch->tx_bytes); + wait_event(test_ch->wait_q, + atomic_read(&test_ch->card_detected_event)); + atomic_set(&test_ch->card_detected_event, 0); + pr_info(TEST_MODULE_NAME ": card_detected_event " + "for chan %s\n", test_ch->name); + if (test_ch->card_removed) + continue; + is_err = 0; + /* Need to wait for the modem to be ready */ + msleep(5000); + pr_info(TEST_MODULE_NAME ": sending the config message " + "for chan %s\n", test_ch->name); + send_config_msg(test_ch); + } + + /* wait for data ready event */ + /* use a func to avoid compiler optimizations */ + write_avail = sdio_write_avail(test_ch->ch); + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d, " + "read_avail=%d for chan %s\n", + test_ch->name, write_avail, read_avail, + test_ch->name); + if ((write_avail == 0) && (read_avail == 0)) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->any_notify_count)); + atomic_set(&test_ch->any_notify_count, 0); + } + if (atomic_read(&test_ch->card_detected_event)) { + atomic_set(&test_ch->card_detected_event, 0); + pr_info(TEST_MODULE_NAME ": card_detected_event " + "for chan %s, tx_total=0x%x\n", + test_ch->name, test_ch->tx_bytes); + if (test_ch->card_removed) + continue; + /* Need to wait for the modem to be ready */ + msleep(5000); + is_err = 0; + pr_info(TEST_MODULE_NAME ": sending the config message " + "for chan %s\n", test_ch->name); + send_config_msg(test_ch); + } + + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d\n", + test_ch->name, write_avail); + if (write_avail > 0) { + size = min(packet_size, write_avail) ; + pr_debug(TEST_MODULE_NAME ":tx size = %d for chan %s\n", + size, test_ch->name); + test_ch->buf[0] = tx_packet_count; + test_ch->buf[(size/4)-1] = tx_packet_count; + + TEST_DBG(TEST_MODULE_NAME ":channel %s, sdio_write, " + "size=%d\n", test_ch->name, size); + if (is_err) { + msleep(100); + continue; + } + ret = sdio_write(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_info(TEST_MODULE_NAME ":sdio_write err=%d" + " for chan %s\n", + -ret, test_ch->name); + is_err = 1; + msleep(20); + continue; + } + tx_packet_count++; + test_ch->tx_bytes += size; + test_ch->config_msg.num_packets--; + } + + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, read_avail=%d\n", + test_ch->name, read_avail); + if (read_avail > 0) { + size = min(packet_size, read_avail); + pr_debug(TEST_MODULE_NAME ":rx size = %d.\n", size); + TEST_DBG(TEST_MODULE_NAME ":channel %s, sdio_read, " + "size=%d\n", test_ch->name, size); + if (is_err) { + msleep(100); + continue; + } + ret = sdio_read(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_info(TEST_MODULE_NAME ": sdio_read size %d " + " err=%d" + " for chan %s\n", + size, -ret, test_ch->name); + is_err = 1; + msleep(20); + continue; + } + rx_packet_count++; + test_ch->rx_bytes += size; + } + + TEST_DBG(TEST_MODULE_NAME + ":total rx bytes = %d , rx_packet#=%d" + " for chan %s\n", + test_ch->rx_bytes, rx_packet_count, test_ch->name); + TEST_DBG(TEST_MODULE_NAME + ":total tx bytes = %d , tx_packet#=%d" + " for chan %s\n", + test_ch->tx_bytes, tx_packet_count, test_ch->name); + + udelay(500); + + } /* while (tx_packet_count < max_packets ) */ + + pr_info(TEST_MODULE_NAME ":total rx bytes = 0x%x , rx_packet#=%d for" + " chan %s.\n", + test_ch->rx_bytes, rx_packet_count, test_ch->name); + pr_info(TEST_MODULE_NAME ":total tx bytes = 0x%x , tx_packet#=%d" + " for chan %s.\n", + test_ch->tx_bytes, tx_packet_count, test_ch->name); + + pr_err(TEST_MODULE_NAME ": Modem Reset TEST END for chan %s.\n", + test_ch->name); + + pr_err(TEST_MODULE_NAME ": TEST PASS for chan %s\n", test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_PASSED; + check_test_completion(); + return; +} + +/** + * Worker thread to handle the tests types + */ +static void worker(struct work_struct *work) +{ + struct test_channel *test_ch = NULL; + struct test_work *test_work = container_of(work, + struct test_work, + work); + int test_type = 0; + + test_ch = test_work->test_ch; + + if (test_ch == NULL) { + pr_err(TEST_MODULE_NAME ":NULL test_ch\n"); + return; + } + + test_type = test_ch->test_type; + + switch (test_type) { + case SDIO_TEST_LOOPBACK_HOST: + loopback_test(test_ch); + break; + case SDIO_TEST_LOOPBACK_CLIENT: + sender_test(test_ch); + break; + case SDIO_TEST_PERF: + a2_performance_test(test_ch); + break; + case SDIO_TEST_LPM_CLIENT_WAKER: + lpm_test(test_ch); + break; + case SDIO_TEST_LPM_HOST_WAKER: + lpm_test_host_waker(test_ch); + break; + case SDIO_TEST_HOST_SENDER_NO_LP: + sender_no_loopback_test(test_ch); + break; + case SDIO_TEST_LPM_RANDOM: + lpm_continuous_rand_test(test_ch); + break; + case SDIO_TEST_RTT: + a2_rtt_test(test_ch); + break; + case SDIO_TEST_CLOSE_CHANNEL: + if (test_ch->ch_id != SDIO_SMEM) + open_close_test(test_ch); + break; + case SDIO_TEST_MODEM_RESET: + modem_reset_test(test_ch); + break; + case SDIO_TEST_A2_VALIDATION: + a2_validation_test(test_ch); + break; + default: + pr_err(TEST_MODULE_NAME ":Bad Test type = %d.\n", + (int) test_type); + } +} + + +/** + * Notification Callback + * + * Notify the worker + * + */ +static void notify(void *priv, unsigned channel_event) +{ + struct test_channel *test_ch = (struct test_channel *) priv; + + pr_debug(TEST_MODULE_NAME ": %s - notify event=%d.\n", + __func__, channel_event); + + if (test_ch->ch == NULL) { + pr_info(TEST_MODULE_NAME ": %s - notify before ch ready.\n", + __func__); + return; + } + + switch (channel_event) { + case SDIO_EVENT_DATA_READ_AVAIL: + atomic_inc(&test_ch->rx_notify_count); + atomic_set(&test_ch->any_notify_count, 1); + TEST_DBG(TEST_MODULE_NAME ": %s - SDIO_EVENT_DATA_READ_AVAIL, " + "any_notify_count=%d, rx_notify_count=%d\n", + __func__, + atomic_read(&test_ch->any_notify_count), + atomic_read(&test_ch->rx_notify_count)); + /* + * when there is pending data on a channel we would like to + * turn on the bit mask that implies that there is pending + * data for that channel on that deivce + */ + if (test_ch->test_device != NULL && + test_ch->test_type == SDIO_TEST_LPM_RANDOM) { + spin_lock_irqsave(&test_ch->test_device->lpm_array_lock, + test_ch->test_device-> + lpm_array_lock_flags); + test_ch->test_device->read_avail_mask |= + test_ch->channel_mask_id; + test_ch->notify_counter_per_chan++; + + lpm_test_update_entry(test_ch, LPM_NOTIFY, "NOTIFY", 0); + spin_unlock_irqrestore(&test_ch->test_device-> + lpm_array_lock, + test_ch->test_device-> + lpm_array_lock_flags); + } + break; + + case SDIO_EVENT_DATA_WRITE_AVAIL: + atomic_inc(&test_ch->tx_notify_count); + atomic_set(&test_ch->any_notify_count, 1); + TEST_DBG(TEST_MODULE_NAME ": %s - SDIO_EVENT_DATA_WRITE_AVAIL, " + "any_notify_count=%d, tx_notify_count=%d\n", + __func__, + atomic_read(&test_ch->any_notify_count), + atomic_read(&test_ch->tx_notify_count)); + break; + + default: + BUG(); + } + wake_up(&test_ch->wait_q); + +} + +#ifdef CONFIG_MSM_SDIO_SMEM +static int sdio_smem_test_cb(int event) +{ + struct test_channel *tch = test_ctx->test_ch_arr[SDIO_SMEM]; + int i; + int *smem_buf = (int *)test_ctx->smem_buf; + uint32_t val = 0; + int ret = 0; + + pr_debug(TEST_MODULE_NAME ":%s: Received event %d\n", __func__, event); + + if (!tch) { + pr_err(TEST_MODULE_NAME ": %s NULL tch\n", __func__); + return -EINVAL; + } + + switch (event) { + case SDIO_SMEM_EVENT_READ_DONE: + tch->rx_bytes += SMEM_MAX_XFER_SIZE; + for (i = 0; i < SMEM_MAX_XFER_SIZE;) { + val = (int)*smem_buf; + if ((val != test_ctx->smem_counter) && tch->is_used) { + pr_err(TEST_MODULE_NAME ":%s: Invalid value %d " + "expected %d in smem arr", + __func__, val, test_ctx->smem_counter); + pr_err(TEST_MODULE_NAME ":SMEM test FAILED\n"); + tch->test_completed = 1; + tch->test_result = TEST_FAILED; + check_test_completion(); + ret = -EINVAL; + goto exit; + } + i += 4; + smem_buf++; + test_ctx->smem_counter++; + } + if (tch->rx_bytes >= 40000000) { + if ((!tch->test_completed) && tch->is_used) { + pr_info(TEST_MODULE_NAME ":SMEM test PASSED\n"); + tch->test_completed = 1; + tch->test_result = TEST_PASSED; + check_test_completion(); + } + } + break; + case SDIO_SMEM_EVENT_READ_ERR: + if (tch->is_used) { + pr_err(TEST_MODULE_NAME ":Read overflow, " + "SMEM test FAILED\n"); + tch->test_completed = 1; + tch->test_result = TEST_FAILED; + ret = -EIO; + } + break; + default: + if (tch->is_used) { + pr_err(TEST_MODULE_NAME ":Unhandled event %d\n", event); + ret = -EINVAL; + } + break; + } +exit: + return ret; +} + +static int sdio_smem_open(struct sdio_smem_client *sdio_smem) +{ + int ret = 0; + + if (!sdio_smem) { + pr_info(TEST_MODULE_NAME "%s: NULL sdio_smem_client\n", + __func__); + return -EINVAL; + } + + if (test_ctx->test_ch_arr[SDIO_SMEM]->ch_ready) { + pr_info(TEST_MODULE_NAME "%s: SDIO_SMEM channel is already opened\n", + __func__); + return 0; + } + + test_ctx->test_ch_arr[SDIO_SMEM]->ch_ready = 1; + sdio_smem->buf = test_ctx->smem_buf; + sdio_smem->size = SMEM_MAX_XFER_SIZE; + sdio_smem->cb_func = sdio_smem_test_cb; + ret = sdio_smem_register_client(); + if (ret) + pr_info(TEST_MODULE_NAME "%s: Error (%d) registering sdio_smem " + "test client\n", + __func__, ret); + + return ret; +} + +static int sdio_smem_test_probe(struct platform_device *pdev) +{ + test_ctx->sdio_smem = container_of(pdev, struct sdio_smem_client, + plat_dev); + + return sdio_smem_open(test_ctx->sdio_smem); +} + +static struct platform_driver sdio_smem_client_drv = { + .probe = sdio_smem_test_probe, + .driver = { + .name = "SDIO_SMEM_CLIENT", + .owner = THIS_MODULE, + }, +}; +#endif + +static void sdio_test_lpm_timeout_handler(unsigned long data) +{ + struct test_channel *tch = (struct test_channel *)data; + + pr_info(TEST_MODULE_NAME ": %s - LPM TEST TIMEOUT Expired after " + "%d ms\n", __func__, tch->timeout_ms); + tch->test_completed = 1; + pr_info(TEST_MODULE_NAME ": %s - tch->test_result = TEST_FAILED\n", + __func__); + tch->test_completed = 1; + tch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + +static void sdio_test_lpm_timer_handler(unsigned long data) +{ + struct test_channel *tch = (struct test_channel *)data; + + pr_info(TEST_MODULE_NAME ": %s - LPM TEST Timer Expired after " + "%d ms\n", __func__, tch->timer_interval_ms); + + if (!tch) { + pr_err(TEST_MODULE_NAME ": %s - LPM TEST FAILED. " + "tch is NULL\n", __func__); + return; + } + + if (!tch->ch) { + pr_err(TEST_MODULE_NAME ": %s - LPM TEST FAILED. tch->ch " + "is NULL\n", __func__); + tch->test_result = TEST_FAILED; + return; + } + + /* Verfiy that we voted for sleep */ + if (tch->is_ok_to_sleep) { + tch->test_result = TEST_PASSED; + pr_info(TEST_MODULE_NAME ": %s - 8K voted for sleep\n", + __func__); + } else { + tch->test_result = TEST_FAILED; + pr_info(TEST_MODULE_NAME ": %s - 8K voted against sleep\n", + __func__); + + } + + sdio_al_unregister_lpm_cb(tch->sdio_al_device); + + if (tch->test_type == SDIO_TEST_LPM_HOST_WAKER) { + atomic_set(&tch->wakeup_client, 1); + wake_up(&tch->wait_q); + } +} + +int sdio_test_wakeup_callback(void *device_handle, int is_vote_for_sleep) +{ + int i = 0; + + TEST_DBG(TEST_MODULE_NAME ": %s is_vote_for_sleep=%d!!!", + __func__, is_vote_for_sleep); + + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + + if ((!tch) || (!tch->is_used) || (!tch->ch_ready)) + continue; + if (tch->sdio_al_device == device_handle) { + tch->is_ok_to_sleep = is_vote_for_sleep; + + if (tch->test_type == SDIO_TEST_LPM_RANDOM) { + spin_lock_irqsave(&tch->test_device-> + lpm_array_lock, + tch->test_device-> + lpm_array_lock_flags); + if (is_vote_for_sleep == 1) + lpm_test_update_entry(tch, + LPM_SLEEP, + "SLEEP ", 0); + else + lpm_test_update_entry(tch, + LPM_WAKEUP, + "WAKEUP", 0); + + spin_unlock_irqrestore(&tch->test_device-> + lpm_array_lock, + tch->test_device-> + lpm_array_lock_flags); + break; + } + } + } + + return 0; +} + +static int sdio_test_find_dev(struct test_channel *tch) +{ + int j; + int null_index = -1; + + for (j = 0 ; j < MAX_NUM_OF_SDIO_DEVICES; ++j) { + + struct sdio_test_device *test_dev = + &test_ctx->test_dev_arr[j]; + + if (test_dev->sdio_al_device == NULL) { + if (null_index == -1) + null_index = j; + continue; + } + + if (test_dev->sdio_al_device == + tch->ch->sdio_al_dev) { + test_dev->open_channels_counter_to_recv++; + test_dev->open_channels_counter_to_send++; + tch->test_device = test_dev; + /* setting mask id for pending data for + this channel */ + tch->channel_mask_id = test_dev->next_mask_id; + test_dev->next_mask_id *= 2; + pr_info(TEST_MODULE_NAME ": %s - channel %s " + "got read_mask_id = 0x%x. device " + "next_mask_id=0x%x", + __func__, tch->name, tch->channel_mask_id, + test_dev->next_mask_id); + break; + } + } + + /* + * happens ones a new device is "discovered" while testing. i.e + * if testing a few channels, a new deivce will be "discovered" once + * the first channel of a device is being tested + */ + if (j == MAX_NUM_OF_SDIO_DEVICES) { + + struct sdio_test_device *test_dev = + &test_ctx-> + test_dev_arr[null_index]; + test_dev->sdio_al_device = + tch->ch->sdio_al_dev; + + test_ctx->number_of_active_devices++; + test_ctx->max_number_of_devices++; + test_dev->open_channels_counter_to_recv++; + test_dev->open_channels_counter_to_send++; + test_dev->next_avail_entry_in_array = 0; + tch->test_device = test_dev; + tch->test_device->array_size = + LPM_ARRAY_SIZE; + test_dev->modem_result_per_dev = 1; + tch->modem_result_per_chan = 0; + test_dev->next_avail_entry_in_array = 0; + + spin_lock_init(&test_dev-> + lpm_array_lock); + + if (tch->test_type == SDIO_TEST_LPM_RANDOM) { + pr_err(MODULE_NAME ": %s - " + "Allocating Msg Array for " + "Maximum open channels for device (%d) " + "Channels. Array has %d entries", + __func__, + LPM_MAX_OPEN_CHAN_PER_DEV, + test_dev->array_size); + + test_dev->lpm_arr = + kzalloc(sizeof( + struct lpm_entry_type) * + tch-> + test_device->array_size, + GFP_KERNEL); + + if (!test_dev->lpm_arr) { + pr_err(MODULE_NAME ": %s - " + "lpm_arr is NULL", + __func__); + return -ENOMEM; + } + } + + /* + * in new device, initialize next_mask_id, and setting + * mask_id to the channel + */ + test_dev->next_mask_id = 0x1; + tch->channel_mask_id = test_dev->next_mask_id; + test_dev->next_mask_id *= 2; + pr_info(TEST_MODULE_NAME ": %s - channel %s got " + "read_mask_id = 0x%x. device next_mask_id=0x%x", + __func__, + tch->name, + tch->channel_mask_id, + test_dev->next_mask_id); + } + + return 0; +} + +static void check_test_result(void) +{ + int result = 1; + int i = 0; + + test_ctx->max_number_of_devices = 0; + + pr_info(TEST_MODULE_NAME ": %s - Woke Up\n", __func__); + + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + + if ((!tch) || (!tch->is_used) || (!tch->ch_ready)) + continue; + + if (tch->test_type == SDIO_TEST_LPM_RANDOM) + result &= tch->test_device->final_result_per_dev; + else + if (tch->test_result == TEST_FAILED) { + pr_info(TEST_MODULE_NAME ": %s - " + "Test FAILED\n", __func__); + test_ctx->test_result = TEST_FAILED; + pr_err(TEST_MODULE_NAME ": %s - " + "test_result %d", + __func__, test_ctx->test_result); + return; + } + } + + if (result == 0) { + pr_info(TEST_MODULE_NAME ": %s - Test FAILED\n", __func__); + test_ctx->test_result = TEST_FAILED; + pr_err(TEST_MODULE_NAME ": %s - " + "test_result %d", + __func__, test_ctx->test_result); + return; + } + + pr_info(TEST_MODULE_NAME ": %s - Test PASSED", __func__); + test_ctx->test_result = TEST_PASSED; + pr_err(TEST_MODULE_NAME ": %s - " + "test_result %d", + __func__, test_ctx->test_result); + return; +} + +/** + * Test Main + */ +static int test_start(void) +{ + int ret = -ENOMEM; + int i; + + pr_debug(TEST_MODULE_NAME ":Starting Test ....\n"); + + test_ctx->test_completed = 0; + test_ctx->test_result = TEST_NO_RESULT; + test_ctx->debug.dun_throughput = 0; + test_ctx->debug.rmnt_throughput = 0; + test_ctx->number_of_active_devices = 0; + + pr_err(TEST_MODULE_NAME ": %s - test_result %d", + __func__, test_ctx->test_result); + + memset(test_ctx->test_dev_arr, 0, + sizeof(struct sdio_test_device)*MAX_NUM_OF_SDIO_DEVICES); + + /* Open The Channels */ + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + + if ((!tch) || (!tch->is_used)) + continue; + + tch->rx_bytes = 0; + tch->tx_bytes = 0; + + atomic_set(&tch->tx_notify_count, 0); + atomic_set(&tch->rx_notify_count, 0); + atomic_set(&tch->any_notify_count, 0); + atomic_set(&tch->wakeup_client, 0); + + /* in case there are values left from previous tests */ + tch->notify_counter_per_chan = 0; + tch->next_index_in_sent_msg_per_chan = 0; + + memset(tch->buf, 0x00, tch->buf_size); + tch->test_result = TEST_NO_RESULT; + + tch->test_completed = 0; + + ret = open_sdio_ch(tch); + if (ret) + continue; + + if (tch->ch_id != SDIO_SMEM) { + ret = sdio_test_find_dev(tch); + + if (ret) { + pr_err(TEST_MODULE_NAME ": %s - " + "sdio_test_find_dev() returned with " + "error", __func__); + return -ENODEV; + } + + tch->sdio_al_device = tch->ch->sdio_al_dev; + } + + if ((tch->test_type == SDIO_TEST_LPM_HOST_WAKER) || + (tch->test_type == SDIO_TEST_LPM_CLIENT_WAKER) || + (tch->test_type == SDIO_TEST_LPM_RANDOM)) + sdio_al_register_lpm_cb(tch->sdio_al_device, + sdio_test_wakeup_callback); + } + + /* + * make some space between opening the channels and sending the + * config messages + */ + msleep(100); + + /* + * try to delay send_config_msg of all channels to after the point + * when we open them all + */ + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + + if ((!tch) || (!tch->is_used)) + continue; + + if ((tch->ch_ready) && (tch->ch_id != SDIO_SMEM)) + send_config_msg(tch); + + if ((tch->test_type == SDIO_TEST_LPM_HOST_WAKER) || + (tch->test_type == SDIO_TEST_LPM_CLIENT_WAKER) || + (tch->test_type == SDIO_TEST_LPM_RANDOM)) { + if (tch->timer_interval_ms > 0) { + pr_info(TEST_MODULE_NAME ": %s - init timer, " + "ms=%d\n", + __func__, tch->timer_interval_ms); + init_timer(&tch->timer); + tch->timer.data = (unsigned long)tch; + tch->timer.function = + sdio_test_lpm_timer_handler; + tch->timer.expires = jiffies + + msecs_to_jiffies(tch->timer_interval_ms); + add_timer(&tch->timer); + } + } + } + + pr_debug(TEST_MODULE_NAME ":queue_work..\n"); + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + + if ((!tch) || (!tch->is_used) || (!tch->ch_ready)) + continue; + + if (tch->ch_id == SDIO_SMEM) { +#ifdef CONFIG_MSM_SDIO_SMEM + if (tch->test_type == SDIO_TEST_CLOSE_CHANNEL) + open_close_smem_test(tch); +#endif + } else { + queue_work(tch->workqueue, &tch->test_work.work); + } + + } + + pr_info(TEST_MODULE_NAME ": %s - Waiting for the test completion\n", + __func__); + + wait_event(test_ctx->wait_q, test_ctx->test_completed); + check_test_result(); + + /* + * Close the channels and zero the is_used flag so that if the modem + * will be reset after the test completion we won't re-open + * the channels + */ + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + + if ((!tch) || (!tch->is_used)) + continue; + if (!tch->ch_ready) { + tch->is_used = 0; + continue; + } + + close_sdio_ch(tch); + tch->is_used = 0; + } + + if (test_ctx->test_result == TEST_PASSED) + return 0; + else + return -EINVAL; +} + +static int set_params_loopback_9k(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_LOOPBACK_CLIENT; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT; + tch->config_msg.num_packets = 10000; + tch->config_msg.num_iterations = 1; + + tch->packet_length = 512; + if (tch->ch_id == SDIO_RPC) + tch->packet_length = 128; + tch->timer_interval_ms = 0; + + return 0; +} +static int set_params_loopback_9k_close(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_CLOSE_CHANNEL; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT; + tch->config_msg.num_packets = 5000; + tch->config_msg.num_iterations = 1; + tch->max_burst_size = 10; + switch (tch->ch_id) { + case SDIO_DUN: + case SDIO_RPC: + tch->packet_length = 128; /* max is 2K*/ + break; + case SDIO_DIAG: + case SDIO_RMNT: + default: + tch->packet_length = 512; /* max is 4k */ + } + tch->timer_interval_ms = 0; + return 0; +} +static int set_params_a2_perf(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_PERF; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT; + + switch (tch->ch_id) { + case SDIO_DIAG: + tch->packet_length = 512; + break; + case SDIO_DUN: + tch->packet_length = DUN_PACKET_SIZE; + break; + case SDIO_CSVT: + tch->packet_length = CSVT_PACKET_SIZE; + break; + default: + tch->packet_length = MAX_XFER_SIZE; + break; + } + + pr_info(TEST_MODULE_NAME ": %s: packet_length=%d", __func__, + tch->packet_length); + + tch->config_msg.num_packets = 10000; + tch->config_msg.num_iterations = 1; + tch->random_packet_size = 0; + + tch->timer_interval_ms = 0; + + return 0; +} + +static int set_params_rtt(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_RTT; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT; + + switch (tch->ch_id) { + case SDIO_RMNT: + tch->packet_length = SDIO_RMNT_RTT_PACKET_SIZE; + break; + case SDIO_CSVT: + tch->packet_length = SDIO_CSVT_RTT_PACKET_SIZE; + break; + default: + pr_err(TEST_MODULE_NAME ": %s - ch_id invalid.\n", __func__); + return -EINVAL; + } + + pr_info(TEST_MODULE_NAME ": %s: packet_length=%d", __func__, + tch->packet_length); + + tch->config_msg.num_packets = 200; + tch->config_msg.num_iterations = 1; + tch->random_packet_size = 0; + + tch->timer_interval_ms = 0; + + return 0; +} + +static int set_params_a2_small_pkts(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_PERF; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT; + tch->packet_length = 128; + + tch->config_msg.num_packets = 1000000; + tch->config_msg.num_iterations = 1; + tch->random_packet_size = 1; + + tch->timer_interval_ms = 0; + + return 0; +} + +static int set_params_modem_reset(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_MODEM_RESET; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT; + tch->packet_length = 512; + if (tch->ch_id == SDIO_RPC) + tch->packet_length = 128; + else if ((tch->ch_id == SDIO_RMNT) || (tch->ch_id == SDIO_DUN)) + tch->packet_length = MAX_XFER_SIZE; + + tch->config_msg.num_packets = 50000; + tch->config_msg.num_iterations = 1; + + tch->timer_interval_ms = 0; + + return 0; +} + +static int set_params_a2_validation(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_A2_VALIDATION; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT; + + if (tch->ch_id == SDIO_RMNT) + tch->packet_length = RMNT_PACKET_SIZE; + else if (tch->ch_id == SDIO_DUN) + tch->packet_length = DUN_PACKET_SIZE; + else + tch->packet_length = MAX_XFER_SIZE; + + tch->config_msg.num_packets = 10000; + tch->config_msg.num_iterations = 1; + tch->timer_interval_ms = 0; + + return 0; +} + +static int set_params_smem_test(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->timer_interval_ms = 0; + + return 0; +} + +static int set_params_lpm_test(struct test_channel *tch, + enum sdio_test_case_type test, + int timer_interval_ms) +{ + static int first_time = 1; + if (!tch) { + pr_err(TEST_MODULE_NAME ": %s - NULL channel\n", __func__); + return -EINVAL; + } + + tch->is_used = 1; + tch->test_type = test; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = test; + tch->config_msg.num_packets = LPM_TEST_NUM_OF_PACKETS; + tch->config_msg.num_iterations = 1; + tch->timer_interval_ms = timer_interval_ms; + tch->timeout_ms = 10000; + + tch->packet_length = 0; + if (test != SDIO_TEST_LPM_RANDOM) { + init_timer(&tch->timeout_timer); + tch->timeout_timer.data = (unsigned long)tch; + tch->timeout_timer.function = sdio_test_lpm_timeout_handler; + tch->timeout_timer.expires = jiffies + + msecs_to_jiffies(tch->timeout_ms); + add_timer(&tch->timeout_timer); + pr_info(TEST_MODULE_NAME ": %s - Initiated LPM TIMEOUT TIMER." + "set to %d ms\n", + __func__, tch->timeout_ms); + } + + if (first_time) { + pr_info(TEST_MODULE_NAME ": %s - wake_lock_init() called\n", + __func__); + wake_lock_init(&test_ctx->wake_lock, + WAKE_LOCK_SUSPEND, TEST_MODULE_NAME); + first_time = 0; + } + + pr_info(TEST_MODULE_NAME ": %s - wake_lock() for the TEST is " + "called channel %s. to prevent real sleeping\n", + __func__, tch->name); + wake_lock(&test_ctx->wake_lock); + + return 0; +} + +static int set_params_8k_sender_no_lp(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_HOST_SENDER_NO_LP; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_HOST_SENDER_NO_LP; + tch->config_msg.num_packets = 1000; + tch->config_msg.num_iterations = 1; + + tch->packet_length = 512; + if (tch->ch_id == SDIO_RPC) + tch->packet_length = 128; + tch->timer_interval_ms = 0; + + return 0; +} + +static void set_pseudo_random_seed(void) +{ + /* Set the seed accoring to the kernel command parameters if any or + get a random value */ + if (seed != 0) { + test_ctx->lpm_pseudo_random_seed = seed; + } else { + test_ctx->lpm_pseudo_random_seed = + (unsigned int)(get_jiffies_64() & 0xFFFF); + test_ctx->lpm_pseudo_random_seed = + pseudo_random_seed(&test_ctx->lpm_pseudo_random_seed); + } + + pr_info(TEST_MODULE_NAME ":%s: seed is %u", + __func__, test_ctx->lpm_pseudo_random_seed); +} + +/* + for each channel + 1. open channel + 2. close channel +*/ +static int close_channel_lpm_test(int channel_num) +{ + int ret = 0; + struct test_channel *tch = NULL; + tch = test_ctx->test_ch_arr[channel_num]; + + if (!tch) { + pr_info(TEST_MODULE_NAME ":%s ch#%d is NULL\n", + __func__, channel_num); + return 0; + } + + ret = open_sdio_ch(tch); + if (ret) { + pr_err(TEST_MODULE_NAME":%s open channel %s" + " failed\n", __func__, tch->name); + return ret; + } else { + pr_info(TEST_MODULE_NAME":%s open channel %s" + " success\n", __func__, tch->name); + } + ret = close_sdio_ch(tch); + if (ret) { + pr_err(TEST_MODULE_NAME":%s close channel %s" + " failed\n", __func__, tch->name); + return ret; + } else { + pr_info(TEST_MODULE_NAME":%s close channel %s" + " success\n", __func__, tch->name); + } + + tch->is_used = 0; + + return ret; +} + +/** + * Write File. + * + * @note Trigger the test from user space by: + * echo 1 > /dev/sdio_al_test + * + */ +ssize_t test_write(struct file *filp, const char __user *buf, size_t size, + loff_t *f_pos) +{ + sdio_al_test_initial_dev_and_chan(test_ctx); + + if (strict_strtol(buf, 10, &test_ctx->testcase)) + return -EINVAL; + + switch (test_ctx->testcase) { + case 98: + pr_info(TEST_MODULE_NAME " set runtime debug on"); + test_ctx->runtime_debug = 1; + return size; + case 99: + pr_info(TEST_MODULE_NAME " set runtime debug off"); + test_ctx->runtime_debug = 0; + return size; + default: + pr_info(TEST_MODULE_NAME ":Bad Test number = %d.\n", + (int)test_ctx->testcase); + return size; + } + + return size; +} + +/** + * Test Channel Init. + */ +int test_channel_init(char *name) +{ + struct test_channel *test_ch; + int ch_id = 0; + int ret; + + pr_debug(TEST_MODULE_NAME ":%s.\n", __func__); + pr_info(TEST_MODULE_NAME ": init test channel %s.\n", name); + + ch_id = channel_name_to_id(name); + pr_debug(TEST_MODULE_NAME ":id = %d.\n", ch_id); + if (test_ctx->test_ch_arr[ch_id] == NULL) { + test_ch = kzalloc(sizeof(*test_ch), GFP_KERNEL); + if (test_ch == NULL) { + pr_err(TEST_MODULE_NAME ":kzalloc err for allocating " + "test_ch %s.\n", + name); + return -ENOMEM; + } + test_ctx->test_ch_arr[ch_id] = test_ch; + + test_ch->ch_id = ch_id; + + strncpy(test_ch->name, name, + strnlen(name, TEST_CH_NAME_SIZE)-SDIO_TEST_POSTFIX_SIZE); + + test_ch->buf_size = MAX_XFER_SIZE; + + test_ch->buf = kzalloc(test_ch->buf_size, GFP_KERNEL); + if (test_ch->buf == NULL) { + kfree(test_ch); + test_ctx->test_ch = NULL; + return -ENOMEM; + } + + if (test_ch->ch_id == SDIO_SMEM) { + test_ctx->smem_buf = kzalloc(SMEM_MAX_XFER_SIZE, + GFP_KERNEL); + if (test_ctx->smem_buf == NULL) { + pr_err(TEST_MODULE_NAME ":%s: Unable to " + "allocate smem buf\n", + __func__); + kfree(test_ch); + test_ctx->test_ch = NULL; + return -ENOMEM; + } + +#ifdef CONFIG_MSM_SDIO_SMEM + ret = platform_driver_register(&sdio_smem_client_drv); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s: Unable to " + "register sdio smem " + "test client\n", + __func__); + return ret; + } +#endif + } else { + test_ch->workqueue = + create_singlethread_workqueue(test_ch->name); + test_ch->test_work.test_ch = test_ch; + INIT_WORK(&test_ch->test_work.work, worker); + + init_waitqueue_head(&test_ch->wait_q); + } + } else { + test_ch = test_ctx->test_ch_arr[ch_id]; + pr_info(TEST_MODULE_NAME ":%s: ch %s was detected again\n", + __func__, test_ch->name); + test_ch->card_removed = 0; + if ((test_ch->is_used) && + (test_ch->test_type == SDIO_TEST_MODEM_RESET)) { + if (test_ch->ch_id == SDIO_SMEM) { +#ifdef CONFIG_MSM_SDIO_SMEM + ret = add_sdio_smem(); + if (ret) { + test_ch->ch_ready = false; + return 0; + } +#endif + } else { + ret = open_sdio_ch(test_ch); + if (ret) { + pr_info(TEST_MODULE_NAME + ":%s: open channel %s failed\n", + __func__, test_ch->name); + return 0; + } + ret = sdio_test_find_dev(test_ch); + + if (ret) { + pr_err(TEST_MODULE_NAME ": %s - " + "sdio_test_find_dev() returned " + "with error", __func__); + return -ENODEV; + } + + test_ch->sdio_al_device = + test_ch->ch->sdio_al_dev; + } + atomic_set(&test_ch->card_detected_event, 1); + wake_up(&test_ch->wait_q); + } + } + + return 0; +} + +static int sdio_test_channel_probe(struct platform_device *pdev) +{ + if (!pdev) + return -EIO; + return test_channel_init((char *)pdev->name); +} + +static int sdio_test_channel_remove(struct platform_device *pdev) +{ + int ch_id; + + if (!pdev) + return -EIO; + + ch_id = channel_name_to_id((char *)pdev->name); + if (test_ctx->test_ch_arr[ch_id] == NULL) + return 0; + + pr_info(TEST_MODULE_NAME "%s: remove ch %s\n", + __func__, test_ctx->test_ch_arr[ch_id]->name); + + if ((ch_id == SDIO_SMEM) && (test_ctx->smem_pdev)) { + platform_device_unregister(test_ctx->smem_pdev); + test_ctx->smem_pdev = NULL; + } + + test_ctx->test_ch_arr[ch_id]->ch_ready = 0; + test_ctx->test_ch_arr[ch_id]->card_removed = 1; + + return 0; + +} + +static int sdio_test_channel_csvt_probe(struct platform_device *pdev) +{ + int ret = 0; + + if (!pdev) + return -ENODEV; + + test_ctx->csvt_app_pdev = platform_device_alloc("SDIO_CSVT_TEST_APP", + -1); + ret = platform_device_add(test_ctx->csvt_app_pdev); + if (ret) { + pr_err(MODULE_NAME ":platform_device_add failed, " + "ret=%d\n", ret); + return ret; + } + + return sdio_test_channel_probe(pdev); +} + +static int sdio_test_channel_csvt_remove(struct platform_device *pdev) +{ + if (!pdev) + return -ENODEV; + + platform_device_unregister(test_ctx->csvt_app_pdev); + + return sdio_test_channel_remove(pdev); +} + +static struct platform_driver sdio_rpc_drv = { + .probe = sdio_test_channel_probe, + .remove = sdio_test_channel_remove, + .driver = { + .name = "SDIO_RPC_TEST", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver sdio_qmi_drv = { + .probe = sdio_test_channel_probe, + .remove = sdio_test_channel_remove, + .driver = { + .name = "SDIO_QMI_TEST", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver sdio_diag_drv = { + .probe = sdio_test_channel_probe, + .remove = sdio_test_channel_remove, + .driver = { + .name = "SDIO_DIAG_TEST", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver sdio_smem_drv = { + .probe = sdio_test_channel_probe, + .remove = sdio_test_channel_remove, + .driver = { + .name = "SDIO_SMEM_TEST", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver sdio_rmnt_drv = { + .probe = sdio_test_channel_probe, + .remove = sdio_test_channel_remove, + .driver = { + .name = "SDIO_RMNT_TEST", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver sdio_dun_drv = { + .probe = sdio_test_channel_probe, + .remove = sdio_test_channel_remove, + .driver = { + .name = "SDIO_DUN_TEST", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver sdio_csvt_drv = { + .probe = sdio_test_channel_csvt_probe, + .remove = sdio_test_channel_csvt_remove, + .driver = { + .name = "SDIO_CSVT_TEST", + .owner = THIS_MODULE, + }, +}; + +static struct class *test_class; + +const struct file_operations test_fops = { + .owner = THIS_MODULE, + .write = test_write, +}; + +/** + * Module Init. + */ +static int __init test_init(void) +{ + int ret; + + pr_debug(TEST_MODULE_NAME ":test_init.\n"); + + test_ctx = kzalloc(sizeof(struct test_context), GFP_KERNEL); + + if (test_ctx == NULL) { + pr_err(TEST_MODULE_NAME ":kzalloc err.\n"); + return -ENOMEM; + } + test_ctx->test_ch = NULL; + test_ctx->signature = TEST_SIGNATURE; + + test_ctx->name = "UNKNOWN"; + + init_waitqueue_head(&test_ctx->wait_q); + +#ifdef CONFIG_DEBUG_FS + sdio_al_test_debugfs_init(); +#endif + + test_class = class_create(THIS_MODULE, TEST_MODULE_NAME); + + ret = alloc_chrdev_region(&test_ctx->dev_num, 0, 1, TEST_MODULE_NAME); + if (ret) { + pr_err(TEST_MODULE_NAME "alloc_chrdev_region err.\n"); + return -ENODEV; + } + + test_ctx->dev = device_create(test_class, NULL, test_ctx->dev_num, + test_ctx, TEST_MODULE_NAME); + if (IS_ERR(test_ctx->dev)) { + pr_err(TEST_MODULE_NAME ":device_create err.\n"); + return -ENODEV; + } + + test_ctx->cdev = cdev_alloc(); + if (test_ctx->cdev == NULL) { + pr_err(TEST_MODULE_NAME ":cdev_alloc err.\n"); + return -ENODEV; + } + cdev_init(test_ctx->cdev, &test_fops); + test_ctx->cdev->owner = THIS_MODULE; + + ret = cdev_add(test_ctx->cdev, test_ctx->dev_num, 1); + if (ret) + pr_err(TEST_MODULE_NAME ":cdev_add err=%d\n", -ret); + else + pr_debug(TEST_MODULE_NAME ":SDIO-AL-Test init OK..\n"); + + platform_driver_register(&sdio_rpc_drv); + platform_driver_register(&sdio_qmi_drv); + platform_driver_register(&sdio_diag_drv); + platform_driver_register(&sdio_smem_drv); + platform_driver_register(&sdio_rmnt_drv); + platform_driver_register(&sdio_dun_drv); + platform_driver_register(&sdio_csvt_drv); + + return ret; +} + +/** + * Module Exit. + */ +static void __exit test_exit(void) +{ + int i; + + pr_debug(TEST_MODULE_NAME ":test_exit.\n"); + + test_ctx->exit_flag = true; + + msleep(100); /* allow gracefully exit of the worker thread */ + + cdev_del(test_ctx->cdev); + device_destroy(test_class, test_ctx->dev_num); + unregister_chrdev_region(test_ctx->dev_num, 1); + + platform_driver_unregister(&sdio_rpc_drv); + platform_driver_unregister(&sdio_qmi_drv); + platform_driver_unregister(&sdio_diag_drv); + platform_driver_unregister(&sdio_smem_drv); + platform_driver_unregister(&sdio_rmnt_drv); + platform_driver_unregister(&sdio_dun_drv); + platform_driver_unregister(&sdio_csvt_drv); + + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + if (!tch) + continue; + kfree(tch->buf); + kfree(tch); + } + +#ifdef CONFIG_DEBUG_FS + sdio_al_test_debugfs_cleanup(); +#endif + + kfree(test_ctx); + + pr_debug(TEST_MODULE_NAME ":test_exit complete.\n"); +} + +module_init(test_init); +module_exit(test_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SDIO_AL Test"); +MODULE_AUTHOR("Amir Samuelov "); + + diff --git a/arch/arm/mach-msm/sdio_cmux.c b/arch/arm/mach-msm/sdio_cmux.c new file mode 100644 index 00000000..70935b27 --- /dev/null +++ b/arch/arm/mach-msm/sdio_cmux.c @@ -0,0 +1,885 @@ +/* Copyright (c) 2010-2011, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "modem_notifier.h" + +#define MAX_WRITE_RETRY 5 +#define MAGIC_NO_V1 0x33FC + +static int msm_sdio_cmux_debug_mask; +module_param_named(debug_mask, msm_sdio_cmux_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +enum cmd_type { + DATA = 0, + OPEN, + CLOSE, + STATUS, + NUM_CMDS +}; + +#define DSR_POS 0x1 +#define CTS_POS 0x2 +#define RI_POS 0x4 +#define CD_POS 0x8 + +struct sdio_cmux_ch { + int lc_id; + + struct mutex lc_lock; + wait_queue_head_t open_wait_queue; + int is_remote_open; + int is_local_open; + int is_channel_reset; + + char local_status; + char remote_status; + + struct mutex tx_lock; + struct list_head tx_list; + + void *priv; + void (*receive_cb)(void *, int, void *); + void (*write_done)(void *, int, void *); + void (*status_callback)(int, void *); +} logical_ch[SDIO_CMUX_NUM_CHANNELS]; + +struct sdio_cmux_hdr { + uint16_t magic_no; + uint8_t status; /* This field is reserved for commands other + * than STATUS */ + uint8_t cmd; + uint8_t pad_bytes; + uint8_t lc_id; + uint16_t pkt_len; +}; + +struct sdio_cmux_pkt { + struct sdio_cmux_hdr *hdr; + void *data; +}; + +struct sdio_cmux_list_elem { + struct list_head list; + struct sdio_cmux_pkt cmux_pkt; +}; + +#define logical_ch_is_local_open(x) \ + (logical_ch[(x)].is_local_open) + +#define logical_ch_is_remote_open(x) \ + (logical_ch[(x)].is_remote_open) + +static void sdio_cdemux_fn(struct work_struct *work); +static DECLARE_WORK(sdio_cdemux_work, sdio_cdemux_fn); +static struct workqueue_struct *sdio_cdemux_wq; + +static DEFINE_MUTEX(write_lock); +static uint32_t bytes_to_write; +static DEFINE_MUTEX(temp_rx_lock); +static LIST_HEAD(temp_rx_list); + +static void sdio_cmux_fn(struct work_struct *work); +static DECLARE_WORK(sdio_cmux_work, sdio_cmux_fn); +static struct workqueue_struct *sdio_cmux_wq; + +static struct sdio_channel *sdio_qmi_chl; +static uint32_t sdio_cmux_inited; + +static uint32_t abort_tx; +static DEFINE_MUTEX(modem_reset_lock); + +static DEFINE_MUTEX(probe_lock); + +enum { + MSM_SDIO_CMUX_DEBUG = 1U << 0, + MSM_SDIO_CMUX_DUMP_BUFFER = 1U << 1, +}; + +static struct platform_device sdio_ctl_dev = { + .name = "SDIO_CTL", + .id = -1, +}; + +#if defined(DEBUG) +#define D_DUMP_BUFFER(prestr, cnt, buf) \ +do { \ + if (msm_sdio_cmux_debug_mask & MSM_SDIO_CMUX_DUMP_BUFFER) { \ + int i; \ + pr_debug("%s", prestr); \ + for (i = 0; i < cnt; i++) \ + pr_info("%.2x", buf[i]); \ + pr_debug("\n"); \ + } \ +} while (0) + +#define D(x...) \ +do { \ + if (msm_sdio_cmux_debug_mask & MSM_SDIO_CMUX_DEBUG) \ + pr_debug(x); \ +} while (0) + +#else +#define D_DUMP_BUFFER(prestr, cnt, buf) do {} while (0) +#define D(x...) do {} while (0) +#endif + +static int sdio_cmux_ch_alloc(int id) +{ + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) { + pr_err("%s: Invalid lc_id - %d\n", __func__, id); + return -EINVAL; + } + + logical_ch[id].lc_id = id; + mutex_init(&logical_ch[id].lc_lock); + init_waitqueue_head(&logical_ch[id].open_wait_queue); + logical_ch[id].is_remote_open = 0; + logical_ch[id].is_local_open = 0; + logical_ch[id].is_channel_reset = 0; + + INIT_LIST_HEAD(&logical_ch[id].tx_list); + mutex_init(&logical_ch[id].tx_lock); + + logical_ch[id].priv = NULL; + logical_ch[id].receive_cb = NULL; + logical_ch[id].write_done = NULL; + return 0; +} + +static int sdio_cmux_ch_clear_and_signal(int id) +{ + struct sdio_cmux_list_elem *list_elem; + + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) { + pr_err("%s: Invalid lc_id - %d\n", __func__, id); + return -EINVAL; + } + + mutex_lock(&logical_ch[id].lc_lock); + logical_ch[id].is_remote_open = 0; + mutex_lock(&logical_ch[id].tx_lock); + while (!list_empty(&logical_ch[id].tx_list)) { + list_elem = list_first_entry(&logical_ch[id].tx_list, + struct sdio_cmux_list_elem, + list); + list_del(&list_elem->list); + kfree(list_elem->cmux_pkt.hdr); + kfree(list_elem); + } + mutex_unlock(&logical_ch[id].tx_lock); + if (logical_ch[id].receive_cb) + logical_ch[id].receive_cb(NULL, 0, logical_ch[id].priv); + if (logical_ch[id].write_done) + logical_ch[id].write_done(NULL, 0, logical_ch[id].priv); + mutex_unlock(&logical_ch[id].lc_lock); + wake_up(&logical_ch[id].open_wait_queue); + return 0; +} + +static int sdio_cmux_write_cmd(const int id, enum cmd_type type) +{ + int write_size = 0; + void *write_data = NULL; + struct sdio_cmux_list_elem *list_elem; + + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) { + pr_err("%s: Invalid lc_id - %d\n", __func__, id); + return -EINVAL; + } + + if (type < 0 || type > NUM_CMDS) { + pr_err("%s: Invalid cmd - %d\n", __func__, type); + return -EINVAL; + } + + write_size = sizeof(struct sdio_cmux_hdr); + list_elem = kmalloc(sizeof(struct sdio_cmux_list_elem), GFP_KERNEL); + if (!list_elem) { + pr_err("%s: list_elem alloc failed\n", __func__); + return -ENOMEM; + } + + write_data = kmalloc(write_size, GFP_KERNEL); + if (!write_data) { + pr_err("%s: write_data alloc failed\n", __func__); + kfree(list_elem); + return -ENOMEM; + } + + list_elem->cmux_pkt.hdr = (struct sdio_cmux_hdr *)write_data; + list_elem->cmux_pkt.data = NULL; + + list_elem->cmux_pkt.hdr->lc_id = (uint8_t)id; + list_elem->cmux_pkt.hdr->pkt_len = (uint16_t)0; + list_elem->cmux_pkt.hdr->cmd = (uint8_t)type; + list_elem->cmux_pkt.hdr->status = (uint8_t)0; + if (type == STATUS) + list_elem->cmux_pkt.hdr->status = logical_ch[id].local_status; + list_elem->cmux_pkt.hdr->pad_bytes = (uint8_t)0; + list_elem->cmux_pkt.hdr->magic_no = (uint16_t)MAGIC_NO_V1; + + mutex_lock(&logical_ch[id].tx_lock); + list_add_tail(&list_elem->list, &logical_ch[id].tx_list); + mutex_unlock(&logical_ch[id].tx_lock); + + mutex_lock(&write_lock); + bytes_to_write += write_size; + mutex_unlock(&write_lock); + queue_work(sdio_cmux_wq, &sdio_cmux_work); + + return 0; +} + +int sdio_cmux_open(const int id, + void (*receive_cb)(void *, int, void *), + void (*write_done)(void *, int, void *), + void (*status_callback)(int, void *), + void *priv) +{ + int r; + struct sdio_cmux_list_elem *list_elem, *list_elem_tmp; + + if (!sdio_cmux_inited) + return -ENODEV; + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) { + pr_err("%s: Invalid id - %d\n", __func__, id); + return -EINVAL; + } + + r = wait_event_timeout(logical_ch[id].open_wait_queue, + logical_ch[id].is_remote_open, (1 * HZ)); + if (r < 0) { + pr_err("ERROR %s: wait_event_timeout() failed for" + " ch%d with rc %d\n", __func__, id, r); + return r; + } + if (r == 0) { + pr_err("ERROR %s: Wait Timed Out for ch%d\n", __func__, id); + return -ETIMEDOUT; + } + + mutex_lock(&logical_ch[id].lc_lock); + if (!logical_ch[id].is_remote_open) { + pr_err("%s: Remote ch%d not opened\n", __func__, id); + mutex_unlock(&logical_ch[id].lc_lock); + return -EINVAL; + } + if (logical_ch[id].is_local_open) { + mutex_unlock(&logical_ch[id].lc_lock); + return 0; + } + logical_ch[id].is_local_open = 1; + logical_ch[id].priv = priv; + logical_ch[id].receive_cb = receive_cb; + logical_ch[id].write_done = write_done; + logical_ch[id].status_callback = status_callback; + if (logical_ch[id].receive_cb) { + mutex_lock(&temp_rx_lock); + list_for_each_entry_safe(list_elem, list_elem_tmp, + &temp_rx_list, list) { + if ((int)list_elem->cmux_pkt.hdr->lc_id == id) { + logical_ch[id].receive_cb( + list_elem->cmux_pkt.data, + (int)list_elem->cmux_pkt.hdr->pkt_len, + logical_ch[id].priv); + list_del(&list_elem->list); + kfree(list_elem->cmux_pkt.hdr); + kfree(list_elem); + } + } + mutex_unlock(&temp_rx_lock); + } + mutex_unlock(&logical_ch[id].lc_lock); + sdio_cmux_write_cmd(id, OPEN); + return 0; +} +EXPORT_SYMBOL(sdio_cmux_open); + +int sdio_cmux_close(int id) +{ + struct sdio_cmux_ch *ch; + + if (!sdio_cmux_inited) + return -ENODEV; + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) { + pr_err("%s: Invalid channel close\n", __func__); + return -EINVAL; + } + + ch = &logical_ch[id]; + mutex_lock(&ch->lc_lock); + ch->receive_cb = NULL; + mutex_lock(&ch->tx_lock); + ch->write_done = NULL; + mutex_unlock(&ch->tx_lock); + ch->is_local_open = 0; + ch->priv = NULL; + mutex_unlock(&ch->lc_lock); + sdio_cmux_write_cmd(ch->lc_id, CLOSE); + return 0; +} +EXPORT_SYMBOL(sdio_cmux_close); + +int sdio_cmux_write_avail(int id) +{ + int write_avail; + + mutex_lock(&logical_ch[id].lc_lock); + if (logical_ch[id].is_channel_reset) { + mutex_unlock(&logical_ch[id].lc_lock); + return -ENETRESET; + } + mutex_unlock(&logical_ch[id].lc_lock); + write_avail = sdio_write_avail(sdio_qmi_chl); + return write_avail - bytes_to_write; +} +EXPORT_SYMBOL(sdio_cmux_write_avail); + +int sdio_cmux_write(int id, void *data, int len) +{ + struct sdio_cmux_list_elem *list_elem; + uint32_t write_size; + void *write_data = NULL; + struct sdio_cmux_ch *ch; + int ret; + + if (!sdio_cmux_inited) + return -ENODEV; + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) { + pr_err("%s: Invalid channel id %d\n", __func__, id); + return -ENODEV; + } + + ch = &logical_ch[id]; + if (len <= 0) { + pr_err("%s: Invalid len %d bytes to write\n", + __func__, len); + return -EINVAL; + } + + write_size = sizeof(struct sdio_cmux_hdr) + len; + list_elem = kmalloc(sizeof(struct sdio_cmux_list_elem), GFP_KERNEL); + if (!list_elem) { + pr_err("%s: list_elem alloc failed\n", __func__); + return -ENOMEM; + } + + write_data = kmalloc(write_size, GFP_KERNEL); + if (!write_data) { + pr_err("%s: write_data alloc failed\n", __func__); + kfree(list_elem); + return -ENOMEM; + } + + list_elem->cmux_pkt.hdr = (struct sdio_cmux_hdr *)write_data; + list_elem->cmux_pkt.data = (void *)((char *)write_data + + sizeof(struct sdio_cmux_hdr)); + memcpy(list_elem->cmux_pkt.data, data, len); + + list_elem->cmux_pkt.hdr->lc_id = (uint8_t)ch->lc_id; + list_elem->cmux_pkt.hdr->pkt_len = (uint16_t)len; + list_elem->cmux_pkt.hdr->cmd = (uint8_t)DATA; + list_elem->cmux_pkt.hdr->status = (uint8_t)0; + list_elem->cmux_pkt.hdr->pad_bytes = (uint8_t)0; + list_elem->cmux_pkt.hdr->magic_no = (uint16_t)MAGIC_NO_V1; + + mutex_lock(&ch->lc_lock); + if (!ch->is_remote_open || !ch->is_local_open) { + pr_err("%s: Local ch%d sending data before sending/receiving" + " OPEN command\n", __func__, ch->lc_id); + if (ch->is_channel_reset) + ret = -ENETRESET; + else + ret = -ENODEV; + mutex_unlock(&ch->lc_lock); + kfree(write_data); + kfree(list_elem); + return ret; + } + mutex_lock(&ch->tx_lock); + list_add_tail(&list_elem->list, &ch->tx_list); + mutex_unlock(&ch->tx_lock); + mutex_unlock(&ch->lc_lock); + + mutex_lock(&write_lock); + bytes_to_write += write_size; + mutex_unlock(&write_lock); + queue_work(sdio_cmux_wq, &sdio_cmux_work); + + return len; +} +EXPORT_SYMBOL(sdio_cmux_write); + +int is_remote_open(int id) +{ + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) + return -ENODEV; + + return logical_ch_is_remote_open(id); +} +EXPORT_SYMBOL(is_remote_open); + +int sdio_cmux_is_channel_reset(int id) +{ + int ret; + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) + return -ENODEV; + + mutex_lock(&logical_ch[id].lc_lock); + ret = logical_ch[id].is_channel_reset; + mutex_unlock(&logical_ch[id].lc_lock); + return ret; +} +EXPORT_SYMBOL(sdio_cmux_is_channel_reset); + +int sdio_cmux_tiocmget(int id) +{ + int ret = (logical_ch[id].remote_status & DSR_POS ? TIOCM_DSR : 0) | + (logical_ch[id].remote_status & CTS_POS ? TIOCM_CTS : 0) | + (logical_ch[id].remote_status & CD_POS ? TIOCM_CD : 0) | + (logical_ch[id].remote_status & RI_POS ? TIOCM_RI : 0) | + (logical_ch[id].local_status & CTS_POS ? TIOCM_RTS : 0) | + (logical_ch[id].local_status & DSR_POS ? TIOCM_DTR : 0); + return ret; +} +EXPORT_SYMBOL(sdio_cmux_tiocmget); + +int sdio_cmux_tiocmset(int id, unsigned int set, unsigned int clear) +{ + if (set & TIOCM_DTR) + logical_ch[id].local_status |= DSR_POS; + + if (set & TIOCM_RTS) + logical_ch[id].local_status |= CTS_POS; + + if (clear & TIOCM_DTR) + logical_ch[id].local_status &= ~DSR_POS; + + if (clear & TIOCM_RTS) + logical_ch[id].local_status &= ~CTS_POS; + + sdio_cmux_write_cmd(id, STATUS); + return 0; +} +EXPORT_SYMBOL(sdio_cmux_tiocmset); + +static int copy_packet(void *pkt, int size) +{ + struct sdio_cmux_list_elem *list_elem = NULL; + void *temp_pkt = NULL; + + list_elem = kmalloc(sizeof(struct sdio_cmux_list_elem), GFP_KERNEL); + if (!list_elem) { + pr_err("%s: list_elem alloc failed\n", __func__); + return -ENOMEM; + } + temp_pkt = kmalloc(size, GFP_KERNEL); + if (!temp_pkt) { + pr_err("%s: temp_pkt alloc failed\n", __func__); + kfree(list_elem); + return -ENOMEM; + } + + memcpy(temp_pkt, pkt, size); + list_elem->cmux_pkt.hdr = temp_pkt; + list_elem->cmux_pkt.data = (void *)((char *)temp_pkt + + sizeof(struct sdio_cmux_hdr)); + mutex_lock(&temp_rx_lock); + list_add_tail(&list_elem->list, &temp_rx_list); + mutex_unlock(&temp_rx_lock); + return 0; +} + +static int process_cmux_pkt(void *pkt, int size) +{ + struct sdio_cmux_hdr *mux_hdr; + uint32_t id, data_size; + void *data; + char *dump_buf = (char *)pkt; + + D_DUMP_BUFFER("process_cmux_pkt:", size, dump_buf); + mux_hdr = (struct sdio_cmux_hdr *)pkt; + switch (mux_hdr->cmd) { + case OPEN: + id = (uint32_t)(mux_hdr->lc_id); + D("%s: Received OPEN command for ch%d\n", __func__, id); + mutex_lock(&logical_ch[id].lc_lock); + logical_ch[id].is_remote_open = 1; + if (logical_ch[id].is_channel_reset) { + sdio_cmux_write_cmd(id, OPEN); + logical_ch[id].is_channel_reset = 0; + } + mutex_unlock(&logical_ch[id].lc_lock); + wake_up(&logical_ch[id].open_wait_queue); + break; + + case CLOSE: + id = (uint32_t)(mux_hdr->lc_id); + D("%s: Received CLOSE command for ch%d\n", __func__, id); + sdio_cmux_ch_clear_and_signal(id); + break; + + case DATA: + id = (uint32_t)(mux_hdr->lc_id); + D("%s: Received DATA for ch%d\n", __func__, id); + /*Channel is not locally open & if single packet received + then drop it*/ + mutex_lock(&logical_ch[id].lc_lock); + if (!logical_ch[id].is_remote_open) { + mutex_unlock(&logical_ch[id].lc_lock); + pr_err("%s: Remote Ch%d sent data before sending/" + "receiving OPEN command\n", __func__, id); + return -ENODEV; + } + + data = (void *)((char *)pkt + sizeof(struct sdio_cmux_hdr)); + data_size = (int)(((struct sdio_cmux_hdr *)pkt)->pkt_len); + if (logical_ch[id].receive_cb) + logical_ch[id].receive_cb(data, data_size, + logical_ch[id].priv); + else + copy_packet(pkt, size); + mutex_unlock(&logical_ch[id].lc_lock); + break; + + case STATUS: + id = (uint32_t)(mux_hdr->lc_id); + D("%s: Received STATUS command for ch%d\n", __func__, id); + if (logical_ch[id].remote_status != mux_hdr->status) { + mutex_lock(&logical_ch[id].lc_lock); + logical_ch[id].remote_status = mux_hdr->status; + mutex_unlock(&logical_ch[id].lc_lock); + if (logical_ch[id].status_callback) + logical_ch[id].status_callback( + sdio_cmux_tiocmget(id), + logical_ch[id].priv); + } + break; + } + return 0; +} + +static void parse_cmux_data(void *data, int size) +{ + int data_parsed = 0, pkt_size; + char *temp_ptr; + + D("Entered %s\n", __func__); + temp_ptr = (char *)data; + while (data_parsed < size) { + pkt_size = sizeof(struct sdio_cmux_hdr) + + (int)(((struct sdio_cmux_hdr *)temp_ptr)->pkt_len); + D("Parsed %d bytes, Current Pkt Size %d bytes," + " Total size %d bytes\n", data_parsed, pkt_size, size); + process_cmux_pkt((void *)temp_ptr, pkt_size); + data_parsed += pkt_size; + temp_ptr += pkt_size; + } + + kfree(data); +} + +static void sdio_cdemux_fn(struct work_struct *work) +{ + int r = 0, read_avail = 0; + void *cmux_data; + + while (1) { + read_avail = sdio_read_avail(sdio_qmi_chl); + if (read_avail < 0) { + pr_err("%s: sdio_read_avail failed with rc %d\n", + __func__, read_avail); + return; + } + + if (read_avail == 0) { + D("%s: Nothing to read\n", __func__); + return; + } + + D("%s: kmalloc %d bytes\n", __func__, read_avail); + cmux_data = kmalloc(read_avail, GFP_KERNEL); + if (!cmux_data) { + pr_err("%s: kmalloc Failed\n", __func__); + return; + } + + D("%s: sdio_read %d bytes\n", __func__, read_avail); + r = sdio_read(sdio_qmi_chl, cmux_data, read_avail); + if (r < 0) { + pr_err("%s: sdio_read failed with rc %d\n", + __func__, r); + kfree(cmux_data); + return; + } + + parse_cmux_data(cmux_data, read_avail); + } + return; +} + +static void sdio_cmux_fn(struct work_struct *work) +{ + int i, r = 0; + void *write_data; + uint32_t write_size, write_avail, write_retry = 0; + int bytes_written; + struct sdio_cmux_list_elem *list_elem = NULL; + struct sdio_cmux_ch *ch; + + for (i = 0; i < SDIO_CMUX_NUM_CHANNELS; ++i) { + ch = &logical_ch[i]; + bytes_written = 0; + mutex_lock(&ch->tx_lock); + while (!list_empty(&ch->tx_list)) { + list_elem = list_first_entry(&ch->tx_list, + struct sdio_cmux_list_elem, + list); + list_del(&list_elem->list); + mutex_unlock(&ch->tx_lock); + + write_data = (void *)list_elem->cmux_pkt.hdr; + write_size = sizeof(struct sdio_cmux_hdr) + + (uint32_t)list_elem->cmux_pkt.hdr->pkt_len; + + mutex_lock(&modem_reset_lock); + while (!(abort_tx) && + ((write_avail = sdio_write_avail(sdio_qmi_chl)) + < write_size)) { + mutex_unlock(&modem_reset_lock); + pr_err("%s: sdio_write_avail %d bytes, " + "write size %d bytes. Waiting...\n", + __func__, write_avail, write_size); + msleep(250); + mutex_lock(&modem_reset_lock); + } + while (!(abort_tx) && + ((r = sdio_write(sdio_qmi_chl, + write_data, write_size)) < 0) + && (r != -ENODEV) + && (write_retry++ < MAX_WRITE_RETRY)) { + mutex_unlock(&modem_reset_lock); + pr_err("%s: sdio_write failed with rc %d." + "Retrying...", __func__, r); + msleep(250); + mutex_lock(&modem_reset_lock); + } + if (!r && !abort_tx) { + D("%s: sdio_write_completed %dbytes\n", + __func__, write_size); + bytes_written += write_size; + } else if (r == -ENODEV) { + pr_err("%s: aborting_tx because sdio_write" + " returned %d\n", __func__, r); + r = 0; + abort_tx = 1; + } + mutex_unlock(&modem_reset_lock); + kfree(list_elem->cmux_pkt.hdr); + kfree(list_elem); + mutex_lock(&write_lock); + bytes_to_write -= write_size; + mutex_unlock(&write_lock); + mutex_lock(&ch->tx_lock); + } + if (ch->write_done) + ch->write_done(NULL, bytes_written, ch->priv); + mutex_unlock(&ch->tx_lock); + } + return; +} + +static void sdio_qmi_chl_notify(void *priv, unsigned event) +{ + if (event == SDIO_EVENT_DATA_READ_AVAIL) { + D("%s: Received SDIO_EVENT_DATA_READ_AVAIL\n", __func__); + queue_work(sdio_cdemux_wq, &sdio_cdemux_work); + } +} + +#ifdef CONFIG_DEBUG_FS + +static int debug_tbl(char *buf, int max) +{ + int i = 0; + int j; + + for (j = 0; j < SDIO_CMUX_NUM_CHANNELS; ++j) { + i += scnprintf(buf + i, max - i, + "ch%02d local open=%s remote open=%s\n", + j, logical_ch_is_local_open(j) ? "Y" : "N", + logical_ch_is_remote_open(j) ? "Y" : "N"); + } + + return i; +} + +#define DEBUG_BUFMAX 4096 +static char debug_buffer[DEBUG_BUFMAX]; + +static ssize_t debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int (*fill)(char *buf, int max) = file->private_data; + int bsize = fill(debug_buffer, DEBUG_BUFMAX); + return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize); +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + + +static const struct file_operations debug_ops = { + .read = debug_read, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + int (*fill)(char *buf, int max)) +{ + debugfs_create_file(name, mode, dent, fill, &debug_ops); +} + +#endif + +static int sdio_cmux_probe(struct platform_device *pdev) +{ + int i, r; + + mutex_lock(&probe_lock); + D("%s Begins\n", __func__); + if (sdio_cmux_inited) { + mutex_lock(&modem_reset_lock); + r = sdio_open("SDIO_QMI", &sdio_qmi_chl, NULL, + sdio_qmi_chl_notify); + if (r < 0) { + mutex_unlock(&modem_reset_lock); + pr_err("%s: sdio_open() failed\n", __func__); + goto error0; + } + abort_tx = 0; + mutex_unlock(&modem_reset_lock); + mutex_unlock(&probe_lock); + return 0; + } + + for (i = 0; i < SDIO_CMUX_NUM_CHANNELS; ++i) + sdio_cmux_ch_alloc(i); + INIT_LIST_HEAD(&temp_rx_list); + + sdio_cmux_wq = create_singlethread_workqueue("sdio_cmux"); + if (IS_ERR(sdio_cmux_wq)) { + pr_err("%s: create_singlethread_workqueue() ENOMEM\n", + __func__); + r = -ENOMEM; + goto error0; + } + + sdio_cdemux_wq = create_singlethread_workqueue("sdio_cdemux"); + if (IS_ERR(sdio_cdemux_wq)) { + pr_err("%s: create_singlethread_workqueue() ENOMEM\n", + __func__); + r = -ENOMEM; + goto error1; + } + + r = sdio_open("SDIO_QMI", &sdio_qmi_chl, NULL, sdio_qmi_chl_notify); + if (r < 0) { + pr_err("%s: sdio_open() failed\n", __func__); + goto error2; + } + + platform_device_register(&sdio_ctl_dev); + sdio_cmux_inited = 1; + D("SDIO Control MUX Driver Initialized.\n"); + mutex_unlock(&probe_lock); + return 0; + +error2: + destroy_workqueue(sdio_cdemux_wq); +error1: + destroy_workqueue(sdio_cmux_wq); +error0: + mutex_unlock(&probe_lock); + return r; +} + +static int sdio_cmux_remove(struct platform_device *pdev) +{ + int i; + + mutex_lock(&modem_reset_lock); + abort_tx = 1; + + for (i = 0; i < SDIO_CMUX_NUM_CHANNELS; ++i) { + mutex_lock(&logical_ch[i].lc_lock); + logical_ch[i].is_channel_reset = 1; + mutex_unlock(&logical_ch[i].lc_lock); + sdio_cmux_ch_clear_and_signal(i); + } + sdio_qmi_chl = NULL; + mutex_unlock(&modem_reset_lock); + + return 0; +} + +static struct platform_driver sdio_cmux_driver = { + .probe = sdio_cmux_probe, + .remove = sdio_cmux_remove, + .driver = { + .name = "SDIO_QMI", + .owner = THIS_MODULE, + }, +}; + +static int __init sdio_cmux_init(void) +{ +#ifdef CONFIG_DEBUG_FS + struct dentry *dent; + + dent = debugfs_create_dir("sdio_cmux", 0); + if (!IS_ERR(dent)) + debug_create("tbl", 0444, dent, debug_tbl); +#endif + + msm_sdio_cmux_debug_mask = 0; + return platform_driver_register(&sdio_cmux_driver); +} + +module_init(sdio_cmux_init); +MODULE_DESCRIPTION("MSM SDIO Control MUX"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/sdio_ctl.c b/arch/arm/mach-msm/sdio_ctl.c new file mode 100644 index 00000000..cacdce98 --- /dev/null +++ b/arch/arm/mach-msm/sdio_ctl.c @@ -0,0 +1,575 @@ +/* Copyright (c) 2010-2012, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * SDIO Control Driver -- Provides a binary SDIO muxed control port + * interface. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "modem_notifier.h" +#include + +#define MAX_WRITE_RETRY 5 +#define MAGIC_NO_V1 0x33FC +#define NUM_SDIO_CTL_PORTS 10 +#define DEVICE_NAME "sdioctl" +#define MAX_BUF_SIZE 2048 +#define DEBUG + +static int msm_sdio_ctl_debug_mask; +module_param_named(debug_mask, msm_sdio_ctl_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +struct sdio_ctl_dev { + int id; + char name[9]; + struct cdev cdev; + struct device *devicep; + struct mutex dev_lock; + int ref_count; + + struct mutex rx_lock; + uint32_t read_avail; + struct list_head rx_list; + + wait_queue_head_t read_wait_queue; + wait_queue_head_t write_wait_queue; +} *sdio_ctl_devp[NUM_SDIO_CTL_PORTS]; + +struct sdio_ctl_pkt { + int data_size; + void *data; +}; + +struct sdio_ctl_list_elem { + struct list_head list; + struct sdio_ctl_pkt ctl_pkt; +}; + +struct class *sdio_ctl_classp; +static dev_t sdio_ctl_number; +static uint32_t sdio_ctl_inited; + +enum { + MSM_SDIO_CTL_DEBUG = 1U << 0, + MSM_SDIO_CTL_DUMP_BUFFER = 1U << 1, +}; + +#if defined(DEBUG) +#define D_DUMP_BUFFER(prestr, cnt, buf) \ +do { \ + if (msm_sdio_ctl_debug_mask & MSM_SDIO_CTL_DUMP_BUFFER) { \ + int i; \ + pr_info("%s", prestr); \ + for (i = 0; i < cnt; i++) \ + pr_info("%.2x", buf[i]); \ + pr_info("\n"); \ + } \ +} while (0) + +#define D(x...) \ +do { \ + if (msm_sdio_ctl_debug_mask & MSM_SDIO_CTL_DEBUG) \ + pr_info(x); \ +} while (0) + +#else +#define D_DUMP_BUFFER(prestr, cnt, buf) do {} while (0) +#define D(x...) do {} while (0) +#endif + +static uint32_t cmux_ch_id[] = { + SDIO_CMUX_DATA_CTL_0, + SDIO_CMUX_DATA_CTL_1, + SDIO_CMUX_DATA_CTL_2, + SDIO_CMUX_DATA_CTL_3, + SDIO_CMUX_DATA_CTL_4, + SDIO_CMUX_DATA_CTL_5, + SDIO_CMUX_DATA_CTL_6, + SDIO_CMUX_DATA_CTL_7, + SDIO_CMUX_USB_CTL_0, + SDIO_CMUX_CSVT_CTL_0 +}; + +static int get_ctl_dev_index(int id) +{ + int dev_index; + for (dev_index = 0; dev_index < NUM_SDIO_CTL_PORTS; dev_index++) { + if (cmux_ch_id[dev_index] == id) + return dev_index; + } + return -ENODEV; +} + +static void sdio_ctl_receive_cb(void *data, int size, void *priv) +{ + struct sdio_ctl_list_elem *list_elem = NULL; + int id = ((struct sdio_ctl_dev *)(priv))->id; + int dev_index; + + if (id < 0 || id > cmux_ch_id[NUM_SDIO_CTL_PORTS - 1]) + return; + dev_index = get_ctl_dev_index(id); + if (dev_index < 0) { + pr_err("%s: Ch%d is not exported to user-space\n", + __func__, id); + return; + } + + if (!data || size <= 0) { + wake_up(&sdio_ctl_devp[dev_index]->read_wait_queue); + return; + } + + list_elem = kmalloc(sizeof(struct sdio_ctl_list_elem), GFP_KERNEL); + if (!list_elem) { + pr_err("%s: list_elem alloc failed\n", __func__); + return; + } + + list_elem->ctl_pkt.data = kmalloc(size, GFP_KERNEL); + if (!list_elem->ctl_pkt.data) { + pr_err("%s: list_elem->data alloc failed\n", __func__); + kfree(list_elem); + return; + } + memcpy(list_elem->ctl_pkt.data, data, size); + list_elem->ctl_pkt.data_size = size; + mutex_lock(&sdio_ctl_devp[dev_index]->rx_lock); + list_add_tail(&list_elem->list, &sdio_ctl_devp[dev_index]->rx_list); + sdio_ctl_devp[dev_index]->read_avail += size; + mutex_unlock(&sdio_ctl_devp[dev_index]->rx_lock); + wake_up(&sdio_ctl_devp[dev_index]->read_wait_queue); +} + +static void sdio_ctl_write_done(void *data, int size, void *priv) +{ + int id = ((struct sdio_ctl_dev *)(priv))->id; + int dev_index; + if (id < 0 || id > cmux_ch_id[NUM_SDIO_CTL_PORTS - 1]) + return; + + dev_index = get_ctl_dev_index(id); + if (dev_index < 0) { + pr_err("%s: Ch%d is not exported to user-space\n", + __func__, id); + return; + } + wake_up(&sdio_ctl_devp[dev_index]->write_wait_queue); +} + +static long sdio_ctl_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret; + struct sdio_ctl_dev *sdio_ctl_devp; + + sdio_ctl_devp = file->private_data; + if (!sdio_ctl_devp) + return -ENODEV; + + switch (cmd) { + case TIOCMGET: + ret = sdio_cmux_tiocmget(sdio_ctl_devp->id); + break; + case TIOCMSET: + ret = sdio_cmux_tiocmset(sdio_ctl_devp->id, arg, ~arg); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static unsigned int sdio_ctl_poll(struct file *file, poll_table *wait) +{ + struct sdio_ctl_dev *sdio_ctl_devp; + unsigned int mask = 0; + + sdio_ctl_devp = file->private_data; + if (!sdio_ctl_devp) { + pr_err("%s: on a NULL device\n", __func__); + return POLLERR; + } + + poll_wait(file, &sdio_ctl_devp->read_wait_queue, wait); + mutex_lock(&sdio_ctl_devp->rx_lock); + if (sdio_cmux_is_channel_reset(sdio_ctl_devp->id)) { + mutex_unlock(&sdio_ctl_devp->rx_lock); + pr_err("%s notifying reset for sdio_ctl_dev id:%d\n", + __func__, sdio_ctl_devp->id); + return POLLERR; + } + + if (sdio_ctl_devp->read_avail > 0) + mask |= POLLIN | POLLRDNORM; + + mutex_unlock(&sdio_ctl_devp->rx_lock); + + return mask; +} + +ssize_t sdio_ctl_read(struct file *file, + char __user *buf, + size_t count, + loff_t *ppos) +{ + int r = 0, id, bytes_to_read; + struct sdio_ctl_dev *sdio_ctl_devp; + struct sdio_ctl_list_elem *list_elem = NULL; + + sdio_ctl_devp = file->private_data; + + if (!sdio_ctl_devp) + return -ENODEV; + + D("%s: read from ch%d\n", __func__, sdio_ctl_devp->id); + + id = sdio_ctl_devp->id; + mutex_lock(&sdio_ctl_devp->rx_lock); + while (sdio_ctl_devp->read_avail <= 0) { + mutex_unlock(&sdio_ctl_devp->rx_lock); + r = wait_event_interruptible(sdio_ctl_devp->read_wait_queue, + sdio_ctl_devp->read_avail > 0 || + !is_remote_open(id)); + if (sdio_cmux_is_channel_reset(id)) + return -ENETRESET; + + if (!is_remote_open(id)) + return -ENODEV; + + if (r < 0) { + /* qualify error message */ + /* we get this anytime a signal comes in */ + if (r != -ERESTARTSYS) + pr_err("ERROR:%s: wait_event_interruptible " + "ret %i\n", __func__, r); + return r; + } + mutex_lock(&sdio_ctl_devp->rx_lock); + } + + if (list_empty(&sdio_ctl_devp->rx_list)) { + mutex_unlock(&sdio_ctl_devp->rx_lock); + D("%s: Nothing in ch%d's rx_list\n", __func__, + sdio_ctl_devp->id); + return -EAGAIN; + } + + list_elem = list_first_entry(&sdio_ctl_devp->rx_list, + struct sdio_ctl_list_elem, list); + bytes_to_read = (uint32_t)(list_elem->ctl_pkt.data_size); + if (bytes_to_read > count) { + mutex_unlock(&sdio_ctl_devp->rx_lock); + pr_err("%s: Packet size %d > buf size %d\n", __func__, + bytes_to_read, count); + return -ENOMEM; + } + + if (copy_to_user(buf, list_elem->ctl_pkt.data, bytes_to_read)) { + mutex_unlock(&sdio_ctl_devp->rx_lock); + pr_err("%s: copy_to_user failed for ch%d\n", __func__, + sdio_ctl_devp->id); + return -EFAULT; + } + sdio_ctl_devp->read_avail -= bytes_to_read; + list_del(&list_elem->list); + kfree(list_elem->ctl_pkt.data); + kfree(list_elem); + mutex_unlock(&sdio_ctl_devp->rx_lock); + D("%s: Returning %d bytes to ch%d\n", __func__, + bytes_to_read, sdio_ctl_devp->id); + return bytes_to_read; +} + + +ssize_t sdio_ctl_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int r = 0, id; + char *temp_buf; + struct sdio_ctl_dev *sdio_ctl_devp; + + if (count <= 0) + return -EINVAL; + + sdio_ctl_devp = file->private_data; + if (!sdio_ctl_devp) + return -ENODEV; + + D("%s: writing %i bytes on ch%d\n", + __func__, count, sdio_ctl_devp->id); + id = sdio_ctl_devp->id; + mutex_lock(&sdio_ctl_devp->dev_lock); + while (sdio_cmux_write_avail(id) < count) { + mutex_unlock(&sdio_ctl_devp->dev_lock); + r = wait_event_interruptible(sdio_ctl_devp->write_wait_queue, + sdio_cmux_write_avail(id) >= count + || !is_remote_open(id)); + + if (sdio_cmux_is_channel_reset(id)) + return -ENETRESET; + + if (!is_remote_open(id)) + return -ENODEV; + + if (r < 0) { + /* qualify error message */ + /* we get this anytime a signal comes in */ + if (r != -ERESTARTSYS) + pr_err("ERROR:%s: wait_event_interruptible " + "ret %i\n", __func__, r); + return r; + } + mutex_lock(&sdio_ctl_devp->dev_lock); + } + + temp_buf = kmalloc(count, GFP_KERNEL); + if (!temp_buf) { + mutex_unlock(&sdio_ctl_devp->dev_lock); + pr_err("%s: temp_buf alloc failed\n", __func__); + return -ENOMEM; + } + + if (copy_from_user(temp_buf, buf, count)) { + mutex_unlock(&sdio_ctl_devp->dev_lock); + pr_err("%s: copy_from_user failed\n", __func__); + kfree(temp_buf); + return -EFAULT; + } + + r = sdio_cmux_write(id, (void *)temp_buf, count); + kfree(temp_buf); + mutex_unlock(&sdio_ctl_devp->dev_lock); + return r; +} + + +int sdio_ctl_open(struct inode *inode, struct file *file) +{ + int r = 0; + struct sdio_ctl_dev *sdio_ctl_devp; + + if (!sdio_ctl_inited) + return -EIO; + + sdio_ctl_devp = container_of(inode->i_cdev, struct sdio_ctl_dev, cdev); + + if (!sdio_ctl_devp) + return -ENODEV; + + D("%s called on sdioctl%d device\n", __func__, sdio_ctl_devp->id); + r = sdio_cmux_open(sdio_ctl_devp->id, sdio_ctl_receive_cb, + sdio_ctl_write_done, NULL, + sdio_ctl_devp); + if (r < 0) { + pr_err("ERROR %s: sdio_cmux_open failed with rc %d\n", + __func__, r); + return r; + } + + mutex_lock(&sdio_ctl_devp->dev_lock); + sdio_ctl_devp->ref_count++; + mutex_unlock(&sdio_ctl_devp->dev_lock); + + file->private_data = sdio_ctl_devp; + return 0; +} + +int sdio_ctl_release(struct inode *inode, struct file *file) +{ + struct sdio_ctl_dev *sdio_ctl_devp; + struct sdio_ctl_list_elem *list_elem = NULL; + + sdio_ctl_devp = file->private_data; + if (!sdio_ctl_devp) + return -EINVAL; + + D("%s called on sdioctl%d device\n", __func__, sdio_ctl_devp->id); + + mutex_lock(&sdio_ctl_devp->dev_lock); + if (sdio_ctl_devp->ref_count > 0) { + sdio_ctl_devp->ref_count--; + if (!sdio_ctl_devp->ref_count) { + mutex_lock(&sdio_ctl_devp->rx_lock); + while (!list_empty(&sdio_ctl_devp->rx_list)) { + list_elem = list_first_entry( + &sdio_ctl_devp->rx_list, + struct sdio_ctl_list_elem, + list); + list_del(&list_elem->list); + kfree(list_elem->ctl_pkt.data); + kfree(list_elem); + } + sdio_ctl_devp->read_avail = 0; + mutex_unlock(&sdio_ctl_devp->rx_lock); + sdio_cmux_close(sdio_ctl_devp->id); + } + } + mutex_unlock(&sdio_ctl_devp->dev_lock); + + file->private_data = NULL; + return 0; +} + +static const struct file_operations sdio_ctl_fops = { + .owner = THIS_MODULE, + .open = sdio_ctl_open, + .release = sdio_ctl_release, + .read = sdio_ctl_read, + .write = sdio_ctl_write, + .poll = sdio_ctl_poll, + .unlocked_ioctl = sdio_ctl_ioctl, +}; + +static int sdio_ctl_probe(struct platform_device *pdev) +{ + int i; + int r; + + pr_info("%s Begins\n", __func__); + for (i = 0; i < NUM_SDIO_CTL_PORTS; ++i) { + sdio_ctl_devp[i] = kzalloc(sizeof(struct sdio_ctl_dev), + GFP_KERNEL); + if (IS_ERR(sdio_ctl_devp[i])) { + pr_err("ERROR:%s kmalloc() ENOMEM\n", __func__); + r = -ENOMEM; + goto error0; + } + + sdio_ctl_devp[i]->id = cmux_ch_id[i]; + sdio_ctl_devp[i]->ref_count = 0; + + mutex_init(&sdio_ctl_devp[i]->dev_lock); + init_waitqueue_head(&sdio_ctl_devp[i]->read_wait_queue); + init_waitqueue_head(&sdio_ctl_devp[i]->write_wait_queue); + mutex_init(&sdio_ctl_devp[i]->rx_lock); + INIT_LIST_HEAD(&sdio_ctl_devp[i]->rx_list); + sdio_ctl_devp[i]->read_avail = 0; + } + + r = alloc_chrdev_region(&sdio_ctl_number, 0, NUM_SDIO_CTL_PORTS, + DEVICE_NAME); + if (IS_ERR_VALUE(r)) { + pr_err("ERROR:%s: alloc_chrdev_region() ret %i.\n", + __func__, r); + goto error0; + } + + sdio_ctl_classp = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(sdio_ctl_classp)) { + pr_err("ERROR:%s: class_create() ENOMEM\n", __func__); + r = -ENOMEM; + goto error1; + } + + for (i = 0; i < NUM_SDIO_CTL_PORTS; ++i) { + cdev_init(&sdio_ctl_devp[i]->cdev, &sdio_ctl_fops); + sdio_ctl_devp[i]->cdev.owner = THIS_MODULE; + + r = cdev_add(&sdio_ctl_devp[i]->cdev, (sdio_ctl_number + i), + 1); + + if (IS_ERR_VALUE(r)) { + pr_err("%s: cdev_add() ret %i\n", __func__, r); + kfree(sdio_ctl_devp[i]); + goto error2; + } + + sdio_ctl_devp[i]->devicep = + device_create(sdio_ctl_classp, NULL, + (sdio_ctl_number + i), NULL, + DEVICE_NAME "%d", cmux_ch_id[i]); + + if (IS_ERR(sdio_ctl_devp[i]->devicep)) { + pr_err("%s: device_create() ENOMEM\n", __func__); + r = -ENOMEM; + cdev_del(&sdio_ctl_devp[i]->cdev); + kfree(sdio_ctl_devp[i]); + goto error2; + } + } + + sdio_ctl_inited = 1; + D("SDIO Control Port Driver Initialized.\n"); + return 0; + +error2: + while (--i >= 0) { + cdev_del(&sdio_ctl_devp[i]->cdev); + device_destroy(sdio_ctl_classp, + MKDEV(MAJOR(sdio_ctl_number), i)); + } + + class_destroy(sdio_ctl_classp); + i = NUM_SDIO_CTL_PORTS; +error1: + unregister_chrdev_region(MAJOR(sdio_ctl_number), NUM_SDIO_CTL_PORTS); +error0: + while (--i >= 0) + kfree(sdio_ctl_devp[i]); + return r; +} + +static int sdio_ctl_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < NUM_SDIO_CTL_PORTS; ++i) { + cdev_del(&sdio_ctl_devp[i]->cdev); + kfree(sdio_ctl_devp[i]); + device_destroy(sdio_ctl_classp, + MKDEV(MAJOR(sdio_ctl_number), i)); + } + class_destroy(sdio_ctl_classp); + unregister_chrdev_region(MAJOR(sdio_ctl_number), NUM_SDIO_CTL_PORTS); + + return 0; +} + +static struct platform_driver sdio_ctl_driver = { + .probe = sdio_ctl_probe, + .remove = sdio_ctl_remove, + .driver = { + .name = "SDIO_CTL", + .owner = THIS_MODULE, + }, +}; + +static int __init sdio_ctl_init(void) +{ + msm_sdio_ctl_debug_mask = 0; + return platform_driver_register(&sdio_ctl_driver); +} + +module_init(sdio_ctl_init); +MODULE_DESCRIPTION("MSM SDIO Control Port"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/sdio_dmux.c b/arch/arm/mach-msm/sdio_dmux.c new file mode 100644 index 00000000..c6d665d9 --- /dev/null +++ b/arch/arm/mach-msm/sdio_dmux.c @@ -0,0 +1,925 @@ +/* Copyright (c) 2010-2011, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * SDIO DMUX module. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define SDIO_CH_LOCAL_OPEN 0x1 +#define SDIO_CH_REMOTE_OPEN 0x2 +#define SDIO_CH_IN_RESET 0x4 + +#define SDIO_MUX_HDR_MAGIC_NO 0x33fc + +#define SDIO_MUX_HDR_CMD_DATA 0 +#define SDIO_MUX_HDR_CMD_OPEN 1 +#define SDIO_MUX_HDR_CMD_CLOSE 2 + +#define LOW_WATERMARK 2 +#define HIGH_WATERMARK 4 + +static int msm_sdio_dmux_debug_enable; +module_param_named(debug_enable, msm_sdio_dmux_debug_enable, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +#if defined(DEBUG) +static uint32_t sdio_dmux_read_cnt; +static uint32_t sdio_dmux_write_cnt; +static uint32_t sdio_dmux_write_cpy_cnt; +static uint32_t sdio_dmux_write_cpy_bytes; + +#define DBG(x...) do { \ + if (msm_sdio_dmux_debug_enable) \ + pr_debug(x); \ + } while (0) + +#define DBG_INC_READ_CNT(x) do { \ + sdio_dmux_read_cnt += (x); \ + if (msm_sdio_dmux_debug_enable) \ + pr_debug("%s: total read bytes %u\n", \ + __func__, sdio_dmux_read_cnt); \ + } while (0) + +#define DBG_INC_WRITE_CNT(x) do { \ + sdio_dmux_write_cnt += (x); \ + if (msm_sdio_dmux_debug_enable) \ + pr_debug("%s: total written bytes %u\n", \ + __func__, sdio_dmux_write_cnt); \ + } while (0) + +#define DBG_INC_WRITE_CPY(x) do { \ + sdio_dmux_write_cpy_bytes += (x); \ + sdio_dmux_write_cpy_cnt++; \ + if (msm_sdio_dmux_debug_enable) \ + pr_debug("%s: total write copy cnt %u, bytes %u\n", \ + __func__, sdio_dmux_write_cpy_cnt, \ + sdio_dmux_write_cpy_bytes); \ + } while (0) +#else +#define DBG(x...) do { } while (0) +#define DBG_INC_READ_CNT(x...) do { } while (0) +#define DBG_INC_WRITE_CNT(x...) do { } while (0) +#define DBG_INC_WRITE_CPY(x...) do { } while (0) +#endif + +struct sdio_ch_info { + uint32_t status; + void (*receive_cb)(void *, struct sk_buff *); + void (*write_done)(void *, struct sk_buff *); + void *priv; + spinlock_t lock; + int num_tx_pkts; + int use_wm; +}; + +static struct sk_buff_head sdio_mux_write_pool; +static spinlock_t sdio_mux_write_lock; + +static struct sdio_channel *sdio_mux_ch; +static struct sdio_ch_info sdio_ch[SDIO_DMUX_NUM_CHANNELS]; +struct wake_lock sdio_mux_ch_wakelock; +static int sdio_mux_initialized; +static int fatal_error; + +struct sdio_mux_hdr { + uint16_t magic_num; + uint8_t reserved; + uint8_t cmd; + uint8_t pad_len; + uint8_t ch_id; + uint16_t pkt_len; +}; + +struct sdio_partial_pkt_info { + uint32_t valid; + struct sk_buff *skb; + struct sdio_mux_hdr *hdr; +}; + +static void sdio_mux_read_data(struct work_struct *work); +static void sdio_mux_write_data(struct work_struct *work); +static void sdio_mux_send_open_cmd(uint32_t id); + +static DEFINE_MUTEX(sdio_mux_lock); +static DECLARE_WORK(work_sdio_mux_read, sdio_mux_read_data); +static DECLARE_WORK(work_sdio_mux_write, sdio_mux_write_data); +static DECLARE_DELAYED_WORK(delayed_work_sdio_mux_write, sdio_mux_write_data); + +static struct workqueue_struct *sdio_mux_workqueue; +static struct sdio_partial_pkt_info sdio_partial_pkt; + +#define sdio_ch_is_open(x) \ + (sdio_ch[(x)].status == (SDIO_CH_LOCAL_OPEN | SDIO_CH_REMOTE_OPEN)) + +#define sdio_ch_is_local_open(x) \ + (sdio_ch[(x)].status & SDIO_CH_LOCAL_OPEN) + +#define sdio_ch_is_remote_open(x) \ + (sdio_ch[(x)].status & SDIO_CH_REMOTE_OPEN) + +#define sdio_ch_is_in_reset(x) \ + (sdio_ch[(x)].status & SDIO_CH_IN_RESET) + +static inline void skb_set_data(struct sk_buff *skb, + unsigned char *data, + unsigned int len) +{ + /* panic if tail > end */ + skb->data = data; + skb->tail = skb->data + len; + skb->len = len; + skb->truesize = len + sizeof(struct sk_buff); +} + +static void sdio_mux_save_partial_pkt(struct sdio_mux_hdr *hdr, + struct sk_buff *skb_mux) +{ + struct sk_buff *skb; + + /* i think we can avoid cloning here */ + skb = skb_clone(skb_mux, GFP_KERNEL); + if (!skb) { + pr_err("%s: cannot clone skb\n", __func__); + return; + } + + /* protect? */ + skb_set_data(skb, (unsigned char *)hdr, + skb->tail - (unsigned char *)hdr); + sdio_partial_pkt.skb = skb; + sdio_partial_pkt.valid = 1; + DBG("%s: head %p data %p tail %p end %p len %d\n", __func__, + skb->head, skb->data, skb->tail, skb->end, skb->len); + return; +} + +static void *handle_sdio_mux_data(struct sdio_mux_hdr *hdr, + struct sk_buff *skb_mux) +{ + struct sk_buff *skb; + void *rp = (void *)hdr; + unsigned long flags; + + /* protect? */ + rp += sizeof(*hdr); + if (rp < (void *)skb_mux->tail) + rp += (hdr->pkt_len + hdr->pad_len); + + if (rp > (void *)skb_mux->tail) { + /* partial packet */ + sdio_mux_save_partial_pkt(hdr, skb_mux); + goto packet_done; + } + + DBG("%s: hdr %p next %p tail %p pkt_size %d\n", + __func__, hdr, rp, skb_mux->tail, hdr->pkt_len + hdr->pad_len); + + skb = skb_clone(skb_mux, GFP_KERNEL); + if (!skb) { + pr_err("%s: cannot clone skb\n", __func__); + goto packet_done; + } + + skb_set_data(skb, (unsigned char *)(hdr + 1), hdr->pkt_len); + DBG("%s: head %p data %p tail %p end %p len %d\n", + __func__, skb->head, skb->data, skb->tail, skb->end, skb->len); + + /* probably we should check channel status */ + /* discard packet early if local side not open */ + spin_lock_irqsave(&sdio_ch[hdr->ch_id].lock, flags); + if (sdio_ch[hdr->ch_id].receive_cb) + sdio_ch[hdr->ch_id].receive_cb(sdio_ch[hdr->ch_id].priv, skb); + else + dev_kfree_skb_any(skb); + spin_unlock_irqrestore(&sdio_ch[hdr->ch_id].lock, flags); + +packet_done: + return rp; +} + +static void *handle_sdio_mux_command(struct sdio_mux_hdr *hdr, + struct sk_buff *skb_mux) +{ + void *rp; + unsigned long flags; + int send_open = 0; + + DBG("%s: cmd %d ch %d\n", __func__, hdr->cmd, hdr->ch_id); + switch (hdr->cmd) { + case SDIO_MUX_HDR_CMD_DATA: + rp = handle_sdio_mux_data(hdr, skb_mux); + break; + case SDIO_MUX_HDR_CMD_OPEN: + spin_lock_irqsave(&sdio_ch[hdr->ch_id].lock, flags); + sdio_ch[hdr->ch_id].status |= SDIO_CH_REMOTE_OPEN; + sdio_ch[hdr->ch_id].num_tx_pkts = 0; + + if (sdio_ch_is_in_reset(hdr->ch_id)) { + DBG("%s: in reset - sending open cmd\n", __func__); + sdio_ch[hdr->ch_id].status &= ~SDIO_CH_IN_RESET; + send_open = 1; + } + + /* notify client so it can update its status */ + if (sdio_ch[hdr->ch_id].receive_cb) + sdio_ch[hdr->ch_id].receive_cb( + sdio_ch[hdr->ch_id].priv, NULL); + + if (sdio_ch[hdr->ch_id].write_done) + sdio_ch[hdr->ch_id].write_done( + sdio_ch[hdr->ch_id].priv, NULL); + spin_unlock_irqrestore(&sdio_ch[hdr->ch_id].lock, flags); + rp = hdr + 1; + if (send_open) + sdio_mux_send_open_cmd(hdr->ch_id); + + break; + case SDIO_MUX_HDR_CMD_CLOSE: + /* probably should drop pending write */ + spin_lock_irqsave(&sdio_ch[hdr->ch_id].lock, flags); + sdio_ch[hdr->ch_id].status &= ~SDIO_CH_REMOTE_OPEN; + spin_unlock_irqrestore(&sdio_ch[hdr->ch_id].lock, flags); + rp = hdr + 1; + break; + default: + rp = hdr + 1; + } + + return rp; +} + +static void *handle_sdio_partial_pkt(struct sk_buff *skb_mux) +{ + struct sk_buff *p_skb; + struct sdio_mux_hdr *p_hdr; + void *ptr, *rp = skb_mux->data; + + /* protoect? */ + if (sdio_partial_pkt.valid) { + p_skb = sdio_partial_pkt.skb; + + ptr = skb_push(skb_mux, p_skb->len); + memcpy(ptr, p_skb->data, p_skb->len); + sdio_partial_pkt.skb = NULL; + sdio_partial_pkt.valid = 0; + dev_kfree_skb_any(p_skb); + + DBG("%s: head %p data %p tail %p end %p len %d\n", __func__, + skb_mux->head, skb_mux->data, skb_mux->tail, + skb_mux->end, skb_mux->len); + + p_hdr = (struct sdio_mux_hdr *)skb_mux->data; + rp = handle_sdio_mux_command(p_hdr, skb_mux); + } + return rp; +} + +static void sdio_mux_read_data(struct work_struct *work) +{ + struct sk_buff *skb_mux; + void *ptr = 0; + int sz, rc, len = 0; + struct sdio_mux_hdr *hdr; + static int workqueue_pinned; + + if (!workqueue_pinned) { + struct cpumask cpus; + + cpumask_clear(&cpus); + cpumask_set_cpu(0, &cpus); + + if (sched_setaffinity(current->pid, &cpus)) + pr_err("%s: sdio_dmux set CPU affinity failed\n", + __func__); + workqueue_pinned = 1; + } + + DBG("%s: reading\n", __func__); + /* should probably have a separate read lock */ + mutex_lock(&sdio_mux_lock); + sz = sdio_read_avail(sdio_mux_ch); + DBG("%s: read avail %d\n", __func__, sz); + if (sz <= 0) { + if (sz) + pr_err("%s: read avail failed %d\n", __func__, sz); + mutex_unlock(&sdio_mux_lock); + return; + } + + /* net_ip_aling is probably not required */ + if (sdio_partial_pkt.valid) + len = sdio_partial_pkt.skb->len; + + /* If allocation fails attempt to get a smaller chunk of mem */ + do { + skb_mux = __dev_alloc_skb(sz + NET_IP_ALIGN + len, GFP_KERNEL); + if (skb_mux) + break; + + pr_err("%s: cannot allocate skb of size:%d + " + "%d (NET_SKB_PAD)\n", __func__, + sz + NET_IP_ALIGN + len, NET_SKB_PAD); + /* the skb structure adds NET_SKB_PAD bytes to the memory + * request, which may push the actual request above PAGE_SIZE + * in that case, we need to iterate one more time to make sure + * we get the memory request under PAGE_SIZE + */ + if (sz + NET_IP_ALIGN + len + NET_SKB_PAD <= PAGE_SIZE) { + pr_err("%s: allocation failed\n", __func__); + mutex_unlock(&sdio_mux_lock); + return; + } + sz /= 2; + } while (1); + + skb_reserve(skb_mux, NET_IP_ALIGN + len); + ptr = skb_put(skb_mux, sz); + + /* half second wakelock is fine? */ + wake_lock_timeout(&sdio_mux_ch_wakelock, HZ / 2); + rc = sdio_read(sdio_mux_ch, ptr, sz); + DBG("%s: read %d\n", __func__, rc); + if (rc) { + pr_err("%s: sdio read failed %d\n", __func__, rc); + dev_kfree_skb_any(skb_mux); + mutex_unlock(&sdio_mux_lock); + queue_work(sdio_mux_workqueue, &work_sdio_mux_read); + return; + } + mutex_unlock(&sdio_mux_lock); + + DBG_INC_READ_CNT(sz); + DBG("%s: head %p data %p tail %p end %p len %d\n", __func__, + skb_mux->head, skb_mux->data, skb_mux->tail, + skb_mux->end, skb_mux->len); + + /* move to a separate function */ + /* probably do skb_pull instead of pointer adjustment */ + hdr = handle_sdio_partial_pkt(skb_mux); + while ((void *)hdr < (void *)skb_mux->tail) { + + if (((void *)hdr + sizeof(*hdr)) > (void *)skb_mux->tail) { + /* handle partial header */ + sdio_mux_save_partial_pkt(hdr, skb_mux); + break; + } + + if (hdr->magic_num != SDIO_MUX_HDR_MAGIC_NO) { + pr_err("%s: packet error\n", __func__); + break; + } + + hdr = handle_sdio_mux_command(hdr, skb_mux); + } + dev_kfree_skb_any(skb_mux); + + DBG("%s: read done\n", __func__); + queue_work(sdio_mux_workqueue, &work_sdio_mux_read); +} + +static int sdio_mux_write(struct sk_buff *skb) +{ + int rc, sz; + + mutex_lock(&sdio_mux_lock); + sz = sdio_write_avail(sdio_mux_ch); + DBG("%s: avail %d len %d\n", __func__, sz, skb->len); + if (skb->len <= sz) { + rc = sdio_write(sdio_mux_ch, skb->data, skb->len); + DBG("%s: write returned %d\n", __func__, rc); + if (rc == 0) + DBG_INC_WRITE_CNT(skb->len); + } else + rc = -ENOMEM; + + mutex_unlock(&sdio_mux_lock); + return rc; +} + +static int sdio_mux_write_cmd(void *data, uint32_t len) +{ + int avail, rc; + for (;;) { + mutex_lock(&sdio_mux_lock); + avail = sdio_write_avail(sdio_mux_ch); + DBG("%s: avail %d len %d\n", __func__, avail, len); + if (avail >= len) { + rc = sdio_write(sdio_mux_ch, data, len); + DBG("%s: write returned %d\n", __func__, rc); + if (!rc) { + DBG_INC_WRITE_CNT(len); + break; + } + } + mutex_unlock(&sdio_mux_lock); + msleep(250); + } + mutex_unlock(&sdio_mux_lock); + return 0; +} + +static void sdio_mux_send_open_cmd(uint32_t id) +{ + struct sdio_mux_hdr hdr = { + .magic_num = SDIO_MUX_HDR_MAGIC_NO, + .cmd = SDIO_MUX_HDR_CMD_OPEN, + .reserved = 0, + .ch_id = id, + .pkt_len = 0, + .pad_len = 0 + }; + + sdio_mux_write_cmd((void *)&hdr, sizeof(hdr)); +} + +static void sdio_mux_write_data(struct work_struct *work) +{ + int rc, reschedule = 0; + int notify = 0; + struct sk_buff *skb; + unsigned long flags; + int avail; + int ch_id; + + spin_lock_irqsave(&sdio_mux_write_lock, flags); + while ((skb = __skb_dequeue(&sdio_mux_write_pool))) { + ch_id = ((struct sdio_mux_hdr *)skb->data)->ch_id; + + avail = sdio_write_avail(sdio_mux_ch); + if (avail < skb->len) { + /* we may have to wait for write avail + * notification from sdio al + */ + DBG("%s: sdio_write_avail(%d) < skb->len(%d)\n", + __func__, avail, skb->len); + + reschedule = 1; + break; + } + spin_unlock_irqrestore(&sdio_mux_write_lock, flags); + rc = sdio_mux_write(skb); + spin_lock_irqsave(&sdio_mux_write_lock, flags); + if (rc == 0) { + + spin_lock(&sdio_ch[ch_id].lock); + sdio_ch[ch_id].num_tx_pkts--; + spin_unlock(&sdio_ch[ch_id].lock); + + if (sdio_ch[ch_id].write_done) + sdio_ch[ch_id].write_done( + sdio_ch[ch_id].priv, skb); + else + dev_kfree_skb_any(skb); + } else if (rc == -EAGAIN || rc == -ENOMEM) { + /* recoverable error - retry again later */ + reschedule = 1; + break; + } else if (rc == -ENODEV) { + /* + * sdio_al suffered some kind of fatal error + * prevent future writes and clean up pending ones + */ + fatal_error = 1; + do { + ch_id = ((struct sdio_mux_hdr *) + skb->data)->ch_id; + spin_lock(&sdio_ch[ch_id].lock); + sdio_ch[ch_id].num_tx_pkts--; + spin_unlock(&sdio_ch[ch_id].lock); + dev_kfree_skb_any(skb); + } while ((skb = __skb_dequeue(&sdio_mux_write_pool))); + spin_unlock_irqrestore(&sdio_mux_write_lock, flags); + return; + } else { + /* unknown error condition - drop the + * skb and reschedule for the + * other skb's + */ + pr_err("%s: sdio_mux_write error %d" + " for ch %d, skb=%p\n", + __func__, rc, ch_id, skb); + notify = 1; + break; + } + } + + if (reschedule) { + if (sdio_ch_is_in_reset(ch_id)) { + notify = 1; + } else { + __skb_queue_head(&sdio_mux_write_pool, skb); + queue_delayed_work(sdio_mux_workqueue, + &delayed_work_sdio_mux_write, + msecs_to_jiffies(250) + ); + } + } + + if (notify) { + spin_lock(&sdio_ch[ch_id].lock); + sdio_ch[ch_id].num_tx_pkts--; + spin_unlock(&sdio_ch[ch_id].lock); + + if (sdio_ch[ch_id].write_done) + sdio_ch[ch_id].write_done( + sdio_ch[ch_id].priv, skb); + else + dev_kfree_skb_any(skb); + } + spin_unlock_irqrestore(&sdio_mux_write_lock, flags); +} + +int msm_sdio_is_channel_in_reset(uint32_t id) +{ + int rc = 0; + + if (id >= SDIO_DMUX_NUM_CHANNELS) + return -EINVAL; + + if (sdio_ch_is_in_reset(id)) + rc = 1; + + return rc; +} + +int msm_sdio_dmux_write(uint32_t id, struct sk_buff *skb) +{ + int rc = 0; + struct sdio_mux_hdr *hdr; + unsigned long flags; + struct sk_buff *new_skb; + + if (id >= SDIO_DMUX_NUM_CHANNELS) + return -EINVAL; + if (!skb) + return -EINVAL; + if (!sdio_mux_initialized) + return -ENODEV; + if (fatal_error) + return -ENODEV; + + DBG("%s: writing to ch %d len %d\n", __func__, id, skb->len); + spin_lock_irqsave(&sdio_ch[id].lock, flags); + if (sdio_ch_is_in_reset(id)) { + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + pr_err("%s: port is in reset: %d\n", __func__, + sdio_ch[id].status); + return -ENETRESET; + } + if (!sdio_ch_is_local_open(id)) { + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + pr_err("%s: port not open: %d\n", __func__, sdio_ch[id].status); + return -ENODEV; + } + if (sdio_ch[id].use_wm && + (sdio_ch[id].num_tx_pkts >= HIGH_WATERMARK)) { + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + pr_err("%s: watermark exceeded: %d\n", __func__, id); + return -EAGAIN; + } + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + + spin_lock_irqsave(&sdio_mux_write_lock, flags); + /* if skb do not have any tailroom for padding, + copy the skb into a new expanded skb */ + if ((skb->len & 0x3) && (skb_tailroom(skb) < (4 - (skb->len & 0x3)))) { + /* revisit, probably dev_alloc_skb and memcpy is effecient */ + new_skb = skb_copy_expand(skb, skb_headroom(skb), + 4 - (skb->len & 0x3), GFP_ATOMIC); + if (new_skb == NULL) { + pr_err("%s: cannot allocate skb\n", __func__); + rc = -ENOMEM; + goto write_done; + } + dev_kfree_skb_any(skb); + skb = new_skb; + DBG_INC_WRITE_CPY(skb->len); + } + + hdr = (struct sdio_mux_hdr *)skb_push(skb, sizeof(struct sdio_mux_hdr)); + + /* caller should allocate for hdr and padding + hdr is fine, padding is tricky */ + hdr->magic_num = SDIO_MUX_HDR_MAGIC_NO; + hdr->cmd = SDIO_MUX_HDR_CMD_DATA; + hdr->reserved = 0; + hdr->ch_id = id; + hdr->pkt_len = skb->len - sizeof(struct sdio_mux_hdr); + if (skb->len & 0x3) + skb_put(skb, 4 - (skb->len & 0x3)); + + hdr->pad_len = skb->len - (sizeof(struct sdio_mux_hdr) + hdr->pkt_len); + + DBG("%s: data %p, tail %p skb len %d pkt len %d pad len %d\n", + __func__, skb->data, skb->tail, skb->len, + hdr->pkt_len, hdr->pad_len); + __skb_queue_tail(&sdio_mux_write_pool, skb); + + spin_lock(&sdio_ch[id].lock); + sdio_ch[id].num_tx_pkts++; + spin_unlock(&sdio_ch[id].lock); + + queue_work(sdio_mux_workqueue, &work_sdio_mux_write); + +write_done: + spin_unlock_irqrestore(&sdio_mux_write_lock, flags); + return rc; +} + +int msm_sdio_dmux_open(uint32_t id, void *priv, + void (*receive_cb)(void *, struct sk_buff *), + void (*write_done)(void *, struct sk_buff *)) +{ + unsigned long flags; + + DBG("%s: opening ch %d\n", __func__, id); + if (!sdio_mux_initialized) + return -ENODEV; + if (id >= SDIO_DMUX_NUM_CHANNELS) + return -EINVAL; + + spin_lock_irqsave(&sdio_ch[id].lock, flags); + if (sdio_ch_is_local_open(id)) { + pr_info("%s: Already opened %d\n", __func__, id); + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + goto open_done; + } + + sdio_ch[id].receive_cb = receive_cb; + sdio_ch[id].write_done = write_done; + sdio_ch[id].priv = priv; + sdio_ch[id].status |= SDIO_CH_LOCAL_OPEN; + sdio_ch[id].num_tx_pkts = 0; + sdio_ch[id].use_wm = 0; + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + + sdio_mux_send_open_cmd(id); + +open_done: + pr_info("%s: opened ch %d\n", __func__, id); + return 0; +} + +int msm_sdio_dmux_close(uint32_t id) +{ + struct sdio_mux_hdr hdr; + unsigned long flags; + + if (id >= SDIO_DMUX_NUM_CHANNELS) + return -EINVAL; + DBG("%s: closing ch %d\n", __func__, id); + if (!sdio_mux_initialized) + return -ENODEV; + spin_lock_irqsave(&sdio_ch[id].lock, flags); + + sdio_ch[id].receive_cb = NULL; + sdio_ch[id].priv = NULL; + sdio_ch[id].status &= ~SDIO_CH_LOCAL_OPEN; + sdio_ch[id].status &= ~SDIO_CH_IN_RESET; + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + + hdr.magic_num = SDIO_MUX_HDR_MAGIC_NO; + hdr.cmd = SDIO_MUX_HDR_CMD_CLOSE; + hdr.reserved = 0; + hdr.ch_id = id; + hdr.pkt_len = 0; + hdr.pad_len = 0; + + sdio_mux_write_cmd((void *)&hdr, sizeof(hdr)); + + pr_info("%s: closed ch %d\n", __func__, id); + return 0; +} + +static void sdio_mux_notify(void *_dev, unsigned event) +{ + DBG("%s: event %d notified\n", __func__, event); + + /* write avail may not be enouogh for a packet, but should be fine */ + if ((event == SDIO_EVENT_DATA_WRITE_AVAIL) && + sdio_write_avail(sdio_mux_ch)) + queue_work(sdio_mux_workqueue, &work_sdio_mux_write); + + if ((event == SDIO_EVENT_DATA_READ_AVAIL) && + sdio_read_avail(sdio_mux_ch)) + queue_work(sdio_mux_workqueue, &work_sdio_mux_read); +} + +int msm_sdio_dmux_is_ch_full(uint32_t id) +{ + unsigned long flags; + int ret; + + if (id >= SDIO_DMUX_NUM_CHANNELS) + return -EINVAL; + + spin_lock_irqsave(&sdio_ch[id].lock, flags); + sdio_ch[id].use_wm = 1; + ret = sdio_ch[id].num_tx_pkts >= HIGH_WATERMARK; + DBG("%s: ch %d num tx pkts=%d, HWM=%d\n", __func__, + id, sdio_ch[id].num_tx_pkts, ret); + if (!sdio_ch_is_local_open(id)) { + ret = -ENODEV; + pr_err("%s: port not open: %d\n", __func__, sdio_ch[id].status); + } + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + + return ret; +} + +int msm_sdio_dmux_is_ch_low(uint32_t id) +{ + int ret; + + if (id >= SDIO_DMUX_NUM_CHANNELS) + return -EINVAL; + + sdio_ch[id].use_wm = 1; + ret = sdio_ch[id].num_tx_pkts <= LOW_WATERMARK; + DBG("%s: ch %d num tx pkts=%d, LWM=%d\n", __func__, + id, sdio_ch[id].num_tx_pkts, ret); + if (!sdio_ch_is_local_open(id)) { + ret = -ENODEV; + pr_err("%s: port not open: %d\n", __func__, sdio_ch[id].status); + } + + return ret; +} + +#ifdef CONFIG_DEBUG_FS + +static int debug_tbl(char *buf, int max) +{ + int i = 0; + int j; + + for (j = 0; j < SDIO_DMUX_NUM_CHANNELS; ++j) { + i += scnprintf(buf + i, max - i, + "ch%02d local open=%s remote open=%s\n", + j, sdio_ch_is_local_open(j) ? "Y" : "N", + sdio_ch_is_remote_open(j) ? "Y" : "N"); + } + + return i; +} + +#define DEBUG_BUFMAX 4096 +static char debug_buffer[DEBUG_BUFMAX]; + +static ssize_t debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int (*fill)(char *buf, int max) = file->private_data; + int bsize = fill(debug_buffer, DEBUG_BUFMAX); + return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize); +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + + +static const struct file_operations debug_ops = { + .read = debug_read, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + int (*fill)(char *buf, int max)) +{ + debugfs_create_file(name, mode, dent, fill, &debug_ops); +} + +#endif + +static int sdio_dmux_probe(struct platform_device *pdev) +{ + int rc; + + DBG("%s probe called\n", __func__); + + if (!sdio_mux_initialized) { + sdio_mux_workqueue = create_singlethread_workqueue("sdio_dmux"); + if (!sdio_mux_workqueue) + return -ENOMEM; + + skb_queue_head_init(&sdio_mux_write_pool); + spin_lock_init(&sdio_mux_write_lock); + + for (rc = 0; rc < SDIO_DMUX_NUM_CHANNELS; ++rc) + spin_lock_init(&sdio_ch[rc].lock); + + + wake_lock_init(&sdio_mux_ch_wakelock, WAKE_LOCK_SUSPEND, + "sdio_dmux"); + } + + rc = sdio_open("SDIO_RMNT", &sdio_mux_ch, NULL, sdio_mux_notify); + if (rc < 0) { + pr_err("%s: sido open failed %d\n", __func__, rc); + wake_lock_destroy(&sdio_mux_ch_wakelock); + destroy_workqueue(sdio_mux_workqueue); + sdio_mux_initialized = 0; + return rc; + } + + fatal_error = 0; + sdio_mux_initialized = 1; + return 0; +} + +static int sdio_dmux_remove(struct platform_device *pdev) +{ + int i; + unsigned long ch_lock_flags; + unsigned long write_lock_flags; + struct sk_buff *skb; + + DBG("%s remove called\n", __func__); + if (!sdio_mux_initialized) + return 0; + + /* set reset state for any open channels */ + for (i = 0; i < SDIO_DMUX_NUM_CHANNELS; ++i) { + spin_lock_irqsave(&sdio_ch[i].lock, ch_lock_flags); + if (sdio_ch_is_open(i)) { + sdio_ch[i].status |= SDIO_CH_IN_RESET; + sdio_ch[i].status &= ~SDIO_CH_REMOTE_OPEN; + + /* notify client so it can update its status */ + if (sdio_ch[i].receive_cb) + sdio_ch[i].receive_cb( + sdio_ch[i].priv, NULL); + } + spin_unlock_irqrestore(&sdio_ch[i].lock, ch_lock_flags); + } + + /* cancel any pending writes */ + spin_lock_irqsave(&sdio_mux_write_lock, write_lock_flags); + while ((skb = __skb_dequeue(&sdio_mux_write_pool))) { + i = ((struct sdio_mux_hdr *)skb->data)->ch_id; + if (sdio_ch[i].write_done) + sdio_ch[i].write_done( + sdio_ch[i].priv, skb); + else + dev_kfree_skb_any(skb); + } + spin_unlock_irqrestore(&sdio_mux_write_lock, + write_lock_flags); + + return 0; +} + +static struct platform_driver sdio_dmux_driver = { + .probe = sdio_dmux_probe, + .remove = sdio_dmux_remove, + .driver = { + .name = "SDIO_RMNT", + .owner = THIS_MODULE, + }, +}; + +static int __init sdio_dmux_init(void) +{ +#ifdef CONFIG_DEBUG_FS + struct dentry *dent; + + dent = debugfs_create_dir("sdio_dmux", 0); + if (!IS_ERR(dent)) + debug_create("tbl", 0444, dent, debug_tbl); +#endif + return platform_driver_register(&sdio_dmux_driver); +} + +module_init(sdio_dmux_init); +MODULE_DESCRIPTION("MSM SDIO DMUX"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/sdio_smem.c b/arch/arm/mach-msm/sdio_smem.c new file mode 100644 index 00000000..edc0d23e --- /dev/null +++ b/arch/arm/mach-msm/sdio_smem.c @@ -0,0 +1,175 @@ +/* Copyright (c) 2010-2011, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include + +static void sdio_smem_read(struct work_struct *work); + +static struct sdio_channel *channel; +static struct workqueue_struct *workq; +static DECLARE_WORK(work_read, sdio_smem_read); +static DECLARE_WAIT_QUEUE_HEAD(waitq); +static int bytes_avail; +static int sdio_ch_opened; + +static void sdio_smem_release(struct device *dev) +{ + pr_debug("sdio smem released\n"); +} + +static struct sdio_smem_client client; + +static void sdio_smem_read(struct work_struct *work) +{ + int err; + int read_avail; + char *data = client.buf; + + if (!sdio_ch_opened) + return; + + read_avail = sdio_read_avail(channel); + if (read_avail > bytes_avail || + read_avail < 0) { + pr_err("Error: read_avail=%d bytes_avail=%d\n", + read_avail, bytes_avail); + goto read_err; + } + + if (read_avail == 0) + return; + + err = sdio_read(channel, + &data[client.size - bytes_avail], + read_avail); + if (err) { + pr_err("sdio_read error (%d)", err); + goto read_err; + } + + bytes_avail -= read_avail; + pr_debug("read %d bytes (bytes_avail = %d)\n", + read_avail, bytes_avail); + + if (!bytes_avail) { + bytes_avail = client.size; + err = client.cb_func(SDIO_SMEM_EVENT_READ_DONE); + } + if (err) + pr_err("error (%d) on callback\n", err); + + return; + +read_err: + if (sdio_ch_opened) + client.cb_func(SDIO_SMEM_EVENT_READ_ERR); + return; +} + +static void sdio_smem_notify(void *priv, unsigned event) +{ + pr_debug("%d event received\n", event); + + if (event == SDIO_EVENT_DATA_READ_AVAIL || + event == SDIO_EVENT_DATA_WRITE_AVAIL) + queue_work(workq, &work_read); +} + +int sdio_smem_register_client(void) +{ + int err = 0; + + if (!client.buf || !client.size || !client.cb_func) + return -EINVAL; + + pr_debug("buf = %p\n", client.buf); + pr_debug("size = 0x%x\n", client.size); + + bytes_avail = client.size; + workq = create_singlethread_workqueue("sdio_smem"); + if (!workq) + return -ENOMEM; + + sdio_ch_opened = 1; + err = sdio_open("SDIO_SMEM", &channel, NULL, sdio_smem_notify); + if (err) { + sdio_ch_opened = 0; + pr_err("sdio_open error (%d)\n", err); + destroy_workqueue(workq); + return err; + } + pr_debug("SDIO SMEM channel opened\n"); + return err; +} + +int sdio_smem_unregister_client(void) +{ + int err = 0; + + sdio_ch_opened = 0; + err = sdio_close(channel); + if (err) { + pr_err("sdio_close error (%d)\n", err); + return err; + } + pr_debug("SDIO SMEM channel closed\n"); + flush_workqueue(workq); + destroy_workqueue(workq); + bytes_avail = 0; + client.buf = NULL; + client.cb_func = NULL; + client.size = 0; + + return 0; +} + +static int sdio_smem_probe(struct platform_device *pdev) +{ + client.plat_dev.name = "SDIO_SMEM_CLIENT"; + client.plat_dev.id = -1; + client.plat_dev.dev.release = sdio_smem_release; + + return platform_device_register(&client.plat_dev); +} + +static int sdio_smem_remove(struct platform_device *pdev) +{ + platform_device_unregister(&client.plat_dev); + memset(&client, 0, sizeof(client)); + sdio_ch_opened = 0; + return 0; +} +static struct platform_driver sdio_smem_drv = { + .probe = sdio_smem_probe, + .remove = sdio_smem_remove, + .driver = { + .name = "SDIO_SMEM", + .owner = THIS_MODULE, + }, +}; + +static int __init sdio_smem_init(void) +{ + return platform_driver_register(&sdio_smem_drv); +}; + +module_init(sdio_smem_init); + +MODULE_DESCRIPTION("SDIO SMEM"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/sdio_tty.c b/arch/arm/mach-msm/sdio_tty.c new file mode 100644 index 00000000..c4b76733 --- /dev/null +++ b/arch/arm/mach-msm/sdio_tty.c @@ -0,0 +1,824 @@ +/* Copyright (c) 2011, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define INPUT_SPEED 4800 +#define OUTPUT_SPEED 4800 +#define SDIO_TTY_MODULE_NAME "sdio_tty" +#define SDIO_TTY_MAX_PACKET_SIZE 4096 +#define MAX_SDIO_TTY_DRV 1 +#define MAX_SDIO_TTY_DEVS 2 +#define MAX_SDIO_TTY_DEV_NAME_SIZE 25 + +/* Configurations per channel device */ +/* CSVT */ +#define SDIO_TTY_CSVT_DEV "sdio_tty_csvt_0" +#define SDIO_TTY_CSVT_TEST_DEV "sdio_tty_csvt_test_0" +#define SDIO_TTY_CH_CSVT "SDIO_CSVT" + +enum sdio_tty_state { + TTY_INITIAL = 0, + TTY_REGISTERED = 1, + TTY_OPENED = 2, + TTY_CLOSED = 3, +}; + +enum sdio_tty_devices { + SDIO_CSVT, + SDIO_CSVT_TEST_APP, +}; + +static const struct platform_device_id sdio_tty_id_table[] = { + { "SDIO_CSVT", SDIO_CSVT }, + { "SDIO_CSVT_TEST_APP", SDIO_CSVT_TEST_APP }, + { }, +}; +MODULE_DEVICE_TABLE(platform, sdio_tty_id_table); + +struct sdio_tty { + struct sdio_channel *ch; + char *sdio_ch_name; + char tty_dev_name[MAX_SDIO_TTY_DEV_NAME_SIZE]; + int device_id; + struct workqueue_struct *workq; + struct work_struct work_read; + wait_queue_head_t waitq; + struct tty_driver *tty_drv; + struct tty_struct *tty_str; + int debug_msg_on; + char *read_buf; + enum sdio_tty_state sdio_tty_state; + int is_sdio_open; + int tty_open_count; + int total_rx; + int total_tx; +}; + +static struct sdio_tty *sdio_tty[MAX_SDIO_TTY_DEVS]; + +#ifdef CONFIG_DEBUG_FS +struct dentry *sdio_tty_debug_root; +struct dentry *sdio_tty_debug_info; +#endif + +#define DEBUG_MSG(sdio_tty_drv, x...) if (sdio_tty_drv->debug_msg_on) pr_info(x) + +/* + * Enable sdio_tty debug messages + * By default the sdio_tty debug messages are turned off + */ +static int csvt_debug_msg_on; +module_param(csvt_debug_msg_on, int, 0); + +static void sdio_tty_read(struct work_struct *work) +{ + int ret = 0; + int read_avail = 0; + int left = 0; + int total_push = 0; + int num_push = 0; + struct sdio_tty *sdio_tty_drv = NULL; + + sdio_tty_drv = container_of(work, struct sdio_tty, work_read); + + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty", __func__); + return ; + } + + if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d", + __func__, sdio_tty_drv->sdio_tty_state); + return; + } + + if (!sdio_tty_drv->read_buf) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL read_buf for dev %s", + __func__, sdio_tty_drv->tty_dev_name); + return; + } + + /* Read the data from the SDIO channel as long as there is available + data */ + while (1) { + if (test_bit(TTY_THROTTLED, &sdio_tty_drv->tty_str->flags)) { + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME + ": %s: TTY_THROTTLED bit is set for " + "dev %s, exit", __func__, + sdio_tty_drv->tty_dev_name); + return; + } + + total_push = 0; + read_avail = sdio_read_avail(sdio_tty_drv->ch); + + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME + ": %s: read_avail is %d for dev %s", __func__, + read_avail, sdio_tty_drv->tty_dev_name); + + if (read_avail == 0) { + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME + ": %s: read_avail is 0 for dev %s", + __func__, sdio_tty_drv->tty_dev_name); + return; + } + + if (read_avail > SDIO_TTY_MAX_PACKET_SIZE) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: read_avail(%d) is " + "bigger than SDIO_TTY_MAX_PACKET_SIZE(%d) " + "for dev %s", __func__, read_avail, + SDIO_TTY_MAX_PACKET_SIZE, + sdio_tty_drv->tty_dev_name); + return; + } + + ret = sdio_read(sdio_tty_drv->ch, + sdio_tty_drv->read_buf, + read_avail); + if (ret < 0) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_read error(%d) " + "for dev %s", __func__, ret, + sdio_tty_drv->tty_dev_name); + return; + } + + left = read_avail; + do { + num_push = tty_insert_flip_string( + sdio_tty_drv->tty_str, + sdio_tty_drv->read_buf+total_push, + left); + total_push += num_push; + left -= num_push; + tty_flip_buffer_push(sdio_tty_drv->tty_str); + } while (left != 0); + + if (total_push != read_avail) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: failed, total_push" + "(%d) != read_avail(%d) for dev %s\n", + __func__, total_push, read_avail, + sdio_tty_drv->tty_dev_name); + } + + tty_flip_buffer_push(sdio_tty_drv->tty_str); + sdio_tty_drv->total_rx += read_avail; + + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Rx: %d, " + "Total Rx = %d bytes for dev %s", __func__, + read_avail, sdio_tty_drv->total_rx, + sdio_tty_drv->tty_dev_name); + } +} + +/** + * sdio_tty_write_room + * + * This is the write_room function of the tty driver. + * + * @tty: pointer to tty struct. + * @return free bytes for write. + * + */ +static int sdio_tty_write_room(struct tty_struct *tty) +{ + int write_avail = 0; + struct sdio_tty *sdio_tty_drv = NULL; + + if (!tty) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__); + return -ENODEV; + } + sdio_tty_drv = tty->driver_data; + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + return -ENODEV; + } + + if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d", + __func__, sdio_tty_drv->sdio_tty_state); + return -EPERM; + } + + write_avail = sdio_write_avail(sdio_tty_drv->ch); + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: write_avail=%d " + "for dev %s", __func__, write_avail, + sdio_tty_drv->tty_dev_name); + + return write_avail; +} + +/** + * sdio_tty_write_callback + * this is the write callback of the tty driver. + * + * @tty: pointer to tty struct. + * @buf: buffer to write from. + * @count: number of bytes to write. + * @return bytes written or negative value on error. + * + * if destination buffer has not enough room for the incoming + * data, writes the possible amount of bytes . + */ +static int sdio_tty_write_callback(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + int write_avail = 0; + int len = count; + int ret = 0; + struct sdio_tty *sdio_tty_drv = NULL; + + if (!tty) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__); + return -ENODEV; + } + sdio_tty_drv = tty->driver_data; + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + return -ENODEV; + } + + if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d", + __func__, sdio_tty_drv->sdio_tty_state); + return -EPERM; + } + + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Write Callback " + "called with %d bytes for dev %s\n", __func__, count, + sdio_tty_drv->tty_dev_name); + write_avail = sdio_write_avail(sdio_tty_drv->ch); + if (write_avail == 0) { + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: " + "write_avail is 0 for dev %s\n", + __func__, sdio_tty_drv->tty_dev_name); + return 0; + } + if (write_avail > SDIO_TTY_MAX_PACKET_SIZE) { + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: " + "write_avail(%d) is bigger than max packet " + "size(%d) for dev %s, setting to " + "max_packet_size\n", __func__, write_avail, + SDIO_TTY_MAX_PACKET_SIZE, + sdio_tty_drv->tty_dev_name); + write_avail = SDIO_TTY_MAX_PACKET_SIZE; + } + if (write_avail < count) { + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: " + "write_avail(%d) is smaller than required(%d) " + "for dev %s, writing only %d bytes\n", + __func__, write_avail, count, + sdio_tty_drv->tty_dev_name, write_avail); + len = write_avail; + } + ret = sdio_write(sdio_tty_drv->ch, buf, len); + if (ret) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_write failed for " + "dev %s, ret=%d\n", __func__, + sdio_tty_drv->tty_dev_name, ret); + return 0; + } + + sdio_tty_drv->total_tx += len; + + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Tx: %d, " + "Total Tx = %d for dev %s", __func__, len, + sdio_tty_drv->total_tx, sdio_tty_drv->tty_dev_name); + return len; +} + +static void sdio_tty_notify(void *priv, unsigned event) +{ + struct sdio_tty *sdio_tty_drv = priv; + + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + } + + if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d", + __func__, sdio_tty_drv->sdio_tty_state); + return; + } + + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: event %d " + "received for dev %s\n", __func__, event, + sdio_tty_drv->tty_dev_name); + + if (event == SDIO_EVENT_DATA_READ_AVAIL) + queue_work(sdio_tty_drv->workq, &sdio_tty_drv->work_read); +} + +/** + * sdio_tty_open + * This is the open callback of the tty driver. it opens + * the sdio channel, and creates the workqueue. + * + * @tty: a pointer to the tty struct. + * @file: file descriptor. + * @return 0 on success or negative value on error. + */ +static int sdio_tty_open(struct tty_struct *tty, struct file *file) +{ + int ret = 0; + int i = 0; + struct sdio_tty *sdio_tty_drv = NULL; + + if (!tty) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__); + return -ENODEV; + } + + for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) { + if (sdio_tty[i] == NULL) + continue; + if (!strncmp(sdio_tty[i]->tty_dev_name, tty->name, + MAX_SDIO_TTY_DEV_NAME_SIZE)) { + sdio_tty_drv = sdio_tty[i]; + break; + } + } + + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + return -ENODEV; + } + + sdio_tty_drv->tty_open_count++; + if (sdio_tty_drv->sdio_tty_state == TTY_OPENED) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: tty dev(%s) is already open", + __func__, sdio_tty_drv->tty_dev_name); + return -EBUSY; + } + + tty->driver_data = sdio_tty_drv; + + sdio_tty_drv->tty_str = tty; + sdio_tty_drv->tty_str->low_latency = 1; + sdio_tty_drv->tty_str->icanon = 0; + set_bit(TTY_NO_WRITE_SPLIT, &sdio_tty_drv->tty_str->flags); + + sdio_tty_drv->read_buf = kzalloc(SDIO_TTY_MAX_PACKET_SIZE, GFP_KERNEL); + if (sdio_tty_drv->read_buf == NULL) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to allocate read_buf " + "for dev %s", __func__, sdio_tty_drv->tty_dev_name); + return -ENOMEM; + } + + sdio_tty_drv->workq = create_singlethread_workqueue("sdio_tty_read"); + if (!sdio_tty_drv->workq) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to create workq " + "for dev %s", __func__, sdio_tty_drv->tty_dev_name); + return -ENOMEM; + } + + if (!sdio_tty_drv->is_sdio_open) { + ret = sdio_open(sdio_tty_drv->sdio_ch_name, &sdio_tty_drv->ch, + sdio_tty_drv, sdio_tty_notify); + if (ret < 0) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_open err=%d " + "for dev %s\n", __func__, ret, + sdio_tty_drv->tty_dev_name); + destroy_workqueue(sdio_tty_drv->workq); + return ret; + } + + pr_info(SDIO_TTY_MODULE_NAME ": %s: SDIO_TTY channel(%s) " + "opened\n", __func__, sdio_tty_drv->sdio_ch_name); + + sdio_tty_drv->is_sdio_open = 1; + } else { + /* If SDIO channel is already open try to read the data + * from the modem + */ + queue_work(sdio_tty_drv->workq, &sdio_tty_drv->work_read); + + } + + sdio_tty_drv->sdio_tty_state = TTY_OPENED; + + pr_info(SDIO_TTY_MODULE_NAME ": %s: TTY device(%s) opened\n", + __func__, sdio_tty_drv->tty_dev_name); + + return ret; +} + +/** + * sdio_tty_close + * This is the close callback of the tty driver. it requests + * the main thread to exit, and waits for notification of it. + * it also de-allocates the buffers, and unregisters the tty + * driver and device. + * + * @tty: a pointer to the tty struct. + * @file: file descriptor. + * @return None. + */ +static void sdio_tty_close(struct tty_struct *tty, struct file *file) +{ + struct sdio_tty *sdio_tty_drv = NULL; + + if (!tty) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__); + return; + } + sdio_tty_drv = tty->driver_data; + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + return; + } + if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: trying to close a " + "TTY device that was not opened\n", __func__); + return; + } + if (--sdio_tty_drv->tty_open_count != 0) + return; + + flush_workqueue(sdio_tty_drv->workq); + destroy_workqueue(sdio_tty_drv->workq); + + kfree(sdio_tty_drv->read_buf); + sdio_tty_drv->read_buf = NULL; + + sdio_tty_drv->sdio_tty_state = TTY_CLOSED; + + pr_info(SDIO_TTY_MODULE_NAME ": %s: SDIO_TTY device(%s) closed\n", + __func__, sdio_tty_drv->tty_dev_name); +} + +static void sdio_tty_unthrottle(struct tty_struct *tty) +{ + struct sdio_tty *sdio_tty_drv = NULL; + + if (!tty) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__); + return; + } + sdio_tty_drv = tty->driver_data; + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + return; + } + + if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d", + __func__, sdio_tty_drv->sdio_tty_state); + return; + } + + queue_work(sdio_tty_drv->workq, &sdio_tty_drv->work_read); + return; +} + +static const struct tty_operations sdio_tty_ops = { + .open = sdio_tty_open, + .close = sdio_tty_close, + .write = sdio_tty_write_callback, + .write_room = sdio_tty_write_room, + .unthrottle = sdio_tty_unthrottle, +}; + +int sdio_tty_init_tty(char *tty_name, char *sdio_ch_name, + enum sdio_tty_devices device_id, int debug_msg_on) +{ + int ret = 0; + int i = 0; + struct device *tty_dev = NULL; + struct sdio_tty *sdio_tty_drv = NULL; + + sdio_tty_drv = kzalloc(sizeof(struct sdio_tty), GFP_KERNEL); + if (sdio_tty_drv == NULL) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to allocate sdio_tty " + "for dev %s", __func__, tty_name); + return -ENOMEM; + } + + for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) { + if (sdio_tty[i] == NULL) { + sdio_tty[i] = sdio_tty_drv; + break; + } + } + + if (i == MAX_SDIO_TTY_DEVS) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: tty dev(%s) creation failed," + " max limit(%d) reached.", __func__, tty_name, + MAX_SDIO_TTY_DEVS); + kfree(sdio_tty_drv); + return -ENODEV; + } + + snprintf(sdio_tty_drv->tty_dev_name, MAX_SDIO_TTY_DEV_NAME_SIZE, + "%s%d", tty_name, 0); + sdio_tty_drv->sdio_ch_name = sdio_ch_name; + sdio_tty_drv->device_id = device_id; + pr_info(SDIO_TTY_MODULE_NAME ": %s: dev=%s, id=%d, channel=%s\n", + __func__, sdio_tty_drv->tty_dev_name, sdio_tty_drv->device_id, + sdio_tty_drv->sdio_ch_name); + + INIT_WORK(&sdio_tty_drv->work_read, sdio_tty_read); + + sdio_tty_drv->tty_drv = alloc_tty_driver(MAX_SDIO_TTY_DRV); + + if (!sdio_tty_drv->tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s - tty_drv is NULL for dev %s", + __func__, sdio_tty_drv->tty_dev_name); + kfree(sdio_tty_drv); + return -ENODEV; + } + + sdio_tty_drv->tty_drv->name = tty_name; + sdio_tty_drv->tty_drv->owner = THIS_MODULE; + sdio_tty_drv->tty_drv->driver_name = "SDIO_tty"; + /* uses dynamically assigned dev_t values */ + sdio_tty_drv->tty_drv->type = TTY_DRIVER_TYPE_SERIAL; + sdio_tty_drv->tty_drv->subtype = SERIAL_TYPE_NORMAL; + sdio_tty_drv->tty_drv->flags = TTY_DRIVER_REAL_RAW + | TTY_DRIVER_DYNAMIC_DEV + | TTY_DRIVER_RESET_TERMIOS; + + /* initializing the tty driver */ + sdio_tty_drv->tty_drv->init_termios = tty_std_termios; + sdio_tty_drv->tty_drv->init_termios.c_cflag = + B4800 | CS8 | CREAD | HUPCL | CLOCAL; + sdio_tty_drv->tty_drv->init_termios.c_ispeed = INPUT_SPEED; + sdio_tty_drv->tty_drv->init_termios.c_ospeed = OUTPUT_SPEED; + + tty_set_operations(sdio_tty_drv->tty_drv, &sdio_tty_ops); + + ret = tty_register_driver(sdio_tty_drv->tty_drv); + if (ret) { + put_tty_driver(sdio_tty_drv->tty_drv); + pr_err(SDIO_TTY_MODULE_NAME ": %s: tty_register_driver() " + "failed for dev %s\n", __func__, + sdio_tty_drv->tty_dev_name); + + sdio_tty_drv->tty_drv = NULL; + kfree(sdio_tty_drv); + return -ENODEV; + } + + tty_dev = tty_register_device(sdio_tty_drv->tty_drv, 0, NULL); + if (IS_ERR(tty_dev)) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: tty_register_device() " + "failed for dev %s\n", __func__, + sdio_tty_drv->tty_dev_name); + tty_unregister_driver(sdio_tty_drv->tty_drv); + put_tty_driver(sdio_tty_drv->tty_drv); + kfree(sdio_tty_drv); + return -ENODEV; + } + + sdio_tty_drv->sdio_tty_state = TTY_REGISTERED; + if (debug_msg_on) { + pr_info(SDIO_TTY_MODULE_NAME ": %s: turn on debug msg for %s", + __func__, sdio_tty_drv->tty_dev_name); + sdio_tty_drv->debug_msg_on = debug_msg_on; + } + return 0; +} + +int sdio_tty_uninit_tty(void *sdio_tty_handle) +{ + int ret = 0; + int i = 0; + struct sdio_tty *sdio_tty_drv = sdio_tty_handle; + + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + return -ENODEV; + } + if (sdio_tty_drv->sdio_tty_state == TTY_OPENED) { + flush_workqueue(sdio_tty_drv->workq); + destroy_workqueue(sdio_tty_drv->workq); + + kfree(sdio_tty_drv->read_buf); + sdio_tty_drv->read_buf = NULL; + } + + if (sdio_tty_drv->sdio_tty_state != TTY_INITIAL) { + tty_unregister_device(sdio_tty_drv->tty_drv, 0); + + ret = tty_unregister_driver(sdio_tty_drv->tty_drv); + if (ret) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: " + "tty_unregister_driver() failed for dev %s\n", + __func__, sdio_tty_drv->tty_dev_name); + } + put_tty_driver(sdio_tty_drv->tty_drv); + sdio_tty_drv->sdio_tty_state = TTY_INITIAL; + sdio_tty_drv->tty_drv = NULL; + } + + for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) { + if (sdio_tty[i] == NULL) + continue; + if (sdio_tty[i]->device_id == sdio_tty_drv->device_id) { + sdio_tty[i] = NULL; + break; + } + } + + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Freeing sdio_tty " + "structure, dev=%s", __func__, + sdio_tty_drv->tty_dev_name); + kfree(sdio_tty_drv); + + return 0; +} + +static int sdio_tty_probe(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + enum sdio_tty_devices device_id = id->driver_data; + char *device_name = NULL; + char *channel_name = NULL; + int debug_msg_on = 0; + int ret = 0; + + pr_debug(SDIO_TTY_MODULE_NAME ": %s for %s", __func__, pdev->name); + + switch (device_id) { + case SDIO_CSVT: + device_name = SDIO_TTY_CSVT_DEV; + channel_name = SDIO_TTY_CH_CSVT; + debug_msg_on = csvt_debug_msg_on; + break; + case SDIO_CSVT_TEST_APP: + device_name = SDIO_TTY_CSVT_TEST_DEV; + channel_name = SDIO_TTY_CH_CSVT; + debug_msg_on = csvt_debug_msg_on; + break; + default: + pr_err(SDIO_TTY_MODULE_NAME ": %s Invalid device:%s, id:%d", + __func__, pdev->name, device_id); + ret = -ENODEV; + break; + } + + if (device_name) { + ret = sdio_tty_init_tty(device_name, channel_name, + device_id, debug_msg_on); + if (ret) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_init_tty " + "failed for dev:%s", __func__, device_name); + } + } + return ret; +} + +static int sdio_tty_remove(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + enum sdio_tty_devices device_id = id->driver_data; + struct sdio_tty *sdio_tty_drv = NULL; + int i = 0; + int ret = 0; + + pr_debug(SDIO_TTY_MODULE_NAME ": %s for %s", __func__, pdev->name); + + for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) { + if (sdio_tty[i] == NULL) + continue; + if (sdio_tty[i]->device_id == device_id) { + sdio_tty_drv = sdio_tty[i]; + break; + } + } + + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + return -ENODEV; + } + + ret = sdio_tty_uninit_tty(sdio_tty_drv); + if (ret) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_uninit_tty " + "failed for %s", __func__, pdev->name); + } + return ret; +} + +static struct platform_driver sdio_tty_pdrv = { + .probe = sdio_tty_probe, + .remove = sdio_tty_remove, + .id_table = sdio_tty_id_table, + .driver = { + .name = "SDIO_TTY", + .owner = THIS_MODULE, + }, +}; + +#ifdef CONFIG_DEBUG_FS +void sdio_tty_print_info(void) +{ + int i = 0; + + for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) { + if (sdio_tty[i] == NULL) + continue; + pr_info(SDIO_TTY_MODULE_NAME ": %s: Total Rx=%d, Tx = %d " + "for dev %s", __func__, sdio_tty[i]->total_rx, + sdio_tty[i]->total_tx, sdio_tty[i]->tty_dev_name); + } +} + +static int tty_debug_info_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t tty_debug_info_write(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + sdio_tty_print_info(); + return count; +} + +const struct file_operations tty_debug_info_ops = { + .open = tty_debug_info_open, + .write = tty_debug_info_write, +}; +#endif + +/* + * Module Init. + * + * Register SDIO TTY driver. + * + */ +static int __init sdio_tty_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&sdio_tty_pdrv); + if (ret) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: platform_driver_register " + "failed", __func__); + } +#ifdef CONFIG_DEBUG_FS + else { + sdio_tty_debug_root = debugfs_create_dir("sdio_tty", NULL); + if (sdio_tty_debug_root) { + sdio_tty_debug_info = debugfs_create_file( + "sdio_tty_debug", + S_IRUGO | S_IWUGO, + sdio_tty_debug_root, + NULL, + &tty_debug_info_ops); + } + } +#endif + return ret; +}; + +/* + * Module Exit. + * + * Unregister SDIO TTY driver. + * + */ +static void __exit sdio_tty_exit(void) +{ +#ifdef CONFIG_DEBUG_FS + debugfs_remove(sdio_tty_debug_info); + debugfs_remove(sdio_tty_debug_root); +#endif + platform_driver_unregister(&sdio_tty_pdrv); +} + +module_init(sdio_tty_init); +module_exit(sdio_tty_exit); + +MODULE_DESCRIPTION("SDIO TTY"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Maya Erez "); diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index ce65979a..324dd312 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -284,7 +284,7 @@ void __init dma_contiguous_remap(void) if (end > arm_lowmem_limit) end = arm_lowmem_limit; if (start >= end) - continue; + return; map.pfn = __phys_to_pfn(start); map.virtual = __phys_to_virt(start); diff --git a/arch/arm/mm/dma-mapping.cd b/arch/arm/mm/dma-mapping.cd new file mode 100644 index 00000000..517c5a2e --- /dev/null +++ b/arch/arm/mm/dma-mapping.cd @@ -0,0 +1,989 @@ +/* + * linux/arch/arm/mm/dma-mapping.c + * + * Copyright (C) 2000-2004 Russell King + * + * 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. + * + * DMA uncached mapping support. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mm.h" + +static u64 get_coherent_dma_mask(struct device *dev) +{ + u64 mask = (u64)arm_dma_limit; + + if (dev) { + mask = dev->coherent_dma_mask; + + /* + * Sanity check the DMA mask - it must be non-zero, and + * must be able to be satisfied by a DMA allocation. + */ + if (mask == 0) { + dev_warn(dev, "coherent DMA mask is unset\n"); + return 0; + } + + if ((~mask) & (u64)arm_dma_limit) { + dev_warn(dev, "coherent DMA mask %#llx is smaller " + "than system GFP_DMA mask %#llx\n", + mask, (u64)arm_dma_limit); + return 0; + } + } + + return mask; +} + +static void __dma_clear_buffer(struct page *page, size_t size) +{ + void *ptr; + /* + * Ensure that the allocated pages are zeroed, and that any data + * lurking in the kernel direct-mapped region is invalidated. + */ + ptr = page_address(page); + memset(ptr, 0, size); + dmac_flush_range(ptr, ptr + size); + outer_flush_range(__pa(ptr), __pa(ptr) + size); +} + +/* + * Allocate a DMA buffer for 'dev' of size 'size' using the + * specified gfp mask. Note that 'size' must be page aligned. + */ +static struct page *__dma_alloc_buffer(struct device *dev, size_t size, gfp_t gfp) +{ + unsigned long order = get_order(size); + struct page *page, *p, *e; + + page = alloc_pages(gfp, order); + if (!page) + return NULL; + + /* + * Now split the huge page and free the excess pages + */ + split_page(page, order); + for (p = page + (size >> PAGE_SHIFT), e = page + (1 << order); p < e; p++) + __free_page(p); + + __dma_clear_buffer(page, size); + + return page; +} + +/* + * Free a DMA buffer. 'size' must be page aligned. + */ +static void __dma_free_buffer(struct page *page, size_t size) +{ + struct page *e = page + (size >> PAGE_SHIFT); + + while (page < e) { + __free_page(page); + page++; + } +} + +#ifdef CONFIG_MMU + +#define CONSISTENT_OFFSET(x) (((unsigned long)(x) - consistent_base) >> PAGE_SHIFT) +#define CONSISTENT_PTE_INDEX(x) (((unsigned long)(x) - consistent_base) >> PMD_SHIFT) + +/* + * These are the page tables (2MB each) covering uncached, DMA consistent allocations + */ +static pte_t **consistent_pte; + +#define DEFAULT_CONSISTENT_DMA_SIZE (7*SZ_2M) + +unsigned long consistent_base = CONSISTENT_END - DEFAULT_CONSISTENT_DMA_SIZE; + +void __init init_consistent_dma_size(unsigned long size) +{ + unsigned long base = CONSISTENT_END - ALIGN(size, SZ_2M); + + BUG_ON(consistent_pte); /* Check we're called before DMA region init */ + BUG_ON(base < VMALLOC_END); + + /* Grow region to accommodate specified size */ + if (base < consistent_base) + consistent_base = base; +} + +#include "vmregion.h" + +static struct arm_vmregion_head consistent_head = { + .vm_lock = __SPIN_LOCK_UNLOCKED(&consistent_head.vm_lock), + .vm_list = LIST_HEAD_INIT(consistent_head.vm_list), + .vm_end = CONSISTENT_END, +}; + +#ifdef CONFIG_HUGETLB_PAGE +#error ARM Coherent DMA allocator does not (yet) support huge TLB +#endif + +/* + * Initialise the consistent memory allocation. + */ +static int __init consistent_init(void) +{ + int ret = 0; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + int i = 0; + unsigned long base = consistent_base; + unsigned long num_ptes = (CONSISTENT_END - base) >> PMD_SHIFT; + + if (IS_ENABLED(CONFIG_CMA) && !IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)) + return 0; + + consistent_pte = kmalloc(num_ptes * sizeof(pte_t), GFP_KERNEL); + if (!consistent_pte) { + pr_err("%s: no memory\n", __func__); + return -ENOMEM; + } + + pr_debug("DMA memory: 0x%08lx - 0x%08lx:\n", base, CONSISTENT_END); + consistent_head.vm_start = base; + + do { + pgd = pgd_offset(&init_mm, base); + + pud = pud_alloc(&init_mm, pgd, base); + if (!pud) { + printk(KERN_ERR "%s: no pud tables\n", __func__); + ret = -ENOMEM; + break; + } + + pmd = pmd_alloc(&init_mm, pud, base); + if (!pmd) { + printk(KERN_ERR "%s: no pmd tables\n", __func__); + ret = -ENOMEM; + break; + } + WARN_ON(!pmd_none(*pmd)); + + pte = pte_alloc_kernel(pmd, base); + if (!pte) { + printk(KERN_ERR "%s: no pte tables\n", __func__); + ret = -ENOMEM; + break; + } + + consistent_pte[i++] = pte; + base += PMD_SIZE; + } while (base < CONSISTENT_END); + + return ret; +} +core_initcall(consistent_init); + +static void *__alloc_from_contiguous(struct device *dev, size_t size, + pgprot_t prot, struct page **ret_page); + +static struct arm_vmregion_head coherent_head = { + .vm_lock = __SPIN_LOCK_UNLOCKED(&coherent_head.vm_lock), + .vm_list = LIST_HEAD_INIT(coherent_head.vm_list), +}; + +size_t coherent_pool_size = DEFAULT_CONSISTENT_DMA_SIZE / 8; + +static int __init early_coherent_pool(char *p) +{ + coherent_pool_size = memparse(p, &p); + return 0; +} +early_param("coherent_pool", early_coherent_pool); + +/* + * Initialise the coherent pool for atomic allocations. + */ +static int __init coherent_init(void) +{ + pgprot_t prot = pgprot_dmacoherent(pgprot_kernel); + size_t size = coherent_pool_size; + struct page *page; + void *ptr; + + if (!IS_ENABLED(CONFIG_CMA)) + return 0; + + ptr = __alloc_from_contiguous(NULL, size, prot, &page); + if (ptr) { + coherent_head.vm_start = (unsigned long) ptr; + coherent_head.vm_end = (unsigned long) ptr + size; + printk(KERN_INFO "DMA: preallocated %u KiB pool for atomic coherent allocations\n", + (unsigned)size / 1024); + return 0; + } + printk(KERN_ERR "DMA: failed to allocate %u KiB pool for atomic coherent allocation\n", + (unsigned)size / 1024); + return -ENOMEM; +} +/* + * CMA is activated by core_initcall, so we must be called after it. + */ +postcore_initcall(coherent_init); + +struct dma_contig_early_reserve { + phys_addr_t base; + unsigned long size; +}; + +static struct dma_contig_early_reserve dma_mmu_remap[MAX_CMA_AREAS] __initdata; + +static int dma_mmu_remap_num __initdata; + +void __init dma_contiguous_early_fixup(phys_addr_t base, unsigned long size) +{ + dma_mmu_remap[dma_mmu_remap_num].base = base; + dma_mmu_remap[dma_mmu_remap_num].size = size; + dma_mmu_remap_num++; +} + +void __init dma_contiguous_remap(void) +{ + int i; + for (i = 0; i < dma_mmu_remap_num; i++) { + phys_addr_t start = dma_mmu_remap[i].base; + phys_addr_t end = start + dma_mmu_remap[i].size; + struct map_desc map; + unsigned long addr; + + if (end > arm_lowmem_limit) + end = arm_lowmem_limit; + if (start >= end) + return; + + map.pfn = __phys_to_pfn(start); + map.virtual = __phys_to_virt(start); + map.length = end - start; + map.type = MT_MEMORY_DMA_READY; + + /* + * Clear previous low-memory mapping + */ + for (addr = __phys_to_virt(start); addr < __phys_to_virt(end); + addr += PGDIR_SIZE) + pmd_clear(pmd_off_k(addr)); + + iotable_init(&map, 1); + } +} + +static void * +__dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot, + const void *caller) +{ + struct arm_vmregion *c; + size_t align; + int bit; + + if (!consistent_pte) { + printk(KERN_ERR "%s: not initialised\n", __func__); + dump_stack(); + return NULL; + } + + /* + * Align the virtual region allocation - maximum alignment is + * a section size, minimum is a page size. This helps reduce + * fragmentation of the DMA space, and also prevents allocations + * smaller than a section from crossing a section boundary. + */ + bit = fls(size - 1); + if (bit > SECTION_SHIFT) + bit = SECTION_SHIFT; + align = 1 << bit; + + /* + * Allocate a virtual address in the consistent mapping region. + */ + c = arm_vmregion_alloc(&consistent_head, align, size, + gfp & ~(__GFP_DMA | __GFP_HIGHMEM), caller); + if (c) { + pte_t *pte; + int idx = CONSISTENT_PTE_INDEX(c->vm_start); + u32 off = CONSISTENT_OFFSET(c->vm_start) & (PTRS_PER_PTE-1); + + pte = consistent_pte[idx] + off; + c->vm_pages = page; + + do { + BUG_ON(!pte_none(*pte)); + + set_pte_ext(pte, mk_pte(page, prot), 0); + page++; + pte++; + off++; + if (off >= PTRS_PER_PTE) { + off = 0; + pte = consistent_pte[++idx]; + } + } while (size -= PAGE_SIZE); + + dsb(); + + return (void *)c->vm_start; + } + return NULL; +} + +static void __dma_free_remap(void *cpu_addr, size_t size) +{ + struct arm_vmregion *c; + unsigned long addr; + pte_t *ptep; + int idx; + u32 off; + + c = arm_vmregion_find_remove(&consistent_head, (unsigned long)cpu_addr); + if (!c) { + printk(KERN_ERR "%s: trying to free invalid coherent area: %p\n", + __func__, cpu_addr); + dump_stack(); + return; + } + + if ((c->vm_end - c->vm_start) != size) { + printk(KERN_ERR "%s: freeing wrong coherent size (%ld != %d)\n", + __func__, c->vm_end - c->vm_start, size); + dump_stack(); + size = c->vm_end - c->vm_start; + } + + idx = CONSISTENT_PTE_INDEX(c->vm_start); + off = CONSISTENT_OFFSET(c->vm_start) & (PTRS_PER_PTE-1); + ptep = consistent_pte[idx] + off; + addr = c->vm_start; + do { + pte_t pte = ptep_get_and_clear(&init_mm, addr, ptep); + + ptep++; + addr += PAGE_SIZE; + off++; + if (off >= PTRS_PER_PTE) { + off = 0; + ptep = consistent_pte[++idx]; + } + + if (pte_none(pte) || !pte_present(pte)) + printk(KERN_CRIT "%s: bad page in kernel page table\n", + __func__); + } while (size -= PAGE_SIZE); + + flush_tlb_kernel_range(c->vm_start, c->vm_end); + + arm_vmregion_free(&consistent_head, c); +} + +static int __dma_update_pte(pte_t *pte, pgtable_t token, unsigned long addr, + void *data) +{ + struct page *page = virt_to_page(addr); + pgprot_t prot = *(pgprot_t *)data; + + set_pte_ext(pte, mk_pte(page, prot), 0); + return 0; +} + +static void __dma_remap(struct page *page, size_t size, pgprot_t prot) +{ + unsigned long start = (unsigned long) page_address(page); + unsigned end = start + size; + + apply_to_page_range(&init_mm, start, size, __dma_update_pte, &prot); + dsb(); + flush_tlb_kernel_range(start, end); +} + +static void *__alloc_remap_buffer(struct device *dev, size_t size, gfp_t gfp, + pgprot_t prot, struct page **ret_page, + const void *caller) +{ + struct page *page; + void *ptr; + page = __dma_alloc_buffer(dev, size, gfp); + if (!page) + return NULL; + + ptr = __dma_alloc_remap(page, size, gfp, prot, caller); + if (!ptr) { + __dma_free_buffer(page, size); + return NULL; + } + + *ret_page = page; + return ptr; +} + +static void *__alloc_from_pool(struct device *dev, size_t size, + struct page **ret_page, const void *caller) +{ + struct arm_vmregion *c; + size_t align; + + if (!coherent_head.vm_start) { + printk(KERN_ERR "%s: coherent pool not initialised!\n", + __func__); + dump_stack(); + return NULL; + } + + /* + * Align the region allocation - allocations from pool are rather + * small, so align them to their order in pages, minimum is a page + * size. This helps reduce fragmentation of the DMA space. + */ + align = PAGE_SIZE << get_order(size); + c = arm_vmregion_alloc(&coherent_head, align, size, 0, caller); + if (c) { + void *ptr = (void *)c->vm_start; + struct page *page = virt_to_page(ptr); + *ret_page = page; + return ptr; + } + return NULL; +} + +static int __free_from_pool(void *cpu_addr, size_t size) +{ + unsigned long start = (unsigned long)cpu_addr; + unsigned long end = start + size; + struct arm_vmregion *c; + + if (start < coherent_head.vm_start || end > coherent_head.vm_end) + return 0; + + c = arm_vmregion_find_remove(&coherent_head, (unsigned long)start); + + if ((c->vm_end - c->vm_start) != size) { + printk(KERN_ERR "%s: freeing wrong coherent size (%ld != %d)\n", + __func__, c->vm_end - c->vm_start, size); + dump_stack(); + size = c->vm_end - c->vm_start; + } + + arm_vmregion_free(&coherent_head, c); + return 1; +} + +static void *__alloc_from_contiguous(struct device *dev, size_t size, + pgprot_t prot, struct page **ret_page) +{ + unsigned long order = get_order(size); + size_t count = size >> PAGE_SHIFT; + struct page *page; + + page = dma_alloc_from_contiguous(dev, count, order); + if (!page) + return NULL; + + __dma_clear_buffer(page, size); + __dma_remap(page, size, prot); + + *ret_page = page; + return page_address(page); +} + +static void __free_from_contiguous(struct device *dev, struct page *page, + size_t size) +{ + __dma_remap(page, size, pgprot_kernel); + dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT); +} + +static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot) +{ + if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs)) + prot = pgprot_writecombine(prot); + else if (dma_get_attr(DMA_ATTR_STRONGLY_ORDERED, attrs)) + prot = pgprot_stronglyordered(prot); + /* if non-consistent just pass back what was given */ + else if (!dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs)) + prot = pgprot_dmacoherent(prot); + + return prot; +} + +#define nommu() 0 + +#else /* !CONFIG_MMU */ + +#define nommu() 1 + +#define __alloc_remap_buffer(dev, size, gfp, prot, ret, c) NULL +#define __alloc_from_pool(dev, size, ret_page, c) NULL +#define __alloc_from_contiguous(dev, size, prot, ret) NULL +#define __free_from_pool(cpu_addr, size) 0 +#define __free_from_contiguous(dev, page, size) do { } while (0) +#define __dma_free_remap(cpu_addr, size) do { } while (0) + +#endif /* CONFIG_MMU */ + +static void *__alloc_simple_buffer(struct device *dev, size_t size, gfp_t gfp, + struct page **ret_page) +{ + struct page *page; + page = __dma_alloc_buffer(dev, size, gfp); + if (!page) + return NULL; + + *ret_page = page; + return page_address(page); +} + + + +static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, + gfp_t gfp, pgprot_t prot, const void *caller) +{ + u64 mask = get_coherent_dma_mask(dev); + struct page *page; + void *addr; + +#ifdef CONFIG_DMA_API_DEBUG + u64 limit = (mask + 1) & ~mask; + if (limit && size >= limit) { + dev_warn(dev, "coherent allocation too big (requested %#x mask %#llx)\n", + size, mask); + return NULL; + } +#endif + + if (!mask) + return NULL; + + if (mask < 0xffffffffULL) + gfp |= GFP_DMA; + + /* + * Following is a work-around (a.k.a. hack) to prevent pages + * with __GFP_COMP being passed to split_page() which cannot + * handle them. The real problem is that this flag probably + * should be 0 on ARM as it is not supported on this + * platform; see CONFIG_HUGETLBFS. + */ + gfp &= ~(__GFP_COMP); + + *handle = ~0; + size = PAGE_ALIGN(size); + + if (arch_is_coherent() || nommu()) + addr = __alloc_simple_buffer(dev, size, gfp, &page); + else if (!IS_ENABLED(CONFIG_CMA)) + addr = __alloc_remap_buffer(dev, size, gfp, prot, &page, caller); + else if (gfp & GFP_ATOMIC) + addr = __alloc_from_pool(dev, size, &page, caller); + else + addr = __alloc_from_contiguous(dev, size, prot, &page); + + if (addr) + *handle = pfn_to_dma(dev, page_to_pfn(page)); + + return addr; +} + +/* + * Allocate DMA-coherent memory space and return both the kernel remapped + * virtual and bus address for that space. + */ +void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, + gfp_t gfp) +{ + void *memory; + + if (dma_alloc_from_coherent(dev, size, handle, &memory)) + return memory; + + return __dma_alloc(dev, size, handle, gfp, + pgprot_dmacoherent(pgprot_kernel), + __builtin_return_address(0)); +} +EXPORT_SYMBOL(dma_alloc_coherent); + +/* + * Allocate a writecombining region, in much the same way as + * dma_alloc_coherent above. + */ +void * +dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp) +{ + return __dma_alloc(dev, size, handle, gfp, + pgprot_writecombine(pgprot_kernel), + __builtin_return_address(0)); +} +EXPORT_SYMBOL(dma_alloc_writecombine); + +static int dma_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size) +{ + int ret = -ENXIO; +#ifdef CONFIG_MMU + unsigned long pfn = dma_to_pfn(dev, dma_addr); + ret = remap_pfn_range(vma, vma->vm_start, + pfn + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +#endif /* CONFIG_MMU */ + + return ret; +} + +int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size) +{ + vma->vm_page_prot = pgprot_dmacoherent(vma->vm_page_prot); + return dma_mmap(dev, vma, cpu_addr, dma_addr, size); +} +EXPORT_SYMBOL(dma_mmap_coherent); + +int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size) +{ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + return dma_mmap(dev, vma, cpu_addr, dma_addr, size); +} +EXPORT_SYMBOL(dma_mmap_writecombine); + + +/* + * Free a buffer as defined by the above mapping. + */ +void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle) +{ + struct page *page = pfn_to_page(dma_to_pfn(dev, handle)); + + if (dma_release_from_coherent(dev, get_order(size), cpu_addr)) + return; + + size = PAGE_ALIGN(size); + + if (arch_is_coherent() || nommu()) { + __dma_free_buffer(page, size); + } else if (!IS_ENABLED(CONFIG_CMA)) { + __dma_free_remap(cpu_addr, size); + __dma_free_buffer(page, size); + } else { + if (__free_from_pool(cpu_addr, size)) + return; + /* + * Non-atomic allocations cannot be freed with IRQs disabled + */ + WARN_ON(irqs_disabled()); + __free_from_contiguous(dev, page, size); + } +} +EXPORT_SYMBOL(dma_free_coherent); + +/* + * Make an area consistent for devices. + * Note: Drivers should NOT use this function directly, as it will break + * platforms with CONFIG_DMABOUNCE. + * Use the driver DMA support - see dma-mapping.h (dma_sync_*) + */ +void ___dma_single_cpu_to_dev(const void *kaddr, size_t size, + enum dma_data_direction dir) +{ +#ifdef CONFIG_OUTER_CACHE + unsigned long paddr; + + BUG_ON(!virt_addr_valid(kaddr) || !virt_addr_valid(kaddr + size - 1)); +#endif + + dmac_map_area(kaddr, size, dir); + +#ifdef CONFIG_OUTER_CACHE + paddr = __pa(kaddr); + if (dir == DMA_FROM_DEVICE) { + outer_inv_range(paddr, paddr + size); + } else { + outer_clean_range(paddr, paddr + size); + } +#endif + /* FIXME: non-speculating: flush on bidirectional mappings? */ +} +EXPORT_SYMBOL(___dma_single_cpu_to_dev); + +void ___dma_single_dev_to_cpu(const void *kaddr, size_t size, + enum dma_data_direction dir) +{ +#ifdef CONFIG_OUTER_CACHE + BUG_ON(!virt_addr_valid(kaddr) || !virt_addr_valid(kaddr + size - 1)); + + /* FIXME: non-speculating: not required */ + /* don't bother invalidating if DMA to device */ + if (dir != DMA_TO_DEVICE) { + unsigned long paddr = __pa(kaddr); + outer_inv_range(paddr, paddr + size); + } +#endif + dmac_unmap_area(kaddr, size, dir); +} +EXPORT_SYMBOL(___dma_single_dev_to_cpu); + +static void dma_cache_maint_page(struct page *page, unsigned long offset, + size_t size, enum dma_data_direction dir, + void (*op)(const void *, size_t, int)) +{ + /* + * A single sg entry may refer to multiple physically contiguous + * pages. But we still need to process highmem pages individually. + * If highmem is not configured then the bulk of this loop gets + * optimized out. + */ + size_t left = size; + do { + size_t len = left; + void *vaddr; + + if (PageHighMem(page)) { + if (len + offset > PAGE_SIZE) { + if (offset >= PAGE_SIZE) { + page += offset / PAGE_SIZE; + offset %= PAGE_SIZE; + } + len = PAGE_SIZE - offset; + } + vaddr = kmap_high_get(page); + if (vaddr) { + vaddr += offset; + op(vaddr, len, dir); + kunmap_high(page); + } else if (cache_is_vipt()) { + /* unmapped pages might still be cached */ + vaddr = kmap_atomic(page); + op(vaddr + offset, len, dir); + kunmap_atomic(vaddr); + } + } else { + vaddr = page_address(page) + offset; + op(vaddr, len, dir); + } + offset = 0; + page++; + left -= len; + } while (left); +} + +void ___dma_page_cpu_to_dev(struct page *page, unsigned long off, + size_t size, enum dma_data_direction dir) +{ + unsigned long paddr; + + dma_cache_maint_page(page, off, size, dir, dmac_map_area); + + paddr = page_to_phys(page) + off; + if (dir == DMA_FROM_DEVICE) { + outer_inv_range(paddr, paddr + size); + } else { + outer_clean_range(paddr, paddr + size); + } + /* FIXME: non-speculating: flush on bidirectional mappings? */ +} +EXPORT_SYMBOL(___dma_page_cpu_to_dev); + +void ___dma_page_dev_to_cpu(struct page *page, unsigned long off, + size_t size, enum dma_data_direction dir) +{ + unsigned long paddr = page_to_phys(page) + off; + + /* FIXME: non-speculating: not required */ + /* don't bother invalidating if DMA to device */ + if (dir != DMA_TO_DEVICE) + outer_inv_range(paddr, paddr + size); + + dma_cache_maint_page(page, off, size, dir, dmac_unmap_area); + + /* + * Mark the D-cache clean for this page to avoid extra flushing. + */ + if (dir != DMA_TO_DEVICE && off == 0 && size >= PAGE_SIZE) + set_bit(PG_dcache_clean, &page->flags); +} +EXPORT_SYMBOL(___dma_page_dev_to_cpu); + +/** + * dma_map_sg - map a set of SG buffers for streaming mode DMA + * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices + * @sg: list of buffers + * @nents: number of buffers to map + * @dir: DMA transfer direction + * + * Map a set of buffers described by scatterlist in streaming mode for DMA. + * This is the scatter-gather version of the dma_map_single interface. + * Here the scatter gather list elements are each tagged with the + * appropriate dma address and length. They are obtained via + * sg_dma_{address,length}. + * + * Device ownership issues as mentioned for dma_map_single are the same + * here. + */ +int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction dir) +{ + struct scatterlist *s; + int i, j; + + BUG_ON(!valid_dma_direction(dir)); + + for_each_sg(sg, s, nents, i) { + s->dma_address = __dma_map_page(dev, sg_page(s), s->offset, + s->length, dir); + if (dma_mapping_error(dev, s->dma_address)) + goto bad_mapping; + } + debug_dma_map_sg(dev, sg, nents, nents, dir); + return nents; + + bad_mapping: + for_each_sg(sg, s, i, j) + __dma_unmap_page(dev, sg_dma_address(s), sg_dma_len(s), dir); + return 0; +} +EXPORT_SYMBOL(dma_map_sg); + +/** + * dma_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg + * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices + * @sg: list of buffers + * @nents: number of buffers to unmap (same as was passed to dma_map_sg) + * @dir: DMA transfer direction (same as was passed to dma_map_sg) + * + * Unmap a set of streaming mode DMA translations. Again, CPU access + * rules concerning calls here are the same as for dma_unmap_single(). + */ +void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction dir) +{ + struct scatterlist *s; + int i; + + debug_dma_unmap_sg(dev, sg, nents, dir); + + for_each_sg(sg, s, nents, i) + __dma_unmap_page(dev, sg_dma_address(s), sg_dma_len(s), dir); +} +EXPORT_SYMBOL(dma_unmap_sg); + +/** + * dma_sync_sg_for_cpu + * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices + * @sg: list of buffers + * @nents: number of buffers to map (returned from dma_map_sg) + * @dir: DMA transfer direction (same as was passed to dma_map_sg) + */ +void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir) +{ + struct scatterlist *s; + int i; + + for_each_sg(sg, s, nents, i) { + if (!dmabounce_sync_for_cpu(dev, sg_dma_address(s), 0, + sg_dma_len(s), dir)) + continue; + + __dma_page_dev_to_cpu(sg_page(s), s->offset, + s->length, dir); + } + + debug_dma_sync_sg_for_cpu(dev, sg, nents, dir); +} +EXPORT_SYMBOL(dma_sync_sg_for_cpu); + +/** + * dma_sync_sg_for_device + * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices + * @sg: list of buffers + * @nents: number of buffers to map (returned from dma_map_sg) + * @dir: DMA transfer direction (same as was passed to dma_map_sg) + */ +void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir) +{ + struct scatterlist *s; + int i; + + for_each_sg(sg, s, nents, i) { + if (!dmabounce_sync_for_device(dev, sg_dma_address(s), 0, + sg_dma_len(s), dir)) + continue; + + __dma_page_cpu_to_dev(sg_page(s), s->offset, + s->length, dir); + } + + debug_dma_sync_sg_for_device(dev, sg, nents, dir); +} +EXPORT_SYMBOL(dma_sync_sg_for_device); + +/* + * Return whether the given device DMA address mask can be supported + * properly. For example, if your device can only drive the low 24-bits + * during bus mastering, then you would pass 0x00ffffff as the mask + * to this function. + */ +int dma_supported(struct device *dev, u64 mask) +{ + if (mask < (u64)arm_dma_limit) + return 0; + return 1; +} +EXPORT_SYMBOL(dma_supported); + +int dma_set_mask(struct device *dev, u64 dma_mask) +{ + if (!dev->dma_mask || !dma_supported(dev, dma_mask)) + return -EIO; + +#ifndef CONFIG_DMABOUNCE + *dev->dma_mask = dma_mask; +#endif + + return 0; +} +EXPORT_SYMBOL(dma_set_mask); + +#define PREALLOC_DMA_DEBUG_ENTRIES 4096 + +static int __init dma_debug_do_init(void) +{ +#ifdef CONFIG_MMU + arm_vmregion_create_proc("dma-mappings", &consistent_head); +#endif + dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES); + return 0; +} +fs_initcall(dma_debug_do_init); diff --git a/arch/arm/mm/mm.h b/arch/arm/mm/mm.h index 8877dddc..802611b0 100644 --- a/arch/arm/mm/mm.h +++ b/arch/arm/mm/mm.h @@ -62,6 +62,9 @@ extern void __flush_dcache_page(struct address_space *mapping, struct page *page #define VM_ARM_MTYPE(mt) ((mt) << 20) #define VM_ARM_MTYPE_MASK (0x1f << 20) +/* consistent regions used by dma_alloc_attrs() */ +#define VM_ARM_DMA_CONSISTENT 0x20000000 + #endif #ifdef CONFIG_ZONE_DMA diff --git a/block/Kconfig.iosched b/block/Kconfig.iosched index 3066f240..4e534631 100644 --- a/block/Kconfig.iosched +++ b/block/Kconfig.iosched @@ -2,26 +2,49 @@ if BLOCK menu "IO Schedulers" -config IOSCHED_NOOP - bool +config IOSCHED_BFQ + tristate "BFQ I/O scheduler" + depends on EXPERIMENTAL + default n + ---help--- + The BFQ I/O scheduler tries to distribute bandwidth among + all processes according to their weights. + It aims at distributing the bandwidth as desired, independently of + the disk parameters and with any workload. It also tries to + guarantee low latency to interactive and soft real-time + applications. If compiled built-in (saying Y here), BFQ can + be configured to support hierarchical scheduling. + +config CGROUP_BFQIO + bool "BFQ hierarchical scheduling support" + depends on CGROUPS && IOSCHED_BFQ=y + default n + ---help--- + Enable hierarchical scheduling in BFQ, using the cgroups + filesystem interface. The name of the subsystem will be + bfqio. + +config IOSCHED_CFQ + tristate "CFQ I/O scheduler" + # If BLK_CGROUP is a module, CFQ has to be built as module. + depends on (BLK_CGROUP=m && m) || !BLK_CGROUP || BLK_CGROUP=y default y ---help--- - The no-op I/O scheduler is a minimal scheduler that does basic merging - and sorting. Its main uses include non-disk based block devices like - memory devices, and specialised software or hardware environments - that do their own scheduling and require only minimal assistance from - the kernel. + The CFQ I/O scheduler tries to distribute bandwidth equally + among all processes in the system. It should provide a fair + and low latency working environment, suitable for both desktop + and server systems. -config IOSCHED_TEST - tristate "Test I/O scheduler" - depends on DEBUG_FS - default m + This is the default I/O scheduler. + + Note: If BLK_CGROUP=m, then CFQ can be built only as module. + +config CFQ_GROUP_IOSCHED + bool "CFQ Group Scheduling support" + depends on IOSCHED_CFQ && BLK_CGROUP + default n ---help--- - The test I/O scheduler is a duplicate of the noop scheduler with - addition of test utlity. - It allows testing a block device by dispatching specific requests - according to the test case and declare PASS/FAIL according to the - requests completion error code. + Enable group IO scheduling in CFQ. config IOSCHED_DEADLINE tristate "Deadline I/O scheduler" @@ -40,27 +63,71 @@ config IOSCHED_FIOPS IOPS equally among all processes in the system. It's mainly for Flash based storage. -config IOSCHED_CFQ - tristate "CFQ I/O scheduler" - # If BLK_CGROUP is a module, CFQ has to be built as module. - depends on (BLK_CGROUP=m && m) || !BLK_CGROUP || BLK_CGROUP=y +config IOSCHED_NOOP + bool default y ---help--- - The CFQ I/O scheduler tries to distribute bandwidth equally - among all processes in the system. It should provide a fair - and low latency working environment, suitable for both desktop - and server systems. + The no-op I/O scheduler is a minimal scheduler that does basic merging + and sorting. Its main uses include non-disk based block devices like + memory devices, and specialised software or hardware environments + that do their own scheduling and require only minimal assistance from + the kernel. - This is the default I/O scheduler. +config IOSCHED_ROW + tristate "ROW I/O scheduler" + default y + ---help--- + The ROW I/O scheduler gives priority to READ requests over the + WRITE requests when dispatching, without starving WRITE requests. + Requests are kept in priority queues. Dispatching is done in a RR + manner when the dispatch quantum for each queue is calculated + according to queue priority. + Most suitable for mobile devices. + +config IOSCHED_SIO + tristate "Simple I/O scheduler" + default y + ---help--- + The Simple I/O scheduler is an extremely simple scheduler, + based on noop and deadline, that relies on deadlines to + ensure fairness. The algorithm does not do any sorting but + basic merging, trying to keep a minimum overhead. It is aimed + mainly for aleatory access devices (eg: flash devices). + +config IOSCHED_SIOPLUS + tristate "Simple I/O scheduler plus" + default y + ---help--- + The Simple I/O scheduler is an extremely simple scheduler, + based on noop and deadline, that relies on deadlines to + ensure fairness. The algorithm does not do any sorting but + basic merging, trying to keep a minimum overhead. It is aimed + mainly for aleatory access devices (eg: flash devices). - Note: If BLK_CGROUP=m, then CFQ can be built only as module. +config IOSCHED_TEST + tristate "Test I/O scheduler" + depends on DEBUG_FS + default m + ---help--- + The test I/O scheduler is a duplicate of the noop scheduler with + addition of test utlity. + It allows testing a block device by dispatching specific requests + according to the test case and declare PASS/FAIL according to the + requests completion error code. -config CFQ_GROUP_IOSCHED - bool "CFQ Group Scheduling support" - depends on IOSCHED_CFQ && BLK_CGROUP +config IOSCHED_VR + tristate "V(R) I/O scheduler" default n - ---help--- - Enable group IO scheduling in CFQ. + ---help--- + Requests are chosen according to SSTF with a penalty of rev_penalty + for switching head direction. + +config IOSCHED_ZEN + tristate "Zen I/O scheduler" + default y + ---help--- + FCFS, dispatches are back-inserted, deadlines ensure fairness. + Should work best with devices where there is no travel delay. choice prompt "Default I/O scheduler" @@ -69,26 +136,64 @@ choice Select the I/O scheduler which will be used by default for all block devices. + config DEFAULT_BFQ + bool "BFQ" if IOSCHED_BFQ=y + help + Selects BFQ as the default I/O scheduler which will be + used by default for all block devices. + The BFQ I/O scheduler aims at distributing the bandwidth + as desired, independently of the disk parameters and with + any workload. It also tries to guarantee low latency to + interactive and soft real-time applications. + + config DEFAULT_CFQ + bool "CFQ" if IOSCHED_CFQ=y + config DEFAULT_DEADLINE bool "Deadline" if IOSCHED_DEADLINE=y config DEFAULT_FIOPS bool "FIOPS" if IOSCHED_FIOPS=y - config DEFAULT_CFQ - bool "CFQ" if IOSCHED_CFQ=y - config DEFAULT_NOOP bool "No-op" + config DEFAULT_ROW + bool "ROW" if IOSCHED_ROW=y + help + The ROW I/O scheduler gives priority to READ requests + over the WRITE requests when dispatching, without starving + WRITE requests. Requests are kept in priority queues. + Dispatching is done in a RR manner when the dispatch quantum + for each queue is defined according to queue priority. + Most suitable for mobile devices. + + config DEFAULT_SIO + bool "SIO" if IOSCHED_SIO=y + + config DEFAULT_SIOPLUS + bool "SIOPLUS" if IOSCHED_SIOPLUS=y + + config DEFAULT_VR + bool "VR" if IOSCHED_VR=y + + config DEFAULT_ZEN + bool "ZEN" if IOSCHED_ZEN=y + endchoice config DEFAULT_IOSCHED string + default "bfq" if DEFAULT_BFQ + default "cfq" if DEFAULT_CFQ default "deadline" if DEFAULT_DEADLINE default "fiops" if DEFAULT_FIOPS - default "cfq" if DEFAULT_CFQ default "noop" if DEFAULT_NOOP + default "row" if DEFAULT_ROW + default "sio" if DEFAULT_SIO + default "sioplus" if DEFAULT_SIOPLUS + default "vr" if DEFAULT_VR + default "zen" if DEFAULT_ZEN endmenu diff --git a/block/Makefile b/block/Makefile index a91fda5d..e4a5e913 100644 --- a/block/Makefile +++ b/block/Makefile @@ -12,11 +12,17 @@ obj-$(CONFIG_BLK_DEV_BSG) += bsg.o obj-$(CONFIG_BLK_DEV_BSGLIB) += bsg-lib.o obj-$(CONFIG_BLK_CGROUP) += blk-cgroup.o obj-$(CONFIG_BLK_DEV_THROTTLING) += blk-throttle.o -obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o +obj-$(CONFIG_IOSCHED_BFQ) += bfq-iosched.o +obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o obj-$(CONFIG_IOSCHED_DEADLINE) += deadline-iosched.o obj-$(CONFIG_IOSCHED_FIOPS) += fiops-iosched.o -obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o +obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o +obj-$(CONFIG_IOSCHED_ROW) += row-iosched.o +obj-$(CONFIG_IOSCHED_SIO) += sio-iosched.o +obj-$(CONFIG_IOSCHED_SIOPLUS) += sioplus-iosched.o obj-$(CONFIG_IOSCHED_TEST) += test-iosched.o +obj-$(CONFIG_IOSCHED_VR) += vr-iosched.o +obj-$(CONFIG_IOSCHED_ZEN) += zen-iosched.o obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o obj-$(CONFIG_BLK_DEV_INTEGRITY) += blk-integrity.o diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c new file mode 100644 index 00000000..8399c920 --- /dev/null +++ b/block/bfq-cgroup.c @@ -0,0 +1,908 @@ +/* + * BFQ: CGROUPS support. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + * + * Copyright (C) 2010 Paolo Valente + * + * Licensed under the GPL-2 as detailed in the accompanying COPYING.BFQ + * file. + */ + +#ifdef CONFIG_CGROUP_BFQIO +static struct bfqio_cgroup bfqio_root_cgroup = { + .weight = BFQ_DEFAULT_GRP_WEIGHT, + .ioprio = BFQ_DEFAULT_GRP_IOPRIO, + .ioprio_class = BFQ_DEFAULT_GRP_CLASS, +}; + +static inline void bfq_init_entity(struct bfq_entity *entity, + struct bfq_group *bfqg) +{ + entity->weight = entity->new_weight; + entity->orig_weight = entity->new_weight; + entity->ioprio = entity->new_ioprio; + entity->ioprio_class = entity->new_ioprio_class; + entity->parent = bfqg->my_entity; + entity->sched_data = &bfqg->sched_data; +} + +static struct bfqio_cgroup *cgroup_to_bfqio(struct cgroup *cgroup) +{ + return container_of(cgroup_subsys_state(cgroup, bfqio_subsys_id), + struct bfqio_cgroup, css); +} + +/* + * Search the bfq_group for bfqd into the hash table (by now only a list) + * of bgrp. Must be called under rcu_read_lock(). + */ +static struct bfq_group *bfqio_lookup_group(struct bfqio_cgroup *bgrp, + struct bfq_data *bfqd) +{ + struct bfq_group *bfqg; + struct hlist_node *n; + void *key; + + hlist_for_each_entry_rcu(bfqg, n, &bgrp->group_data, group_node) { + key = rcu_dereference(bfqg->bfqd); + if (key == bfqd) + return bfqg; + } + + return NULL; +} + +static inline void bfq_group_init_entity(struct bfqio_cgroup *bgrp, + struct bfq_group *bfqg) +{ + struct bfq_entity *entity = &bfqg->entity; + + /* + * If the weight of the entity has never been set via the sysfs + * interface, then bgrp->weight == 0. In this case we initialize + * the weight from the current ioprio value. Otherwise, the group + * weight, if set, has priority over the ioprio value. + */ + if (bgrp->weight == 0) { + entity->new_weight = bfq_ioprio_to_weight(bgrp->ioprio); + entity->new_ioprio = bgrp->ioprio; + } else { + if (bgrp->weight < BFQ_MIN_WEIGHT || + bgrp->weight > BFQ_MAX_WEIGHT) { + printk(KERN_CRIT "bfq_group_init_entity: " + "bgrp->weight %d\n", bgrp->weight); + BUG(); + } + entity->new_weight = bgrp->weight; + entity->new_ioprio = bfq_weight_to_ioprio(bgrp->weight); + } + entity->orig_weight = entity->weight = entity->new_weight; + entity->ioprio = entity->new_ioprio; + entity->ioprio_class = entity->new_ioprio_class = bgrp->ioprio_class; + entity->my_sched_data = &bfqg->sched_data; + bfqg->active_entities = 0; +} + +static inline void bfq_group_set_parent(struct bfq_group *bfqg, + struct bfq_group *parent) +{ + struct bfq_entity *entity; + + BUG_ON(parent == NULL); + BUG_ON(bfqg == NULL); + + entity = &bfqg->entity; + entity->parent = parent->my_entity; + entity->sched_data = &parent->sched_data; +} + +/** + * bfq_group_chain_alloc - allocate a chain of groups. + * @bfqd: queue descriptor. + * @cgroup: the leaf cgroup this chain starts from. + * + * Allocate a chain of groups starting from the one belonging to + * @cgroup up to the root cgroup. Stop if a cgroup on the chain + * to the root has already an allocated group on @bfqd. + */ +static struct bfq_group *bfq_group_chain_alloc(struct bfq_data *bfqd, + struct cgroup *cgroup) +{ + struct bfqio_cgroup *bgrp; + struct bfq_group *bfqg, *prev = NULL, *leaf = NULL; + + for (; cgroup != NULL; cgroup = cgroup->parent) { + bgrp = cgroup_to_bfqio(cgroup); + + bfqg = bfqio_lookup_group(bgrp, bfqd); + if (bfqg != NULL) { + /* + * All the cgroups in the path from there to the + * root must have a bfq_group for bfqd, so we don't + * need any more allocations. + */ + break; + } + + bfqg = kzalloc(sizeof(*bfqg), GFP_ATOMIC); + if (bfqg == NULL) + goto cleanup; + + bfq_group_init_entity(bgrp, bfqg); + bfqg->my_entity = &bfqg->entity; + + if (leaf == NULL) { + leaf = bfqg; + prev = leaf; + } else { + bfq_group_set_parent(prev, bfqg); + /* + * Build a list of allocated nodes using the bfqd + * filed, that is still unused and will be + * initialized only after the node will be + * connected. + */ + prev->bfqd = bfqg; + prev = bfqg; + } + } + + return leaf; + +cleanup: + while (leaf != NULL) { + prev = leaf; + leaf = leaf->bfqd; + kfree(prev); + } + + return NULL; +} + +/** + * bfq_group_chain_link - link an allocated group chain to a cgroup + * hierarchy. + * @bfqd: the queue descriptor. + * @cgroup: the leaf cgroup to start from. + * @leaf: the leaf group (to be associated to @cgroup). + * + * Try to link a chain of groups to a cgroup hierarchy, connecting the + * nodes bottom-up, so we can be sure that when we find a cgroup in the + * hierarchy that already as a group associated to @bfqd all the nodes + * in the path to the root cgroup have one too. + * + * On locking: the queue lock protects the hierarchy (there is a hierarchy + * per device) while the bfqio_cgroup lock protects the list of groups + * belonging to the same cgroup. + */ +static void bfq_group_chain_link(struct bfq_data *bfqd, struct cgroup *cgroup, + struct bfq_group *leaf) +{ + struct bfqio_cgroup *bgrp; + struct bfq_group *bfqg, *next, *prev = NULL; + unsigned long flags; + + assert_spin_locked(bfqd->queue->queue_lock); + + for (; cgroup != NULL && leaf != NULL; cgroup = cgroup->parent) { + bgrp = cgroup_to_bfqio(cgroup); + next = leaf->bfqd; + + bfqg = bfqio_lookup_group(bgrp, bfqd); + BUG_ON(bfqg != NULL); + + spin_lock_irqsave(&bgrp->lock, flags); + + rcu_assign_pointer(leaf->bfqd, bfqd); + hlist_add_head_rcu(&leaf->group_node, &bgrp->group_data); + hlist_add_head(&leaf->bfqd_node, &bfqd->group_list); + + spin_unlock_irqrestore(&bgrp->lock, flags); + + prev = leaf; + leaf = next; + } + + BUG_ON(cgroup == NULL && leaf != NULL); + if (cgroup != NULL && prev != NULL) { + bgrp = cgroup_to_bfqio(cgroup); + bfqg = bfqio_lookup_group(bgrp, bfqd); + bfq_group_set_parent(prev, bfqg); + } +} + +/** + * bfq_find_alloc_group - return the group associated to @bfqd in @cgroup. + * @bfqd: queue descriptor. + * @cgroup: cgroup being searched for. + * + * Return a group associated to @bfqd in @cgroup, allocating one if + * necessary. When a group is returned all the cgroups in the path + * to the root have a group associated to @bfqd. + * + * If the allocation fails, return the root group: this breaks guarantees + * but is a safe fallback. If this loss becomes a problem it can be + * mitigated using the equivalent weight (given by the product of the + * weights of the groups in the path from @group to the root) in the + * root scheduler. + * + * We allocate all the missing nodes in the path from the leaf cgroup + * to the root and we connect the nodes only after all the allocations + * have been successful. + */ +static struct bfq_group *bfq_find_alloc_group(struct bfq_data *bfqd, + struct cgroup *cgroup) +{ + struct bfqio_cgroup *bgrp = cgroup_to_bfqio(cgroup); + struct bfq_group *bfqg; + + bfqg = bfqio_lookup_group(bgrp, bfqd); + if (bfqg != NULL) + return bfqg; + + bfqg = bfq_group_chain_alloc(bfqd, cgroup); + if (bfqg != NULL) + bfq_group_chain_link(bfqd, cgroup, bfqg); + else + bfqg = bfqd->root_group; + + return bfqg; +} + +/** + * bfq_bfqq_move - migrate @bfqq to @bfqg. + * @bfqd: queue descriptor. + * @bfqq: the queue to move. + * @entity: @bfqq's entity. + * @bfqg: the group to move to. + * + * Move @bfqq to @bfqg, deactivating it from its old group and reactivating + * it on the new one. Avoid putting the entity on the old group idle tree. + * + * Must be called under the queue lock; the cgroup owning @bfqg must + * not disappear (by now this just means that we are called under + * rcu_read_lock()). + */ +static void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq, + struct bfq_entity *entity, struct bfq_group *bfqg) +{ + int busy, resume; + + busy = bfq_bfqq_busy(bfqq); + resume = !RB_EMPTY_ROOT(&bfqq->sort_list); + + BUG_ON(resume && !entity->on_st); + BUG_ON(busy && !resume && entity->on_st && + bfqq != bfqd->in_service_queue); + + if (busy) { + BUG_ON(atomic_read(&bfqq->ref) < 2); + + if (!resume) + bfq_del_bfqq_busy(bfqd, bfqq, 0); + else + bfq_deactivate_bfqq(bfqd, bfqq, 0); + } else if (entity->on_st) + bfq_put_idle_entity(bfq_entity_service_tree(entity), entity); + + /* + * Here we use a reference to bfqg. We don't need a refcounter + * as the cgroup reference will not be dropped, so that its + * destroy() callback will not be invoked. + */ + entity->parent = bfqg->my_entity; + entity->sched_data = &bfqg->sched_data; + + if (busy && resume) + bfq_activate_bfqq(bfqd, bfqq); + + if (bfqd->in_service_queue == NULL && !bfqd->rq_in_driver) + bfq_schedule_dispatch(bfqd); +} + +/** + * __bfq_bic_change_cgroup - move @bic to @cgroup. + * @bfqd: the queue descriptor. + * @bic: the bic to move. + * @cgroup: the cgroup to move to. + * + * Move bic to cgroup, assuming that bfqd->queue is locked; the caller + * has to make sure that the reference to cgroup is valid across the call. + * + * NOTE: an alternative approach might have been to store the current + * cgroup in bfqq and getting a reference to it, reducing the lookup + * time here, at the price of slightly more complex code. + */ +static struct bfq_group *__bfq_bic_change_cgroup(struct bfq_data *bfqd, + struct bfq_io_cq *bic, + struct cgroup *cgroup) +{ + struct bfq_queue *async_bfqq = bic_to_bfqq(bic, 0); + struct bfq_queue *sync_bfqq = bic_to_bfqq(bic, 1); + struct bfq_entity *entity; + struct bfq_group *bfqg; + struct bfqio_cgroup *bgrp; + + bgrp = cgroup_to_bfqio(cgroup); + + bfqg = bfq_find_alloc_group(bfqd, cgroup); + if (async_bfqq != NULL) { + entity = &async_bfqq->entity; + + if (entity->sched_data != &bfqg->sched_data) { + bic_set_bfqq(bic, NULL, 0); + bfq_log_bfqq(bfqd, async_bfqq, + "bic_change_group: %p %d", + async_bfqq, atomic_read(&async_bfqq->ref)); + bfq_put_queue(async_bfqq); + } + } + + if (sync_bfqq != NULL) { + entity = &sync_bfqq->entity; + if (entity->sched_data != &bfqg->sched_data) + bfq_bfqq_move(bfqd, sync_bfqq, entity, bfqg); + } + + return bfqg; +} + +/** + * bfq_bic_change_cgroup - move @bic to @cgroup. + * @bic: the bic being migrated. + * @cgroup: the destination cgroup. + * + * When the task owning @bic is moved to @cgroup, @bic is immediately + * moved into its new parent group. + */ +static void bfq_bic_change_cgroup(struct bfq_io_cq *bic, + struct cgroup *cgroup) +{ + struct bfq_data *bfqd; + unsigned long uninitialized_var(flags); + + bfqd = bfq_get_bfqd_locked(&(bic->icq.q->elevator->elevator_data), + &flags); + if (bfqd != NULL) { + __bfq_bic_change_cgroup(bfqd, bic, cgroup); + bfq_put_bfqd_unlock(bfqd, &flags); + } +} + +/** + * bfq_bic_update_cgroup - update the cgroup of @bic. + * @bic: the @bic to update. + * + * Make sure that @bic is enqueued in the cgroup of the current task. + * We need this in addition to moving bics during the cgroup attach + * phase because the task owning @bic could be at its first disk + * access or we may end up in the root cgroup as the result of a + * memory allocation failure and here we try to move to the right + * group. + * + * Must be called under the queue lock. It is safe to use the returned + * value even after the rcu_read_unlock() as the migration/destruction + * paths act under the queue lock too. IOW it is impossible to race with + * group migration/destruction and end up with an invalid group as: + * a) here cgroup has not yet been destroyed, nor its destroy callback + * has started execution, as current holds a reference to it, + * b) if it is destroyed after rcu_read_unlock() [after current is + * migrated to a different cgroup] its attach() callback will have + * taken care of remove all the references to the old cgroup data. + */ +static struct bfq_group *bfq_bic_update_cgroup(struct bfq_io_cq *bic) +{ + struct bfq_data *bfqd = bic_to_bfqd(bic); + struct bfq_group *bfqg; + struct cgroup *cgroup; + + BUG_ON(bfqd == NULL); + + rcu_read_lock(); + cgroup = task_cgroup(current, bfqio_subsys_id); + bfqg = __bfq_bic_change_cgroup(bfqd, bic, cgroup); + rcu_read_unlock(); + + return bfqg; +} + +/** + * bfq_flush_idle_tree - deactivate any entity on the idle tree of @st. + * @st: the service tree being flushed. + */ +static inline void bfq_flush_idle_tree(struct bfq_service_tree *st) +{ + struct bfq_entity *entity = st->first_idle; + + for (; entity != NULL; entity = st->first_idle) + __bfq_deactivate_entity(entity, 0); +} + +/** + * bfq_reparent_leaf_entity - move leaf entity to the root_group. + * @bfqd: the device data structure with the root group. + * @entity: the entity to move. + */ +static inline void bfq_reparent_leaf_entity(struct bfq_data *bfqd, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + + BUG_ON(bfqq == NULL); + bfq_bfqq_move(bfqd, bfqq, entity, bfqd->root_group); + return; +} + +/** + * bfq_reparent_active_entities - move to the root group all active + * entities. + * @bfqd: the device data structure with the root group. + * @bfqg: the group to move from. + * @st: the service tree with the entities. + * + * Needs queue_lock to be taken and reference to be valid over the call. + */ +static inline void bfq_reparent_active_entities(struct bfq_data *bfqd, + struct bfq_group *bfqg, + struct bfq_service_tree *st) +{ + struct rb_root *active = &st->active; + struct bfq_entity *entity = NULL; + + if (!RB_EMPTY_ROOT(&st->active)) + entity = bfq_entity_of(rb_first(active)); + + for (; entity != NULL; entity = bfq_entity_of(rb_first(active))) + bfq_reparent_leaf_entity(bfqd, entity); + + if (bfqg->sched_data.in_service_entity != NULL) + bfq_reparent_leaf_entity(bfqd, + bfqg->sched_data.in_service_entity); + + return; +} + +/** + * bfq_destroy_group - destroy @bfqg. + * @bgrp: the bfqio_cgroup containing @bfqg. + * @bfqg: the group being destroyed. + * + * Destroy @bfqg, making sure that it is not referenced from its parent. + */ +static void bfq_destroy_group(struct bfqio_cgroup *bgrp, struct bfq_group *bfqg) +{ + struct bfq_data *bfqd; + struct bfq_service_tree *st; + struct bfq_entity *entity = bfqg->my_entity; + unsigned long uninitialized_var(flags); + int i; + + hlist_del(&bfqg->group_node); + + /* + * Empty all service_trees belonging to this group before + * deactivating the group itself. + */ + for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) { + st = bfqg->sched_data.service_tree + i; + + /* + * The idle tree may still contain bfq_queues belonging + * to exited task because they never migrated to a different + * cgroup from the one being destroyed now. No one else + * can access them so it's safe to act without any lock. + */ + bfq_flush_idle_tree(st); + + /* + * It may happen that some queues are still active + * (busy) upon group destruction (if the corresponding + * processes have been forced to terminate). We move + * all the leaf entities corresponding to these queues + * to the root_group. + * Also, it may happen that the group has an entity + * in service, which is disconnected from the active + * tree: it must be moved, too. + * There is no need to put the sync queues, as the + * scheduler has taken no reference. + */ + bfqd = bfq_get_bfqd_locked(&bfqg->bfqd, &flags); + if (bfqd != NULL) { + bfq_reparent_active_entities(bfqd, bfqg, st); + bfq_put_bfqd_unlock(bfqd, &flags); + } + BUG_ON(!RB_EMPTY_ROOT(&st->active)); + BUG_ON(!RB_EMPTY_ROOT(&st->idle)); + } + BUG_ON(bfqg->sched_data.next_in_service != NULL); + BUG_ON(bfqg->sched_data.in_service_entity != NULL); + + /* + * We may race with device destruction, take extra care when + * dereferencing bfqg->bfqd. + */ + bfqd = bfq_get_bfqd_locked(&bfqg->bfqd, &flags); + if (bfqd != NULL) { + hlist_del(&bfqg->bfqd_node); + __bfq_deactivate_entity(entity, 0); + bfq_put_async_queues(bfqd, bfqg); + bfq_put_bfqd_unlock(bfqd, &flags); + } + BUG_ON(entity->tree != NULL); + + /* + * No need to defer the kfree() to the end of the RCU grace + * period: we are called from the destroy() callback of our + * cgroup, so we can be sure that no one is a) still using + * this cgroup or b) doing lookups in it. + */ + kfree(bfqg); +} + +static void bfq_end_wr_async(struct bfq_data *bfqd) +{ + struct hlist_node *pos, *n; + struct bfq_group *bfqg; + + hlist_for_each_entry_safe(bfqg, pos, n, &bfqd->group_list, bfqd_node) + bfq_end_wr_async_queues(bfqd, bfqg); + bfq_end_wr_async_queues(bfqd, bfqd->root_group); +} + +/** + * bfq_disconnect_groups - disconnect @bfqd from all its groups. + * @bfqd: the device descriptor being exited. + * + * When the device exits we just make sure that no lookup can return + * the now unused group structures. They will be deallocated on cgroup + * destruction. + */ +static void bfq_disconnect_groups(struct bfq_data *bfqd) +{ + struct hlist_node *pos, *n; + struct bfq_group *bfqg; + + bfq_log(bfqd, "disconnect_groups beginning"); + hlist_for_each_entry_safe(bfqg, pos, n, &bfqd->group_list, bfqd_node) { + hlist_del(&bfqg->bfqd_node); + + __bfq_deactivate_entity(bfqg->my_entity, 0); + + /* + * Don't remove from the group hash, just set an + * invalid key. No lookups can race with the + * assignment as bfqd is being destroyed; this + * implies also that new elements cannot be added + * to the list. + */ + rcu_assign_pointer(bfqg->bfqd, NULL); + + bfq_log(bfqd, "disconnect_groups: put async for group %p", + bfqg); + bfq_put_async_queues(bfqd, bfqg); + } +} + +static inline void bfq_free_root_group(struct bfq_data *bfqd) +{ + struct bfqio_cgroup *bgrp = &bfqio_root_cgroup; + struct bfq_group *bfqg = bfqd->root_group; + + bfq_put_async_queues(bfqd, bfqg); + + spin_lock_irq(&bgrp->lock); + hlist_del_rcu(&bfqg->group_node); + spin_unlock_irq(&bgrp->lock); + + /* + * No need to synchronize_rcu() here: since the device is gone + * there cannot be any read-side access to its root_group. + */ + kfree(bfqg); +} + +static struct bfq_group *bfq_alloc_root_group(struct bfq_data *bfqd, int node) +{ + struct bfq_group *bfqg; + struct bfqio_cgroup *bgrp; + int i; + + bfqg = kzalloc_node(sizeof(*bfqg), GFP_KERNEL, node); + if (bfqg == NULL) + return NULL; + + bfqg->entity.parent = NULL; + for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) + bfqg->sched_data.service_tree[i] = BFQ_SERVICE_TREE_INIT; + + bgrp = &bfqio_root_cgroup; + spin_lock_irq(&bgrp->lock); + rcu_assign_pointer(bfqg->bfqd, bfqd); + hlist_add_head_rcu(&bfqg->group_node, &bgrp->group_data); + spin_unlock_irq(&bgrp->lock); + + return bfqg; +} + +#define SHOW_FUNCTION(__VAR) \ +static u64 bfqio_cgroup_##__VAR##_read(struct cgroup *cgroup, \ + struct cftype *cftype) \ +{ \ + struct bfqio_cgroup *bgrp; \ + u64 ret; \ + \ + if (!cgroup_lock_live_group(cgroup)) \ + return -ENODEV; \ + \ + bgrp = cgroup_to_bfqio(cgroup); \ + spin_lock_irq(&bgrp->lock); \ + ret = bgrp->__VAR; \ + spin_unlock_irq(&bgrp->lock); \ + \ + cgroup_unlock(); \ + \ + return ret; \ +} + +SHOW_FUNCTION(weight); +SHOW_FUNCTION(ioprio); +SHOW_FUNCTION(ioprio_class); +#undef SHOW_FUNCTION + +#define STORE_FUNCTION(__VAR, __MIN, __MAX) \ +static int bfqio_cgroup_##__VAR##_write(struct cgroup *cgroup, \ + struct cftype *cftype, \ + u64 val) \ +{ \ + struct bfqio_cgroup *bgrp; \ + struct bfq_group *bfqg; \ + struct hlist_node *n; \ + \ + if (val < (__MIN) || val > (__MAX)) \ + return -EINVAL; \ + \ + if (!cgroup_lock_live_group(cgroup)) \ + return -ENODEV; \ + \ + bgrp = cgroup_to_bfqio(cgroup); \ + \ + spin_lock_irq(&bgrp->lock); \ + bgrp->__VAR = (unsigned short)val; \ + hlist_for_each_entry(bfqg, n, &bgrp->group_data, group_node) { \ + /* \ + * Setting the ioprio_changed flag of the entity \ + * to 1 with new_##__VAR == ##__VAR would re-set \ + * the value of the weight to its ioprio mapping. \ + * Set the flag only if necessary. \ + */ \ + if ((unsigned short)val != bfqg->entity.new_##__VAR) { \ + bfqg->entity.new_##__VAR = (unsigned short)val; \ + /* \ + * Make sure that the above new value has been \ + * stored in bfqg->entity.new_##__VAR before \ + * setting the ioprio_changed flag. In fact, \ + * this flag may be read asynchronously (in \ + * critical sections protected by a different \ + * lock than that held here), and finding this \ + * flag set may cause the execution of the code \ + * for updating parameters whose value may \ + * depend also on bfqg->entity.new_##__VAR (in \ + * __bfq_entity_update_weight_prio). \ + * This barrier makes sure that the new value \ + * of bfqg->entity.new_##__VAR is correctly \ + * seen in that code. \ + */ \ + smp_wmb(); \ + bfqg->entity.ioprio_changed = 1; \ + } \ + } \ + spin_unlock_irq(&bgrp->lock); \ + \ + cgroup_unlock(); \ + \ + return 0; \ +} + +STORE_FUNCTION(weight, BFQ_MIN_WEIGHT, BFQ_MAX_WEIGHT); +STORE_FUNCTION(ioprio, 0, IOPRIO_BE_NR - 1); +STORE_FUNCTION(ioprio_class, IOPRIO_CLASS_RT, IOPRIO_CLASS_IDLE); +#undef STORE_FUNCTION + +static struct cftype bfqio_files[] = { + { + .name = "weight", + .read_u64 = bfqio_cgroup_weight_read, + .write_u64 = bfqio_cgroup_weight_write, + }, + { + .name = "ioprio", + .read_u64 = bfqio_cgroup_ioprio_read, + .write_u64 = bfqio_cgroup_ioprio_write, + }, + { + .name = "ioprio_class", + .read_u64 = bfqio_cgroup_ioprio_class_read, + .write_u64 = bfqio_cgroup_ioprio_class_write, + }, +}; + +static int bfqio_populate(struct cgroup_subsys *subsys, struct cgroup *cgroup) +{ + return cgroup_add_files(cgroup, subsys, bfqio_files, + ARRAY_SIZE(bfqio_files)); +} + +static struct cgroup_subsys_state *bfqio_create(struct cgroup *cgroup) +{ + struct bfqio_cgroup *bgrp; + + if (cgroup->parent != NULL) { + bgrp = kzalloc(sizeof(*bgrp), GFP_KERNEL); + if (bgrp == NULL) + return ERR_PTR(-ENOMEM); + } else + bgrp = &bfqio_root_cgroup; + + spin_lock_init(&bgrp->lock); + INIT_HLIST_HEAD(&bgrp->group_data); + bgrp->ioprio = BFQ_DEFAULT_GRP_IOPRIO; + bgrp->ioprio_class = BFQ_DEFAULT_GRP_CLASS; + + return &bgrp->css; +} + +/* + * We cannot support shared io contexts, as we have no means to support + * two tasks with the same ioc in two different groups without major rework + * of the main bic/bfqq data structures. By now we allow a task to change + * its cgroup only if it's the only owner of its ioc; the drawback of this + * behavior is that a group containing a task that forked using CLONE_IO + * will not be destroyed until the tasks sharing the ioc die. + */ +static int bfqio_can_attach(struct cgroup *cgroup, struct cgroup_taskset *tset) +{ + struct task_struct *task; + struct io_context *ioc; + int ret = 0; + + cgroup_taskset_for_each(task, cgroup, tset) { + /* task_lock() is needed to avoid races with exit_io_context() */ + task_lock(task); + ioc = task->io_context; + if (ioc != NULL && atomic_read(&ioc->nr_tasks) > 1) + /* + * ioc == NULL means that the task is either too + * young or exiting: if it has still no ioc the + * ioc can't be shared, if the task is exiting the + * attach will fail anyway, no matter what we + * return here. + */ + ret = -EINVAL; + task_unlock(task); + if (ret) + break; + } + + return ret; +} + +static void bfqio_attach(struct cgroup *cgroup, struct cgroup_taskset *tset) +{ + struct task_struct *task; + struct io_context *ioc; + struct io_cq *icq; + struct hlist_node *n; + + /* + * IMPORTANT NOTE: The move of more than one process at a time to a + * new group has not yet been tested. + */ + cgroup_taskset_for_each(task, cgroup, tset) { + ioc = get_task_io_context(task, GFP_ATOMIC, NUMA_NO_NODE); + if (ioc) { + /* + * Handle cgroup change here. + */ + rcu_read_lock(); + hlist_for_each_entry_rcu(icq, n, &ioc->icq_list, ioc_node) + if (!strncmp( + icq->q->elevator->type->elevator_name, + "bfq", ELV_NAME_MAX)) + bfq_bic_change_cgroup(icq_to_bic(icq), + cgroup); + rcu_read_unlock(); + put_io_context(ioc); + } + } +} + +static void bfqio_destroy(struct cgroup *cgroup) +{ + struct bfqio_cgroup *bgrp = cgroup_to_bfqio(cgroup); + struct hlist_node *n, *tmp; + struct bfq_group *bfqg; + + /* + * Since we are destroying the cgroup, there are no more tasks + * referencing it, and all the RCU grace periods that may have + * referenced it are ended (as the destruction of the parent + * cgroup is RCU-safe); bgrp->group_data will not be accessed by + * anything else and we don't need any synchronization. + */ + hlist_for_each_entry_safe(bfqg, n, tmp, &bgrp->group_data, group_node) + bfq_destroy_group(bgrp, bfqg); + + BUG_ON(!hlist_empty(&bgrp->group_data)); + + kfree(bgrp); +} + +struct cgroup_subsys bfqio_subsys = { + .name = "bfqio", + .create = bfqio_create, + .can_attach = bfqio_can_attach, + .attach = bfqio_attach, + .destroy = bfqio_destroy, + .populate = bfqio_populate, + .subsys_id = bfqio_subsys_id, +}; +#else +static inline void bfq_init_entity(struct bfq_entity *entity, + struct bfq_group *bfqg) +{ + entity->weight = entity->new_weight; + entity->orig_weight = entity->new_weight; + entity->ioprio = entity->new_ioprio; + entity->ioprio_class = entity->new_ioprio_class; + entity->sched_data = &bfqg->sched_data; +} + +static inline struct bfq_group * +bfq_bic_update_cgroup(struct bfq_io_cq *bic) +{ + struct bfq_data *bfqd = bic_to_bfqd(bic); + return bfqd->root_group; +} + +static inline void bfq_bfqq_move(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + struct bfq_entity *entity, + struct bfq_group *bfqg) +{ +} + +static void bfq_end_wr_async(struct bfq_data *bfqd) +{ + bfq_end_wr_async_queues(bfqd, bfqd->root_group); +} + +static inline void bfq_disconnect_groups(struct bfq_data *bfqd) +{ + bfq_put_async_queues(bfqd, bfqd->root_group); +} + +static inline void bfq_free_root_group(struct bfq_data *bfqd) +{ + kfree(bfqd->root_group); +} + +static struct bfq_group *bfq_alloc_root_group(struct bfq_data *bfqd, int node) +{ + struct bfq_group *bfqg; + int i; + + bfqg = kmalloc_node(sizeof(*bfqg), GFP_KERNEL | __GFP_ZERO, node); + if (bfqg == NULL) + return NULL; + + for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) + bfqg->sched_data.service_tree[i] = BFQ_SERVICE_TREE_INIT; + + return bfqg; +} +#endif diff --git a/block/bfq-ioc.c b/block/bfq-ioc.c new file mode 100644 index 00000000..7f6b0004 --- /dev/null +++ b/block/bfq-ioc.c @@ -0,0 +1,36 @@ +/* + * BFQ: I/O context handling. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + * + * Copyright (C) 2010 Paolo Valente + */ + +/** + * icq_to_bic - convert iocontext queue structure to bfq_io_cq. + * @icq: the iocontext queue. + */ +static inline struct bfq_io_cq *icq_to_bic(struct io_cq *icq) +{ + /* bic->icq is the first member, %NULL will convert to %NULL */ + return container_of(icq, struct bfq_io_cq, icq); +} + +/** + * bfq_bic_lookup - search into @ioc a bic associated to @bfqd. + * @bfqd: the lookup key. + * @ioc: the io_context of the process doing I/O. + * + * Queue lock must be held. + */ +static inline struct bfq_io_cq *bfq_bic_lookup(struct bfq_data *bfqd, + struct io_context *ioc) +{ + if (ioc) + return icq_to_bic(ioc_lookup_icq(ioc, bfqd->queue)); + return NULL; +} diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c new file mode 100644 index 00000000..ab55091e --- /dev/null +++ b/block/bfq-iosched.c @@ -0,0 +1,4207 @@ +/* + * Budget Fair Queueing (BFQ) disk scheduler. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + * + * Copyright (C) 2010 Paolo Valente + * + * Licensed under the GPL-2 as detailed in the accompanying COPYING.BFQ + * file. + * + * BFQ is a proportional-share storage-I/O scheduling algorithm based on + * the slice-by-slice service scheme of CFQ. But BFQ assigns budgets, + * measured in number of sectors, to processes instead of time slices. The + * device is not granted to the in-service process for a given time slice, + * but until it has exhausted its assigned budget. This change from the time + * to the service domain allows BFQ to distribute the device throughput + * among processes as desired, without any distortion due to ZBR, workload + * fluctuations or other factors. BFQ uses an ad hoc internal scheduler, + * called B-WF2Q+, to schedule processes according to their budgets. More + * precisely, BFQ schedules queues associated to processes. Thanks to the + * accurate policy of B-WF2Q+, BFQ can afford to assign high budgets to + * I/O-bound processes issuing sequential requests (to boost the + * throughput), and yet guarantee a low latency to interactive and soft + * real-time applications. + * + * BFQ is described in [1], where also a reference to the initial, more + * theoretical paper on BFQ can be found. The interested reader can find + * in the latter paper full details on the main algorithm, as well as + * formulas of the guarantees and formal proofs of all the properties. + * With respect to the version of BFQ presented in these papers, this + * implementation adds a few more heuristics, such as the one that + * guarantees a low latency to soft real-time applications, and a + * hierarchical extension based on H-WF2Q+. + * + * B-WF2Q+ is based on WF2Q+, that is described in [2], together with + * H-WF2Q+, while the augmented tree used to implement B-WF2Q+ with O(log N) + * complexity derives from the one introduced with EEVDF in [3]. + * + * [1] P. Valente and M. Andreolini, ``Improving Application Responsiveness + * with the BFQ Disk I/O Scheduler'', + * Proceedings of the 5th Annual International Systems and Storage + * Conference (SYSTOR '12), June 2012. + * + * http://algogroup.unimo.it/people/paolo/disk_sched/bf1-v1-suite-results.pdf + * + * [2] Jon C.R. Bennett and H. Zhang, ``Hierarchical Packet Fair Queueing + * Algorithms,'' IEEE/ACM Transactions on Networking, 5(5):675-689, + * Oct 1997. + * + * http://www.cs.cmu.edu/~hzhang/papers/TON-97-Oct.ps.gz + * + * [3] I. Stoica and H. Abdel-Wahab, ``Earliest Eligible Virtual Deadline + * First: A Flexible and Accurate Mechanism for Proportional Share + * Resource Allocation,'' technical report. + * + * http://www.cs.berkeley.edu/~istoica/papers/eevdf-tr-95.pdf + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "bfq.h" +#include "blk.h" + +/* Expiration time of sync (0) and async (1) requests, in jiffies. */ +static const int bfq_fifo_expire[2] = { HZ / 4, HZ / 8 }; + +/* Maximum backwards seek, in KiB. */ +static const int bfq_back_max = 16 * 1024; + +/* Penalty of a backwards seek, in number of sectors. */ +static const int bfq_back_penalty = 2; + +/* Idling period duration, in jiffies. */ +static int bfq_slice_idle = 0; + +/* Default maximum budget values, in sectors and number of requests. */ +static const int bfq_default_max_budget = 16 * 1024; +static const int bfq_max_budget_async_rq = 4; + +/* + * Async to sync throughput distribution is controlled as follows: + * when an async request is served, the entity is charged the number + * of sectors of the request, multiplied by the factor below + */ +static const int bfq_async_charge_factor = 10; + +/* Default timeout values, in jiffies, approximating CFQ defaults. */ +static const int bfq_timeout_sync = HZ / 8; +static int bfq_timeout_async = HZ / 25; + +struct kmem_cache *bfq_pool; + +/* Below this threshold (in ms), we consider thinktime immediate. */ +#define BFQ_MIN_TT 2 + +/* hw_tag detection: parallel requests threshold and min samples needed. */ +#define BFQ_HW_QUEUE_THRESHOLD 4 +#define BFQ_HW_QUEUE_SAMPLES 32 + +#define BFQQ_SEEK_THR (sector_t)(8 * 1024) +#define BFQQ_SEEKY(bfqq) ((bfqq)->seek_mean > BFQQ_SEEK_THR) + +/* Min samples used for peak rate estimation (for autotuning). */ +#define BFQ_PEAK_RATE_SAMPLES 32 + +/* Shift used for peak rate fixed precision calculations. */ +#define BFQ_RATE_SHIFT 16 + +/* + * By default, BFQ computes the duration of the weight raising for + * interactive applications automatically, using the following formula: + * duration = (R / r) * T, where r is the peak rate of the device, and + * R and T are two reference parameters. + * In particular, R is the peak rate of the reference device (see below), + * and T is a reference time: given the systems that are likely to be + * installed on the reference device according to its speed class, T is + * about the maximum time needed, under BFQ and while reading two files in + * parallel, to load typical large applications on these systems. + * In practice, the slower/faster the device at hand is, the more/less it + * takes to load applications with respect to the reference device. + * Accordingly, the longer/shorter BFQ grants weight raising to interactive + * applications. + * + * BFQ uses four different reference pairs (R, T), depending on: + * . whether the device is rotational or non-rotational; + * . whether the device is slow, such as old or portable HDDs, as well as + * SD cards, or fast, such as newer HDDs and SSDs. + * + * The device's speed class is dynamically (re)detected in + * bfq_update_peak_rate() every time the estimated peak rate is updated. + * + * In the following definitions, R_slow[0]/R_fast[0] and T_slow[0]/T_fast[0] + * are the reference values for a slow/fast rotational device, whereas + * R_slow[1]/R_fast[1] and T_slow[1]/T_fast[1] are the reference values for + * a slow/fast non-rotational device. Finally, device_speed_thresh are the + * thresholds used to switch between speed classes. + * Both the reference peak rates and the thresholds are measured in + * sectors/usec, left-shifted by BFQ_RATE_SHIFT. + */ +static int R_slow[2] = {1536, 10752}; +static int R_fast[2] = {17415, 34791}; +/* + * To improve readability, a conversion function is used to initialize the + * following arrays, which entails that they can be initialized only in a + * function. + */ +static int T_slow[2]; +static int T_fast[2]; +static int device_speed_thresh[2]; + +#define BFQ_SERVICE_TREE_INIT ((struct bfq_service_tree) \ + { RB_ROOT, RB_ROOT, NULL, NULL, 0, 0 }) + +#define RQ_BIC(rq) ((struct bfq_io_cq *) (rq)->elv.priv[0]) +#define RQ_BFQQ(rq) ((rq)->elv.priv[1]) + +static inline void bfq_schedule_dispatch(struct bfq_data *bfqd); + +#include "bfq-ioc.c" +#include "bfq-sched.c" +#include "bfq-cgroup.c" + +#define bfq_class_idle(bfqq) ((bfqq)->entity.ioprio_class ==\ + IOPRIO_CLASS_IDLE) +#define bfq_class_rt(bfqq) ((bfqq)->entity.ioprio_class ==\ + IOPRIO_CLASS_RT) + +#define bfq_sample_valid(samples) ((samples) > 80) + +/* + * The following macro groups conditions that need to be evaluated when + * checking if existing queues and groups form a symmetric scenario + * and therefore idling can be reduced or disabled for some of the + * queues. See the comment to the function bfq_bfqq_must_not_expire() + * for further details. + */ +#ifdef CONFIG_CGROUP_BFQIO +#define symmetric_scenario (!bfqd->active_numerous_groups && \ + !bfq_differentiated_weights(bfqd)) +#else +#define symmetric_scenario (!bfq_differentiated_weights(bfqd)) +#endif + +/* + * We regard a request as SYNC, if either it's a read or has the SYNC bit + * set (in which case it could also be a direct WRITE). + */ +static inline int bfq_bio_sync(struct bio *bio) +{ + if (bio_data_dir(bio) == READ || (bio->bi_rw & REQ_SYNC)) + return 1; + + return 0; +} + +/* + * Scheduler run of queue, if there are requests pending and no one in the + * driver that will restart queueing. + */ +static inline void bfq_schedule_dispatch(struct bfq_data *bfqd) +{ + if (bfqd->queued != 0) { + bfq_log(bfqd, "schedule dispatch"); + kblockd_schedule_work(bfqd->queue, &bfqd->unplug_work); + } +} + +/* + * Lifted from AS - choose which of rq1 and rq2 that is best served now. + * We choose the request that is closesr to the head right now. Distance + * behind the head is penalized and only allowed to a certain extent. + */ +static struct request *bfq_choose_req(struct bfq_data *bfqd, + struct request *rq1, + struct request *rq2, + sector_t last) +{ + sector_t s1, s2, d1 = 0, d2 = 0; + unsigned long back_max; +#define BFQ_RQ1_WRAP 0x01 /* request 1 wraps */ +#define BFQ_RQ2_WRAP 0x02 /* request 2 wraps */ + unsigned wrap = 0; /* bit mask: requests behind the disk head? */ + + if (rq1 == NULL || rq1 == rq2) + return rq2; + if (rq2 == NULL) + return rq1; + + if (rq_is_sync(rq1) && !rq_is_sync(rq2)) + return rq1; + else if (rq_is_sync(rq2) && !rq_is_sync(rq1)) + return rq2; + if ((rq1->cmd_flags & REQ_META) && !(rq2->cmd_flags & REQ_META)) + return rq1; + else if ((rq2->cmd_flags & REQ_META) && !(rq1->cmd_flags & REQ_META)) + return rq2; + + s1 = blk_rq_pos(rq1); + s2 = blk_rq_pos(rq2); + + /* + * By definition, 1KiB is 2 sectors. + */ + back_max = bfqd->bfq_back_max * 2; + + /* + * Strict one way elevator _except_ in the case where we allow + * short backward seeks which are biased as twice the cost of a + * similar forward seek. + */ + if (s1 >= last) + d1 = s1 - last; + else if (s1 + back_max >= last) + d1 = (last - s1) * bfqd->bfq_back_penalty; + else + wrap |= BFQ_RQ1_WRAP; + + if (s2 >= last) + d2 = s2 - last; + else if (s2 + back_max >= last) + d2 = (last - s2) * bfqd->bfq_back_penalty; + else + wrap |= BFQ_RQ2_WRAP; + + /* Found required data */ + + /* + * By doing switch() on the bit mask "wrap" we avoid having to + * check two variables for all permutations: --> faster! + */ + switch (wrap) { + case 0: /* common case for CFQ: rq1 and rq2 not wrapped */ + if (d1 < d2) + return rq1; + else if (d2 < d1) + return rq2; + else { + if (s1 >= s2) + return rq1; + else + return rq2; + } + + case BFQ_RQ2_WRAP: + return rq1; + case BFQ_RQ1_WRAP: + return rq2; + case (BFQ_RQ1_WRAP|BFQ_RQ2_WRAP): /* both rqs wrapped */ + default: + /* + * Since both rqs are wrapped, + * start with the one that's further behind head + * (--> only *one* back seek required), + * since back seek takes more time than forward. + */ + if (s1 <= s2) + return rq1; + else + return rq2; + } +} + +static struct bfq_queue * +bfq_rq_pos_tree_lookup(struct bfq_data *bfqd, struct rb_root *root, + sector_t sector, struct rb_node **ret_parent, + struct rb_node ***rb_link) +{ + struct rb_node **p, *parent; + struct bfq_queue *bfqq = NULL; + + parent = NULL; + p = &root->rb_node; + while (*p) { + struct rb_node **n; + + parent = *p; + bfqq = rb_entry(parent, struct bfq_queue, pos_node); + + /* + * Sort strictly based on sector. Smallest to the left, + * largest to the right. + */ + if (sector > blk_rq_pos(bfqq->next_rq)) + n = &(*p)->rb_right; + else if (sector < blk_rq_pos(bfqq->next_rq)) + n = &(*p)->rb_left; + else + break; + p = n; + bfqq = NULL; + } + + *ret_parent = parent; + if (rb_link) + *rb_link = p; + + bfq_log(bfqd, "rq_pos_tree_lookup %llu: returning %d", + (long long unsigned)sector, + bfqq != NULL ? bfqq->pid : 0); + + return bfqq; +} + +static void bfq_rq_pos_tree_add(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + struct rb_node **p, *parent; + struct bfq_queue *__bfqq; + + if (bfqq->pos_root != NULL) { + rb_erase(&bfqq->pos_node, bfqq->pos_root); + bfqq->pos_root = NULL; + } + + if (bfq_class_idle(bfqq)) + return; + if (!bfqq->next_rq) + return; + + bfqq->pos_root = &bfqd->rq_pos_tree; + __bfqq = bfq_rq_pos_tree_lookup(bfqd, bfqq->pos_root, + blk_rq_pos(bfqq->next_rq), &parent, &p); + if (__bfqq == NULL) { + rb_link_node(&bfqq->pos_node, parent, p); + rb_insert_color(&bfqq->pos_node, bfqq->pos_root); + } else + bfqq->pos_root = NULL; +} + +/* + * Tell whether there are active queues or groups with differentiated weights. + */ +static inline bool bfq_differentiated_weights(struct bfq_data *bfqd) +{ + /* + * For weights to differ, at least one of the trees must contain + * at least two nodes. + */ + return (!RB_EMPTY_ROOT(&bfqd->queue_weights_tree) && + (bfqd->queue_weights_tree.rb_node->rb_left || + bfqd->queue_weights_tree.rb_node->rb_right) +#ifdef CONFIG_CGROUP_BFQIO + ) || + (!RB_EMPTY_ROOT(&bfqd->group_weights_tree) && + (bfqd->group_weights_tree.rb_node->rb_left || + bfqd->group_weights_tree.rb_node->rb_right) +#endif + ); +} + +/* + * If the weight-counter tree passed as input contains no counter for + * the weight of the input entity, then add that counter; otherwise just + * increment the existing counter. + * + * Note that weight-counter trees contain few nodes in mostly symmetric + * scenarios. For example, if all queues have the same weight, then the + * weight-counter tree for the queues may contain at most one node. + * This holds even if low_latency is on, because weight-raised queues + * are not inserted in the tree. + * In most scenarios, the rate at which nodes are created/destroyed + * should be low too. + */ +static void bfq_weights_tree_add(struct bfq_data *bfqd, + struct bfq_entity *entity, + struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* + * Do not insert if the entity is already associated with a + * counter, which happens if: + * 1) the entity is associated with a queue, + * 2) a request arrival has caused the queue to become both + * non-weight-raised, and hence change its weight, and + * backlogged; in this respect, each of the two events + * causes an invocation of this function, + * 3) this is the invocation of this function caused by the + * second event. This second invocation is actually useless, + * and we handle this fact by exiting immediately. More + * efficient or clearer solutions might possibly be adopted. + */ + if (entity->weight_counter) + return; + + while (*new) { + struct bfq_weight_counter *__counter = container_of(*new, + struct bfq_weight_counter, + weights_node); + parent = *new; + + if (entity->weight == __counter->weight) { + entity->weight_counter = __counter; + goto inc_counter; + } + if (entity->weight < __counter->weight) + new = &((*new)->rb_left); + else + new = &((*new)->rb_right); + } + + entity->weight_counter = kzalloc(sizeof(struct bfq_weight_counter), + GFP_ATOMIC); + entity->weight_counter->weight = entity->weight; + rb_link_node(&entity->weight_counter->weights_node, parent, new); + rb_insert_color(&entity->weight_counter->weights_node, root); + +inc_counter: + entity->weight_counter->num_active++; +} + +/* + * Decrement the weight counter associated with the entity, and, if the + * counter reaches 0, remove the counter from the tree. + * See the comments to the function bfq_weights_tree_add() for considerations + * about overhead. + */ +static void bfq_weights_tree_remove(struct bfq_data *bfqd, + struct bfq_entity *entity, + struct rb_root *root) +{ + if (!entity->weight_counter) + return; + + BUG_ON(RB_EMPTY_ROOT(root)); + BUG_ON(entity->weight_counter->weight != entity->weight); + + BUG_ON(!entity->weight_counter->num_active); + entity->weight_counter->num_active--; + if (entity->weight_counter->num_active > 0) + goto reset_entity_pointer; + + rb_erase(&entity->weight_counter->weights_node, root); + kfree(entity->weight_counter); + +reset_entity_pointer: + entity->weight_counter = NULL; +} + +static struct request *bfq_find_next_rq(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + struct request *last) +{ + struct rb_node *rbnext = rb_next(&last->rb_node); + struct rb_node *rbprev = rb_prev(&last->rb_node); + struct request *next = NULL, *prev = NULL; + + BUG_ON(RB_EMPTY_NODE(&last->rb_node)); + + if (rbprev != NULL) + prev = rb_entry_rq(rbprev); + + if (rbnext != NULL) + next = rb_entry_rq(rbnext); + else { + rbnext = rb_first(&bfqq->sort_list); + if (rbnext && rbnext != &last->rb_node) + next = rb_entry_rq(rbnext); + } + + return bfq_choose_req(bfqd, next, prev, blk_rq_pos(last)); +} + +/* see the definition of bfq_async_charge_factor for details */ +static inline unsigned long bfq_serv_to_charge(struct request *rq, + struct bfq_queue *bfqq) +{ + return blk_rq_sectors(rq) * + (1 + ((!bfq_bfqq_sync(bfqq)) * (bfqq->wr_coeff == 1) * + bfq_async_charge_factor)); +} + +/** + * bfq_updated_next_req - update the queue after a new next_rq selection. + * @bfqd: the device data the queue belongs to. + * @bfqq: the queue to update. + * + * If the first request of a queue changes we make sure that the queue + * has enough budget to serve at least its first request (if the + * request has grown). We do this because if the queue has not enough + * budget for its first request, it has to go through two dispatch + * rounds to actually get it dispatched. + */ +static void bfq_updated_next_req(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + struct bfq_entity *entity = &bfqq->entity; + struct bfq_service_tree *st = bfq_entity_service_tree(entity); + struct request *next_rq = bfqq->next_rq; + unsigned long new_budget; + + if (next_rq == NULL) + return; + + if (bfqq == bfqd->in_service_queue) + /* + * In order not to break guarantees, budgets cannot be + * changed after an entity has been selected. + */ + return; + + BUG_ON(entity->tree != &st->active); + BUG_ON(entity == entity->sched_data->in_service_entity); + + new_budget = max_t(unsigned long, bfqq->max_budget, + bfq_serv_to_charge(next_rq, bfqq)); + if (entity->budget != new_budget) { + entity->budget = new_budget; + bfq_log_bfqq(bfqd, bfqq, "updated next rq: new budget %lu", + new_budget); + bfq_activate_bfqq(bfqd, bfqq); + } +} + +static inline unsigned int bfq_wr_duration(struct bfq_data *bfqd) +{ + u64 dur; + + if (bfqd->bfq_wr_max_time > 0) + return bfqd->bfq_wr_max_time; + + dur = bfqd->RT_prod; + do_div(dur, bfqd->peak_rate); + + return dur; +} + +static inline unsigned +bfq_bfqq_cooperations(struct bfq_queue *bfqq) +{ + return bfqq->bic ? bfqq->bic->cooperations : 0; +} + +static inline void +bfq_bfqq_resume_state(struct bfq_queue *bfqq, struct bfq_io_cq *bic) +{ + if (bic->saved_idle_window) + bfq_mark_bfqq_idle_window(bfqq); + else + bfq_clear_bfqq_idle_window(bfqq); + if (bic->saved_IO_bound) + bfq_mark_bfqq_IO_bound(bfqq); + else + bfq_clear_bfqq_IO_bound(bfqq); + /* Assuming that the flag in_large_burst is already correctly set */ + if (bic->wr_time_left && bfqq->bfqd->low_latency && + !bfq_bfqq_in_large_burst(bfqq) && + bic->cooperations < bfqq->bfqd->bfq_coop_thresh) { + /* + * Start a weight raising period with the duration given by + * the raising_time_left snapshot. + */ + if (bfq_bfqq_busy(bfqq)) + bfqq->bfqd->wr_busy_queues++; + bfqq->wr_coeff = bfqq->bfqd->bfq_wr_coeff; + bfqq->wr_cur_max_time = bic->wr_time_left; + bfqq->last_wr_start_finish = jiffies; + bfqq->entity.ioprio_changed = 1; + } + /* + * Clear wr_time_left to prevent bfq_bfqq_save_state() from + * getting confused about the queue's need of a weight-raising + * period. + */ + bic->wr_time_left = 0; +} + +/* Must be called with the queue_lock held. */ +static int bfqq_process_refs(struct bfq_queue *bfqq) +{ + int process_refs, io_refs; + + io_refs = bfqq->allocated[READ] + bfqq->allocated[WRITE]; + process_refs = atomic_read(&bfqq->ref) - io_refs - bfqq->entity.on_st; + BUG_ON(process_refs < 0); + return process_refs; +} + +/* Empty burst list and add just bfqq (see comments to bfq_handle_burst) */ +static inline void bfq_reset_burst_list(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + struct bfq_queue *item; + struct hlist_node *pos, *n; + + hlist_for_each_entry_safe(item, pos, n, + &bfqd->burst_list, burst_list_node) + hlist_del_init(&item->burst_list_node); + hlist_add_head(&bfqq->burst_list_node, &bfqd->burst_list); + bfqd->burst_size = 1; +} + +/* Add bfqq to the list of queues in current burst (see bfq_handle_burst) */ +static void bfq_add_to_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + /* Increment burst size to take into account also bfqq */ + bfqd->burst_size++; + + if (bfqd->burst_size == bfqd->bfq_large_burst_thresh) { + struct bfq_queue *pos, *bfqq_item; + struct hlist_node *p, *n; + + /* + * Enough queues have been activated shortly after each + * other to consider this burst as large. + */ + bfqd->large_burst = true; + + /* + * We can now mark all queues in the burst list as + * belonging to a large burst. + */ + hlist_for_each_entry(bfqq_item, n, &bfqd->burst_list, + burst_list_node) + bfq_mark_bfqq_in_large_burst(bfqq_item); + bfq_mark_bfqq_in_large_burst(bfqq); + + /* + * From now on, and until the current burst finishes, any + * new queue being activated shortly after the last queue + * was inserted in the burst can be immediately marked as + * belonging to a large burst. So the burst list is not + * needed any more. Remove it. + */ + hlist_for_each_entry_safe(pos, p, n, &bfqd->burst_list, + burst_list_node) + hlist_del_init(&pos->burst_list_node); + } else /* burst not yet large: add bfqq to the burst list */ + hlist_add_head(&bfqq->burst_list_node, &bfqd->burst_list); +} + +/* + * If many queues happen to become active shortly after each other, then, + * to help the processes associated to these queues get their job done as + * soon as possible, it is usually better to not grant either weight-raising + * or device idling to these queues. In this comment we describe, firstly, + * the reasons why this fact holds, and, secondly, the next function, which + * implements the main steps needed to properly mark these queues so that + * they can then be treated in a different way. + * + * As for the terminology, we say that a queue becomes active, i.e., + * switches from idle to backlogged, either when it is created (as a + * consequence of the arrival of an I/O request), or, if already existing, + * when a new request for the queue arrives while the queue is idle. + * Bursts of activations, i.e., activations of different queues occurring + * shortly after each other, are typically caused by services or applications + * that spawn or reactivate many parallel threads/processes. Examples are + * systemd during boot or git grep. + * + * These services or applications benefit mostly from a high throughput: + * the quicker the requests of the activated queues are cumulatively served, + * the sooner the target job of these queues gets completed. As a consequence, + * weight-raising any of these queues, which also implies idling the device + * for it, is almost always counterproductive: in most cases it just lowers + * throughput. + * + * On the other hand, a burst of activations may be also caused by the start + * of an application that does not consist in a lot of parallel I/O-bound + * threads. In fact, with a complex application, the burst may be just a + * consequence of the fact that several processes need to be executed to + * start-up the application. To start an application as quickly as possible, + * the best thing to do is to privilege the I/O related to the application + * with respect to all other I/O. Therefore, the best strategy to start as + * quickly as possible an application that causes a burst of activations is + * to weight-raise all the queues activated during the burst. This is the + * exact opposite of the best strategy for the other type of bursts. + * + * In the end, to take the best action for each of the two cases, the two + * types of bursts need to be distinguished. Fortunately, this seems + * relatively easy to do, by looking at the sizes of the bursts. In + * particular, we found a threshold such that bursts with a larger size + * than that threshold are apparently caused only by services or commands + * such as systemd or git grep. For brevity, hereafter we call just 'large' + * these bursts. BFQ *does not* weight-raise queues whose activations occur + * in a large burst. In addition, for each of these queues BFQ performs or + * does not perform idling depending on which choice boosts the throughput + * most. The exact choice depends on the device and request pattern at + * hand. + * + * Turning back to the next function, it implements all the steps needed + * to detect the occurrence of a large burst and to properly mark all the + * queues belonging to it (so that they can then be treated in a different + * way). This goal is achieved by maintaining a special "burst list" that + * holds, temporarily, the queues that belong to the burst in progress. The + * list is then used to mark these queues as belonging to a large burst if + * the burst does become large. The main steps are the following. + * + * . when the very first queue is activated, the queue is inserted into the + * list (as it could be the first queue in a possible burst) + * + * . if the current burst has not yet become large, and a queue Q that does + * not yet belong to the burst is activated shortly after the last time + * at which a new queue entered the burst list, then the function appends + * Q to the burst list + * + * . if, as a consequence of the previous step, the burst size reaches + * the large-burst threshold, then + * + * . all the queues in the burst list are marked as belonging to a + * large burst + * + * . the burst list is deleted; in fact, the burst list already served + * its purpose (keeping temporarily track of the queues in a burst, + * so as to be able to mark them as belonging to a large burst in the + * previous sub-step), and now is not needed any more + * + * . the device enters a large-burst mode + * + * . if a queue Q that does not belong to the burst is activated while + * the device is in large-burst mode and shortly after the last time + * at which a queue either entered the burst list or was marked as + * belonging to the current large burst, then Q is immediately marked + * as belonging to a large burst. + * + * . if a queue Q that does not belong to the burst is activated a while + * later, i.e., not shortly after, than the last time at which a queue + * either entered the burst list or was marked as belonging to the + * current large burst, then the current burst is deemed as finished and: + * + * . the large-burst mode is reset if set + * + * . the burst list is emptied + * + * . Q is inserted in the burst list, as Q may be the first queue + * in a possible new burst (then the burst list contains just Q + * after this step). + */ +static void bfq_handle_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq, + bool idle_for_long_time) +{ + /* + * If bfqq happened to be activated in a burst, but has been idle + * for at least as long as an interactive queue, then we assume + * that, in the overall I/O initiated in the burst, the I/O + * associated to bfqq is finished. So bfqq does not need to be + * treated as a queue belonging to a burst anymore. Accordingly, + * we reset bfqq's in_large_burst flag if set, and remove bfqq + * from the burst list if it's there. We do not decrement instead + * burst_size, because the fact that bfqq does not need to belong + * to the burst list any more does not invalidate the fact that + * bfqq may have been activated during the current burst. + */ + if (idle_for_long_time) { + hlist_del_init(&bfqq->burst_list_node); + bfq_clear_bfqq_in_large_burst(bfqq); + } + + /* + * If bfqq is already in the burst list or is part of a large + * burst, then there is nothing else to do. + */ + if (!hlist_unhashed(&bfqq->burst_list_node) || + bfq_bfqq_in_large_burst(bfqq)) + return; + + /* + * If bfqq's activation happens late enough, then the current + * burst is finished, and related data structures must be reset. + * + * In this respect, consider the special case where bfqq is the very + * first queue being activated. In this case, last_ins_in_burst is + * not yet significant when we get here. But it is easy to verify + * that, whether or not the following condition is true, bfqq will + * end up being inserted into the burst list. In particular the + * list will happen to contain only bfqq. And this is exactly what + * has to happen, as bfqq may be the first queue in a possible + * burst. + */ + if (time_is_before_jiffies(bfqd->last_ins_in_burst + + bfqd->bfq_burst_interval)) { + bfqd->large_burst = false; + bfq_reset_burst_list(bfqd, bfqq); + return; + } + + /* + * If we get here, then bfqq is being activated shortly after the + * last queue. So, if the current burst is also large, we can mark + * bfqq as belonging to this large burst immediately. + */ + if (bfqd->large_burst) { + bfq_mark_bfqq_in_large_burst(bfqq); + return; + } + + /* + * If we get here, then a large-burst state has not yet been + * reached, but bfqq is being activated shortly after the last + * queue. Then we add bfqq to the burst. + */ + bfq_add_to_burst(bfqd, bfqq); +} + +static void bfq_add_request(struct request *rq) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq); + struct bfq_entity *entity = &bfqq->entity; + struct bfq_data *bfqd = bfqq->bfqd; + struct request *next_rq, *prev; + unsigned long old_wr_coeff = bfqq->wr_coeff; + bool interactive = false; + + bfq_log_bfqq(bfqd, bfqq, "add_request %d", rq_is_sync(rq)); + bfqq->queued[rq_is_sync(rq)]++; + bfqd->queued++; + + elv_rb_add(&bfqq->sort_list, rq); + + /* + * Check if this request is a better next-serve candidate. + */ + prev = bfqq->next_rq; + next_rq = bfq_choose_req(bfqd, bfqq->next_rq, rq, bfqd->last_position); + BUG_ON(next_rq == NULL); + bfqq->next_rq = next_rq; + + /* + * Adjust priority tree position, if next_rq changes. + */ + if (prev != bfqq->next_rq) + bfq_rq_pos_tree_add(bfqd, bfqq); + + if (!bfq_bfqq_busy(bfqq)) { + bool soft_rt, coop_or_in_burst, + idle_for_long_time = time_is_before_jiffies( + bfqq->budget_timeout + + bfqd->bfq_wr_min_idle_time); + + if (bfq_bfqq_sync(bfqq)) { + bool already_in_burst = + !hlist_unhashed(&bfqq->burst_list_node) || + bfq_bfqq_in_large_burst(bfqq); + bfq_handle_burst(bfqd, bfqq, idle_for_long_time); + /* + * If bfqq was not already in the current burst, + * then, at this point, bfqq either has been + * added to the current burst or has caused the + * current burst to terminate. In particular, in + * the second case, bfqq has become the first + * queue in a possible new burst. + * In both cases last_ins_in_burst needs to be + * moved forward. + */ + if (!already_in_burst) + bfqd->last_ins_in_burst = jiffies; + } + + coop_or_in_burst = bfq_bfqq_in_large_burst(bfqq) || + bfq_bfqq_cooperations(bfqq) >= bfqd->bfq_coop_thresh; + soft_rt = bfqd->bfq_wr_max_softrt_rate > 0 && + !coop_or_in_burst && + time_is_before_jiffies(bfqq->soft_rt_next_start); + interactive = !coop_or_in_burst && idle_for_long_time; + entity->budget = max_t(unsigned long, bfqq->max_budget, + bfq_serv_to_charge(next_rq, bfqq)); + + if (!bfq_bfqq_IO_bound(bfqq)) { + if (time_before(jiffies, + RQ_BIC(rq)->ttime.last_end_request + + bfqd->bfq_slice_idle)) { + bfqq->requests_within_timer++; + if (bfqq->requests_within_timer >= + bfqd->bfq_requests_within_timer) + bfq_mark_bfqq_IO_bound(bfqq); + } else + bfqq->requests_within_timer = 0; + } + + if (!bfqd->low_latency) + goto add_bfqq_busy; + + if (bfq_bfqq_just_split(bfqq)) + goto set_ioprio_changed; + + /* + * If the queue: + * - is not being boosted, + * - has been idle for enough time, + * - is not a sync queue or is linked to a bfq_io_cq (it is + * shared "for its nature" or it is not shared and its + * requests have not been redirected to a shared queue) + * start a weight-raising period. + */ + if (old_wr_coeff == 1 && (interactive || soft_rt) && + (!bfq_bfqq_sync(bfqq) || bfqq->bic != NULL)) { + bfqq->wr_coeff = bfqd->bfq_wr_coeff; + if (interactive) + bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); + else + bfqq->wr_cur_max_time = + bfqd->bfq_wr_rt_max_time; + bfq_log_bfqq(bfqd, bfqq, + "wrais starting at %lu, rais_max_time %u", + jiffies, + jiffies_to_msecs(bfqq->wr_cur_max_time)); + } else if (old_wr_coeff > 1) { + if (interactive) + bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); + else if (coop_or_in_burst || + (bfqq->wr_cur_max_time == + bfqd->bfq_wr_rt_max_time && + !soft_rt)) { + bfqq->wr_coeff = 1; + bfq_log_bfqq(bfqd, bfqq, + "wrais ending at %lu, rais_max_time %u", + jiffies, + jiffies_to_msecs(bfqq-> + wr_cur_max_time)); + } else if (time_before( + bfqq->last_wr_start_finish + + bfqq->wr_cur_max_time, + jiffies + + bfqd->bfq_wr_rt_max_time) && + soft_rt) { + /* + * + * The remaining weight-raising time is lower + * than bfqd->bfq_wr_rt_max_time, which means + * that the application is enjoying weight + * raising either because deemed soft-rt in + * the near past, or because deemed interactive + * a long ago. + * In both cases, resetting now the current + * remaining weight-raising time for the + * application to the weight-raising duration + * for soft rt applications would not cause any + * latency increase for the application (as the + * new duration would be higher than the + * remaining time). + * + * In addition, the application is now meeting + * the requirements for being deemed soft rt. + * In the end we can correctly and safely + * (re)charge the weight-raising duration for + * the application with the weight-raising + * duration for soft rt applications. + * + * In particular, doing this recharge now, i.e., + * before the weight-raising period for the + * application finishes, reduces the probability + * of the following negative scenario: + * 1) the weight of a soft rt application is + * raised at startup (as for any newly + * created application), + * 2) since the application is not interactive, + * at a certain time weight-raising is + * stopped for the application, + * 3) at that time the application happens to + * still have pending requests, and hence + * is destined to not have a chance to be + * deemed soft rt before these requests are + * completed (see the comments to the + * function bfq_bfqq_softrt_next_start() + * for details on soft rt detection), + * 4) these pending requests experience a high + * latency because the application is not + * weight-raised while they are pending. + */ + bfqq->last_wr_start_finish = jiffies; + bfqq->wr_cur_max_time = + bfqd->bfq_wr_rt_max_time; + } + } +set_ioprio_changed: + if (old_wr_coeff != bfqq->wr_coeff) + entity->ioprio_changed = 1; +add_bfqq_busy: + bfqq->last_idle_bklogged = jiffies; + bfqq->service_from_backlogged = 0; + bfq_clear_bfqq_softrt_update(bfqq); + bfq_add_bfqq_busy(bfqd, bfqq); + } else { + if (bfqd->low_latency && old_wr_coeff == 1 && !rq_is_sync(rq) && + time_is_before_jiffies( + bfqq->last_wr_start_finish + + bfqd->bfq_wr_min_inter_arr_async)) { + bfqq->wr_coeff = bfqd->bfq_wr_coeff; + bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); + + bfqd->wr_busy_queues++; + entity->ioprio_changed = 1; + bfq_log_bfqq(bfqd, bfqq, + "non-idle wrais starting at %lu, rais_max_time %u", + jiffies, + jiffies_to_msecs(bfqq->wr_cur_max_time)); + } + if (prev != bfqq->next_rq) + bfq_updated_next_req(bfqd, bfqq); + } + + if (bfqd->low_latency && + (old_wr_coeff == 1 || bfqq->wr_coeff == 1 || interactive)) + bfqq->last_wr_start_finish = jiffies; +} + +static struct request *bfq_find_rq_fmerge(struct bfq_data *bfqd, + struct bio *bio) +{ + struct task_struct *tsk = current; + struct bfq_io_cq *bic; + struct bfq_queue *bfqq; + + bic = bfq_bic_lookup(bfqd, tsk->io_context); + if (bic == NULL) + return NULL; + + bfqq = bic_to_bfqq(bic, bfq_bio_sync(bio)); + if (bfqq != NULL) { + sector_t sector = bio->bi_sector + bio_sectors(bio); + + return elv_rb_find(&bfqq->sort_list, sector); + } + + return NULL; +} + +static void bfq_activate_request(struct request_queue *q, struct request *rq) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + + bfqd->rq_in_driver++; + bfqd->last_position = blk_rq_pos(rq) + blk_rq_sectors(rq); + bfq_log(bfqd, "activate_request: new bfqd->last_position %llu", + (long long unsigned)bfqd->last_position); +} + +static inline void bfq_deactivate_request(struct request_queue *q, + struct request *rq) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + + BUG_ON(bfqd->rq_in_driver == 0); + bfqd->rq_in_driver--; +} + +static void bfq_remove_request(struct request *rq) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq); + struct bfq_data *bfqd = bfqq->bfqd; + const int sync = rq_is_sync(rq); + + if (bfqq->next_rq == rq) { + bfqq->next_rq = bfq_find_next_rq(bfqd, bfqq, rq); + bfq_updated_next_req(bfqd, bfqq); + } + + if (rq->queuelist.prev != &rq->queuelist) + list_del_init(&rq->queuelist); + BUG_ON(bfqq->queued[sync] == 0); + bfqq->queued[sync]--; + bfqd->queued--; + elv_rb_del(&bfqq->sort_list, rq); + + if (RB_EMPTY_ROOT(&bfqq->sort_list)) { + if (bfq_bfqq_busy(bfqq) && bfqq != bfqd->in_service_queue) + bfq_del_bfqq_busy(bfqd, bfqq, 1); + /* + * Remove queue from request-position tree as it is empty. + */ + if (bfqq->pos_root != NULL) { + rb_erase(&bfqq->pos_node, bfqq->pos_root); + bfqq->pos_root = NULL; + } + } + + if (rq->cmd_flags & REQ_META) { + BUG_ON(bfqq->meta_pending == 0); + bfqq->meta_pending--; + } +} + +static int bfq_merge(struct request_queue *q, struct request **req, + struct bio *bio) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct request *__rq; + + __rq = bfq_find_rq_fmerge(bfqd, bio); + if (__rq != NULL && elv_rq_merge_ok(__rq, bio)) { + *req = __rq; + return ELEVATOR_FRONT_MERGE; + } + + return ELEVATOR_NO_MERGE; +} + +static void bfq_merged_request(struct request_queue *q, struct request *req, + int type) +{ + if (type == ELEVATOR_FRONT_MERGE && + rb_prev(&req->rb_node) && + blk_rq_pos(req) < + blk_rq_pos(container_of(rb_prev(&req->rb_node), + struct request, rb_node))) { + struct bfq_queue *bfqq = RQ_BFQQ(req); + struct bfq_data *bfqd = bfqq->bfqd; + struct request *prev, *next_rq; + + /* Reposition request in its sort_list */ + elv_rb_del(&bfqq->sort_list, req); + elv_rb_add(&bfqq->sort_list, req); + /* Choose next request to be served for bfqq */ + prev = bfqq->next_rq; + next_rq = bfq_choose_req(bfqd, bfqq->next_rq, req, + bfqd->last_position); + BUG_ON(next_rq == NULL); + bfqq->next_rq = next_rq; + /* + * If next_rq changes, update both the queue's budget to + * fit the new request and the queue's position in its + * rq_pos_tree. + */ + if (prev != bfqq->next_rq) { + bfq_updated_next_req(bfqd, bfqq); + bfq_rq_pos_tree_add(bfqd, bfqq); + } + } +} + +static void bfq_merged_requests(struct request_queue *q, struct request *rq, + struct request *next) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq), *next_bfqq = RQ_BFQQ(next); + + /* + * If next and rq belong to the same bfq_queue and next is older + * than rq, then reposition rq in the fifo (by substituting next + * with rq). Otherwise, if next and rq belong to different + * bfq_queues, never reposition rq: in fact, we would have to + * reposition it with respect to next's position in its own fifo, + * which would most certainly be too expensive with respect to + * the benefits. + */ + if (bfqq == next_bfqq && + !list_empty(&rq->queuelist) && !list_empty(&next->queuelist) && + time_before(rq_fifo_time(next), rq_fifo_time(rq))) { + list_del_init(&rq->queuelist); + list_replace_init(&next->queuelist, &rq->queuelist); + rq_set_fifo_time(rq, rq_fifo_time(next)); + } + + if (bfqq->next_rq == next) + bfqq->next_rq = rq; + + bfq_remove_request(next); +} + +/* Must be called with bfqq != NULL */ +static inline void bfq_bfqq_end_wr(struct bfq_queue *bfqq) +{ + BUG_ON(bfqq == NULL); + if (bfq_bfqq_busy(bfqq)) + bfqq->bfqd->wr_busy_queues--; + bfqq->wr_coeff = 1; + bfqq->wr_cur_max_time = 0; + /* Trigger a weight change on the next activation of the queue */ + bfqq->entity.ioprio_changed = 1; +} + +static void bfq_end_wr_async_queues(struct bfq_data *bfqd, + struct bfq_group *bfqg) +{ + int i, j; + + for (i = 0; i < 2; i++) + for (j = 0; j < IOPRIO_BE_NR; j++) + if (bfqg->async_bfqq[i][j] != NULL) + bfq_bfqq_end_wr(bfqg->async_bfqq[i][j]); + if (bfqg->async_idle_bfqq != NULL) + bfq_bfqq_end_wr(bfqg->async_idle_bfqq); +} + +static void bfq_end_wr(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq; + + spin_lock_irq(bfqd->queue->queue_lock); + + list_for_each_entry(bfqq, &bfqd->active_list, bfqq_list) + bfq_bfqq_end_wr(bfqq); + list_for_each_entry(bfqq, &bfqd->idle_list, bfqq_list) + bfq_bfqq_end_wr(bfqq); + bfq_end_wr_async(bfqd); + + spin_unlock_irq(bfqd->queue->queue_lock); +} + +static inline sector_t bfq_io_struct_pos(void *io_struct, bool request) +{ + if (request) + return blk_rq_pos(io_struct); + else + return ((struct bio *)io_struct)->bi_sector; +} + +static inline sector_t bfq_dist_from(sector_t pos1, + sector_t pos2) +{ + if (pos1 >= pos2) + return pos1 - pos2; + else + return pos2 - pos1; +} + +static inline int bfq_rq_close_to_sector(void *io_struct, bool request, + sector_t sector) +{ + return bfq_dist_from(bfq_io_struct_pos(io_struct, request), sector) <= + BFQQ_SEEK_THR; +} + +static struct bfq_queue *bfqq_close(struct bfq_data *bfqd, sector_t sector) +{ + struct rb_root *root = &bfqd->rq_pos_tree; + struct rb_node *parent, *node; + struct bfq_queue *__bfqq; + + if (RB_EMPTY_ROOT(root)) + return NULL; + + /* + * First, if we find a request starting at the end of the last + * request, choose it. + */ + __bfqq = bfq_rq_pos_tree_lookup(bfqd, root, sector, &parent, NULL); + if (__bfqq != NULL) + return __bfqq; + + /* + * If the exact sector wasn't found, the parent of the NULL leaf + * will contain the closest sector (rq_pos_tree sorted by + * next_request position). + */ + __bfqq = rb_entry(parent, struct bfq_queue, pos_node); + if (bfq_rq_close_to_sector(__bfqq->next_rq, true, sector)) + return __bfqq; + + if (blk_rq_pos(__bfqq->next_rq) < sector) + node = rb_next(&__bfqq->pos_node); + else + node = rb_prev(&__bfqq->pos_node); + if (node == NULL) + return NULL; + + __bfqq = rb_entry(node, struct bfq_queue, pos_node); + if (bfq_rq_close_to_sector(__bfqq->next_rq, true, sector)) + return __bfqq; + + return NULL; +} + +/* + * bfqd - obvious + * cur_bfqq - passed in so that we don't decide that the current queue + * is closely cooperating with itself + * sector - used as a reference point to search for a close queue + */ +static struct bfq_queue *bfq_close_cooperator(struct bfq_data *bfqd, + struct bfq_queue *cur_bfqq, + sector_t sector) +{ + struct bfq_queue *bfqq; + + if (bfq_class_idle(cur_bfqq)) + return NULL; + if (!bfq_bfqq_sync(cur_bfqq)) + return NULL; + if (BFQQ_SEEKY(cur_bfqq)) + return NULL; + + /* If device has only one backlogged bfq_queue, don't search. */ + if (bfqd->busy_queues == 1) + return NULL; + + /* + * We should notice if some of the queues are cooperating, e.g. + * working closely on the same area of the disk. In that case, + * we can group them together and don't waste time idling. + */ + bfqq = bfqq_close(bfqd, sector); + if (bfqq == NULL || bfqq == cur_bfqq) + return NULL; + + /* + * Do not merge queues from different bfq_groups. + */ + if (bfqq->entity.parent != cur_bfqq->entity.parent) + return NULL; + + /* + * It only makes sense to merge sync queues. + */ + if (!bfq_bfqq_sync(bfqq)) + return NULL; + if (BFQQ_SEEKY(bfqq)) + return NULL; + + /* + * Do not merge queues of different priority classes. + */ + if (bfq_class_rt(bfqq) != bfq_class_rt(cur_bfqq)) + return NULL; + + return bfqq; +} + +static struct bfq_queue * +bfq_setup_merge(struct bfq_queue *bfqq, struct bfq_queue *new_bfqq) +{ + int process_refs, new_process_refs; + struct bfq_queue *__bfqq; + + /* + * If there are no process references on the new_bfqq, then it is + * unsafe to follow the ->new_bfqq chain as other bfqq's in the chain + * may have dropped their last reference (not just their last process + * reference). + */ + if (!bfqq_process_refs(new_bfqq)) + return NULL; + + /* Avoid a circular list and skip interim queue merges. */ + while ((__bfqq = new_bfqq->new_bfqq)) { + if (__bfqq == bfqq) + return NULL; + new_bfqq = __bfqq; + } + + process_refs = bfqq_process_refs(bfqq); + new_process_refs = bfqq_process_refs(new_bfqq); + /* + * If the process for the bfqq has gone away, there is no + * sense in merging the queues. + */ + if (process_refs == 0 || new_process_refs == 0) + return NULL; + + bfq_log_bfqq(bfqq->bfqd, bfqq, "scheduling merge with queue %d", + new_bfqq->pid); + + /* + * Merging is just a redirection: the requests of the process + * owning one of the two queues are redirected to the other queue. + * The latter queue, in its turn, is set as shared if this is the + * first time that the requests of some process are redirected to + * it. + * + * We redirect bfqq to new_bfqq and not the opposite, because we + * are in the context of the process owning bfqq, hence we have + * the io_cq of this process. So we can immediately configure this + * io_cq to redirect the requests of the process to new_bfqq. + * + * NOTE, even if new_bfqq coincides with the in-service queue, the + * io_cq of new_bfqq is not available, because, if the in-service + * queue is shared, bfqd->in_service_bic may not point to the + * io_cq of the in-service queue. + * Redirecting the requests of the process owning bfqq to the + * currently in-service queue is in any case the best option, as + * we feed the in-service queue with new requests close to the + * last request served and, by doing so, hopefully increase the + * throughput. + */ + bfqq->new_bfqq = new_bfqq; + atomic_add(process_refs, &new_bfqq->ref); + return new_bfqq; +} + +/* + * Attempt to schedule a merge of bfqq with the currently in-service queue + * or with a close queue among the scheduled queues. + * Return NULL if no merge was scheduled, a pointer to the shared bfq_queue + * structure otherwise. + * + * The OOM queue is not allowed to participate to cooperation: in fact, since + * the requests temporarily redirected to the OOM queue could be redirected + * again to dedicated queues at any time, the state needed to correctly + * handle merging with the OOM queue would be quite complex and expensive + * to maintain. Besides, in such a critical condition as an out of memory, + * the benefits of queue merging may be little relevant, or even negligible. + */ +static struct bfq_queue * +bfq_setup_cooperator(struct bfq_data *bfqd, struct bfq_queue *bfqq, + void *io_struct, bool request) +{ + struct bfq_queue *in_service_bfqq, *new_bfqq; + + if (bfqq->new_bfqq) + return bfqq->new_bfqq; + + if (!io_struct || unlikely(bfqq == &bfqd->oom_bfqq)) + return NULL; + + in_service_bfqq = bfqd->in_service_queue; + + if (in_service_bfqq == NULL || in_service_bfqq == bfqq || + !bfqd->in_service_bic || + unlikely(in_service_bfqq == &bfqd->oom_bfqq)) + goto check_scheduled; + + if (bfq_class_idle(in_service_bfqq) || bfq_class_idle(bfqq)) + goto check_scheduled; + + if (bfq_class_rt(in_service_bfqq) != bfq_class_rt(bfqq)) + goto check_scheduled; + + if (in_service_bfqq->entity.parent != bfqq->entity.parent) + goto check_scheduled; + + if (bfq_rq_close_to_sector(io_struct, request, bfqd->last_position) && + bfq_bfqq_sync(in_service_bfqq) && bfq_bfqq_sync(bfqq)) { + new_bfqq = bfq_setup_merge(bfqq, in_service_bfqq); + if (new_bfqq != NULL) + return new_bfqq; /* Merge with in-service queue */ + } + + /* + * Check whether there is a cooperator among currently scheduled + * queues. The only thing we need is that the bio/request is not + * NULL, as we need it to establish whether a cooperator exists. + */ +check_scheduled: + new_bfqq = bfq_close_cooperator(bfqd, bfqq, + bfq_io_struct_pos(io_struct, request)); + if (new_bfqq && likely(new_bfqq != &bfqd->oom_bfqq)) + return bfq_setup_merge(bfqq, new_bfqq); + + return NULL; +} + +static inline void +bfq_bfqq_save_state(struct bfq_queue *bfqq) +{ + /* + * If bfqq->bic == NULL, the queue is already shared or its requests + * have already been redirected to a shared queue; both idle window + * and weight raising state have already been saved. Do nothing. + */ + if (bfqq->bic == NULL) + return; + if (bfqq->bic->wr_time_left) + /* + * This is the queue of a just-started process, and would + * deserve weight raising: we set wr_time_left to the full + * weight-raising duration to trigger weight-raising when + * and if the queue is split and the first request of the + * queue is enqueued. + */ + bfqq->bic->wr_time_left = bfq_wr_duration(bfqq->bfqd); + else if (bfqq->wr_coeff > 1) { + unsigned long wr_duration = + jiffies - bfqq->last_wr_start_finish; + /* + * It may happen that a queue's weight raising period lasts + * longer than its wr_cur_max_time, as weight raising is + * handled only when a request is enqueued or dispatched (it + * does not use any timer). If the weight raising period is + * about to end, don't save it. + */ + if (bfqq->wr_cur_max_time <= wr_duration) + bfqq->bic->wr_time_left = 0; + else + bfqq->bic->wr_time_left = + bfqq->wr_cur_max_time - wr_duration; + /* + * The bfq_queue is becoming shared or the requests of the + * process owning the queue are being redirected to a shared + * queue. Stop the weight raising period of the queue, as in + * both cases it should not be owned by an interactive or + * soft real-time application. + */ + bfq_bfqq_end_wr(bfqq); + } else + bfqq->bic->wr_time_left = 0; + bfqq->bic->saved_idle_window = bfq_bfqq_idle_window(bfqq); + bfqq->bic->saved_IO_bound = bfq_bfqq_IO_bound(bfqq); + bfqq->bic->saved_in_large_burst = bfq_bfqq_in_large_burst(bfqq); + bfqq->bic->was_in_burst_list = !hlist_unhashed(&bfqq->burst_list_node); + bfqq->bic->cooperations++; + bfqq->bic->failed_cooperations = 0; +} + +static inline void +bfq_get_bic_reference(struct bfq_queue *bfqq) +{ + /* + * If bfqq->bic has a non-NULL value, the bic to which it belongs + * is about to begin using a shared bfq_queue. + */ + if (bfqq->bic) + atomic_long_inc(&bfqq->bic->icq.ioc->refcount); +} + +static void +bfq_merge_bfqqs(struct bfq_data *bfqd, struct bfq_io_cq *bic, + struct bfq_queue *bfqq, struct bfq_queue *new_bfqq) +{ + bfq_log_bfqq(bfqd, bfqq, "merging with queue %lu", + (long unsigned)new_bfqq->pid); + /* Save weight raising and idle window of the merged queues */ + bfq_bfqq_save_state(bfqq); + bfq_bfqq_save_state(new_bfqq); + if (bfq_bfqq_IO_bound(bfqq)) + bfq_mark_bfqq_IO_bound(new_bfqq); + bfq_clear_bfqq_IO_bound(bfqq); + /* + * Grab a reference to the bic, to prevent it from being destroyed + * before being possibly touched by a bfq_split_bfqq(). + */ + bfq_get_bic_reference(bfqq); + bfq_get_bic_reference(new_bfqq); + /* + * Merge queues (that is, let bic redirect its requests to new_bfqq) + */ + bic_set_bfqq(bic, new_bfqq, 1); + bfq_mark_bfqq_coop(new_bfqq); + /* + * new_bfqq now belongs to at least two bics (it is a shared queue): + * set new_bfqq->bic to NULL. bfqq either: + * - does not belong to any bic any more, and hence bfqq->bic must + * be set to NULL, or + * - is a queue whose owning bics have already been redirected to a + * different queue, hence the queue is destined to not belong to + * any bic soon and bfqq->bic is already NULL (therefore the next + * assignment causes no harm). + */ + new_bfqq->bic = NULL; + bfqq->bic = NULL; + bfq_put_queue(bfqq); +} + +static inline void bfq_bfqq_increase_failed_cooperations(struct bfq_queue *bfqq) +{ + struct bfq_io_cq *bic = bfqq->bic; + struct bfq_data *bfqd = bfqq->bfqd; + + if (bic && bfq_bfqq_cooperations(bfqq) >= bfqd->bfq_coop_thresh) { + bic->failed_cooperations++; + if (bic->failed_cooperations >= bfqd->bfq_failed_cooperations) + bic->cooperations = 0; + } +} + +static int bfq_allow_merge(struct request_queue *q, struct request *rq, + struct bio *bio) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_io_cq *bic; + struct bfq_queue *bfqq, *new_bfqq; + + /* + * Disallow merge of a sync bio into an async request. + */ + if (bfq_bio_sync(bio) && !rq_is_sync(rq)) + return 0; + + /* + * Lookup the bfqq that this bio will be queued with. Allow + * merge only if rq is queued there. + * Queue lock is held here. + */ + bic = bfq_bic_lookup(bfqd, current->io_context); + if (bic == NULL) + return 0; + + bfqq = bic_to_bfqq(bic, bfq_bio_sync(bio)); + /* + * We take advantage of this function to perform an early merge + * of the queues of possible cooperating processes. + */ + if (bfqq != NULL) { + new_bfqq = bfq_setup_cooperator(bfqd, bfqq, bio, false); + if (new_bfqq != NULL) { + bfq_merge_bfqqs(bfqd, bic, bfqq, new_bfqq); + /* + * If we get here, the bio will be queued in the + * shared queue, i.e., new_bfqq, so use new_bfqq + * to decide whether bio and rq can be merged. + */ + bfqq = new_bfqq; + } else + bfq_bfqq_increase_failed_cooperations(bfqq); + } + + return bfqq == RQ_BFQQ(rq); +} + +static void __bfq_set_in_service_queue(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + if (bfqq != NULL) { + bfq_mark_bfqq_must_alloc(bfqq); + bfq_mark_bfqq_budget_new(bfqq); + bfq_clear_bfqq_fifo_expire(bfqq); + + bfqd->budgets_assigned = (bfqd->budgets_assigned*7 + 256) / 8; + + bfq_log_bfqq(bfqd, bfqq, + "set_in_service_queue, cur-budget = %lu", + bfqq->entity.budget); + } + + bfqd->in_service_queue = bfqq; +} + +/* + * Get and set a new queue for service. + */ +static struct bfq_queue *bfq_set_in_service_queue(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq = bfq_get_next_queue(bfqd); + + __bfq_set_in_service_queue(bfqd, bfqq); + return bfqq; +} + +/* + * If enough samples have been computed, return the current max budget + * stored in bfqd, which is dynamically updated according to the + * estimated disk peak rate; otherwise return the default max budget + */ +static inline unsigned long bfq_max_budget(struct bfq_data *bfqd) +{ + if (bfqd->budgets_assigned < 194) + return bfq_default_max_budget; + else + return bfqd->bfq_max_budget; +} + +/* + * Return min budget, which is a fraction of the current or default + * max budget (trying with 1/32) + */ +static inline unsigned long bfq_min_budget(struct bfq_data *bfqd) +{ + if (bfqd->budgets_assigned < 194) + return bfq_default_max_budget / 32; + else + return bfqd->bfq_max_budget / 32; +} + +static void bfq_arm_slice_timer(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq = bfqd->in_service_queue; + struct bfq_io_cq *bic; + unsigned long sl; + + BUG_ON(!RB_EMPTY_ROOT(&bfqq->sort_list)); + + /* Processes have exited, don't wait. */ + bic = bfqd->in_service_bic; + if (bic == NULL || atomic_read(&bic->icq.ioc->nr_tasks) == 0) + return; + + bfq_mark_bfqq_wait_request(bfqq); + + /* + * We don't want to idle for seeks, but we do want to allow + * fair distribution of slice time for a process doing back-to-back + * seeks. So allow a little bit of time for him to submit a new rq. + * + * To prevent processes with (partly) seeky workloads from + * being too ill-treated, grant them a small fraction of the + * assigned budget before reducing the waiting time to + * BFQ_MIN_TT. This happened to help reduce latency. + */ + sl = bfqd->bfq_slice_idle; + /* + * Unless the queue is being weight-raised or the scenario is + * asymmetric, grant only minimum idle time if the queue either + * has been seeky for long enough or has already proved to be + * constantly seeky. + */ + if (bfq_sample_valid(bfqq->seek_samples) && + ((BFQQ_SEEKY(bfqq) && bfqq->entity.service > + bfq_max_budget(bfqq->bfqd) / 8) || + bfq_bfqq_constantly_seeky(bfqq)) && bfqq->wr_coeff == 1 && + symmetric_scenario) + sl = min(sl, msecs_to_jiffies(BFQ_MIN_TT)); + else if (bfqq->wr_coeff > 1) + sl = sl * 3; + bfqd->last_idling_start = ktime_get(); + mod_timer(&bfqd->idle_slice_timer, jiffies + sl); + bfq_log(bfqd, "arm idle: %u/%u ms", + jiffies_to_msecs(sl), jiffies_to_msecs(bfqd->bfq_slice_idle)); +} + +/* + * Set the maximum time for the in-service queue to consume its + * budget. This prevents seeky processes from lowering the disk + * throughput (always guaranteed with a time slice scheme as in CFQ). + */ +static void bfq_set_budget_timeout(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq = bfqd->in_service_queue; + unsigned int timeout_coeff; + if (bfqq->wr_cur_max_time == bfqd->bfq_wr_rt_max_time) + timeout_coeff = 1; + else + timeout_coeff = bfqq->entity.weight / bfqq->entity.orig_weight; + + bfqd->last_budget_start = ktime_get(); + + bfq_clear_bfqq_budget_new(bfqq); + bfqq->budget_timeout = jiffies + + bfqd->bfq_timeout[bfq_bfqq_sync(bfqq)] * timeout_coeff; + + bfq_log_bfqq(bfqd, bfqq, "set budget_timeout %u", + jiffies_to_msecs(bfqd->bfq_timeout[bfq_bfqq_sync(bfqq)] * + timeout_coeff)); +} + +/* + * Move request from internal lists to the request queue dispatch list. + */ +static void bfq_dispatch_insert(struct request_queue *q, struct request *rq) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_queue *bfqq = RQ_BFQQ(rq); + + /* + * For consistency, the next instruction should have been executed + * after removing the request from the queue and dispatching it. + * We execute instead this instruction before bfq_remove_request() + * (and hence introduce a temporary inconsistency), for efficiency. + * In fact, in a forced_dispatch, this prevents two counters related + * to bfqq->dispatched to risk to be uselessly decremented if bfqq + * is not in service, and then to be incremented again after + * incrementing bfqq->dispatched. + */ + bfqq->dispatched++; + bfq_remove_request(rq); + elv_dispatch_sort(q, rq); + + if (bfq_bfqq_sync(bfqq)) + bfqd->sync_flight++; +} + +/* + * Return expired entry, or NULL to just start from scratch in rbtree. + */ +static struct request *bfq_check_fifo(struct bfq_queue *bfqq) +{ + struct request *rq = NULL; + + if (bfq_bfqq_fifo_expire(bfqq)) + return NULL; + + bfq_mark_bfqq_fifo_expire(bfqq); + + if (list_empty(&bfqq->fifo)) + return NULL; + + rq = rq_entry_fifo(bfqq->fifo.next); + + if (time_before(jiffies, rq_fifo_time(rq))) + return NULL; + + return rq; +} + +static inline unsigned long bfq_bfqq_budget_left(struct bfq_queue *bfqq) +{ + struct bfq_entity *entity = &bfqq->entity; + return entity->budget - entity->service; +} + +static void __bfq_bfqq_expire(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + BUG_ON(bfqq != bfqd->in_service_queue); + + __bfq_bfqd_reset_in_service(bfqd); + + /* + * If this bfqq is shared between multiple processes, check + * to make sure that those processes are still issuing I/Os + * within the mean seek distance. If not, it may be time to + * break the queues apart again. + */ + if (bfq_bfqq_coop(bfqq) && BFQQ_SEEKY(bfqq)) + bfq_mark_bfqq_split_coop(bfqq); + + if (RB_EMPTY_ROOT(&bfqq->sort_list)) { + /* + * Overloading budget_timeout field to store the time + * at which the queue remains with no backlog; used by + * the weight-raising mechanism. + */ + bfqq->budget_timeout = jiffies; + bfq_del_bfqq_busy(bfqd, bfqq, 1); + } else { + bfq_activate_bfqq(bfqd, bfqq); + /* + * Resort priority tree of potential close cooperators. + */ + bfq_rq_pos_tree_add(bfqd, bfqq); + } +} + +/** + * __bfq_bfqq_recalc_budget - try to adapt the budget to the @bfqq behavior. + * @bfqd: device data. + * @bfqq: queue to update. + * @reason: reason for expiration. + * + * Handle the feedback on @bfqq budget. See the body for detailed + * comments. + */ +static void __bfq_bfqq_recalc_budget(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + enum bfqq_expiration reason) +{ + struct request *next_rq; + unsigned long budget, min_budget; + + budget = bfqq->max_budget; + min_budget = bfq_min_budget(bfqd); + + BUG_ON(bfqq != bfqd->in_service_queue); + + bfq_log_bfqq(bfqd, bfqq, "recalc_budg: last budg %lu, budg left %lu", + bfqq->entity.budget, bfq_bfqq_budget_left(bfqq)); + bfq_log_bfqq(bfqd, bfqq, "recalc_budg: last max_budg %lu, min budg %lu", + budget, bfq_min_budget(bfqd)); + bfq_log_bfqq(bfqd, bfqq, "recalc_budg: sync %d, seeky %d", + bfq_bfqq_sync(bfqq), BFQQ_SEEKY(bfqd->in_service_queue)); + + if (bfq_bfqq_sync(bfqq)) { + switch (reason) { + /* + * Caveat: in all the following cases we trade latency + * for throughput. + */ + case BFQ_BFQQ_TOO_IDLE: + /* + * This is the only case where we may reduce + * the budget: if there is no request of the + * process still waiting for completion, then + * we assume (tentatively) that the timer has + * expired because the batch of requests of + * the process could have been served with a + * smaller budget. Hence, betting that + * process will behave in the same way when it + * becomes backlogged again, we reduce its + * next budget. As long as we guess right, + * this budget cut reduces the latency + * experienced by the process. + * + * However, if there are still outstanding + * requests, then the process may have not yet + * issued its next request just because it is + * still waiting for the completion of some of + * the still outstanding ones. So in this + * subcase we do not reduce its budget, on the + * contrary we increase it to possibly boost + * the throughput, as discussed in the + * comments to the BUDGET_TIMEOUT case. + */ + if (bfqq->dispatched > 0) /* still outstanding reqs */ + budget = min(budget * 2, bfqd->bfq_max_budget); + else { + if (budget > 5 * min_budget) + budget -= 4 * min_budget; + else + budget = min_budget; + } + break; + case BFQ_BFQQ_BUDGET_TIMEOUT: + /* + * We double the budget here because: 1) it + * gives the chance to boost the throughput if + * this is not a seeky process (which may have + * bumped into this timeout because of, e.g., + * ZBR), 2) together with charge_full_budget + * it helps give seeky processes higher + * timestamps, and hence be served less + * frequently. + */ + budget = min(budget * 2, bfqd->bfq_max_budget); + break; + case BFQ_BFQQ_BUDGET_EXHAUSTED: + /* + * The process still has backlog, and did not + * let either the budget timeout or the disk + * idling timeout expire. Hence it is not + * seeky, has a short thinktime and may be + * happy with a higher budget too. So + * definitely increase the budget of this good + * candidate to boost the disk throughput. + */ + budget = min(budget * 4, bfqd->bfq_max_budget); + break; + case BFQ_BFQQ_NO_MORE_REQUESTS: + /* + * Leave the budget unchanged. + */ + default: + return; + } + } else /* async queue */ + /* async queues get always the maximum possible budget + * (their ability to dispatch is limited by + * @bfqd->bfq_max_budget_async_rq). + */ + budget = bfqd->bfq_max_budget; + + bfqq->max_budget = budget; + + if (bfqd->budgets_assigned >= 194 && bfqd->bfq_user_max_budget == 0 && + bfqq->max_budget > bfqd->bfq_max_budget) + bfqq->max_budget = bfqd->bfq_max_budget; + + /* + * Make sure that we have enough budget for the next request. + * Since the finish time of the bfqq must be kept in sync with + * the budget, be sure to call __bfq_bfqq_expire() after the + * update. + */ + next_rq = bfqq->next_rq; + if (next_rq != NULL) + bfqq->entity.budget = max_t(unsigned long, bfqq->max_budget, + bfq_serv_to_charge(next_rq, bfqq)); + else + bfqq->entity.budget = bfqq->max_budget; + + bfq_log_bfqq(bfqd, bfqq, "head sect: %u, new budget %lu", + next_rq != NULL ? blk_rq_sectors(next_rq) : 0, + bfqq->entity.budget); +} + +static unsigned long bfq_calc_max_budget(u64 peak_rate, u64 timeout) +{ + unsigned long max_budget; + + /* + * The max_budget calculated when autotuning is equal to the + * amount of sectors transfered in timeout_sync at the + * estimated peak rate. + */ + max_budget = (unsigned long)(peak_rate * 1000 * + timeout >> BFQ_RATE_SHIFT); + + return max_budget; +} + +/* + * In addition to updating the peak rate, checks whether the process + * is "slow", and returns 1 if so. This slow flag is used, in addition + * to the budget timeout, to reduce the amount of service provided to + * seeky processes, and hence reduce their chances to lower the + * throughput. See the code for more details. + */ +static int bfq_update_peak_rate(struct bfq_data *bfqd, struct bfq_queue *bfqq, + int compensate, enum bfqq_expiration reason) +{ + u64 bw, usecs, expected, timeout; + ktime_t delta; + int update = 0; + + if (!bfq_bfqq_sync(bfqq) || bfq_bfqq_budget_new(bfqq)) + return 0; + + if (compensate) + delta = bfqd->last_idling_start; + else + delta = ktime_get(); + delta = ktime_sub(delta, bfqd->last_budget_start); + usecs = ktime_to_us(delta); + + /* Don't trust short/unrealistic values. */ + if (usecs < 100 || usecs >= LONG_MAX) + return 0; + + /* + * Calculate the bandwidth for the last slice. We use a 64 bit + * value to store the peak rate, in sectors per usec in fixed + * point math. We do so to have enough precision in the estimate + * and to avoid overflows. + */ + bw = (u64)bfqq->entity.service << BFQ_RATE_SHIFT; + do_div(bw, (unsigned long)usecs); + + timeout = jiffies_to_msecs(bfqd->bfq_timeout[BLK_RW_SYNC]); + + /* + * Use only long (> 20ms) intervals to filter out spikes for + * the peak rate estimation. + */ + if (usecs > 20000) { + if (bw > bfqd->peak_rate || + (!BFQQ_SEEKY(bfqq) && + reason == BFQ_BFQQ_BUDGET_TIMEOUT)) { + bfq_log(bfqd, "measured bw =%llu", bw); + /* + * To smooth oscillations use a low-pass filter with + * alpha=7/8, i.e., + * new_rate = (7/8) * old_rate + (1/8) * bw + */ + do_div(bw, 8); + if (bw == 0) + return 0; + bfqd->peak_rate *= 7; + do_div(bfqd->peak_rate, 8); + bfqd->peak_rate += bw; + update = 1; + bfq_log(bfqd, "new peak_rate=%llu", bfqd->peak_rate); + } + + update |= bfqd->peak_rate_samples == BFQ_PEAK_RATE_SAMPLES - 1; + + if (bfqd->peak_rate_samples < BFQ_PEAK_RATE_SAMPLES) + bfqd->peak_rate_samples++; + + if (bfqd->peak_rate_samples == BFQ_PEAK_RATE_SAMPLES && + update) { + int dev_type = blk_queue_nonrot(bfqd->queue); + if (bfqd->bfq_user_max_budget == 0) { + bfqd->bfq_max_budget = + bfq_calc_max_budget(bfqd->peak_rate, + timeout); + bfq_log(bfqd, "new max_budget=%lu", + bfqd->bfq_max_budget); + } + if (bfqd->device_speed == BFQ_BFQD_FAST && + bfqd->peak_rate < device_speed_thresh[dev_type]) { + bfqd->device_speed = BFQ_BFQD_SLOW; + bfqd->RT_prod = R_slow[dev_type] * + T_slow[dev_type]; + } else if (bfqd->device_speed == BFQ_BFQD_SLOW && + bfqd->peak_rate > device_speed_thresh[dev_type]) { + bfqd->device_speed = BFQ_BFQD_FAST; + bfqd->RT_prod = R_fast[dev_type] * + T_fast[dev_type]; + } + } + } + + /* + * If the process has been served for a too short time + * interval to let its possible sequential accesses prevail on + * the initial seek time needed to move the disk head on the + * first sector it requested, then give the process a chance + * and for the moment return false. + */ + if (bfqq->entity.budget <= bfq_max_budget(bfqd) / 8) + return 0; + + /* + * A process is considered ``slow'' (i.e., seeky, so that we + * cannot treat it fairly in the service domain, as it would + * slow down too much the other processes) if, when a slice + * ends for whatever reason, it has received service at a + * rate that would not be high enough to complete the budget + * before the budget timeout expiration. + */ + expected = bw * 1000 * timeout >> BFQ_RATE_SHIFT; + + /* + * Caveat: processes doing IO in the slower disk zones will + * tend to be slow(er) even if not seeky. And the estimated + * peak rate will actually be an average over the disk + * surface. Hence, to not be too harsh with unlucky processes, + * we keep a budget/3 margin of safety before declaring a + * process slow. + */ + return expected > (4 * bfqq->entity.budget) / 3; +} + +/* + * To be deemed as soft real-time, an application must meet two + * requirements. First, the application must not require an average + * bandwidth higher than the approximate bandwidth required to playback or + * record a compressed high-definition video. + * The next function is invoked on the completion of the last request of a + * batch, to compute the next-start time instant, soft_rt_next_start, such + * that, if the next request of the application does not arrive before + * soft_rt_next_start, then the above requirement on the bandwidth is met. + * + * The second requirement is that the request pattern of the application is + * isochronous, i.e., that, after issuing a request or a batch of requests, + * the application stops issuing new requests until all its pending requests + * have been completed. After that, the application may issue a new batch, + * and so on. + * For this reason the next function is invoked to compute + * soft_rt_next_start only for applications that meet this requirement, + * whereas soft_rt_next_start is set to infinity for applications that do + * not. + * + * Unfortunately, even a greedy application may happen to behave in an + * isochronous way if the CPU load is high. In fact, the application may + * stop issuing requests while the CPUs are busy serving other processes, + * then restart, then stop again for a while, and so on. In addition, if + * the disk achieves a low enough throughput with the request pattern + * issued by the application (e.g., because the request pattern is random + * and/or the device is slow), then the application may meet the above + * bandwidth requirement too. To prevent such a greedy application to be + * deemed as soft real-time, a further rule is used in the computation of + * soft_rt_next_start: soft_rt_next_start must be higher than the current + * time plus the maximum time for which the arrival of a request is waited + * for when a sync queue becomes idle, namely bfqd->bfq_slice_idle. + * This filters out greedy applications, as the latter issue instead their + * next request as soon as possible after the last one has been completed + * (in contrast, when a batch of requests is completed, a soft real-time + * application spends some time processing data). + * + * Unfortunately, the last filter may easily generate false positives if + * only bfqd->bfq_slice_idle is used as a reference time interval and one + * or both the following cases occur: + * 1) HZ is so low that the duration of a jiffy is comparable to or higher + * than bfqd->bfq_slice_idle. This happens, e.g., on slow devices with + * HZ=100. + * 2) jiffies, instead of increasing at a constant rate, may stop increasing + * for a while, then suddenly 'jump' by several units to recover the lost + * increments. This seems to happen, e.g., inside virtual machines. + * To address this issue, we do not use as a reference time interval just + * bfqd->bfq_slice_idle, but bfqd->bfq_slice_idle plus a few jiffies. In + * particular we add the minimum number of jiffies for which the filter + * seems to be quite precise also in embedded systems and KVM/QEMU virtual + * machines. + */ +static inline unsigned long bfq_bfqq_softrt_next_start(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + return max(bfqq->last_idle_bklogged + + HZ * bfqq->service_from_backlogged / + bfqd->bfq_wr_max_softrt_rate, + jiffies + bfqq->bfqd->bfq_slice_idle + 4); +} + +/* + * Return the largest-possible time instant such that, for as long as possible, + * the current time will be lower than this time instant according to the macro + * time_is_before_jiffies(). + */ +static inline unsigned long bfq_infinity_from_now(unsigned long now) +{ + return now + ULONG_MAX / 2; +} + +/** + * bfq_bfqq_expire - expire a queue. + * @bfqd: device owning the queue. + * @bfqq: the queue to expire. + * @compensate: if true, compensate for the time spent idling. + * @reason: the reason causing the expiration. + * + * + * If the process associated to the queue is slow (i.e., seeky), or in + * case of budget timeout, or, finally, if it is async, we + * artificially charge it an entire budget (independently of the + * actual service it received). As a consequence, the queue will get + * higher timestamps than the correct ones upon reactivation, and + * hence it will be rescheduled as if it had received more service + * than what it actually received. In the end, this class of processes + * will receive less service in proportion to how slowly they consume + * their budgets (and hence how seriously they tend to lower the + * throughput). + * + * In contrast, when a queue expires because it has been idling for + * too much or because it exhausted its budget, we do not touch the + * amount of service it has received. Hence when the queue will be + * reactivated and its timestamps updated, the latter will be in sync + * with the actual service received by the queue until expiration. + * + * Charging a full budget to the first type of queues and the exact + * service to the others has the effect of using the WF2Q+ policy to + * schedule the former on a timeslice basis, without violating the + * service domain guarantees of the latter. + */ +static void bfq_bfqq_expire(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + int compensate, + enum bfqq_expiration reason) +{ + int slow; + BUG_ON(bfqq != bfqd->in_service_queue); + + /* Update disk peak rate for autotuning and check whether the + * process is slow (see bfq_update_peak_rate). + */ + slow = bfq_update_peak_rate(bfqd, bfqq, compensate, reason); + + /* + * As above explained, 'punish' slow (i.e., seeky), timed-out + * and async queues, to favor sequential sync workloads. + * + * Processes doing I/O in the slower disk zones will tend to be + * slow(er) even if not seeky. Hence, since the estimated peak + * rate is actually an average over the disk surface, these + * processes may timeout just for bad luck. To avoid punishing + * them we do not charge a full budget to a process that + * succeeded in consuming at least 2/3 of its budget. + */ + if (slow || (reason == BFQ_BFQQ_BUDGET_TIMEOUT && + bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3)) + bfq_bfqq_charge_full_budget(bfqq); + + bfqq->service_from_backlogged += bfqq->entity.service; + + if (BFQQ_SEEKY(bfqq) && reason == BFQ_BFQQ_BUDGET_TIMEOUT && + !bfq_bfqq_constantly_seeky(bfqq)) { + bfq_mark_bfqq_constantly_seeky(bfqq); + if (!blk_queue_nonrot(bfqd->queue)) + bfqd->const_seeky_busy_in_flight_queues++; + } + + if (reason == BFQ_BFQQ_TOO_IDLE && + bfqq->entity.service <= 2 * bfqq->entity.budget / 10 ) + bfq_clear_bfqq_IO_bound(bfqq); + + if (bfqd->low_latency && bfqq->wr_coeff == 1) + bfqq->last_wr_start_finish = jiffies; + + if (bfqd->low_latency && bfqd->bfq_wr_max_softrt_rate > 0 && + RB_EMPTY_ROOT(&bfqq->sort_list)) { + /* + * If we get here, and there are no outstanding requests, + * then the request pattern is isochronous (see the comments + * to the function bfq_bfqq_softrt_next_start()). Hence we + * can compute soft_rt_next_start. If, instead, the queue + * still has outstanding requests, then we have to wait + * for the completion of all the outstanding requests to + * discover whether the request pattern is actually + * isochronous. + */ + if (bfqq->dispatched == 0) + bfqq->soft_rt_next_start = + bfq_bfqq_softrt_next_start(bfqd, bfqq); + else { + /* + * The application is still waiting for the + * completion of one or more requests: + * prevent it from possibly being incorrectly + * deemed as soft real-time by setting its + * soft_rt_next_start to infinity. In fact, + * without this assignment, the application + * would be incorrectly deemed as soft + * real-time if: + * 1) it issued a new request before the + * completion of all its in-flight + * requests, and + * 2) at that time, its soft_rt_next_start + * happened to be in the past. + */ + bfqq->soft_rt_next_start = + bfq_infinity_from_now(jiffies); + /* + * Schedule an update of soft_rt_next_start to when + * the task may be discovered to be isochronous. + */ + bfq_mark_bfqq_softrt_update(bfqq); + } + } + + bfq_log_bfqq(bfqd, bfqq, + "expire (%d, slow %d, num_disp %d, idle_win %d)", reason, + slow, bfqq->dispatched, bfq_bfqq_idle_window(bfqq)); + + /* + * Increase, decrease or leave budget unchanged according to + * reason. + */ + __bfq_bfqq_recalc_budget(bfqd, bfqq, reason); + __bfq_bfqq_expire(bfqd, bfqq); +} + +/* + * Budget timeout is not implemented through a dedicated timer, but + * just checked on request arrivals and completions, as well as on + * idle timer expirations. + */ +static int bfq_bfqq_budget_timeout(struct bfq_queue *bfqq) +{ + if (bfq_bfqq_budget_new(bfqq) || + time_before(jiffies, bfqq->budget_timeout)) + return 0; + return 1; +} + +/* + * If we expire a queue that is waiting for the arrival of a new + * request, we may prevent the fictitious timestamp back-shifting that + * allows the guarantees of the queue to be preserved (see [1] for + * this tricky aspect). Hence we return true only if this condition + * does not hold, or if the queue is slow enough to deserve only to be + * kicked off for preserving a high throughput. +*/ +static inline int bfq_may_expire_for_budg_timeout(struct bfq_queue *bfqq) +{ + bfq_log_bfqq(bfqq->bfqd, bfqq, + "may_budget_timeout: wait_request %d left %d timeout %d", + bfq_bfqq_wait_request(bfqq), + bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3, + bfq_bfqq_budget_timeout(bfqq)); + + return (!bfq_bfqq_wait_request(bfqq) || + bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3) + && + bfq_bfqq_budget_timeout(bfqq); +} + +/* + * Device idling is allowed only for the queues for which this function + * returns true. For this reason, the return value of this function plays a + * critical role for both throughput boosting and service guarantees. The + * return value is computed through a logical expression. In this rather + * long comment, we try to briefly describe all the details and motivations + * behind the components of this logical expression. + * + * First, the expression is false if bfqq is not sync, or if: bfqq happened + * to become active during a large burst of queue activations, and the + * pattern of requests bfqq contains boosts the throughput if bfqq is + * expired. In fact, queues that became active during a large burst benefit + * only from throughput, as discussed in the comments to bfq_handle_burst. + * In this respect, expiring bfqq certainly boosts the throughput on NCQ- + * capable flash-based devices, whereas, on rotational devices, it boosts + * the throughput only if bfqq contains random requests. + * + * On the opposite end, if (a) bfqq is sync, (b) the above burst-related + * condition does not hold, and (c) bfqq is being weight-raised, then the + * expression always evaluates to true, as device idling is instrumental + * for preserving low-latency guarantees (see [1]). If, instead, conditions + * (a) and (b) do hold, but (c) does not, then the expression evaluates to + * true only if: (1) bfqq is I/O-bound and has a non-null idle window, and + * (2) at least one of the following two conditions holds. + * The first condition is that the device is not performing NCQ, because + * idling the device most certainly boosts the throughput if this condition + * holds and bfqq is I/O-bound and has been granted a non-null idle window. + * The second compound condition is made of the logical AND of two components. + * + * The first component is true only if there is no weight-raised busy + * queue. This guarantees that the device is not idled for a sync non- + * weight-raised queue when there are busy weight-raised queues. The former + * is then expired immediately if empty. Combined with the timestamping + * rules of BFQ (see [1] for details), this causes sync non-weight-raised + * queues to get a lower number of requests served, and hence to ask for a + * lower number of requests from the request pool, before the busy weight- + * raised queues get served again. + * + * This is beneficial for the processes associated with weight-raised + * queues, when the request pool is saturated (e.g., in the presence of + * write hogs). In fact, if the processes associated with the other queues + * ask for requests at a lower rate, then weight-raised processes have a + * higher probability to get a request from the pool immediately (or at + * least soon) when they need one. Hence they have a higher probability to + * actually get a fraction of the disk throughput proportional to their + * high weight. This is especially true with NCQ-capable drives, which + * enqueue several requests in advance and further reorder internally- + * queued requests. + * + * In the end, mistreating non-weight-raised queues when there are busy + * weight-raised queues seems to mitigate starvation problems in the + * presence of heavy write workloads and NCQ, and hence to guarantee a + * higher application and system responsiveness in these hostile scenarios. + * + * If the first component of the compound condition is instead true, i.e., + * there is no weight-raised busy queue, then the second component of the + * compound condition takes into account service-guarantee and throughput + * issues related to NCQ (recall that the compound condition is evaluated + * only if the device is detected as supporting NCQ). + * + * As for service guarantees, allowing the drive to enqueue more than one + * request at a time, and hence delegating de facto final scheduling + * decisions to the drive's internal scheduler, causes loss of control on + * the actual request service order. In this respect, when the drive is + * allowed to enqueue more than one request at a time, the service + * distribution enforced by the drive's internal scheduler is likely to + * coincide with the desired device-throughput distribution only in the + * following, perfectly symmetric, scenario: + * 1) all active queues have the same weight, + * 2) all active groups at the same level in the groups tree have the same + * weight, + * 3) all active groups at the same level in the groups tree have the same + * number of children. + * + * Even in such a scenario, sequential I/O may still receive a preferential + * treatment, but this is not likely to be a big issue with flash-based + * devices, because of their non-dramatic loss of throughput with random + * I/O. Things do differ with HDDs, for which additional care is taken, as + * explained after completing the discussion for flash-based devices. + * + * Unfortunately, keeping the necessary state for evaluating exactly the + * above symmetry conditions would be quite complex and time-consuming. + * Therefore BFQ evaluates instead the following stronger sub-conditions, + * for which it is much easier to maintain the needed state: + * 1) all active queues have the same weight, + * 2) all active groups have the same weight, + * 3) all active groups have at most one active child each. + * In particular, the last two conditions are always true if hierarchical + * support and the cgroups interface are not enabled, hence no state needs + * to be maintained in this case. + * + * According to the above considerations, the second component of the + * compound condition evaluates to true if any of the above symmetry + * sub-condition does not hold, or the device is not flash-based. Therefore, + * if also the first component is true, then idling is allowed for a sync + * queue. These are the only sub-conditions considered if the device is + * flash-based, as, for such a device, it is sensible to force idling only + * for service-guarantee issues. In fact, as for throughput, idling + * NCQ-capable flash-based devices would not boost the throughput even + * with sequential I/O; rather it would lower the throughput in proportion + * to how fast the device is. In the end, (only) if all the three + * sub-conditions hold and the device is flash-based, the compound + * condition evaluates to false and therefore no idling is performed. + * + * As already said, things change with a rotational device, where idling + * boosts the throughput with sequential I/O (even with NCQ). Hence, for + * such a device the second component of the compound condition evaluates + * to true also if the following additional sub-condition does not hold: + * the queue is constantly seeky. Unfortunately, this different behavior + * with respect to flash-based devices causes an additional asymmetry: if + * some sync queues enjoy idling and some other sync queues do not, then + * the latter get a low share of the device throughput, simply because the + * former get many requests served after being set as in service, whereas + * the latter do not. As a consequence, to guarantee the desired throughput + * distribution, on HDDs the compound expression evaluates to true (and + * hence device idling is performed) also if the following last symmetry + * condition does not hold: no other queue is benefiting from idling. Also + * this last condition is actually replaced with a simpler-to-maintain and + * stronger condition: there is no busy queue which is not constantly seeky + * (and hence may also benefit from idling). + * + * To sum up, when all the required symmetry and throughput-boosting + * sub-conditions hold, the second component of the compound condition + * evaluates to false, and hence no idling is performed. This helps to + * keep the drives' internal queues full on NCQ-capable devices, and hence + * to boost the throughput, without causing 'almost' any loss of service + * guarantees. The 'almost' follows from the fact that, if the internal + * queue of one such device is filled while all the sub-conditions hold, + * but at some point in time some sub-condition stops to hold, then it may + * become impossible to let requests be served in the new desired order + * until all the requests already queued in the device have been served. + */ +static inline bool bfq_bfqq_must_not_expire(struct bfq_queue *bfqq) +{ + struct bfq_data *bfqd = bfqq->bfqd; +#define cond_for_seeky_on_ncq_hdd (bfq_bfqq_constantly_seeky(bfqq) && \ + bfqd->busy_in_flight_queues == \ + bfqd->const_seeky_busy_in_flight_queues) + +#define cond_for_expiring_in_burst (bfq_bfqq_in_large_burst(bfqq) && \ + bfqd->hw_tag && \ + (blk_queue_nonrot(bfqd->queue) || \ + bfq_bfqq_constantly_seeky(bfqq))) + +/* + * Condition for expiring a non-weight-raised queue (and hence not idling + * the device). + */ +#define cond_for_expiring_non_wr (bfqd->hw_tag && \ + (bfqd->wr_busy_queues > 0 || \ + (blk_queue_nonrot(bfqd->queue) || \ + cond_for_seeky_on_ncq_hdd))) + + return bfq_bfqq_sync(bfqq) && + !cond_for_expiring_in_burst && + (bfqq->wr_coeff > 1 || !symmetric_scenario || + (bfq_bfqq_IO_bound(bfqq) && bfq_bfqq_idle_window(bfqq) && + !cond_for_expiring_non_wr) + ); +} + +/* + * If the in-service queue is empty but sync, and the function + * bfq_bfqq_must_not_expire returns true, then: + * 1) the queue must remain in service and cannot be expired, and + * 2) the disk must be idled to wait for the possible arrival of a new + * request for the queue. + * See the comments to the function bfq_bfqq_must_not_expire for the reasons + * why performing device idling is the best choice to boost the throughput + * and preserve service guarantees when bfq_bfqq_must_not_expire itself + * returns true. + */ +static inline bool bfq_bfqq_must_idle(struct bfq_queue *bfqq) +{ + struct bfq_data *bfqd = bfqq->bfqd; + + return RB_EMPTY_ROOT(&bfqq->sort_list) && bfqd->bfq_slice_idle != 0 && + bfq_bfqq_must_not_expire(bfqq); +} + +/* + * Select a queue for service. If we have a current queue in service, + * check whether to continue servicing it, or retrieve and set a new one. + */ +static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq; + struct request *next_rq; + enum bfqq_expiration reason = BFQ_BFQQ_BUDGET_TIMEOUT; + + bfqq = bfqd->in_service_queue; + if (bfqq == NULL) + goto new_queue; + + bfq_log_bfqq(bfqd, bfqq, "select_queue: already in-service queue"); + + if (bfq_may_expire_for_budg_timeout(bfqq) && + !timer_pending(&bfqd->idle_slice_timer) && + !bfq_bfqq_must_idle(bfqq)) + goto expire; + + next_rq = bfqq->next_rq; + /* + * If bfqq has requests queued and it has enough budget left to + * serve them, keep the queue, otherwise expire it. + */ + if (next_rq != NULL) { + if (bfq_serv_to_charge(next_rq, bfqq) > + bfq_bfqq_budget_left(bfqq)) { + reason = BFQ_BFQQ_BUDGET_EXHAUSTED; + goto expire; + } else { + /* + * The idle timer may be pending because we may + * not disable disk idling even when a new request + * arrives. + */ + if (timer_pending(&bfqd->idle_slice_timer)) { + /* + * If we get here: 1) at least a new request + * has arrived but we have not disabled the + * timer because the request was too small, + * 2) then the block layer has unplugged + * the device, causing the dispatch to be + * invoked. + * + * Since the device is unplugged, now the + * requests are probably large enough to + * provide a reasonable throughput. + * So we disable idling. + */ + bfq_clear_bfqq_wait_request(bfqq); + del_timer(&bfqd->idle_slice_timer); + } + goto keep_queue; + } + } + + /* + * No requests pending. However, if the in-service queue is idling + * for a new request, or has requests waiting for a completion and + * may idle after their completion, then keep it anyway. + */ + if (timer_pending(&bfqd->idle_slice_timer) || + (bfqq->dispatched != 0 && bfq_bfqq_must_not_expire(bfqq))) { + bfqq = NULL; + goto keep_queue; + } + + reason = BFQ_BFQQ_NO_MORE_REQUESTS; +expire: + bfq_bfqq_expire(bfqd, bfqq, 0, reason); +new_queue: + bfqq = bfq_set_in_service_queue(bfqd); + bfq_log(bfqd, "select_queue: new queue %d returned", + bfqq != NULL ? bfqq->pid : 0); +keep_queue: + return bfqq; +} + +static void bfq_update_wr_data(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + struct bfq_entity *entity = &bfqq->entity; + if (bfqq->wr_coeff > 1) { /* queue is being weight-raised */ + bfq_log_bfqq(bfqd, bfqq, + "raising period dur %u/%u msec, old coeff %u, w %d(%d)", + jiffies_to_msecs(jiffies - bfqq->last_wr_start_finish), + jiffies_to_msecs(bfqq->wr_cur_max_time), + bfqq->wr_coeff, + bfqq->entity.weight, bfqq->entity.orig_weight); + + BUG_ON(bfqq != bfqd->in_service_queue && entity->weight != + entity->orig_weight * bfqq->wr_coeff); + if (entity->ioprio_changed) + bfq_log_bfqq(bfqd, bfqq, "WARN: pending prio change"); + + /* + * If the queue was activated in a burst, or + * too much time has elapsed from the beginning + * of this weight-raising period, or the queue has + * exceeded the acceptable number of cooperations, + * then end weight raising. + */ + if (bfq_bfqq_in_large_burst(bfqq) || + bfq_bfqq_cooperations(bfqq) >= bfqd->bfq_coop_thresh || + time_is_before_jiffies(bfqq->last_wr_start_finish + + bfqq->wr_cur_max_time)) { + bfqq->last_wr_start_finish = jiffies; + bfq_log_bfqq(bfqd, bfqq, + "wrais ending at %lu, rais_max_time %u", + bfqq->last_wr_start_finish, + jiffies_to_msecs(bfqq->wr_cur_max_time)); + bfq_bfqq_end_wr(bfqq); + } + } + /* Update weight both if it must be raised and if it must be lowered */ + if ((entity->weight > entity->orig_weight) != (bfqq->wr_coeff > 1)) + __bfq_entity_update_weight_prio( + bfq_entity_service_tree(entity), + entity); +} + +/* + * Dispatch one request from bfqq, moving it to the request queue + * dispatch list. + */ +static int bfq_dispatch_request(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + int dispatched = 0; + struct request *rq; + unsigned long service_to_charge; + + BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list)); + + /* Follow expired path, else get first next available. */ + rq = bfq_check_fifo(bfqq); + if (rq == NULL) + rq = bfqq->next_rq; + service_to_charge = bfq_serv_to_charge(rq, bfqq); + + if (service_to_charge > bfq_bfqq_budget_left(bfqq)) { + /* + * This may happen if the next rq is chosen in fifo order + * instead of sector order. The budget is properly + * dimensioned to be always sufficient to serve the next + * request only if it is chosen in sector order. The reason + * is that it would be quite inefficient and little useful + * to always make sure that the budget is large enough to + * serve even the possible next rq in fifo order. + * In fact, requests are seldom served in fifo order. + * + * Expire the queue for budget exhaustion, and make sure + * that the next act_budget is enough to serve the next + * request, even if it comes from the fifo expired path. + */ + bfqq->next_rq = rq; + /* + * Since this dispatch is failed, make sure that + * a new one will be performed + */ + if (!bfqd->rq_in_driver) + bfq_schedule_dispatch(bfqd); + goto expire; + } + + /* Finally, insert request into driver dispatch list. */ + bfq_bfqq_served(bfqq, service_to_charge); + bfq_dispatch_insert(bfqd->queue, rq); + + bfq_update_wr_data(bfqd, bfqq); + + bfq_log_bfqq(bfqd, bfqq, + "dispatched %u sec req (%llu), budg left %lu", + blk_rq_sectors(rq), + (long long unsigned)blk_rq_pos(rq), + bfq_bfqq_budget_left(bfqq)); + + dispatched++; + + if (bfqd->in_service_bic == NULL) { + atomic_long_inc(&RQ_BIC(rq)->icq.ioc->refcount); + bfqd->in_service_bic = RQ_BIC(rq); + } + + if (bfqd->busy_queues > 1 && ((!bfq_bfqq_sync(bfqq) && + dispatched >= bfqd->bfq_max_budget_async_rq) || + bfq_class_idle(bfqq))) + goto expire; + + return dispatched; + +expire: + bfq_bfqq_expire(bfqd, bfqq, 0, BFQ_BFQQ_BUDGET_EXHAUSTED); + return dispatched; +} + +static int __bfq_forced_dispatch_bfqq(struct bfq_queue *bfqq) +{ + int dispatched = 0; + + while (bfqq->next_rq != NULL) { + bfq_dispatch_insert(bfqq->bfqd->queue, bfqq->next_rq); + dispatched++; + } + + BUG_ON(!list_empty(&bfqq->fifo)); + return dispatched; +} + +/* + * Drain our current requests. + * Used for barriers and when switching io schedulers on-the-fly. + */ +static int bfq_forced_dispatch(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq, *n; + struct bfq_service_tree *st; + int dispatched = 0; + + bfqq = bfqd->in_service_queue; + if (bfqq != NULL) + __bfq_bfqq_expire(bfqd, bfqq); + + /* + * Loop through classes, and be careful to leave the scheduler + * in a consistent state, as feedback mechanisms and vtime + * updates cannot be disabled during the process. + */ + list_for_each_entry_safe(bfqq, n, &bfqd->active_list, bfqq_list) { + st = bfq_entity_service_tree(&bfqq->entity); + + dispatched += __bfq_forced_dispatch_bfqq(bfqq); + bfqq->max_budget = bfq_max_budget(bfqd); + + bfq_forget_idle(st); + } + + BUG_ON(bfqd->busy_queues != 0); + + return dispatched; +} + +static int bfq_dispatch_requests(struct request_queue *q, int force) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_queue *bfqq; + int max_dispatch; + + bfq_log(bfqd, "dispatch requests: %d busy queues", bfqd->busy_queues); + if (bfqd->busy_queues == 0) + return 0; + + if (unlikely(force)) + return bfq_forced_dispatch(bfqd); + + bfqq = bfq_select_queue(bfqd); + if (bfqq == NULL) + return 0; + + if (bfq_class_idle(bfqq)) + max_dispatch = 1; + + if (!bfq_bfqq_sync(bfqq)) + max_dispatch = bfqd->bfq_max_budget_async_rq; + + if (!bfq_bfqq_sync(bfqq) && bfqq->dispatched >= max_dispatch) { + if (bfqd->busy_queues > 1) + return 0; + if (bfqq->dispatched >= 4 * max_dispatch) + return 0; + } + + if (bfqd->sync_flight != 0 && !bfq_bfqq_sync(bfqq)) + return 0; + + bfq_clear_bfqq_wait_request(bfqq); + BUG_ON(timer_pending(&bfqd->idle_slice_timer)); + + if (!bfq_dispatch_request(bfqd, bfqq)) + return 0; + + bfq_log_bfqq(bfqd, bfqq, "dispatched %s request", + bfq_bfqq_sync(bfqq) ? "sync" : "async"); + + return 1; +} + +/* + * Task holds one reference to the queue, dropped when task exits. Each rq + * in-flight on this queue also holds a reference, dropped when rq is freed. + * + * Queue lock must be held here. + */ +static void bfq_put_queue(struct bfq_queue *bfqq) +{ + struct bfq_data *bfqd = bfqq->bfqd; + + BUG_ON(atomic_read(&bfqq->ref) <= 0); + + bfq_log_bfqq(bfqd, bfqq, "put_queue: %p %d", bfqq, + atomic_read(&bfqq->ref)); + if (!atomic_dec_and_test(&bfqq->ref)) + return; + + BUG_ON(rb_first(&bfqq->sort_list) != NULL); + BUG_ON(bfqq->allocated[READ] + bfqq->allocated[WRITE] != 0); + BUG_ON(bfqq->entity.tree != NULL); + BUG_ON(bfq_bfqq_busy(bfqq)); + BUG_ON(bfqd->in_service_queue == bfqq); + + if (bfq_bfqq_sync(bfqq)) + /* + * The fact that this queue is being destroyed does not + * invalidate the fact that this queue may have been + * activated during the current burst. As a consequence, + * although the queue does not exist anymore, and hence + * needs to be removed from the burst list if there, + * the burst size has not to be decremented. + */ + hlist_del_init(&bfqq->burst_list_node); + + bfq_log_bfqq(bfqd, bfqq, "put_queue: %p freed", bfqq); + + kmem_cache_free(bfq_pool, bfqq); +} + +static void bfq_put_cooperator(struct bfq_queue *bfqq) +{ + struct bfq_queue *__bfqq, *next; + + /* + * If this queue was scheduled to merge with another queue, be + * sure to drop the reference taken on that queue (and others in + * the merge chain). See bfq_setup_merge and bfq_merge_bfqqs. + */ + __bfqq = bfqq->new_bfqq; + while (__bfqq) { + if (__bfqq == bfqq) + break; + next = __bfqq->new_bfqq; + bfq_put_queue(__bfqq); + __bfqq = next; + } +} + +static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + if (bfqq == bfqd->in_service_queue) { + __bfq_bfqq_expire(bfqd, bfqq); + bfq_schedule_dispatch(bfqd); + } + + bfq_log_bfqq(bfqd, bfqq, "exit_bfqq: %p, %d", bfqq, + atomic_read(&bfqq->ref)); + + bfq_put_cooperator(bfqq); + + bfq_put_queue(bfqq); +} + +static inline void bfq_init_icq(struct io_cq *icq) +{ + struct bfq_io_cq *bic = icq_to_bic(icq); + + bic->ttime.last_end_request = jiffies; + /* + * A newly created bic indicates that the process has just + * started doing I/O, and is probably mapping into memory its + * executable and libraries: it definitely needs weight raising. + * There is however the possibility that the process performs, + * for a while, I/O close to some other process. EQM intercepts + * this behavior and may merge the queue corresponding to the + * process with some other queue, BEFORE the weight of the queue + * is raised. Merged queues are not weight-raised (they are assumed + * to belong to processes that benefit only from high throughput). + * If the merge is basically the consequence of an accident, then + * the queue will be split soon and will get back its old weight. + * It is then important to write down somewhere that this queue + * does need weight raising, even if it did not make it to get its + * weight raised before being merged. To this purpose, we overload + * the field raising_time_left and assign 1 to it, to mark the queue + * as needing weight raising. + */ + bic->wr_time_left = 1; +} + +static void bfq_exit_icq(struct io_cq *icq) +{ + struct bfq_io_cq *bic = icq_to_bic(icq); + struct bfq_data *bfqd = bic_to_bfqd(bic); + + if (bic->bfqq[BLK_RW_ASYNC]) { + bfq_exit_bfqq(bfqd, bic->bfqq[BLK_RW_ASYNC]); + bic->bfqq[BLK_RW_ASYNC] = NULL; + } + + if (bic->bfqq[BLK_RW_SYNC]) { + /* + * If the bic is using a shared queue, put the reference + * taken on the io_context when the bic started using a + * shared bfq_queue. + */ + if (bfq_bfqq_coop(bic->bfqq[BLK_RW_SYNC])) + put_io_context(icq->ioc); + bfq_exit_bfqq(bfqd, bic->bfqq[BLK_RW_SYNC]); + bic->bfqq[BLK_RW_SYNC] = NULL; + } +} + +/* + * Update the entity prio values; note that the new values will not + * be used until the next (re)activation. + */ +static void bfq_set_next_ioprio_data(struct bfq_queue *bfqq, struct bfq_io_cq *bic) +{ + struct task_struct *tsk = current; + struct io_context *ioc = bic->icq.ioc; + int ioprio_class; + + ioprio_class = IOPRIO_PRIO_CLASS(ioc->ioprio); + switch (ioprio_class) { + default: + dev_err(bfqq->bfqd->queue->backing_dev_info.dev, + "bfq: bad prio class %d\n", ioprio_class); + case IOPRIO_CLASS_NONE: + /* + * No prio set, inherit CPU scheduling settings. + */ + bfqq->entity.new_ioprio = task_nice_ioprio(tsk); + bfqq->entity.new_ioprio_class = task_nice_ioclass(tsk); + break; + case IOPRIO_CLASS_RT: + bfqq->entity.new_ioprio = task_ioprio(ioc); + bfqq->entity.new_ioprio_class = IOPRIO_CLASS_RT; + break; + case IOPRIO_CLASS_BE: + bfqq->entity.new_ioprio = task_ioprio(ioc); + bfqq->entity.new_ioprio_class = IOPRIO_CLASS_BE; + break; + case IOPRIO_CLASS_IDLE: + bfqq->entity.new_ioprio_class = IOPRIO_CLASS_IDLE; + bfqq->entity.new_ioprio = 7; + bfq_clear_bfqq_idle_window(bfqq); + break; + } + + if (bfqq->entity.new_ioprio < 0 || + bfqq->entity.new_ioprio >= IOPRIO_BE_NR) { + printk(KERN_CRIT "bfq_set_next_ioprio_data: new_ioprio %d\n", + bfqq->entity.new_ioprio); + BUG(); + } + + bfqq->entity.new_weight = bfq_ioprio_to_weight(bfqq->entity.new_ioprio); + bfqq->entity.ioprio_changed = 1; +} + +static void bfq_check_ioprio_change(struct io_context *ioc, + struct bfq_io_cq *bic) +{ + struct bfq_data *bfqd; + struct bfq_queue *bfqq, *new_bfqq; + struct bfq_group *bfqg; + unsigned long uninitialized_var(flags); + int ioprio = bic->icq.ioc->ioprio; + + bfqd = bfq_get_bfqd_locked(&(bic->icq.q->elevator->elevator_data), + &flags); + if (unlikely(bfqd == NULL)) + return; + + bic->ioprio = ioprio; + + bfqq = bic->bfqq[BLK_RW_ASYNC]; + if (bfqq != NULL) { + bfqg = container_of(bfqq->entity.sched_data, struct bfq_group, + sched_data); + new_bfqq = bfq_get_queue(bfqd, bfqg, BLK_RW_ASYNC, bic->icq.ioc, + GFP_ATOMIC); + if (new_bfqq != NULL) { + bic->bfqq[BLK_RW_ASYNC] = new_bfqq; + bfq_log_bfqq(bfqd, bfqq, + "check_ioprio_change: bfqq %p %d", + bfqq, atomic_read(&bfqq->ref)); + bfq_put_queue(bfqq); + } + } + + bfqq = bic->bfqq[BLK_RW_SYNC]; + if (bfqq != NULL) + bfq_set_next_ioprio_data(bfqq, bic); + + bfq_put_bfqd_unlock(bfqd, &flags); +} + +static void bfq_init_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq, + struct bfq_io_cq *bic, pid_t pid, int is_sync) +{ + RB_CLEAR_NODE(&bfqq->entity.rb_node); + INIT_LIST_HEAD(&bfqq->fifo); + INIT_HLIST_NODE(&bfqq->burst_list_node); + + atomic_set(&bfqq->ref, 0); + bfqq->bfqd = bfqd; + + if (bic) + bfq_set_next_ioprio_data(bfqq, bic); + + if (is_sync) { + if (!bfq_class_idle(bfqq)) + bfq_mark_bfqq_idle_window(bfqq); + bfq_mark_bfqq_sync(bfqq); + } + bfq_mark_bfqq_IO_bound(bfqq); + + /* Tentative initial value to trade off between thr and lat */ + bfqq->max_budget = (2 * bfq_max_budget(bfqd)) / 3; + bfqq->pid = pid; + + bfqq->wr_coeff = 1; + bfqq->last_wr_start_finish = 0; + /* + * Set to the value for which bfqq will not be deemed as + * soft rt when it becomes backlogged. + */ + bfqq->soft_rt_next_start = bfq_infinity_from_now(jiffies); +} + +static struct bfq_queue *bfq_find_alloc_queue(struct bfq_data *bfqd, + struct bfq_group *bfqg, + int is_sync, + struct io_context *ioc, + gfp_t gfp_mask) +{ + struct bfq_queue *bfqq, *new_bfqq = NULL; + struct bfq_io_cq *bic; + +retry: + bic = bfq_bic_lookup(bfqd, ioc); + /* bic always exists here */ + bfqq = bic_to_bfqq(bic, is_sync); + + /* + * Always try a new alloc if we fall back to the OOM bfqq + * originally, since it should just be a temporary situation. + */ + if (bfqq == NULL || bfqq == &bfqd->oom_bfqq) { + bfqq = NULL; + if (new_bfqq != NULL) { + bfqq = new_bfqq; + new_bfqq = NULL; + } else if (gfp_mask & __GFP_WAIT) { + spin_unlock_irq(bfqd->queue->queue_lock); + new_bfqq = kmem_cache_alloc_node(bfq_pool, + gfp_mask | __GFP_ZERO, + bfqd->queue->node); + spin_lock_irq(bfqd->queue->queue_lock); + if (new_bfqq != NULL) + goto retry; + } else { + bfqq = kmem_cache_alloc_node(bfq_pool, + gfp_mask | __GFP_ZERO, + bfqd->queue->node); + } + + if (bfqq != NULL) { + bfq_init_bfqq(bfqd, bfqq, bic, current->pid, + is_sync); + bfq_init_entity(&bfqq->entity, bfqg); + bfq_log_bfqq(bfqd, bfqq, "allocated"); + } else { + bfqq = &bfqd->oom_bfqq; + bfq_log_bfqq(bfqd, bfqq, "using oom bfqq"); + } + } + + if (new_bfqq != NULL) + kmem_cache_free(bfq_pool, new_bfqq); + + return bfqq; +} + +static struct bfq_queue **bfq_async_queue_prio(struct bfq_data *bfqd, + struct bfq_group *bfqg, + int ioprio_class, int ioprio) +{ + switch (ioprio_class) { + case IOPRIO_CLASS_RT: + return &bfqg->async_bfqq[0][ioprio]; + case IOPRIO_CLASS_BE: + return &bfqg->async_bfqq[1][ioprio]; + case IOPRIO_CLASS_IDLE: + return &bfqg->async_idle_bfqq; + default: + BUG(); + } +} + +static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd, + struct bfq_group *bfqg, int is_sync, + struct io_context *ioc, gfp_t gfp_mask) +{ + const int ioprio = task_ioprio(ioc); + const int ioprio_class = task_ioprio_class(ioc); + struct bfq_queue **async_bfqq = NULL; + struct bfq_queue *bfqq = NULL; + + if (!is_sync) { + async_bfqq = bfq_async_queue_prio(bfqd, bfqg, ioprio_class, + ioprio); + bfqq = *async_bfqq; + } + + if (bfqq == NULL) + bfqq = bfq_find_alloc_queue(bfqd, bfqg, is_sync, ioc, gfp_mask); + + /* + * Pin the queue now that it's allocated, scheduler exit will + * prune it. + */ + if (!is_sync && *async_bfqq == NULL) { + atomic_inc(&bfqq->ref); + bfq_log_bfqq(bfqd, bfqq, "get_queue, bfqq not in async: %p, %d", + bfqq, atomic_read(&bfqq->ref)); + *async_bfqq = bfqq; + } + + atomic_inc(&bfqq->ref); + bfq_log_bfqq(bfqd, bfqq, "get_queue, at end: %p, %d", bfqq, + atomic_read(&bfqq->ref)); + return bfqq; +} + +static void bfq_update_io_thinktime(struct bfq_data *bfqd, + struct bfq_io_cq *bic) +{ + unsigned long elapsed = jiffies - bic->ttime.last_end_request; + unsigned long ttime = min(elapsed, 2UL * bfqd->bfq_slice_idle); + + bic->ttime.ttime_samples = (7*bic->ttime.ttime_samples + 256) / 8; + bic->ttime.ttime_total = (7*bic->ttime.ttime_total + 256*ttime) / 8; + bic->ttime.ttime_mean = (bic->ttime.ttime_total + 128) / + bic->ttime.ttime_samples; +} + +static void bfq_update_io_seektime(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + struct request *rq) +{ + sector_t sdist; + u64 total; + + if (bfqq->last_request_pos < blk_rq_pos(rq)) + sdist = blk_rq_pos(rq) - bfqq->last_request_pos; + else + sdist = bfqq->last_request_pos - blk_rq_pos(rq); + + /* + * Don't allow the seek distance to get too large from the + * odd fragment, pagein, etc. + */ + if (bfqq->seek_samples == 0) /* first request, not really a seek */ + sdist = 0; + else if (bfqq->seek_samples <= 60) /* second & third seek */ + sdist = min(sdist, (bfqq->seek_mean * 4) + 2*1024*1024); + else + sdist = min(sdist, (bfqq->seek_mean * 4) + 2*1024*64); + + bfqq->seek_samples = (7*bfqq->seek_samples + 256) / 8; + bfqq->seek_total = (7*bfqq->seek_total + (u64)256*sdist) / 8; + total = bfqq->seek_total + (bfqq->seek_samples/2); + do_div(total, bfqq->seek_samples); + bfqq->seek_mean = (sector_t)total; + + bfq_log_bfqq(bfqd, bfqq, "dist=%llu mean=%llu", (u64)sdist, + (u64)bfqq->seek_mean); +} + +/* + * Disable idle window if the process thinks too long or seeks so much that + * it doesn't matter. + */ +static void bfq_update_idle_window(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + struct bfq_io_cq *bic) +{ + int enable_idle; + + /* Don't idle for async or idle io prio class. */ + if (!bfq_bfqq_sync(bfqq) || bfq_class_idle(bfqq)) + return; + + /* Idle window just restored, statistics are meaningless. */ + if (bfq_bfqq_just_split(bfqq)) + return; + + enable_idle = bfq_bfqq_idle_window(bfqq); + + if (atomic_read(&bic->icq.ioc->nr_tasks) == 0 || + bfqd->bfq_slice_idle == 0 || + (bfqd->hw_tag && BFQQ_SEEKY(bfqq) && + bfqq->wr_coeff == 1)) + enable_idle = 0; + else if (bfq_sample_valid(bic->ttime.ttime_samples)) { + if (bic->ttime.ttime_mean > bfqd->bfq_slice_idle && + bfqq->wr_coeff == 1) + enable_idle = 0; + else + enable_idle = 1; + } + bfq_log_bfqq(bfqd, bfqq, "update_idle_window: enable_idle %d", + enable_idle); + + if (enable_idle) + bfq_mark_bfqq_idle_window(bfqq); + else + bfq_clear_bfqq_idle_window(bfqq); +} + +/* + * Called when a new fs request (rq) is added to bfqq. Check if there's + * something we should do about it. + */ +static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq, + struct request *rq) +{ + struct bfq_io_cq *bic = RQ_BIC(rq); + + if (rq->cmd_flags & REQ_META) + bfqq->meta_pending++; + + bfq_update_io_thinktime(bfqd, bic); + bfq_update_io_seektime(bfqd, bfqq, rq); + if (!BFQQ_SEEKY(bfqq) && bfq_bfqq_constantly_seeky(bfqq)) { + bfq_clear_bfqq_constantly_seeky(bfqq); + if (!blk_queue_nonrot(bfqd->queue)) { + BUG_ON(!bfqd->const_seeky_busy_in_flight_queues); + bfqd->const_seeky_busy_in_flight_queues--; + } + } + if (bfqq->entity.service > bfq_max_budget(bfqd) / 8 || + !BFQQ_SEEKY(bfqq)) + bfq_update_idle_window(bfqd, bfqq, bic); + bfq_clear_bfqq_just_split(bfqq); + + bfq_log_bfqq(bfqd, bfqq, + "rq_enqueued: idle_window=%d (seeky %d, mean %llu)", + bfq_bfqq_idle_window(bfqq), BFQQ_SEEKY(bfqq), + (long long unsigned)bfqq->seek_mean); + + bfqq->last_request_pos = blk_rq_pos(rq) + blk_rq_sectors(rq); + + if (bfqq == bfqd->in_service_queue && bfq_bfqq_wait_request(bfqq)) { + int small_req = bfqq->queued[rq_is_sync(rq)] == 1 && + blk_rq_sectors(rq) < 32; + int budget_timeout = bfq_bfqq_budget_timeout(bfqq); + + /* + * There is just this request queued: if the request + * is small and the queue is not to be expired, then + * just exit. + * + * In this way, if the disk is being idled to wait for + * a new request from the in-service queue, we avoid + * unplugging the device and committing the disk to serve + * just a small request. On the contrary, we wait for + * the block layer to decide when to unplug the device: + * hopefully, new requests will be merged to this one + * quickly, then the device will be unplugged and + * larger requests will be dispatched. + */ + if (small_req && !budget_timeout) + return; + + /* + * A large enough request arrived, or the queue is to + * be expired: in both cases disk idling is to be + * stopped, so clear wait_request flag and reset + * timer. + */ + bfq_clear_bfqq_wait_request(bfqq); + del_timer(&bfqd->idle_slice_timer); + + /* + * The queue is not empty, because a new request just + * arrived. Hence we can safely expire the queue, in + * case of budget timeout, without risking that the + * timestamps of the queue are not updated correctly. + * See [1] for more details. + */ + if (budget_timeout) + bfq_bfqq_expire(bfqd, bfqq, 0, BFQ_BFQQ_BUDGET_TIMEOUT); + + /* + * Let the request rip immediately, or let a new queue be + * selected if bfqq has just been expired. + */ + __blk_run_queue(bfqd->queue); + } +} + +static void bfq_insert_request(struct request_queue *q, struct request *rq) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_queue *bfqq = RQ_BFQQ(rq), *new_bfqq; + + assert_spin_locked(bfqd->queue->queue_lock); + + /* + * An unplug may trigger a requeue of a request from the device + * driver: make sure we are in process context while trying to + * merge two bfq_queues. + */ + if (!in_interrupt()) { + new_bfqq = bfq_setup_cooperator(bfqd, bfqq, rq, true); + if (new_bfqq != NULL) { + if (bic_to_bfqq(RQ_BIC(rq), 1) != bfqq) + new_bfqq = bic_to_bfqq(RQ_BIC(rq), 1); + /* + * Release the request's reference to the old bfqq + * and make sure one is taken to the shared queue. + */ + new_bfqq->allocated[rq_data_dir(rq)]++; + bfqq->allocated[rq_data_dir(rq)]--; + atomic_inc(&new_bfqq->ref); + bfq_put_queue(bfqq); + if (bic_to_bfqq(RQ_BIC(rq), 1) == bfqq) + bfq_merge_bfqqs(bfqd, RQ_BIC(rq), + bfqq, new_bfqq); + rq->elv.priv[1] = new_bfqq; + bfqq = new_bfqq; + } else + bfq_bfqq_increase_failed_cooperations(bfqq); + } + + bfq_add_request(rq); + + /* + * Here a newly-created bfq_queue has already started a weight-raising + * period: clear raising_time_left to prevent bfq_bfqq_save_state() + * from assigning it a full weight-raising period. See the detailed + * comments about this field in bfq_init_icq(). + */ + if (bfqq->bic != NULL) + bfqq->bic->wr_time_left = 0; + rq_set_fifo_time(rq, jiffies + bfqd->bfq_fifo_expire[rq_is_sync(rq)]); + list_add_tail(&rq->queuelist, &bfqq->fifo); + + bfq_rq_enqueued(bfqd, bfqq, rq); +} + +static void bfq_update_hw_tag(struct bfq_data *bfqd) +{ + bfqd->max_rq_in_driver = max(bfqd->max_rq_in_driver, + bfqd->rq_in_driver); + + if (bfqd->hw_tag == 1) + return; + + /* + * This sample is valid if the number of outstanding requests + * is large enough to allow a queueing behavior. Note that the + * sum is not exact, as it's not taking into account deactivated + * requests. + */ + if (bfqd->rq_in_driver + bfqd->queued < BFQ_HW_QUEUE_THRESHOLD) + return; + + if (bfqd->hw_tag_samples++ < BFQ_HW_QUEUE_SAMPLES) + return; + + bfqd->hw_tag = bfqd->max_rq_in_driver > BFQ_HW_QUEUE_THRESHOLD; + bfqd->max_rq_in_driver = 0; + bfqd->hw_tag_samples = 0; +} + +static void bfq_completed_request(struct request_queue *q, struct request *rq) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq); + struct bfq_data *bfqd = bfqq->bfqd; + bool sync = bfq_bfqq_sync(bfqq); + + bfq_log_bfqq(bfqd, bfqq, "completed one req with %u sects left (%d)", + blk_rq_sectors(rq), sync); + + bfq_update_hw_tag(bfqd); + + BUG_ON(!bfqd->rq_in_driver); + BUG_ON(!bfqq->dispatched); + bfqd->rq_in_driver--; + bfqq->dispatched--; + + if (!bfqq->dispatched && !bfq_bfqq_busy(bfqq)) { + bfq_weights_tree_remove(bfqd, &bfqq->entity, + &bfqd->queue_weights_tree); + if (!blk_queue_nonrot(bfqd->queue)) { + BUG_ON(!bfqd->busy_in_flight_queues); + bfqd->busy_in_flight_queues--; + if (bfq_bfqq_constantly_seeky(bfqq)) { + BUG_ON(!bfqd-> + const_seeky_busy_in_flight_queues); + bfqd->const_seeky_busy_in_flight_queues--; + } + } + } + + if (sync) { + bfqd->sync_flight--; + RQ_BIC(rq)->ttime.last_end_request = jiffies; + } + + /* + * If we are waiting to discover whether the request pattern of the + * task associated with the queue is actually isochronous, and + * both requisites for this condition to hold are satisfied, then + * compute soft_rt_next_start (see the comments to the function + * bfq_bfqq_softrt_next_start()). + */ + if (bfq_bfqq_softrt_update(bfqq) && bfqq->dispatched == 0 && + RB_EMPTY_ROOT(&bfqq->sort_list)) + bfqq->soft_rt_next_start = + bfq_bfqq_softrt_next_start(bfqd, bfqq); + + /* + * If this is the in-service queue, check if it needs to be expired, + * or if we want to idle in case it has no pending requests. + */ + if (bfqd->in_service_queue == bfqq) { + if (bfq_bfqq_budget_new(bfqq)) + bfq_set_budget_timeout(bfqd); + + if (bfq_bfqq_must_idle(bfqq)) { + bfq_arm_slice_timer(bfqd); + goto out; + } else if (bfq_may_expire_for_budg_timeout(bfqq)) + bfq_bfqq_expire(bfqd, bfqq, 0, BFQ_BFQQ_BUDGET_TIMEOUT); + else if (RB_EMPTY_ROOT(&bfqq->sort_list) && + (bfqq->dispatched == 0 || + !bfq_bfqq_must_not_expire(bfqq))) + bfq_bfqq_expire(bfqd, bfqq, 0, + BFQ_BFQQ_NO_MORE_REQUESTS); + } + + if (!bfqd->rq_in_driver) + bfq_schedule_dispatch(bfqd); + +out: + return; +} + +static inline int __bfq_may_queue(struct bfq_queue *bfqq) +{ + if (bfq_bfqq_wait_request(bfqq) && bfq_bfqq_must_alloc(bfqq)) { + bfq_clear_bfqq_must_alloc(bfqq); + return ELV_MQUEUE_MUST; + } + + return ELV_MQUEUE_MAY; +} + +static int bfq_may_queue(struct request_queue *q, int rw) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct task_struct *tsk = current; + struct bfq_io_cq *bic; + struct bfq_queue *bfqq; + + /* + * Don't force setup of a queue from here, as a call to may_queue + * does not necessarily imply that a request actually will be + * queued. So just lookup a possibly existing queue, or return + * 'may queue' if that fails. + */ + bic = bfq_bic_lookup(bfqd, tsk->io_context); + if (bic == NULL) + return ELV_MQUEUE_MAY; + + bfqq = bic_to_bfqq(bic, rw_is_sync(rw)); + if (bfqq != NULL) + return __bfq_may_queue(bfqq); + + return ELV_MQUEUE_MAY; +} + +/* + * Queue lock held here. + */ +static void bfq_put_request(struct request *rq) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq); + + if (bfqq != NULL) { + const int rw = rq_data_dir(rq); + + BUG_ON(!bfqq->allocated[rw]); + bfqq->allocated[rw]--; + + rq->elv.priv[0] = NULL; + rq->elv.priv[1] = NULL; + + bfq_log_bfqq(bfqq->bfqd, bfqq, "put_request %p, %d", + bfqq, atomic_read(&bfqq->ref)); + bfq_put_queue(bfqq); + } +} + +/* + * Returns NULL if a new bfqq should be allocated, or the old bfqq if this + * was the last process referring to said bfqq. + */ +static struct bfq_queue * +bfq_split_bfqq(struct bfq_io_cq *bic, struct bfq_queue *bfqq) +{ + bfq_log_bfqq(bfqq->bfqd, bfqq, "splitting queue"); + + put_io_context(bic->icq.ioc); + + if (bfqq_process_refs(bfqq) == 1) { + bfqq->pid = current->pid; + bfq_clear_bfqq_coop(bfqq); + bfq_clear_bfqq_split_coop(bfqq); + return bfqq; + } + + bic_set_bfqq(bic, NULL, 1); + + bfq_put_cooperator(bfqq); + + bfq_put_queue(bfqq); + return NULL; +} + +/* + * Allocate bfq data structures associated with this request. + */ +static int bfq_set_request(struct request_queue *q, struct request *rq, + gfp_t gfp_mask) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_io_cq *bic = icq_to_bic(rq->elv.icq); + const int rw = rq_data_dir(rq); + const int is_sync = rq_is_sync(rq); + struct bfq_queue *bfqq; + struct bfq_group *bfqg; + unsigned long flags; + bool split = false; + + /* handle changed prio notifications; cgroup change is handled separately */ + if (unlikely(icq_get_changed(&bic->icq) & ICQ_IOPRIO_CHANGED)) + bfq_check_ioprio_change(bic->icq.ioc, bic); + + might_sleep_if(gfp_mask & __GFP_WAIT); + + spin_lock_irqsave(q->queue_lock, flags); + + if (bic == NULL) + goto queue_fail; + + bfqg = bfq_bic_update_cgroup(bic); + +new_queue: + bfqq = bic_to_bfqq(bic, is_sync); + if (bfqq == NULL || bfqq == &bfqd->oom_bfqq) { + bfqq = bfq_get_queue(bfqd, bfqg, is_sync, bic->icq.ioc, gfp_mask); + bic_set_bfqq(bic, bfqq, is_sync); + if (split && is_sync) { + if ((bic->was_in_burst_list && bfqd->large_burst) || + bic->saved_in_large_burst) + bfq_mark_bfqq_in_large_burst(bfqq); + else { + bfq_clear_bfqq_in_large_burst(bfqq); + if (bic->was_in_burst_list) + hlist_add_head(&bfqq->burst_list_node, + &bfqd->burst_list); + } + } + } else { + /* If the queue was seeky for too long, break it apart. */ + if (bfq_bfqq_coop(bfqq) && bfq_bfqq_split_coop(bfqq)) { + bfq_log_bfqq(bfqd, bfqq, "breaking apart bfqq"); + bfqq = bfq_split_bfqq(bic, bfqq); + split = true; + if (!bfqq) + goto new_queue; + } + } + + bfqq->allocated[rw]++; + atomic_inc(&bfqq->ref); + bfq_log_bfqq(bfqd, bfqq, "set_request: bfqq %p, %d", bfqq, + atomic_read(&bfqq->ref)); + + rq->elv.priv[0] = bic; + rq->elv.priv[1] = bfqq; + + /* + * If a bfq_queue has only one process reference, it is owned + * by only one bfq_io_cq: we can set the bic field of the + * bfq_queue to the address of that structure. Also, if the + * queue has just been split, mark a flag so that the + * information is available to the other scheduler hooks. + */ + if (likely(bfqq != &bfqd->oom_bfqq) && bfqq_process_refs(bfqq) == 1) { + bfqq->bic = bic; + if (split) { + bfq_mark_bfqq_just_split(bfqq); + /* + * If the queue has just been split from a shared + * queue, restore the idle window and the possible + * weight raising period. + */ + bfq_bfqq_resume_state(bfqq, bic); + } + } + + spin_unlock_irqrestore(q->queue_lock, flags); + + return 0; + +queue_fail: + bfq_schedule_dispatch(bfqd); + spin_unlock_irqrestore(q->queue_lock, flags); + + return 1; +} + +static void bfq_kick_queue(struct work_struct *work) +{ + struct bfq_data *bfqd = + container_of(work, struct bfq_data, unplug_work); + struct request_queue *q = bfqd->queue; + + spin_lock_irq(q->queue_lock); + __blk_run_queue(q); + spin_unlock_irq(q->queue_lock); +} + +/* + * Handler of the expiration of the timer running if the in-service queue + * is idling inside its time slice. + */ +static void bfq_idle_slice_timer(unsigned long data) +{ + struct bfq_data *bfqd = (struct bfq_data *)data; + struct bfq_queue *bfqq; + unsigned long flags; + enum bfqq_expiration reason; + + spin_lock_irqsave(bfqd->queue->queue_lock, flags); + + bfqq = bfqd->in_service_queue; + /* + * Theoretical race here: the in-service queue can be NULL or + * different from the queue that was idling if the timer handler + * spins on the queue_lock and a new request arrives for the + * current queue and there is a full dispatch cycle that changes + * the in-service queue. This can hardly happen, but in the worst + * case we just expire a queue too early. + */ + if (bfqq != NULL) { + bfq_log_bfqq(bfqd, bfqq, "slice_timer expired"); + if (bfq_bfqq_budget_timeout(bfqq)) + /* + * Also here the queue can be safely expired + * for budget timeout without wasting + * guarantees + */ + reason = BFQ_BFQQ_BUDGET_TIMEOUT; + else if (bfqq->queued[0] == 0 && bfqq->queued[1] == 0) + /* + * The queue may not be empty upon timer expiration, + * because we may not disable the timer when the + * first request of the in-service queue arrives + * during disk idling. + */ + reason = BFQ_BFQQ_TOO_IDLE; + else + goto schedule_dispatch; + + bfq_bfqq_expire(bfqd, bfqq, 1, reason); + } + +schedule_dispatch: + bfq_schedule_dispatch(bfqd); + + spin_unlock_irqrestore(bfqd->queue->queue_lock, flags); +} + +static void bfq_shutdown_timer_wq(struct bfq_data *bfqd) +{ + del_timer_sync(&bfqd->idle_slice_timer); + cancel_work_sync(&bfqd->unplug_work); +} + +static inline void __bfq_put_async_bfqq(struct bfq_data *bfqd, + struct bfq_queue **bfqq_ptr) +{ + struct bfq_group *root_group = bfqd->root_group; + struct bfq_queue *bfqq = *bfqq_ptr; + + bfq_log(bfqd, "put_async_bfqq: %p", bfqq); + if (bfqq != NULL) { + bfq_bfqq_move(bfqd, bfqq, &bfqq->entity, root_group); + bfq_log_bfqq(bfqd, bfqq, "put_async_bfqq: putting %p, %d", + bfqq, atomic_read(&bfqq->ref)); + bfq_put_queue(bfqq); + *bfqq_ptr = NULL; + } +} + +/* + * Release all the bfqg references to its async queues. If we are + * deallocating the group these queues may still contain requests, so + * we reparent them to the root cgroup (i.e., the only one that will + * exist for sure until all the requests on a device are gone). + */ +static void bfq_put_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg) +{ + int i, j; + + for (i = 0; i < 2; i++) + for (j = 0; j < IOPRIO_BE_NR; j++) + __bfq_put_async_bfqq(bfqd, &bfqg->async_bfqq[i][j]); + + __bfq_put_async_bfqq(bfqd, &bfqg->async_idle_bfqq); +} + +static void bfq_exit_queue(struct elevator_queue *e) +{ + struct bfq_data *bfqd = e->elevator_data; + struct request_queue *q = bfqd->queue; + struct bfq_queue *bfqq, *n; + + bfq_shutdown_timer_wq(bfqd); + + spin_lock_irq(q->queue_lock); + + BUG_ON(bfqd->in_service_queue != NULL); + list_for_each_entry_safe(bfqq, n, &bfqd->idle_list, bfqq_list) + bfq_deactivate_bfqq(bfqd, bfqq, 0); + + bfq_disconnect_groups(bfqd); + spin_unlock_irq(q->queue_lock); + + bfq_shutdown_timer_wq(bfqd); + + synchronize_rcu(); + + BUG_ON(timer_pending(&bfqd->idle_slice_timer)); + + bfq_free_root_group(bfqd); + kfree(bfqd); +} + +static void *bfq_init_queue(struct request_queue *q) +{ + struct bfq_group *bfqg; + struct bfq_data *bfqd; + + bfqd = kzalloc_node(sizeof(*bfqd), GFP_KERNEL, q->node); + if (bfqd == NULL) + return NULL; + + /* + * Our fallback bfqq if bfq_find_alloc_queue() runs into OOM issues. + * Grab a permanent reference to it, so that the normal code flow + * will not attempt to free it. + */ + bfq_init_bfqq(bfqd, &bfqd->oom_bfqq, NULL, 1, 0); + atomic_inc(&bfqd->oom_bfqq.ref); + bfqd->oom_bfqq.entity.new_ioprio = BFQ_DEFAULT_QUEUE_IOPRIO; + bfqd->oom_bfqq.entity.new_ioprio_class = IOPRIO_CLASS_BE; + bfqd->oom_bfqq.entity.new_weight = + bfq_ioprio_to_weight(bfqd->oom_bfqq.entity.new_ioprio); + /* + * Trigger weight initialization, according to ioprio, at the + * oom_bfqq's first activation. The oom_bfqq's ioprio and ioprio + * class won't be changed any more. + */ + bfqd->oom_bfqq.entity.ioprio_changed = 1; + + bfqd->queue = q; + + bfqg = bfq_alloc_root_group(bfqd, q->node); + if (bfqg == NULL) { + kfree(bfqd); + return NULL; + } + + bfqd->root_group = bfqg; + bfq_init_entity(&bfqd->oom_bfqq.entity, bfqd->root_group); +#ifdef CONFIG_CGROUP_BFQIO + bfqd->active_numerous_groups = 0; +#endif + + init_timer(&bfqd->idle_slice_timer); + bfqd->idle_slice_timer.function = bfq_idle_slice_timer; + bfqd->idle_slice_timer.data = (unsigned long)bfqd; + + bfqd->rq_pos_tree = RB_ROOT; + bfqd->queue_weights_tree = RB_ROOT; + bfqd->group_weights_tree = RB_ROOT; + + INIT_WORK(&bfqd->unplug_work, bfq_kick_queue); + + INIT_LIST_HEAD(&bfqd->active_list); + INIT_LIST_HEAD(&bfqd->idle_list); + INIT_HLIST_HEAD(&bfqd->burst_list); + + bfqd->hw_tag = -1; + + bfqd->bfq_max_budget = bfq_default_max_budget; + + bfqd->bfq_fifo_expire[0] = bfq_fifo_expire[0]; + bfqd->bfq_fifo_expire[1] = bfq_fifo_expire[1]; + bfqd->bfq_back_max = bfq_back_max; + bfqd->bfq_back_penalty = bfq_back_penalty; + bfqd->bfq_slice_idle = bfq_slice_idle; + bfqd->bfq_class_idle_last_service = 0; + bfqd->bfq_max_budget_async_rq = bfq_max_budget_async_rq; + bfqd->bfq_timeout[BLK_RW_ASYNC] = bfq_timeout_async; + bfqd->bfq_timeout[BLK_RW_SYNC] = bfq_timeout_sync; + + bfqd->bfq_coop_thresh = 2; + bfqd->bfq_failed_cooperations = 7000; + bfqd->bfq_requests_within_timer = 120; + + bfqd->bfq_large_burst_thresh = 11; + bfqd->bfq_burst_interval = msecs_to_jiffies(500); + + bfqd->low_latency = true; + + bfqd->bfq_wr_coeff = 20; + bfqd->bfq_wr_rt_max_time = msecs_to_jiffies(300); + bfqd->bfq_wr_max_time = 0; + bfqd->bfq_wr_min_idle_time = msecs_to_jiffies(2000); + bfqd->bfq_wr_min_inter_arr_async = msecs_to_jiffies(500); + bfqd->bfq_wr_max_softrt_rate = 7000; /* + * Approximate rate required + * to playback or record a + * high-definition compressed + * video. + */ + bfqd->wr_busy_queues = 0; + bfqd->busy_in_flight_queues = 0; + bfqd->const_seeky_busy_in_flight_queues = 0; + + /* + * Begin by assuming, optimistically, that the device peak rate is + * equal to the highest reference rate. + */ + bfqd->RT_prod = R_fast[blk_queue_nonrot(bfqd->queue)] * + T_fast[blk_queue_nonrot(bfqd->queue)]; + bfqd->peak_rate = R_fast[blk_queue_nonrot(bfqd->queue)]; + bfqd->device_speed = BFQ_BFQD_FAST; + + return bfqd; +} + +static void bfq_slab_kill(void) +{ + if (bfq_pool != NULL) + kmem_cache_destroy(bfq_pool); +} + +static int __init bfq_slab_setup(void) +{ + bfq_pool = KMEM_CACHE(bfq_queue, 0); + if (bfq_pool == NULL) + return -ENOMEM; + return 0; +} + +static ssize_t bfq_var_show(unsigned int var, char *page) +{ + return sprintf(page, "%d\n", var); +} + +static ssize_t bfq_var_store(unsigned long *var, const char *page, + size_t count) +{ + unsigned long new_val; + int ret = kstrtoul(page, 10, &new_val); + + if (ret == 0) + *var = new_val; + + return count; +} + +static ssize_t bfq_wr_max_time_show(struct elevator_queue *e, char *page) +{ + struct bfq_data *bfqd = e->elevator_data; + return sprintf(page, "%d\n", bfqd->bfq_wr_max_time > 0 ? + jiffies_to_msecs(bfqd->bfq_wr_max_time) : + jiffies_to_msecs(bfq_wr_duration(bfqd))); +} + +static ssize_t bfq_weights_show(struct elevator_queue *e, char *page) +{ + struct bfq_queue *bfqq; + struct bfq_data *bfqd = e->elevator_data; + ssize_t num_char = 0; + + num_char += sprintf(page + num_char, "Tot reqs queued %d\n\n", + bfqd->queued); + + spin_lock_irq(bfqd->queue->queue_lock); + + num_char += sprintf(page + num_char, "Active:\n"); + list_for_each_entry(bfqq, &bfqd->active_list, bfqq_list) { + num_char += sprintf(page + num_char, + "pid%d: weight %hu, nr_queued %d %d, dur %d/%u\n", + bfqq->pid, + bfqq->entity.weight, + bfqq->queued[0], + bfqq->queued[1], + jiffies_to_msecs(jiffies - bfqq->last_wr_start_finish), + jiffies_to_msecs(bfqq->wr_cur_max_time)); + } + + num_char += sprintf(page + num_char, "Idle:\n"); + list_for_each_entry(bfqq, &bfqd->idle_list, bfqq_list) { + num_char += sprintf(page + num_char, + "pid%d: weight %hu, dur %d/%u\n", + bfqq->pid, + bfqq->entity.weight, + jiffies_to_msecs(jiffies - + bfqq->last_wr_start_finish), + jiffies_to_msecs(bfqq->wr_cur_max_time)); + } + + spin_unlock_irq(bfqd->queue->queue_lock); + + return num_char; +} + +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, char *page) \ +{ \ + struct bfq_data *bfqd = e->elevator_data; \ + unsigned int __data = __VAR; \ + if (__CONV) \ + __data = jiffies_to_msecs(__data); \ + return bfq_var_show(__data, (page)); \ +} +SHOW_FUNCTION(bfq_fifo_expire_sync_show, bfqd->bfq_fifo_expire[1], 1); +SHOW_FUNCTION(bfq_fifo_expire_async_show, bfqd->bfq_fifo_expire[0], 1); +SHOW_FUNCTION(bfq_back_seek_max_show, bfqd->bfq_back_max, 0); +SHOW_FUNCTION(bfq_back_seek_penalty_show, bfqd->bfq_back_penalty, 0); +SHOW_FUNCTION(bfq_slice_idle_show, bfqd->bfq_slice_idle, 1); +SHOW_FUNCTION(bfq_max_budget_show, bfqd->bfq_user_max_budget, 0); +SHOW_FUNCTION(bfq_max_budget_async_rq_show, + bfqd->bfq_max_budget_async_rq, 0); +SHOW_FUNCTION(bfq_timeout_sync_show, bfqd->bfq_timeout[BLK_RW_SYNC], 1); +SHOW_FUNCTION(bfq_timeout_async_show, bfqd->bfq_timeout[BLK_RW_ASYNC], 1); +SHOW_FUNCTION(bfq_low_latency_show, bfqd->low_latency, 0); +SHOW_FUNCTION(bfq_wr_coeff_show, bfqd->bfq_wr_coeff, 0); +SHOW_FUNCTION(bfq_wr_rt_max_time_show, bfqd->bfq_wr_rt_max_time, 1); +SHOW_FUNCTION(bfq_wr_min_idle_time_show, bfqd->bfq_wr_min_idle_time, 1); +SHOW_FUNCTION(bfq_wr_min_inter_arr_async_show, bfqd->bfq_wr_min_inter_arr_async, + 1); +SHOW_FUNCTION(bfq_wr_max_softrt_rate_show, bfqd->bfq_wr_max_softrt_rate, 0); +#undef SHOW_FUNCTION + +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ +static ssize_t \ +__FUNC(struct elevator_queue *e, const char *page, size_t count) \ +{ \ + struct bfq_data *bfqd = e->elevator_data; \ + unsigned long uninitialized_var(__data); \ + int ret = bfq_var_store(&__data, (page), count); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + if (__CONV) \ + *(__PTR) = msecs_to_jiffies(__data); \ + else \ + *(__PTR) = __data; \ + return ret; \ +} +STORE_FUNCTION(bfq_fifo_expire_sync_store, &bfqd->bfq_fifo_expire[1], 1, + INT_MAX, 1); +STORE_FUNCTION(bfq_fifo_expire_async_store, &bfqd->bfq_fifo_expire[0], 1, + INT_MAX, 1); +STORE_FUNCTION(bfq_back_seek_max_store, &bfqd->bfq_back_max, 0, INT_MAX, 0); +STORE_FUNCTION(bfq_back_seek_penalty_store, &bfqd->bfq_back_penalty, 1, + INT_MAX, 0); +STORE_FUNCTION(bfq_slice_idle_store, &bfqd->bfq_slice_idle, 0, INT_MAX, 1); +STORE_FUNCTION(bfq_max_budget_async_rq_store, &bfqd->bfq_max_budget_async_rq, + 1, INT_MAX, 0); +STORE_FUNCTION(bfq_timeout_async_store, &bfqd->bfq_timeout[BLK_RW_ASYNC], 0, + INT_MAX, 1); +STORE_FUNCTION(bfq_wr_coeff_store, &bfqd->bfq_wr_coeff, 1, INT_MAX, 0); +STORE_FUNCTION(bfq_wr_max_time_store, &bfqd->bfq_wr_max_time, 0, INT_MAX, 1); +STORE_FUNCTION(bfq_wr_rt_max_time_store, &bfqd->bfq_wr_rt_max_time, 0, INT_MAX, + 1); +STORE_FUNCTION(bfq_wr_min_idle_time_store, &bfqd->bfq_wr_min_idle_time, 0, + INT_MAX, 1); +STORE_FUNCTION(bfq_wr_min_inter_arr_async_store, + &bfqd->bfq_wr_min_inter_arr_async, 0, INT_MAX, 1); +STORE_FUNCTION(bfq_wr_max_softrt_rate_store, &bfqd->bfq_wr_max_softrt_rate, 0, + INT_MAX, 0); +#undef STORE_FUNCTION + +/* do nothing for the moment */ +static ssize_t bfq_weights_store(struct elevator_queue *e, + const char *page, size_t count) +{ + return count; +} + +static inline unsigned long bfq_estimated_max_budget(struct bfq_data *bfqd) +{ + u64 timeout = jiffies_to_msecs(bfqd->bfq_timeout[BLK_RW_SYNC]); + + if (bfqd->peak_rate_samples >= BFQ_PEAK_RATE_SAMPLES) + return bfq_calc_max_budget(bfqd->peak_rate, timeout); + else + return bfq_default_max_budget; +} + +static ssize_t bfq_max_budget_store(struct elevator_queue *e, + const char *page, size_t count) +{ + struct bfq_data *bfqd = e->elevator_data; + unsigned long uninitialized_var(__data); + int ret = bfq_var_store(&__data, (page), count); + + if (__data == 0) + bfqd->bfq_max_budget = bfq_estimated_max_budget(bfqd); + else { + if (__data > INT_MAX) + __data = INT_MAX; + bfqd->bfq_max_budget = __data; + } + + bfqd->bfq_user_max_budget = __data; + + return ret; +} + +static ssize_t bfq_timeout_sync_store(struct elevator_queue *e, + const char *page, size_t count) +{ + struct bfq_data *bfqd = e->elevator_data; + unsigned long uninitialized_var(__data); + int ret = bfq_var_store(&__data, (page), count); + + if (__data < 1) + __data = 1; + else if (__data > INT_MAX) + __data = INT_MAX; + + bfqd->bfq_timeout[BLK_RW_SYNC] = msecs_to_jiffies(__data); + if (bfqd->bfq_user_max_budget == 0) + bfqd->bfq_max_budget = bfq_estimated_max_budget(bfqd); + + return ret; +} + +static ssize_t bfq_low_latency_store(struct elevator_queue *e, + const char *page, size_t count) +{ + struct bfq_data *bfqd = e->elevator_data; + unsigned long uninitialized_var(__data); + int ret = bfq_var_store(&__data, (page), count); + + if (__data > 1) + __data = 1; + if (__data == 0 && bfqd->low_latency != 0) + bfq_end_wr(bfqd); + bfqd->low_latency = __data; + + return ret; +} + +#define BFQ_ATTR(name) \ + __ATTR(name, S_IRUGO|S_IWUSR, bfq_##name##_show, bfq_##name##_store) + +static struct elv_fs_entry bfq_attrs[] = { + BFQ_ATTR(fifo_expire_sync), + BFQ_ATTR(fifo_expire_async), + BFQ_ATTR(back_seek_max), + BFQ_ATTR(back_seek_penalty), + BFQ_ATTR(slice_idle), + BFQ_ATTR(max_budget), + BFQ_ATTR(max_budget_async_rq), + BFQ_ATTR(timeout_sync), + BFQ_ATTR(timeout_async), + BFQ_ATTR(low_latency), + BFQ_ATTR(wr_coeff), + BFQ_ATTR(wr_max_time), + BFQ_ATTR(wr_rt_max_time), + BFQ_ATTR(wr_min_idle_time), + BFQ_ATTR(wr_min_inter_arr_async), + BFQ_ATTR(wr_max_softrt_rate), + BFQ_ATTR(weights), + __ATTR_NULL +}; + +static struct elevator_type iosched_bfq = { + .ops = { + .elevator_merge_fn = bfq_merge, + .elevator_merged_fn = bfq_merged_request, + .elevator_merge_req_fn = bfq_merged_requests, + .elevator_allow_merge_fn = bfq_allow_merge, + .elevator_dispatch_fn = bfq_dispatch_requests, + .elevator_add_req_fn = bfq_insert_request, + .elevator_activate_req_fn = bfq_activate_request, + .elevator_deactivate_req_fn = bfq_deactivate_request, + .elevator_completed_req_fn = bfq_completed_request, + .elevator_former_req_fn = elv_rb_former_request, + .elevator_latter_req_fn = elv_rb_latter_request, + .elevator_init_icq_fn = bfq_init_icq, + .elevator_exit_icq_fn = bfq_exit_icq, + .elevator_set_req_fn = bfq_set_request, + .elevator_put_req_fn = bfq_put_request, + .elevator_may_queue_fn = bfq_may_queue, + .elevator_init_fn = bfq_init_queue, + .elevator_exit_fn = bfq_exit_queue, + }, + .icq_size = sizeof(struct bfq_io_cq), + .icq_align = __alignof__(struct bfq_io_cq), + .elevator_attrs = bfq_attrs, + .elevator_name = "bfq", + .elevator_owner = THIS_MODULE, +}; + +static int __init bfq_init(void) +{ + /* + * Can be 0 on HZ < 1000 setups. + */ + if (bfq_slice_idle == 0) + bfq_slice_idle = 0; + + if (bfq_timeout_async == 0) + bfq_timeout_async = 1; + + if (bfq_slab_setup()) + return -ENOMEM; + + /* + * Times to load large popular applications for the typical systems + * installed on the reference devices (see the comments before the + * definitions of the two arrays). + */ + T_slow[0] = msecs_to_jiffies(2600); + T_slow[1] = msecs_to_jiffies(1000); + T_fast[0] = msecs_to_jiffies(5500); + T_fast[1] = msecs_to_jiffies(2000); + + /* + * Thresholds that determine the switch between speed classes (see + * the comments before the definition of the array). + */ + device_speed_thresh[0] = (R_fast[0] + R_slow[0]) / 2; + device_speed_thresh[1] = (R_fast[1] + R_slow[1]) / 2; + + elv_register(&iosched_bfq); + pr_info("BFQ I/O-scheduler: v7r8"); + + return 0; +} + +static void __exit bfq_exit(void) +{ + elv_unregister(&iosched_bfq); + bfq_slab_kill(); +} + +module_init(bfq_init); +module_exit(bfq_exit); + +MODULE_AUTHOR("Fabio Checconi, Paolo Valente"); +MODULE_LICENSE("GPL"); diff --git a/block/bfq-sched.c b/block/bfq-sched.c new file mode 100644 index 00000000..d0890c6d --- /dev/null +++ b/block/bfq-sched.c @@ -0,0 +1,1180 @@ +/* + * BFQ: Hierarchical B-WF2Q+ scheduler. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + * + * Copyright (C) 2010 Paolo Valente + */ + +#ifdef CONFIG_CGROUP_BFQIO +#define for_each_entity(entity) \ + for (; entity != NULL; entity = entity->parent) + +#define for_each_entity_safe(entity, parent) \ + for (; entity && ({ parent = entity->parent; 1; }); entity = parent) + +static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, + int extract, + struct bfq_data *bfqd); + +static inline void bfq_update_budget(struct bfq_entity *next_in_service) +{ + struct bfq_entity *bfqg_entity; + struct bfq_group *bfqg; + struct bfq_sched_data *group_sd; + + BUG_ON(next_in_service == NULL); + + group_sd = next_in_service->sched_data; + + bfqg = container_of(group_sd, struct bfq_group, sched_data); + /* + * bfq_group's my_entity field is not NULL only if the group + * is not the root group. We must not touch the root entity + * as it must never become an in-service entity. + */ + bfqg_entity = bfqg->my_entity; + if (bfqg_entity != NULL) + bfqg_entity->budget = next_in_service->budget; +} + +static int bfq_update_next_in_service(struct bfq_sched_data *sd) +{ + struct bfq_entity *next_in_service; + + if (sd->in_service_entity != NULL) + /* will update/requeue at the end of service */ + return 0; + + /* + * NOTE: this can be improved in many ways, such as returning + * 1 (and thus propagating upwards the update) only when the + * budget changes, or caching the bfqq that will be scheduled + * next from this subtree. By now we worry more about + * correctness than about performance... + */ + next_in_service = bfq_lookup_next_entity(sd, 0, NULL); + sd->next_in_service = next_in_service; + + if (next_in_service != NULL) + bfq_update_budget(next_in_service); + + return 1; +} + +static inline void bfq_check_next_in_service(struct bfq_sched_data *sd, + struct bfq_entity *entity) +{ + BUG_ON(sd->next_in_service != entity); +} +#else +#define for_each_entity(entity) \ + for (; entity != NULL; entity = NULL) + +#define for_each_entity_safe(entity, parent) \ + for (parent = NULL; entity != NULL; entity = parent) + +static inline int bfq_update_next_in_service(struct bfq_sched_data *sd) +{ + return 0; +} + +static inline void bfq_check_next_in_service(struct bfq_sched_data *sd, + struct bfq_entity *entity) +{ +} + +static inline void bfq_update_budget(struct bfq_entity *next_in_service) +{ +} +#endif + +/* + * Shift for timestamp calculations. This actually limits the maximum + * service allowed in one timestamp delta (small shift values increase it), + * the maximum total weight that can be used for the queues in the system + * (big shift values increase it), and the period of virtual time + * wraparounds. + */ +#define WFQ_SERVICE_SHIFT 22 + +/** + * bfq_gt - compare two timestamps. + * @a: first ts. + * @b: second ts. + * + * Return @a > @b, dealing with wrapping correctly. + */ +static inline int bfq_gt(u64 a, u64 b) +{ + return (s64)(a - b) > 0; +} + +static inline struct bfq_queue *bfq_entity_to_bfqq(struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = NULL; + + BUG_ON(entity == NULL); + + if (entity->my_sched_data == NULL) + bfqq = container_of(entity, struct bfq_queue, entity); + + return bfqq; +} + + +/** + * bfq_delta - map service into the virtual time domain. + * @service: amount of service. + * @weight: scale factor (weight of an entity or weight sum). + */ +static inline u64 bfq_delta(unsigned long service, + unsigned long weight) +{ + u64 d = (u64)service << WFQ_SERVICE_SHIFT; + + do_div(d, weight); + return d; +} + +/** + * bfq_calc_finish - assign the finish time to an entity. + * @entity: the entity to act upon. + * @service: the service to be charged to the entity. + */ +static inline void bfq_calc_finish(struct bfq_entity *entity, + unsigned long service) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + + BUG_ON(entity->weight == 0); + + entity->finish = entity->start + + bfq_delta(service, entity->weight); + + if (bfqq != NULL) { + bfq_log_bfqq(bfqq->bfqd, bfqq, + "calc_finish: serv %lu, w %d", + service, entity->weight); + bfq_log_bfqq(bfqq->bfqd, bfqq, + "calc_finish: start %llu, finish %llu, delta %llu", + entity->start, entity->finish, + bfq_delta(service, entity->weight)); + } +} + +/** + * bfq_entity_of - get an entity from a node. + * @node: the node field of the entity. + * + * Convert a node pointer to the relative entity. This is used only + * to simplify the logic of some functions and not as the generic + * conversion mechanism because, e.g., in the tree walking functions, + * the check for a %NULL value would be redundant. + */ +static inline struct bfq_entity *bfq_entity_of(struct rb_node *node) +{ + struct bfq_entity *entity = NULL; + + if (node != NULL) + entity = rb_entry(node, struct bfq_entity, rb_node); + + return entity; +} + +/** + * bfq_extract - remove an entity from a tree. + * @root: the tree root. + * @entity: the entity to remove. + */ +static inline void bfq_extract(struct rb_root *root, + struct bfq_entity *entity) +{ + BUG_ON(entity->tree != root); + + entity->tree = NULL; + rb_erase(&entity->rb_node, root); +} + +/** + * bfq_idle_extract - extract an entity from the idle tree. + * @st: the service tree of the owning @entity. + * @entity: the entity being removed. + */ +static void bfq_idle_extract(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct rb_node *next; + + BUG_ON(entity->tree != &st->idle); + + if (entity == st->first_idle) { + next = rb_next(&entity->rb_node); + st->first_idle = bfq_entity_of(next); + } + + if (entity == st->last_idle) { + next = rb_prev(&entity->rb_node); + st->last_idle = bfq_entity_of(next); + } + + bfq_extract(&st->idle, entity); + + if (bfqq != NULL) + list_del(&bfqq->bfqq_list); +} + +/** + * bfq_insert - generic tree insertion. + * @root: tree root. + * @entity: entity to insert. + * + * This is used for the idle and the active tree, since they are both + * ordered by finish time. + */ +static void bfq_insert(struct rb_root *root, struct bfq_entity *entity) +{ + struct bfq_entity *entry; + struct rb_node **node = &root->rb_node; + struct rb_node *parent = NULL; + + BUG_ON(entity->tree != NULL); + + while (*node != NULL) { + parent = *node; + entry = rb_entry(parent, struct bfq_entity, rb_node); + + if (bfq_gt(entry->finish, entity->finish)) + node = &parent->rb_left; + else + node = &parent->rb_right; + } + + rb_link_node(&entity->rb_node, parent, node); + rb_insert_color(&entity->rb_node, root); + + entity->tree = root; +} + +/** + * bfq_update_min - update the min_start field of a entity. + * @entity: the entity to update. + * @node: one of its children. + * + * This function is called when @entity may store an invalid value for + * min_start due to updates to the active tree. The function assumes + * that the subtree rooted at @node (which may be its left or its right + * child) has a valid min_start value. + */ +static inline void bfq_update_min(struct bfq_entity *entity, + struct rb_node *node) +{ + struct bfq_entity *child; + + if (node != NULL) { + child = rb_entry(node, struct bfq_entity, rb_node); + if (bfq_gt(entity->min_start, child->min_start)) + entity->min_start = child->min_start; + } +} + +/** + * bfq_update_active_node - recalculate min_start. + * @node: the node to update. + * + * @node may have changed position or one of its children may have moved, + * this function updates its min_start value. The left and right subtrees + * are assumed to hold a correct min_start value. + */ +static inline void bfq_update_active_node(struct rb_node *node) +{ + struct bfq_entity *entity = rb_entry(node, struct bfq_entity, rb_node); + + entity->min_start = entity->start; + bfq_update_min(entity, node->rb_right); + bfq_update_min(entity, node->rb_left); +} + +/** + * bfq_update_active_tree - update min_start for the whole active tree. + * @node: the starting node. + * + * @node must be the deepest modified node after an update. This function + * updates its min_start using the values held by its children, assuming + * that they did not change, and then updates all the nodes that may have + * changed in the path to the root. The only nodes that may have changed + * are the ones in the path or their siblings. + */ +static void bfq_update_active_tree(struct rb_node *node) +{ + struct rb_node *parent; + +up: + bfq_update_active_node(node); + + parent = rb_parent(node); + if (parent == NULL) + return; + + if (node == parent->rb_left && parent->rb_right != NULL) + bfq_update_active_node(parent->rb_right); + else if (parent->rb_left != NULL) + bfq_update_active_node(parent->rb_left); + + node = parent; + goto up; +} + +static void bfq_weights_tree_add(struct bfq_data *bfqd, + struct bfq_entity *entity, + struct rb_root *root); + +static void bfq_weights_tree_remove(struct bfq_data *bfqd, + struct bfq_entity *entity, + struct rb_root *root); + + +/** + * bfq_active_insert - insert an entity in the active tree of its + * group/device. + * @st: the service tree of the entity. + * @entity: the entity being inserted. + * + * The active tree is ordered by finish time, but an extra key is kept + * per each node, containing the minimum value for the start times of + * its children (and the node itself), so it's possible to search for + * the eligible node with the lowest finish time in logarithmic time. + */ +static void bfq_active_insert(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct rb_node *node = &entity->rb_node; +#ifdef CONFIG_CGROUP_BFQIO + struct bfq_sched_data *sd = NULL; + struct bfq_group *bfqg = NULL; + struct bfq_data *bfqd = NULL; +#endif + + bfq_insert(&st->active, entity); + + if (node->rb_left != NULL) + node = node->rb_left; + else if (node->rb_right != NULL) + node = node->rb_right; + + bfq_update_active_tree(node); + +#ifdef CONFIG_CGROUP_BFQIO + sd = entity->sched_data; + bfqg = container_of(sd, struct bfq_group, sched_data); + BUG_ON(!bfqg); + bfqd = (struct bfq_data *)bfqg->bfqd; +#endif + if (bfqq != NULL) + list_add(&bfqq->bfqq_list, &bfqq->bfqd->active_list); +#ifdef CONFIG_CGROUP_BFQIO + else { /* bfq_group */ + BUG_ON(!bfqd); + bfq_weights_tree_add(bfqd, entity, &bfqd->group_weights_tree); + } + if (bfqg != bfqd->root_group) { + BUG_ON(!bfqg); + BUG_ON(!bfqd); + bfqg->active_entities++; + if (bfqg->active_entities == 2) + bfqd->active_numerous_groups++; + } +#endif +} + +/** + * bfq_ioprio_to_weight - calc a weight from an ioprio. + * @ioprio: the ioprio value to convert. + */ +static inline unsigned short bfq_ioprio_to_weight(int ioprio) +{ + BUG_ON(ioprio < 0 || ioprio >= IOPRIO_BE_NR); + return IOPRIO_BE_NR - ioprio; +} + +/** + * bfq_weight_to_ioprio - calc an ioprio from a weight. + * @weight: the weight value to convert. + * + * To preserve as mush as possible the old only-ioprio user interface, + * 0 is used as an escape ioprio value for weights (numerically) equal or + * larger than IOPRIO_BE_NR + */ +static inline unsigned short bfq_weight_to_ioprio(int weight) +{ + BUG_ON(weight < BFQ_MIN_WEIGHT || weight > BFQ_MAX_WEIGHT); + return IOPRIO_BE_NR - weight < 0 ? 0 : IOPRIO_BE_NR - weight; +} + +static inline void bfq_get_entity(struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + + if (bfqq != NULL) { + atomic_inc(&bfqq->ref); + bfq_log_bfqq(bfqq->bfqd, bfqq, "get_entity: %p %d", + bfqq, atomic_read(&bfqq->ref)); + } +} + +/** + * bfq_find_deepest - find the deepest node that an extraction can modify. + * @node: the node being removed. + * + * Do the first step of an extraction in an rb tree, looking for the + * node that will replace @node, and returning the deepest node that + * the following modifications to the tree can touch. If @node is the + * last node in the tree return %NULL. + */ +static struct rb_node *bfq_find_deepest(struct rb_node *node) +{ + struct rb_node *deepest; + + if (node->rb_right == NULL && node->rb_left == NULL) + deepest = rb_parent(node); + else if (node->rb_right == NULL) + deepest = node->rb_left; + else if (node->rb_left == NULL) + deepest = node->rb_right; + else { + deepest = rb_next(node); + if (deepest->rb_right != NULL) + deepest = deepest->rb_right; + else if (rb_parent(deepest) != node) + deepest = rb_parent(deepest); + } + + return deepest; +} + +/** + * bfq_active_extract - remove an entity from the active tree. + * @st: the service_tree containing the tree. + * @entity: the entity being removed. + */ +static void bfq_active_extract(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct rb_node *node; +#ifdef CONFIG_CGROUP_BFQIO + struct bfq_sched_data *sd = NULL; + struct bfq_group *bfqg = NULL; + struct bfq_data *bfqd = NULL; +#endif + + node = bfq_find_deepest(&entity->rb_node); + bfq_extract(&st->active, entity); + + if (node != NULL) + bfq_update_active_tree(node); + +#ifdef CONFIG_CGROUP_BFQIO + sd = entity->sched_data; + bfqg = container_of(sd, struct bfq_group, sched_data); + BUG_ON(!bfqg); + bfqd = (struct bfq_data *)bfqg->bfqd; +#endif + if (bfqq != NULL) + list_del(&bfqq->bfqq_list); +#ifdef CONFIG_CGROUP_BFQIO + else { /* bfq_group */ + BUG_ON(!bfqd); + bfq_weights_tree_remove(bfqd, entity, + &bfqd->group_weights_tree); + } + if (bfqg != bfqd->root_group) { + BUG_ON(!bfqg); + BUG_ON(!bfqd); + BUG_ON(!bfqg->active_entities); + bfqg->active_entities--; + if (bfqg->active_entities == 1) { + BUG_ON(!bfqd->active_numerous_groups); + bfqd->active_numerous_groups--; + } + } +#endif +} + +/** + * bfq_idle_insert - insert an entity into the idle tree. + * @st: the service tree containing the tree. + * @entity: the entity to insert. + */ +static void bfq_idle_insert(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct bfq_entity *first_idle = st->first_idle; + struct bfq_entity *last_idle = st->last_idle; + + if (first_idle == NULL || bfq_gt(first_idle->finish, entity->finish)) + st->first_idle = entity; + if (last_idle == NULL || bfq_gt(entity->finish, last_idle->finish)) + st->last_idle = entity; + + bfq_insert(&st->idle, entity); + + if (bfqq != NULL) + list_add(&bfqq->bfqq_list, &bfqq->bfqd->idle_list); +} + +/** + * bfq_forget_entity - remove an entity from the wfq trees. + * @st: the service tree. + * @entity: the entity being removed. + * + * Update the device status and forget everything about @entity, putting + * the device reference to it, if it is a queue. Entities belonging to + * groups are not refcounted. + */ +static void bfq_forget_entity(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct bfq_sched_data *sd; + + BUG_ON(!entity->on_st); + + entity->on_st = 0; + st->wsum -= entity->weight; + if (bfqq != NULL) { + sd = entity->sched_data; + bfq_log_bfqq(bfqq->bfqd, bfqq, "forget_entity: %p %d", + bfqq, atomic_read(&bfqq->ref)); + bfq_put_queue(bfqq); + } +} + +/** + * bfq_put_idle_entity - release the idle tree ref of an entity. + * @st: service tree for the entity. + * @entity: the entity being released. + */ +static void bfq_put_idle_entity(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + bfq_idle_extract(st, entity); + bfq_forget_entity(st, entity); +} + +/** + * bfq_forget_idle - update the idle tree if necessary. + * @st: the service tree to act upon. + * + * To preserve the global O(log N) complexity we only remove one entry here; + * as the idle tree will not grow indefinitely this can be done safely. + */ +static void bfq_forget_idle(struct bfq_service_tree *st) +{ + struct bfq_entity *first_idle = st->first_idle; + struct bfq_entity *last_idle = st->last_idle; + + if (RB_EMPTY_ROOT(&st->active) && last_idle != NULL && + !bfq_gt(last_idle->finish, st->vtime)) { + /* + * Forget the whole idle tree, increasing the vtime past + * the last finish time of idle entities. + */ + st->vtime = last_idle->finish; + } + + if (first_idle != NULL && !bfq_gt(first_idle->finish, st->vtime)) + bfq_put_idle_entity(st, first_idle); +} + +static struct bfq_service_tree * +__bfq_entity_update_weight_prio(struct bfq_service_tree *old_st, + struct bfq_entity *entity) +{ + struct bfq_service_tree *new_st = old_st; + + if (entity->ioprio_changed) { + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + unsigned short prev_weight, new_weight; + struct bfq_data *bfqd = NULL; + struct rb_root *root; +#ifdef CONFIG_CGROUP_BFQIO + struct bfq_sched_data *sd; + struct bfq_group *bfqg; +#endif + + if (bfqq != NULL) + bfqd = bfqq->bfqd; +#ifdef CONFIG_CGROUP_BFQIO + else { + sd = entity->my_sched_data; + bfqg = container_of(sd, struct bfq_group, sched_data); + BUG_ON(!bfqg); + bfqd = (struct bfq_data *)bfqg->bfqd; + BUG_ON(!bfqd); + } +#endif + + BUG_ON(old_st->wsum < entity->weight); + old_st->wsum -= entity->weight; + + if (entity->new_weight != entity->orig_weight) { + if (entity->new_weight < BFQ_MIN_WEIGHT || + entity->new_weight > BFQ_MAX_WEIGHT) { + printk(KERN_CRIT "update_weight_prio: " + "new_weight %d\n", + entity->new_weight); + BUG(); + } + entity->orig_weight = entity->new_weight; + entity->ioprio = + bfq_weight_to_ioprio(entity->orig_weight); + } + + entity->ioprio_class = entity->new_ioprio_class; + entity->ioprio_changed = 0; + + /* + * NOTE: here we may be changing the weight too early, + * this will cause unfairness. The correct approach + * would have required additional complexity to defer + * weight changes to the proper time instants (i.e., + * when entity->finish <= old_st->vtime). + */ + new_st = bfq_entity_service_tree(entity); + + prev_weight = entity->weight; + new_weight = entity->orig_weight * + (bfqq != NULL ? bfqq->wr_coeff : 1); + /* + * If the weight of the entity changes, remove the entity + * from its old weight counter (if there is a counter + * associated with the entity), and add it to the counter + * associated with its new weight. + */ + if (prev_weight != new_weight) { + root = bfqq ? &bfqd->queue_weights_tree : + &bfqd->group_weights_tree; + bfq_weights_tree_remove(bfqd, entity, root); + } + entity->weight = new_weight; + /* + * Add the entity to its weights tree only if it is + * not associated with a weight-raised queue. + */ + if (prev_weight != new_weight && + (bfqq ? bfqq->wr_coeff == 1 : 1)) + /* If we get here, root has been initialized. */ + bfq_weights_tree_add(bfqd, entity, root); + + new_st->wsum += entity->weight; + + if (new_st != old_st) + entity->start = new_st->vtime; + } + + return new_st; +} + +/** + * bfq_bfqq_served - update the scheduler status after selection for + * service. + * @bfqq: the queue being served. + * @served: bytes to transfer. + * + * NOTE: this can be optimized, as the timestamps of upper level entities + * are synchronized every time a new bfqq is selected for service. By now, + * we keep it to better check consistency. + */ +static void bfq_bfqq_served(struct bfq_queue *bfqq, unsigned long served) +{ + struct bfq_entity *entity = &bfqq->entity; + struct bfq_service_tree *st; + + for_each_entity(entity) { + st = bfq_entity_service_tree(entity); + + entity->service += served; + BUG_ON(entity->service > entity->budget); + BUG_ON(st->wsum == 0); + + st->vtime += bfq_delta(served, st->wsum); + bfq_forget_idle(st); + } + bfq_log_bfqq(bfqq->bfqd, bfqq, "bfqq_served %lu secs", served); +} + +/** + * bfq_bfqq_charge_full_budget - set the service to the entity budget. + * @bfqq: the queue that needs a service update. + * + * When it's not possible to be fair in the service domain, because + * a queue is not consuming its budget fast enough (the meaning of + * fast depends on the timeout parameter), we charge it a full + * budget. In this way we should obtain a sort of time-domain + * fairness among all the seeky/slow queues. + */ +static inline void bfq_bfqq_charge_full_budget(struct bfq_queue *bfqq) +{ + struct bfq_entity *entity = &bfqq->entity; + + bfq_log_bfqq(bfqq->bfqd, bfqq, "charge_full_budget"); + + bfq_bfqq_served(bfqq, entity->budget - entity->service); +} + +/** + * __bfq_activate_entity - activate an entity. + * @entity: the entity being activated. + * + * Called whenever an entity is activated, i.e., it is not active and one + * of its children receives a new request, or has to be reactivated due to + * budget exhaustion. It uses the current budget of the entity (and the + * service received if @entity is active) of the queue to calculate its + * timestamps. + */ +static void __bfq_activate_entity(struct bfq_entity *entity) +{ + struct bfq_sched_data *sd = entity->sched_data; + struct bfq_service_tree *st = bfq_entity_service_tree(entity); + + if (entity == sd->in_service_entity) { + BUG_ON(entity->tree != NULL); + /* + * If we are requeueing the current entity we have + * to take care of not charging to it service it has + * not received. + */ + bfq_calc_finish(entity, entity->service); + entity->start = entity->finish; + sd->in_service_entity = NULL; + } else if (entity->tree == &st->active) { + /* + * Requeueing an entity due to a change of some + * next_in_service entity below it. We reuse the + * old start time. + */ + bfq_active_extract(st, entity); + } else if (entity->tree == &st->idle) { + /* + * Must be on the idle tree, bfq_idle_extract() will + * check for that. + */ + bfq_idle_extract(st, entity); + entity->start = bfq_gt(st->vtime, entity->finish) ? + st->vtime : entity->finish; + } else { + /* + * The finish time of the entity may be invalid, and + * it is in the past for sure, otherwise the queue + * would have been on the idle tree. + */ + entity->start = st->vtime; + st->wsum += entity->weight; + bfq_get_entity(entity); + + BUG_ON(entity->on_st); + entity->on_st = 1; + } + + st = __bfq_entity_update_weight_prio(st, entity); + bfq_calc_finish(entity, entity->budget); + bfq_active_insert(st, entity); +} + +/** + * bfq_activate_entity - activate an entity and its ancestors if necessary. + * @entity: the entity to activate. + * + * Activate @entity and all the entities on the path from it to the root. + */ +static void bfq_activate_entity(struct bfq_entity *entity) +{ + struct bfq_sched_data *sd; + + for_each_entity(entity) { + __bfq_activate_entity(entity); + + sd = entity->sched_data; + if (!bfq_update_next_in_service(sd)) + /* + * No need to propagate the activation to the + * upper entities, as they will be updated when + * the in-service entity is rescheduled. + */ + break; + } +} + +/** + * __bfq_deactivate_entity - deactivate an entity from its service tree. + * @entity: the entity to deactivate. + * @requeue: if false, the entity will not be put into the idle tree. + * + * Deactivate an entity, independently from its previous state. If the + * entity was not on a service tree just return, otherwise if it is on + * any scheduler tree, extract it from that tree, and if necessary + * and if the caller did not specify @requeue, put it on the idle tree. + * + * Return %1 if the caller should update the entity hierarchy, i.e., + * if the entity was in service or if it was the next_in_service for + * its sched_data; return %0 otherwise. + */ +static int __bfq_deactivate_entity(struct bfq_entity *entity, int requeue) +{ + struct bfq_sched_data *sd = entity->sched_data; + struct bfq_service_tree *st = bfq_entity_service_tree(entity); + int was_in_service = entity == sd->in_service_entity; + int ret = 0; + + if (!entity->on_st) + return 0; + + BUG_ON(was_in_service && entity->tree != NULL); + + if (was_in_service) { + bfq_calc_finish(entity, entity->service); + sd->in_service_entity = NULL; + } else if (entity->tree == &st->active) + bfq_active_extract(st, entity); + else if (entity->tree == &st->idle) + bfq_idle_extract(st, entity); + else if (entity->tree != NULL) + BUG(); + + if (was_in_service || sd->next_in_service == entity) + ret = bfq_update_next_in_service(sd); + + if (!requeue || !bfq_gt(entity->finish, st->vtime)) + bfq_forget_entity(st, entity); + else + bfq_idle_insert(st, entity); + + BUG_ON(sd->in_service_entity == entity); + BUG_ON(sd->next_in_service == entity); + + return ret; +} + +/** + * bfq_deactivate_entity - deactivate an entity. + * @entity: the entity to deactivate. + * @requeue: true if the entity can be put on the idle tree + */ +static void bfq_deactivate_entity(struct bfq_entity *entity, int requeue) +{ + struct bfq_sched_data *sd; + struct bfq_entity *parent; + + for_each_entity_safe(entity, parent) { + sd = entity->sched_data; + + if (!__bfq_deactivate_entity(entity, requeue)) + /* + * The parent entity is still backlogged, and + * we don't need to update it as it is still + * in service. + */ + break; + + if (sd->next_in_service != NULL) + /* + * The parent entity is still backlogged and + * the budgets on the path towards the root + * need to be updated. + */ + goto update; + + /* + * If we reach there the parent is no more backlogged and + * we want to propagate the dequeue upwards. + */ + requeue = 1; + } + + return; + +update: + entity = parent; + for_each_entity(entity) { + __bfq_activate_entity(entity); + + sd = entity->sched_data; + if (!bfq_update_next_in_service(sd)) + break; + } +} + +/** + * bfq_update_vtime - update vtime if necessary. + * @st: the service tree to act upon. + * + * If necessary update the service tree vtime to have at least one + * eligible entity, skipping to its start time. Assumes that the + * active tree of the device is not empty. + * + * NOTE: this hierarchical implementation updates vtimes quite often, + * we may end up with reactivated processes getting timestamps after a + * vtime skip done because we needed a ->first_active entity on some + * intermediate node. + */ +static void bfq_update_vtime(struct bfq_service_tree *st) +{ + struct bfq_entity *entry; + struct rb_node *node = st->active.rb_node; + + entry = rb_entry(node, struct bfq_entity, rb_node); + if (bfq_gt(entry->min_start, st->vtime)) { + st->vtime = entry->min_start; + bfq_forget_idle(st); + } +} + +/** + * bfq_first_active_entity - find the eligible entity with + * the smallest finish time + * @st: the service tree to select from. + * + * This function searches the first schedulable entity, starting from the + * root of the tree and going on the left every time on this side there is + * a subtree with at least one eligible (start >= vtime) entity. The path on + * the right is followed only if a) the left subtree contains no eligible + * entities and b) no eligible entity has been found yet. + */ +static struct bfq_entity *bfq_first_active_entity(struct bfq_service_tree *st) +{ + struct bfq_entity *entry, *first = NULL; + struct rb_node *node = st->active.rb_node; + + while (node != NULL) { + entry = rb_entry(node, struct bfq_entity, rb_node); +left: + if (!bfq_gt(entry->start, st->vtime)) + first = entry; + + BUG_ON(bfq_gt(entry->min_start, st->vtime)); + + if (node->rb_left != NULL) { + entry = rb_entry(node->rb_left, + struct bfq_entity, rb_node); + if (!bfq_gt(entry->min_start, st->vtime)) { + node = node->rb_left; + goto left; + } + } + if (first != NULL) + break; + node = node->rb_right; + } + + BUG_ON(first == NULL && !RB_EMPTY_ROOT(&st->active)); + return first; +} + +/** + * __bfq_lookup_next_entity - return the first eligible entity in @st. + * @st: the service tree. + * + * Update the virtual time in @st and return the first eligible entity + * it contains. + */ +static struct bfq_entity *__bfq_lookup_next_entity(struct bfq_service_tree *st, + bool force) +{ + struct bfq_entity *entity, *new_next_in_service = NULL; + + if (RB_EMPTY_ROOT(&st->active)) + return NULL; + + bfq_update_vtime(st); + entity = bfq_first_active_entity(st); + BUG_ON(bfq_gt(entity->start, st->vtime)); + + /* + * If the chosen entity does not match with the sched_data's + * next_in_service and we are forcedly serving the IDLE priority + * class tree, bubble up budget update. + */ + if (unlikely(force && entity != entity->sched_data->next_in_service)) { + new_next_in_service = entity; + for_each_entity(new_next_in_service) + bfq_update_budget(new_next_in_service); + } + + return entity; +} + +/** + * bfq_lookup_next_entity - return the first eligible entity in @sd. + * @sd: the sched_data. + * @extract: if true the returned entity will be also extracted from @sd. + * + * NOTE: since we cache the next_in_service entity at each level of the + * hierarchy, the complexity of the lookup can be decreased with + * absolutely no effort just returning the cached next_in_service value; + * we prefer to do full lookups to test the consistency of * the data + * structures. + */ +static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, + int extract, + struct bfq_data *bfqd) +{ + struct bfq_service_tree *st = sd->service_tree; + struct bfq_entity *entity; + int i = 0; + + BUG_ON(sd->in_service_entity != NULL); + + if (bfqd != NULL && + jiffies - bfqd->bfq_class_idle_last_service > BFQ_CL_IDLE_TIMEOUT) { + entity = __bfq_lookup_next_entity(st + BFQ_IOPRIO_CLASSES - 1, + true); + if (entity != NULL) { + i = BFQ_IOPRIO_CLASSES - 1; + bfqd->bfq_class_idle_last_service = jiffies; + sd->next_in_service = entity; + } + } + for (; i < BFQ_IOPRIO_CLASSES; i++) { + entity = __bfq_lookup_next_entity(st + i, false); + if (entity != NULL) { + if (extract) { + bfq_check_next_in_service(sd, entity); + bfq_active_extract(st + i, entity); + sd->in_service_entity = entity; + sd->next_in_service = NULL; + } + break; + } + } + + return entity; +} + +/* + * Get next queue for service. + */ +static struct bfq_queue *bfq_get_next_queue(struct bfq_data *bfqd) +{ + struct bfq_entity *entity = NULL; + struct bfq_sched_data *sd; + struct bfq_queue *bfqq; + + BUG_ON(bfqd->in_service_queue != NULL); + + if (bfqd->busy_queues == 0) + return NULL; + + sd = &bfqd->root_group->sched_data; + for (; sd != NULL; sd = entity->my_sched_data) { + entity = bfq_lookup_next_entity(sd, 1, bfqd); + BUG_ON(entity == NULL); + entity->service = 0; + } + + bfqq = bfq_entity_to_bfqq(entity); + BUG_ON(bfqq == NULL); + + return bfqq; +} + +static void __bfq_bfqd_reset_in_service(struct bfq_data *bfqd) +{ + if (bfqd->in_service_bic != NULL) { + put_io_context(bfqd->in_service_bic->icq.ioc); + bfqd->in_service_bic = NULL; + } + + bfqd->in_service_queue = NULL; + del_timer(&bfqd->idle_slice_timer); +} + +static void bfq_deactivate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq, + int requeue) +{ + struct bfq_entity *entity = &bfqq->entity; + + if (bfqq == bfqd->in_service_queue) + __bfq_bfqd_reset_in_service(bfqd); + + bfq_deactivate_entity(entity, requeue); +} + +static void bfq_activate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + struct bfq_entity *entity = &bfqq->entity; + + bfq_activate_entity(entity); +} + +/* + * Called when the bfqq no longer has requests pending, remove it from + * the service tree. + */ +static void bfq_del_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq, + int requeue) +{ + BUG_ON(!bfq_bfqq_busy(bfqq)); + BUG_ON(!RB_EMPTY_ROOT(&bfqq->sort_list)); + + bfq_log_bfqq(bfqd, bfqq, "del from busy"); + + bfq_clear_bfqq_busy(bfqq); + + BUG_ON(bfqd->busy_queues == 0); + bfqd->busy_queues--; + + if (!bfqq->dispatched) { + bfq_weights_tree_remove(bfqd, &bfqq->entity, + &bfqd->queue_weights_tree); + if (!blk_queue_nonrot(bfqd->queue)) { + BUG_ON(!bfqd->busy_in_flight_queues); + bfqd->busy_in_flight_queues--; + if (bfq_bfqq_constantly_seeky(bfqq)) { + BUG_ON(!bfqd-> + const_seeky_busy_in_flight_queues); + bfqd->const_seeky_busy_in_flight_queues--; + } + } + } + if (bfqq->wr_coeff > 1) + bfqd->wr_busy_queues--; + + bfq_deactivate_bfqq(bfqd, bfqq, requeue); +} + +/* + * Called when an inactive queue receives a new request. + */ +static void bfq_add_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + BUG_ON(bfq_bfqq_busy(bfqq)); + BUG_ON(bfqq == bfqd->in_service_queue); + + bfq_log_bfqq(bfqd, bfqq, "add to busy"); + + bfq_activate_bfqq(bfqd, bfqq); + + bfq_mark_bfqq_busy(bfqq); + bfqd->busy_queues++; + + if (!bfqq->dispatched) { + if (bfqq->wr_coeff == 1) + bfq_weights_tree_add(bfqd, &bfqq->entity, + &bfqd->queue_weights_tree); + if (!blk_queue_nonrot(bfqd->queue)) { + bfqd->busy_in_flight_queues++; + if (bfq_bfqq_constantly_seeky(bfqq)) + bfqd->const_seeky_busy_in_flight_queues++; + } + } + if (bfqq->wr_coeff > 1) + bfqd->wr_busy_queues++; +} diff --git a/block/bfq.h b/block/bfq.h new file mode 100644 index 00000000..e6f1a88f --- /dev/null +++ b/block/bfq.h @@ -0,0 +1,806 @@ +/* + * BFQ-v7r8 for 3.4.0: data structures and common functions prototypes. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + * + * Copyright (C) 2010 Paolo Valente + */ + +#ifndef _BFQ_H +#define _BFQ_H + +#include +#include +#include +#include + +#define BFQ_IOPRIO_CLASSES 3 +#define BFQ_CL_IDLE_TIMEOUT (HZ/5) + +#define BFQ_MIN_WEIGHT 1 +#define BFQ_MAX_WEIGHT 1000 + +#define BFQ_DEFAULT_QUEUE_IOPRIO 4 + +#define BFQ_DEFAULT_GRP_WEIGHT 10 +#define BFQ_DEFAULT_GRP_IOPRIO 0 +#define BFQ_DEFAULT_GRP_CLASS IOPRIO_CLASS_BE + +struct bfq_entity; + +/** + * struct bfq_service_tree - per ioprio_class service tree. + * @active: tree for active entities (i.e., those backlogged). + * @idle: tree for idle entities (i.e., those not backlogged, with V <= F_i). + * @first_idle: idle entity with minimum F_i. + * @last_idle: idle entity with maximum F_i. + * @vtime: scheduler virtual time. + * @wsum: scheduler weight sum; active and idle entities contribute to it. + * + * Each service tree represents a B-WF2Q+ scheduler on its own. Each + * ioprio_class has its own independent scheduler, and so its own + * bfq_service_tree. All the fields are protected by the queue lock + * of the containing bfqd. + */ +struct bfq_service_tree { + struct rb_root active; + struct rb_root idle; + + struct bfq_entity *first_idle; + struct bfq_entity *last_idle; + + u64 vtime; + unsigned long wsum; +}; + +/** + * struct bfq_sched_data - multi-class scheduler. + * @in_service_entity: entity in service. + * @next_in_service: head-of-the-line entity in the scheduler. + * @service_tree: array of service trees, one per ioprio_class. + * + * bfq_sched_data is the basic scheduler queue. It supports three + * ioprio_classes, and can be used either as a toplevel queue or as + * an intermediate queue on a hierarchical setup. + * @next_in_service points to the active entity of the sched_data + * service trees that will be scheduled next. + * + * The supported ioprio_classes are the same as in CFQ, in descending + * priority order, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE. + * Requests from higher priority queues are served before all the + * requests from lower priority queues; among requests of the same + * queue requests are served according to B-WF2Q+. + * All the fields are protected by the queue lock of the containing bfqd. + */ +struct bfq_sched_data { + struct bfq_entity *in_service_entity; + struct bfq_entity *next_in_service; + struct bfq_service_tree service_tree[BFQ_IOPRIO_CLASSES]; +}; + +/** + * struct bfq_weight_counter - counter of the number of all active entities + * with a given weight. + * @weight: weight of the entities that this counter refers to. + * @num_active: number of active entities with this weight. + * @weights_node: weights tree member (see bfq_data's @queue_weights_tree + * and @group_weights_tree). + */ +struct bfq_weight_counter { + short int weight; + unsigned int num_active; + struct rb_node weights_node; +}; + +/** + * struct bfq_entity - schedulable entity. + * @rb_node: service_tree member. + * @weight_counter: pointer to the weight counter associated with this entity. + * @on_st: flag, true if the entity is on a tree (either the active or + * the idle one of its service_tree). + * @finish: B-WF2Q+ finish timestamp (aka F_i). + * @start: B-WF2Q+ start timestamp (aka S_i). + * @tree: tree the entity is enqueued into; %NULL if not on a tree. + * @min_start: minimum start time of the (active) subtree rooted at + * this entity; used for O(log N) lookups into active trees. + * @service: service received during the last round of service. + * @budget: budget used to calculate F_i; F_i = S_i + @budget / @weight. + * @weight: weight of the queue + * @parent: parent entity, for hierarchical scheduling. + * @my_sched_data: for non-leaf nodes in the cgroup hierarchy, the + * associated scheduler queue, %NULL on leaf nodes. + * @sched_data: the scheduler queue this entity belongs to. + * @ioprio: the ioprio in use. + * @new_weight: when a weight change is requested, the new weight value. + * @orig_weight: original weight, used to implement weight boosting + * @new_ioprio: when an ioprio change is requested, the new ioprio value. + * @ioprio_class: the ioprio_class in use. + * @new_ioprio_class: when an ioprio_class change is requested, the new + * ioprio_class value. + * @ioprio_changed: flag, true when the user requested a weight, ioprio or + * ioprio_class change. + * + * A bfq_entity is used to represent either a bfq_queue (leaf node in the + * cgroup hierarchy) or a bfq_group into the upper level scheduler. Each + * entity belongs to the sched_data of the parent group in the cgroup + * hierarchy. Non-leaf entities have also their own sched_data, stored + * in @my_sched_data. + * + * Each entity stores independently its priority values; this would + * allow different weights on different devices, but this + * functionality is not exported to userspace by now. Priorities and + * weights are updated lazily, first storing the new values into the + * new_* fields, then setting the @ioprio_changed flag. As soon as + * there is a transition in the entity state that allows the priority + * update to take place the effective and the requested priority + * values are synchronized. + * + * Unless cgroups are used, the weight value is calculated from the + * ioprio to export the same interface as CFQ. When dealing with + * ``well-behaved'' queues (i.e., queues that do not spend too much + * time to consume their budget and have true sequential behavior, and + * when there are no external factors breaking anticipation) the + * relative weights at each level of the cgroups hierarchy should be + * guaranteed. All the fields are protected by the queue lock of the + * containing bfqd. + */ +struct bfq_entity { + struct rb_node rb_node; + struct bfq_weight_counter *weight_counter; + + int on_st; + + u64 finish; + u64 start; + + struct rb_root *tree; + + u64 min_start; + + unsigned long service, budget; + unsigned short weight, new_weight; + unsigned short orig_weight; + + struct bfq_entity *parent; + + struct bfq_sched_data *my_sched_data; + struct bfq_sched_data *sched_data; + + unsigned short ioprio, new_ioprio; + unsigned short ioprio_class, new_ioprio_class; + + int ioprio_changed; +}; + +struct bfq_group; + +/** + * struct bfq_queue - leaf schedulable entity. + * @ref: reference counter. + * @bfqd: parent bfq_data. + * @new_bfqq: shared bfq_queue if queue is cooperating with + * one or more other queues. + * @pos_node: request-position tree member (see bfq_data's @rq_pos_tree). + * @pos_root: request-position tree root (see bfq_data's @rq_pos_tree). + * @sort_list: sorted list of pending requests. + * @next_rq: if fifo isn't expired, next request to serve. + * @queued: nr of requests queued in @sort_list. + * @allocated: currently allocated requests. + * @meta_pending: pending metadata requests. + * @fifo: fifo list of requests in sort_list. + * @entity: entity representing this queue in the scheduler. + * @max_budget: maximum budget allowed from the feedback mechanism. + * @budget_timeout: budget expiration (in jiffies). + * @dispatched: number of requests on the dispatch list or inside driver. + * @flags: status flags. + * @bfqq_list: node for active/idle bfqq list inside our bfqd. + * @burst_list_node: node for the device's burst list. + * @seek_samples: number of seeks sampled + * @seek_total: sum of the distances of the seeks sampled + * @seek_mean: mean seek distance + * @last_request_pos: position of the last request enqueued + * @requests_within_timer: number of consecutive pairs of request completion + * and arrival, such that the queue becomes idle + * after the completion, but the next request arrives + * within an idle time slice; used only if the queue's + * IO_bound has been cleared. + * @pid: pid of the process owning the queue, used for logging purposes. + * @last_wr_start_finish: start time of the current weight-raising period if + * the @bfq-queue is being weight-raised, otherwise + * finish time of the last weight-raising period + * @wr_cur_max_time: current max raising time for this queue + * @soft_rt_next_start: minimum time instant such that, only if a new + * request is enqueued after this time instant in an + * idle @bfq_queue with no outstanding requests, then + * the task associated with the queue it is deemed as + * soft real-time (see the comments to the function + * bfq_bfqq_softrt_next_start()) + * @last_idle_bklogged: time of the last transition of the @bfq_queue from + * idle to backlogged + * @service_from_backlogged: cumulative service received from the @bfq_queue + * since the last transition from idle to + * backlogged + * @bic: pointer to the bfq_io_cq owning the bfq_queue, set to %NULL if the + * queue is shared + * + * A bfq_queue is a leaf request queue; it can be associated with an + * io_context or more, if it is async or shared between cooperating + * processes. @cgroup holds a reference to the cgroup, to be sure that it + * does not disappear while a bfqq still references it (mostly to avoid + * races between request issuing and task migration followed by cgroup + * destruction). + * All the fields are protected by the queue lock of the containing bfqd. + */ +struct bfq_queue { + atomic_t ref; + struct bfq_data *bfqd; + + /* fields for cooperating queues handling */ + struct bfq_queue *new_bfqq; + struct rb_node pos_node; + struct rb_root *pos_root; + + struct rb_root sort_list; + struct request *next_rq; + int queued[2]; + int allocated[2]; + int meta_pending; + struct list_head fifo; + + struct bfq_entity entity; + + unsigned long max_budget; + unsigned long budget_timeout; + + int dispatched; + + unsigned int flags; + + struct list_head bfqq_list; + + struct hlist_node burst_list_node; + + unsigned int seek_samples; + u64 seek_total; + sector_t seek_mean; + sector_t last_request_pos; + + unsigned int requests_within_timer; + + pid_t pid; + struct bfq_io_cq *bic; + + /* weight-raising fields */ + unsigned long wr_cur_max_time; + unsigned long soft_rt_next_start; + unsigned long last_wr_start_finish; + unsigned int wr_coeff; + unsigned long last_idle_bklogged; + unsigned long service_from_backlogged; +}; + +/** + * struct bfq_ttime - per process thinktime stats. + * @ttime_total: total process thinktime + * @ttime_samples: number of thinktime samples + * @ttime_mean: average process thinktime + */ +struct bfq_ttime { + unsigned long last_end_request; + + unsigned long ttime_total; + unsigned long ttime_samples; + unsigned long ttime_mean; +}; + +/** + * struct bfq_io_cq - per (request_queue, io_context) structure. + * @icq: associated io_cq structure + * @bfqq: array of two process queues, the sync and the async + * @ttime: associated @bfq_ttime struct + * @wr_time_left: snapshot of the time left before weight raising ends + * for the sync queue associated to this process; this + * snapshot is taken to remember this value while the weight + * raising is suspended because the queue is merged with a + * shared queue, and is used to set @raising_cur_max_time + * when the queue is split from the shared queue and its + * weight is raised again + * @saved_idle_window: same purpose as the previous field for the idle + * window + * @saved_IO_bound: same purpose as the previous two fields for the I/O + * bound classification of a queue + * @saved_in_large_burst: same purpose as the previous fields for the + * value of the field keeping the queue's belonging + * to a large burst + * @was_in_burst_list: true if the queue belonged to a burst list + * before its merge with another cooperating queue + * @cooperations: counter of consecutive successful queue merges underwent + * by any of the process' @bfq_queues + * @failed_cooperations: counter of consecutive failed queue merges of any + * of the process' @bfq_queues + */ +struct bfq_io_cq { + struct io_cq icq; /* must be the first member */ + struct bfq_queue *bfqq[2]; + struct bfq_ttime ttime; + int ioprio; + + unsigned int wr_time_left; + bool saved_idle_window; + bool saved_IO_bound; + + bool saved_in_large_burst; + bool was_in_burst_list; + + unsigned int cooperations; + unsigned int failed_cooperations; +}; + +enum bfq_device_speed { + BFQ_BFQD_FAST, + BFQ_BFQD_SLOW, +}; + +/** + * struct bfq_data - per device data structure. + * @queue: request queue for the managed device. + * @root_group: root bfq_group for the device. + * @rq_pos_tree: rbtree sorted by next_request position, used when + * determining if two or more queues have interleaving + * requests (see bfq_close_cooperator()). + * @active_numerous_groups: number of bfq_groups containing more than one + * active @bfq_entity. + * @queue_weights_tree: rbtree of weight counters of @bfq_queues, sorted by + * weight. Used to keep track of whether all @bfq_queues + * have the same weight. The tree contains one counter + * for each distinct weight associated to some active + * and not weight-raised @bfq_queue (see the comments to + * the functions bfq_weights_tree_[add|remove] for + * further details). + * @group_weights_tree: rbtree of non-queue @bfq_entity weight counters, sorted + * by weight. Used to keep track of whether all + * @bfq_groups have the same weight. The tree contains + * one counter for each distinct weight associated to + * some active @bfq_group (see the comments to the + * functions bfq_weights_tree_[add|remove] for further + * details). + * @busy_queues: number of bfq_queues containing requests (including the + * queue in service, even if it is idling). + * @busy_in_flight_queues: number of @bfq_queues containing pending or + * in-flight requests, plus the @bfq_queue in + * service, even if idle but waiting for the + * possible arrival of its next sync request. This + * field is updated only if the device is rotational, + * but used only if the device is also NCQ-capable. + * The reason why the field is updated also for non- + * NCQ-capable rotational devices is related to the + * fact that the value of @hw_tag may be set also + * later than when busy_in_flight_queues may need to + * be incremented for the first time(s). Taking also + * this possibility into account, to avoid unbalanced + * increments/decrements, would imply more overhead + * than just updating busy_in_flight_queues + * regardless of the value of @hw_tag. + * @const_seeky_busy_in_flight_queues: number of constantly-seeky @bfq_queues + * (that is, seeky queues that expired + * for budget timeout at least once) + * containing pending or in-flight + * requests, including the in-service + * @bfq_queue if constantly seeky. This + * field is updated only if the device + * is rotational, but used only if the + * device is also NCQ-capable (see the + * comments to @busy_in_flight_queues). + * @wr_busy_queues: number of weight-raised busy @bfq_queues. + * @queued: number of queued requests. + * @rq_in_driver: number of requests dispatched and waiting for completion. + * @sync_flight: number of sync requests in the driver. + * @max_rq_in_driver: max number of reqs in driver in the last + * @hw_tag_samples completed requests. + * @hw_tag_samples: nr of samples used to calculate hw_tag. + * @hw_tag: flag set to one if the driver is showing a queueing behavior. + * @budgets_assigned: number of budgets assigned. + * @idle_slice_timer: timer set when idling for the next sequential request + * from the queue in service. + * @unplug_work: delayed work to restart dispatching on the request queue. + * @in_service_queue: bfq_queue in service. + * @in_service_bic: bfq_io_cq (bic) associated with the @in_service_queue. + * @last_position: on-disk position of the last served request. + * @last_budget_start: beginning of the last budget. + * @last_idling_start: beginning of the last idle slice. + * @peak_rate: peak transfer rate observed for a budget. + * @peak_rate_samples: number of samples used to calculate @peak_rate. + * @bfq_max_budget: maximum budget allotted to a bfq_queue before + * rescheduling. + * @group_list: list of all the bfq_groups active on the device. + * @active_list: list of all the bfq_queues active on the device. + * @idle_list: list of all the bfq_queues idle on the device. + * @bfq_fifo_expire: timeout for async/sync requests; when it expires + * requests are served in fifo order. + * @bfq_back_penalty: weight of backward seeks wrt forward ones. + * @bfq_back_max: maximum allowed backward seek. + * @bfq_slice_idle: maximum idling time. + * @bfq_user_max_budget: user-configured max budget value + * (0 for auto-tuning). + * @bfq_max_budget_async_rq: maximum budget (in nr of requests) allotted to + * async queues. + * @bfq_timeout: timeout for bfq_queues to consume their budget; used to + * to prevent seeky queues to impose long latencies to well + * behaved ones (this also implies that seeky queues cannot + * receive guarantees in the service domain; after a timeout + * they are charged for the whole allocated budget, to try + * to preserve a behavior reasonably fair among them, but + * without service-domain guarantees). + * @bfq_coop_thresh: number of queue merges after which a @bfq_queue is + * no more granted any weight-raising. + * @bfq_failed_cooperations: number of consecutive failed cooperation + * chances after which weight-raising is restored + * to a queue subject to more than bfq_coop_thresh + * queue merges. + * @bfq_requests_within_timer: number of consecutive requests that must be + * issued within the idle time slice to set + * again idling to a queue which was marked as + * non-I/O-bound (see the definition of the + * IO_bound flag for further details). + * @last_ins_in_burst: last time at which a queue entered the current + * burst of queues being activated shortly after + * each other; for more details about this and the + * following parameters related to a burst of + * activations, see the comments to the function + * @bfq_handle_burst. + * @bfq_burst_interval: reference time interval used to decide whether a + * queue has been activated shortly after + * @last_ins_in_burst. + * @burst_size: number of queues in the current burst of queue activations. + * @bfq_large_burst_thresh: maximum burst size above which the current + * queue-activation burst is deemed as 'large'. + * @large_burst: true if a large queue-activation burst is in progress. + * @burst_list: head of the burst list (as for the above fields, more details + * in the comments to the function bfq_handle_burst). + * @low_latency: if set to true, low-latency heuristics are enabled. + * @bfq_wr_coeff: maximum factor by which the weight of a weight-raised + * queue is multiplied. + * @bfq_wr_max_time: maximum duration of a weight-raising period (jiffies). + * @bfq_wr_rt_max_time: maximum duration for soft real-time processes. + * @bfq_wr_min_idle_time: minimum idle period after which weight-raising + * may be reactivated for a queue (in jiffies). + * @bfq_wr_min_inter_arr_async: minimum period between request arrivals + * after which weight-raising may be + * reactivated for an already busy queue + * (in jiffies). + * @bfq_wr_max_softrt_rate: max service-rate for a soft real-time queue, + * sectors per seconds. + * @RT_prod: cached value of the product R*T used for computing the maximum + * duration of the weight raising automatically. + * @device_speed: device-speed class for the low-latency heuristic. + * @oom_bfqq: fallback dummy bfqq for extreme OOM conditions. + * + * All the fields are protected by the @queue lock. + */ +struct bfq_data { + struct request_queue *queue; + + struct bfq_group *root_group; + struct rb_root rq_pos_tree; + +#ifdef CONFIG_CGROUP_BFQIO + int active_numerous_groups; +#endif + + struct rb_root queue_weights_tree; + struct rb_root group_weights_tree; + + int busy_queues; + int busy_in_flight_queues; + int const_seeky_busy_in_flight_queues; + int wr_busy_queues; + int queued; + int rq_in_driver; + int sync_flight; + + int max_rq_in_driver; + int hw_tag_samples; + int hw_tag; + + int budgets_assigned; + + struct timer_list idle_slice_timer; + struct work_struct unplug_work; + + struct bfq_queue *in_service_queue; + struct bfq_io_cq *in_service_bic; + + sector_t last_position; + + ktime_t last_budget_start; + ktime_t last_idling_start; + int peak_rate_samples; + u64 peak_rate; + unsigned long bfq_max_budget; + + struct hlist_head group_list; + struct list_head active_list; + struct list_head idle_list; + + unsigned int bfq_fifo_expire[2]; + unsigned int bfq_back_penalty; + unsigned int bfq_back_max; + unsigned int bfq_slice_idle; + u64 bfq_class_idle_last_service; + + unsigned int bfq_user_max_budget; + unsigned int bfq_max_budget_async_rq; + unsigned int bfq_timeout[2]; + + unsigned int bfq_coop_thresh; + unsigned int bfq_failed_cooperations; + unsigned int bfq_requests_within_timer; + + unsigned long last_ins_in_burst; + unsigned long bfq_burst_interval; + int burst_size; + unsigned long bfq_large_burst_thresh; + bool large_burst; + struct hlist_head burst_list; + + bool low_latency; + + /* parameters of the low_latency heuristics */ + unsigned int bfq_wr_coeff; + unsigned int bfq_wr_max_time; + unsigned int bfq_wr_rt_max_time; + unsigned int bfq_wr_min_idle_time; + unsigned long bfq_wr_min_inter_arr_async; + unsigned int bfq_wr_max_softrt_rate; + u64 RT_prod; + enum bfq_device_speed device_speed; + + struct bfq_queue oom_bfqq; +}; + +enum bfqq_state_flags { + BFQ_BFQQ_FLAG_busy = 0, /* has requests or is in service */ + BFQ_BFQQ_FLAG_wait_request, /* waiting for a request */ + BFQ_BFQQ_FLAG_must_alloc, /* must be allowed rq alloc */ + BFQ_BFQQ_FLAG_fifo_expire, /* FIFO checked in this slice */ + BFQ_BFQQ_FLAG_idle_window, /* slice idling enabled */ + BFQ_BFQQ_FLAG_sync, /* synchronous queue */ + BFQ_BFQQ_FLAG_budget_new, /* no completion with this budget */ + BFQ_BFQQ_FLAG_IO_bound, /* + * bfqq has timed-out at least once + * having consumed at most 2/10 of + * its budget + */ + BFQ_BFQQ_FLAG_in_large_burst, /* + * bfqq activated in a large burst, + * see comments to bfq_handle_burst. + */ + BFQ_BFQQ_FLAG_constantly_seeky, /* + * bfqq has proved to be slow and + * seeky until budget timeout + */ + BFQ_BFQQ_FLAG_softrt_update, /* + * may need softrt-next-start + * update + */ + BFQ_BFQQ_FLAG_coop, /* bfqq is shared */ + BFQ_BFQQ_FLAG_split_coop, /* shared bfqq will be split */ + BFQ_BFQQ_FLAG_just_split, /* queue has just been split */ +}; + +#define BFQ_BFQQ_FNS(name) \ +static inline void bfq_mark_bfqq_##name(struct bfq_queue *bfqq) \ +{ \ + (bfqq)->flags |= (1 << BFQ_BFQQ_FLAG_##name); \ +} \ +static inline void bfq_clear_bfqq_##name(struct bfq_queue *bfqq) \ +{ \ + (bfqq)->flags &= ~(1 << BFQ_BFQQ_FLAG_##name); \ +} \ +static inline int bfq_bfqq_##name(const struct bfq_queue *bfqq) \ +{ \ + return ((bfqq)->flags & (1 << BFQ_BFQQ_FLAG_##name)) != 0; \ +} + +BFQ_BFQQ_FNS(busy); +BFQ_BFQQ_FNS(wait_request); +BFQ_BFQQ_FNS(must_alloc); +BFQ_BFQQ_FNS(fifo_expire); +BFQ_BFQQ_FNS(idle_window); +BFQ_BFQQ_FNS(sync); +BFQ_BFQQ_FNS(budget_new); +BFQ_BFQQ_FNS(IO_bound); +BFQ_BFQQ_FNS(in_large_burst); +BFQ_BFQQ_FNS(constantly_seeky); +BFQ_BFQQ_FNS(coop); +BFQ_BFQQ_FNS(split_coop); +BFQ_BFQQ_FNS(just_split); +BFQ_BFQQ_FNS(softrt_update); +#undef BFQ_BFQQ_FNS + +/* Logging facilities. */ +#define bfq_log_bfqq(bfqd, bfqq, fmt, args...) \ + blk_add_trace_msg((bfqd)->queue, "bfq%d " fmt, (bfqq)->pid, ##args) + +#define bfq_log(bfqd, fmt, args...) \ + blk_add_trace_msg((bfqd)->queue, "bfq " fmt, ##args) + +/* Expiration reasons. */ +enum bfqq_expiration { + BFQ_BFQQ_TOO_IDLE = 0, /* + * queue has been idling for + * too long + */ + BFQ_BFQQ_BUDGET_TIMEOUT, /* budget took too long to be used */ + BFQ_BFQQ_BUDGET_EXHAUSTED, /* budget consumed */ + BFQ_BFQQ_NO_MORE_REQUESTS, /* the queue has no more requests */ +}; + +#ifdef CONFIG_CGROUP_BFQIO +/** + * struct bfq_group - per (device, cgroup) data structure. + * @entity: schedulable entity to insert into the parent group sched_data. + * @sched_data: own sched_data, to contain child entities (they may be + * both bfq_queues and bfq_groups). + * @group_node: node to be inserted into the bfqio_cgroup->group_data + * list of the containing cgroup's bfqio_cgroup. + * @bfqd_node: node to be inserted into the @bfqd->group_list list + * of the groups active on the same device; used for cleanup. + * @bfqd: the bfq_data for the device this group acts upon. + * @async_bfqq: array of async queues for all the tasks belonging to + * the group, one queue per ioprio value per ioprio_class, + * except for the idle class that has only one queue. + * @async_idle_bfqq: async queue for the idle class (ioprio is ignored). + * @my_entity: pointer to @entity, %NULL for the toplevel group; used + * to avoid too many special cases during group creation/ + * migration. + * @active_entities: number of active entities belonging to the group; + * unused for the root group. Used to know whether there + * are groups with more than one active @bfq_entity + * (see the comments to the function + * bfq_bfqq_must_not_expire()). + * + * Each (device, cgroup) pair has its own bfq_group, i.e., for each cgroup + * there is a set of bfq_groups, each one collecting the lower-level + * entities belonging to the group that are acting on the same device. + * + * Locking works as follows: + * o @group_node is protected by the bfqio_cgroup lock, and is accessed + * via RCU from its readers. + * o @bfqd is protected by the queue lock, RCU is used to access it + * from the readers. + * o All the other fields are protected by the @bfqd queue lock. + */ +struct bfq_group { + struct bfq_entity entity; + struct bfq_sched_data sched_data; + + struct hlist_node group_node; + struct hlist_node bfqd_node; + + void *bfqd; + + struct bfq_queue *async_bfqq[2][IOPRIO_BE_NR]; + struct bfq_queue *async_idle_bfqq; + + struct bfq_entity *my_entity; + + int active_entities; +}; + +/** + * struct bfqio_cgroup - bfq cgroup data structure. + * @css: subsystem state for bfq in the containing cgroup. + * @weight: cgroup weight. + * @ioprio: cgroup ioprio. + * @ioprio_class: cgroup ioprio_class. + * @lock: spinlock that protects @ioprio, @ioprio_class and @group_data. + * @group_data: list containing the bfq_group belonging to this cgroup. + * + * @group_data is accessed using RCU, with @lock protecting the updates, + * @ioprio and @ioprio_class are protected by @lock. + */ +struct bfqio_cgroup { + struct cgroup_subsys_state css; + + unsigned short weight, ioprio, ioprio_class; + + spinlock_t lock; + struct hlist_head group_data; +}; +#else +struct bfq_group { + struct bfq_sched_data sched_data; + + struct bfq_queue *async_bfqq[2][IOPRIO_BE_NR]; + struct bfq_queue *async_idle_bfqq; +}; +#endif + +static inline struct bfq_service_tree * +bfq_entity_service_tree(struct bfq_entity *entity) +{ + struct bfq_sched_data *sched_data = entity->sched_data; + unsigned int idx = entity->ioprio_class - 1; + + BUG_ON(idx >= BFQ_IOPRIO_CLASSES); + BUG_ON(sched_data == NULL); + + return sched_data->service_tree + idx; +} + +static inline struct bfq_queue *bic_to_bfqq(struct bfq_io_cq *bic, + bool is_sync) +{ + return bic->bfqq[is_sync]; +} + +static inline void bic_set_bfqq(struct bfq_io_cq *bic, + struct bfq_queue *bfqq, bool is_sync) +{ + bic->bfqq[is_sync] = bfqq; +} + +static inline struct bfq_data *bic_to_bfqd(struct bfq_io_cq *bic) +{ + return bic->icq.q->elevator->elevator_data; +} + +/** + * bfq_get_bfqd_locked - get a lock to a bfqd using a RCU protected pointer. + * @ptr: a pointer to a bfqd. + * @flags: storage for the flags to be saved. + * + * This function allows bfqg->bfqd to be protected by the + * queue lock of the bfqd they reference; the pointer is dereferenced + * under RCU, so the storage for bfqd is assured to be safe as long + * as the RCU read side critical section does not end. After the + * bfqd->queue->queue_lock is taken the pointer is rechecked, to be + * sure that no other writer accessed it. If we raced with a writer, + * the function returns NULL, with the queue unlocked, otherwise it + * returns the dereferenced pointer, with the queue locked. + */ +static inline struct bfq_data *bfq_get_bfqd_locked(void **ptr, + unsigned long *flags) +{ + struct bfq_data *bfqd; + + rcu_read_lock(); + bfqd = rcu_dereference(*(struct bfq_data **)ptr); + + if (bfqd != NULL) { + spin_lock_irqsave(bfqd->queue->queue_lock, *flags); + if (*ptr == bfqd) + goto out; + spin_unlock_irqrestore(bfqd->queue->queue_lock, *flags); + } + + bfqd = NULL; +out: + rcu_read_unlock(); + return bfqd; +} + +static inline void bfq_put_bfqd_unlock(struct bfq_data *bfqd, + unsigned long *flags) +{ + spin_unlock_irqrestore(bfqd->queue->queue_lock, *flags); +} + +static void bfq_check_ioprio_change(struct io_context *ioc, + struct bfq_io_cq *bic); +static void bfq_put_queue(struct bfq_queue *bfqq); +static void bfq_dispatch_insert(struct request_queue *q, struct request *rq); +static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd, + struct bfq_group *bfqg, int is_sync, + struct io_context *ioc, gfp_t gfp_mask); +static void bfq_end_wr_async_queues(struct bfq_data *bfqd, + struct bfq_group *bfqg); +static void bfq_put_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg); +static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq); + +#endif /* _BFQ_H */ diff --git a/block/fiops-iosched.c b/block/fiops-iosched.c index 671b1d33..9db7f936 100644 --- a/block/fiops-iosched.c +++ b/block/fiops-iosched.c @@ -10,19 +10,11 @@ #include #include #include -#include #include "blk.h" #define VIOS_SCALE_SHIFT 10 #define VIOS_SCALE (1 << VIOS_SCALE_SHIFT) -#define VIOS_READ_SCALE (1) -#define VIOS_WRITE_SCALE (1) -#define VIOS_SYNC_SCALE (2) -#define VIOS_ASYNC_SCALE (5) - -#define VIOS_PRIO_SCALE (5) - struct fiops_rb_root { struct rb_root rb; struct rb_node *left; @@ -32,27 +24,15 @@ struct fiops_rb_root { }; #define FIOPS_RB_ROOT (struct fiops_rb_root) { .rb = RB_ROOT} -enum wl_prio_t { - IDLE_WORKLOAD = 0, - BE_WORKLOAD = 1, - RT_WORKLOAD = 2, - FIOPS_PRIO_NR, -}; - struct fiops_data { struct request_queue *queue; - struct fiops_rb_root service_tree[FIOPS_PRIO_NR]; + struct fiops_rb_root service_tree; unsigned int busy_queues; unsigned int in_flight[2]; struct work_struct unplug_work; - - unsigned int read_scale; - unsigned int write_scale; - unsigned int sync_scale; - unsigned int async_scale; }; struct fiops_ioc { @@ -70,16 +50,13 @@ struct fiops_ioc { struct list_head fifo; pid_t pid; - unsigned short ioprio; - enum wl_prio_t wl_type; }; -#define ioc_service_tree(ioc) (&((ioc)->fiopsd->service_tree[(ioc)->wl_type])) +#define ioc_service_tree(ioc) (&((ioc)->fiopsd->service_tree)) #define RQ_CIC(rq) icq_to_cic((rq)->elv.icq) enum ioc_state_flags { FIOPS_IOC_FLAG_on_rr = 0, /* on round-robin busy list */ - FIOPS_IOC_FLAG_prio_changed, /* task priority has changed */ }; #define FIOPS_IOC_FNS(name) \ @@ -97,23 +74,8 @@ static inline int fiops_ioc_##name(const struct fiops_ioc *ioc) \ } FIOPS_IOC_FNS(on_rr); -FIOPS_IOC_FNS(prio_changed); #undef FIOPS_IOC_FNS -#define fiops_log_ioc(fiopsd, ioc, fmt, args...) \ - blk_add_trace_msg((fiopsd)->queue, "ioc%d " fmt, (ioc)->pid, ##args) -#define fiops_log(fiopsd, fmt, args...) \ - blk_add_trace_msg((fiopsd)->queue, "fiops " fmt, ##args) - -enum wl_prio_t fiops_wl_type(short prio_class) -{ - if (prio_class == IOPRIO_CLASS_RT) - return RT_WORKLOAD; - if (prio_class == IOPRIO_CLASS_BE) - return BE_WORKLOAD; - return IDLE_WORKLOAD; -} - static inline struct fiops_ioc *icq_to_cic(struct io_cq *icq) { /* cic->icq is the first member, %NULL will convert to %NULL */ @@ -194,20 +156,15 @@ static void fiops_service_tree_add(struct fiops_data *fiopsd, int left; /* New added IOC */ - if (RB_EMPTY_NODE(&ioc->rb_node)) { - if (ioc->in_flight > 0) - vios = ioc->vios; - else - vios = max_vios(service_tree->min_vios, ioc->vios); - } else { + if (RB_EMPTY_NODE(&ioc->rb_node)) + vios = max_vios(service_tree->min_vios, ioc->vios); + else { vios = ioc->vios; /* ioc->service_tree might not equal to service_tree */ fiops_rb_erase(&ioc->rb_node, ioc->service_tree); ioc->service_tree = NULL; } - fiops_log_ioc(fiopsd, ioc, "service tree add, vios %lld", vios); - left = 1; parent = NULL; ioc->service_tree = service_tree; @@ -323,17 +280,7 @@ static void fiops_remove_request(struct request *rq) static u64 fiops_scaled_vios(struct fiops_data *fiopsd, struct fiops_ioc *ioc, struct request *rq) { - int vios = VIOS_SCALE; - - if (rq_data_dir(rq) == WRITE) - vios = vios * fiopsd->write_scale / fiopsd->read_scale; - - if (!rq_is_sync(rq)) - vios = vios * fiopsd->async_scale / fiopsd->sync_scale; - - vios += vios * (ioc->ioprio - IOPRIO_NORM) / VIOS_PRIO_SCALE; - - return vios; + return VIOS_SCALE; } /* return vios dispatched */ @@ -358,19 +305,14 @@ static int fiops_forced_dispatch(struct fiops_data *fiopsd) { struct fiops_ioc *ioc; int dispatched = 0; - int i; - - for (i = RT_WORKLOAD; i >= IDLE_WORKLOAD; i--) { - while (!RB_EMPTY_ROOT(&fiopsd->service_tree[i].rb)) { - ioc = fiops_rb_first(&fiopsd->service_tree[i]); - - while (!list_empty(&ioc->fifo)) { - fiops_dispatch_request(fiopsd, ioc); - dispatched++; - } - if (fiops_ioc_on_rr(ioc)) - fiops_del_ioc_rr(fiopsd, ioc); + + while ((ioc = fiops_rb_first(&fiopsd->service_tree)) != NULL) { + while (!list_empty(&ioc->fifo)) { + fiops_dispatch_request(fiopsd, ioc); + dispatched++; } + if (fiops_ioc_on_rr(ioc)) + fiops_del_ioc_rr(fiopsd, ioc); } return dispatched; } @@ -378,36 +320,10 @@ static int fiops_forced_dispatch(struct fiops_data *fiopsd) static struct fiops_ioc *fiops_select_ioc(struct fiops_data *fiopsd) { struct fiops_ioc *ioc; - struct fiops_rb_root *service_tree = NULL; - int i; - struct request *rq; - - for (i = RT_WORKLOAD; i >= IDLE_WORKLOAD; i--) { - if (!RB_EMPTY_ROOT(&fiopsd->service_tree[i].rb)) { - service_tree = &fiopsd->service_tree[i]; - break; - } - } - - if (!service_tree) - return NULL; - - ioc = fiops_rb_first(service_tree); - rq = rq_entry_fifo(ioc->fifo.next); - /* - * we are the only async task and sync requests are in flight, delay a - * moment. If there are other tasks coming, sync tasks have no chance - * to be starved, don't delay - */ - if (!rq_is_sync(rq) && fiopsd->in_flight[1] != 0 && - service_tree->count == 1) { - fiops_log_ioc(fiopsd, ioc, - "postpone async, in_flight async %d sync %d", - fiopsd->in_flight[0], fiopsd->in_flight[1]); + if (RB_EMPTY_ROOT(&fiopsd->service_tree.rb)) return NULL; - } - + ioc = fiops_rb_first(&fiopsd->service_tree); return ioc; } @@ -417,8 +333,6 @@ static void fiops_charge_vios(struct fiops_data *fiopsd, struct fiops_rb_root *service_tree = ioc->service_tree; ioc->vios += vios; - fiops_log_ioc(fiopsd, ioc, "charge vios %lld, new vios %lld", vios, ioc->vios); - if (RB_EMPTY_ROOT(&ioc->sort_list)) fiops_del_ioc_rr(fiopsd, ioc); else @@ -446,49 +360,10 @@ static int fiops_dispatch_requests(struct request_queue *q, int force) return 1; } -static void fiops_init_prio_data(struct fiops_ioc *cic) -{ - struct task_struct *tsk = current; - struct io_context *ioc = cic->icq.ioc; - int ioprio_class; - - if (!fiops_ioc_prio_changed(cic)) - return; - - ioprio_class = IOPRIO_PRIO_CLASS(ioc->ioprio); - switch (ioprio_class) { - default: - printk(KERN_ERR "fiops: bad prio %x\n", ioprio_class); - case IOPRIO_CLASS_NONE: - /* - * no prio set, inherit CPU scheduling settings - */ - cic->ioprio = task_nice_ioprio(tsk); - cic->wl_type = fiops_wl_type(task_nice_ioclass(tsk)); - break; - case IOPRIO_CLASS_RT: - cic->ioprio = task_ioprio(ioc); - cic->wl_type = fiops_wl_type(IOPRIO_CLASS_RT); - break; - case IOPRIO_CLASS_BE: - cic->ioprio = task_ioprio(ioc); - cic->wl_type = fiops_wl_type(IOPRIO_CLASS_BE); - break; - case IOPRIO_CLASS_IDLE: - cic->wl_type = fiops_wl_type(IOPRIO_CLASS_IDLE); - cic->ioprio = 7; - break; - } - - fiops_clear_ioc_prio_changed(cic); -} - static void fiops_insert_request(struct request_queue *q, struct request *rq) { struct fiops_ioc *ioc = RQ_CIC(rq); - fiops_init_prio_data(ioc); - list_add_tail(&rq->queuelist, &ioc->fifo); fiops_add_rq_rb(rq); @@ -512,9 +387,6 @@ static void fiops_completed_request(struct request_queue *q, struct request *rq) fiopsd->in_flight[rq_is_sync(rq)]--; ioc->in_flight--; - fiops_log_ioc(fiopsd, ioc, "in_flight %d, busy queues %d", - ioc->in_flight, fiopsd->busy_queues); - if (fiopsd->in_flight[0] + fiopsd->in_flight[1] == 0) fiops_schedule_dispatch(fiopsd); } @@ -617,7 +489,6 @@ static void fiops_kick_queue(struct work_struct *work) static void *fiops_init_queue(struct request_queue *q) { struct fiops_data *fiopsd; - int i; fiopsd = kzalloc_node(sizeof(*fiopsd), GFP_KERNEL, q->node); if (!fiopsd) @@ -625,16 +496,10 @@ static void *fiops_init_queue(struct request_queue *q) fiopsd->queue = q; - for (i = IDLE_WORKLOAD; i <= RT_WORKLOAD; i++) - fiopsd->service_tree[i] = FIOPS_RB_ROOT; + fiopsd->service_tree = FIOPS_RB_ROOT; INIT_WORK(&fiopsd->unplug_work, fiops_kick_queue); - fiopsd->read_scale = VIOS_READ_SCALE; - fiopsd->write_scale = VIOS_WRITE_SCALE; - fiopsd->sync_scale = VIOS_SYNC_SCALE; - fiopsd->async_scale = VIOS_ASYNC_SCALE; - return fiopsd; } @@ -650,69 +515,8 @@ static void fiops_init_icq(struct io_cq *icq) ioc->fiopsd = fiopsd; ioc->pid = current->pid; - fiops_mark_ioc_prio_changed(ioc); } -/* - * sysfs parts below --> - */ -static ssize_t -fiops_var_show(unsigned int var, char *page) -{ - return sprintf(page, "%d\n", var); -} - -static ssize_t -fiops_var_store(unsigned int *var, const char *page, size_t count) -{ - char *p = (char *) page; - - *var = simple_strtoul(p, &p, 10); - return count; -} - -#define SHOW_FUNCTION(__FUNC, __VAR) \ -static ssize_t __FUNC(struct elevator_queue *e, char *page) \ -{ \ - struct fiops_data *fiopsd = e->elevator_data; \ - return fiops_var_show(__VAR, (page)); \ -} -SHOW_FUNCTION(fiops_read_scale_show, fiopsd->read_scale); -SHOW_FUNCTION(fiops_write_scale_show, fiopsd->write_scale); -SHOW_FUNCTION(fiops_sync_scale_show, fiopsd->sync_scale); -SHOW_FUNCTION(fiops_async_scale_show, fiopsd->async_scale); -#undef SHOW_FUNCTION - -#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX) \ -static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \ -{ \ - struct fiops_data *fiopsd = e->elevator_data; \ - unsigned int __data; \ - int ret = fiops_var_store(&__data, (page), count); \ - if (__data < (MIN)) \ - __data = (MIN); \ - else if (__data > (MAX)) \ - __data = (MAX); \ - *(__PTR) = __data; \ - return ret; \ -} -STORE_FUNCTION(fiops_read_scale_store, &fiopsd->read_scale, 1, 100); -STORE_FUNCTION(fiops_write_scale_store, &fiopsd->write_scale, 1, 100); -STORE_FUNCTION(fiops_sync_scale_store, &fiopsd->sync_scale, 1, 100); -STORE_FUNCTION(fiops_async_scale_store, &fiopsd->async_scale, 1, 100); -#undef STORE_FUNCTION - -#define FIOPS_ATTR(name) \ - __ATTR(name, S_IRUGO|S_IWUSR, fiops_##name##_show, fiops_##name##_store) - -static struct elv_fs_entry fiops_attrs[] = { - FIOPS_ATTR(read_scale), - FIOPS_ATTR(write_scale), - FIOPS_ATTR(sync_scale), - FIOPS_ATTR(async_scale), - __ATTR_NULL -}; - static struct elevator_type iosched_fiops = { .ops = { .elevator_merge_fn = fiops_merge, @@ -730,7 +534,6 @@ static struct elevator_type iosched_fiops = { }, .icq_size = sizeof(struct fiops_ioc), .icq_align = __alignof__(struct fiops_ioc), - .elevator_attrs = fiops_attrs, .elevator_name = "fiops", .elevator_owner = THIS_MODULE, }; diff --git a/block/row-iosched.c b/block/row-iosched.c new file mode 100644 index 00000000..b8c16e70 --- /dev/null +++ b/block/row-iosched.c @@ -0,0 +1,790 @@ +/* + * ROW (Read Over Write) I/O scheduler. + * + * Copyright (c) 2012-2013, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* See Documentation/block/row-iosched.txt */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * enum row_queue_prio - Priorities of the ROW queues + * + * This enum defines the priorities (and the number of queues) + * the requests will be disptributed to. The higher priority - + * the bigger is the dispatch quantum given to that queue. + * ROWQ_PRIO_HIGH_READ - is the higher priority queue. + * + */ +enum row_queue_prio { + ROWQ_PRIO_HIGH_READ = 0, + ROWQ_PRIO_REG_READ, + ROWQ_PRIO_HIGH_SWRITE, + ROWQ_PRIO_REG_SWRITE, + ROWQ_PRIO_REG_WRITE, + ROWQ_PRIO_LOW_READ, + ROWQ_PRIO_LOW_SWRITE, + ROWQ_MAX_PRIO, +}; + +/** + * struct row_queue_params - ROW queue parameters + * @idling_enabled: Flag indicating whether idling is enable on + * the queue + * @quantum: Number of requests to be dispatched from this queue + * in a dispatch cycle + * @is_urgent: Flags indicating whether the queue can notify on + * urgent requests + * + */ +struct row_queue_params { + bool idling_enabled; + int quantum; + bool is_urgent; +}; + +/* + * This array holds the default values of the different configurables + * for each ROW queue. Each row of the array holds the following values: + * {idling_enabled, quantum, is_urgent} + * Each row corresponds to a queue with the same index (according to + * enum row_queue_prio) + */ +static const struct row_queue_params row_queues_def[] = { +/* idling_enabled, quantum, is_urgent */ + {true, 100, true}, /* ROWQ_PRIO_HIGH_READ */ + {true, 100, true}, /* ROWQ_PRIO_REG_READ */ + {false, 2, false}, /* ROWQ_PRIO_HIGH_SWRITE */ + {false, 1, false}, /* ROWQ_PRIO_REG_SWRITE */ + {false, 1, false}, /* ROWQ_PRIO_REG_WRITE */ + {false, 1, false}, /* ROWQ_PRIO_LOW_READ */ + {false, 1, false} /* ROWQ_PRIO_LOW_SWRITE */ +}; + +/* Default values for idling on read queues (in msec) */ +#define ROW_IDLE_TIME_MSEC 5 +#define ROW_READ_FREQ_MSEC 20 + +/** + * struct rowq_idling_data - parameters for idling on the queue + * @last_insert_time: time the last request was inserted + * to the queue + * @begin_idling: flag indicating wether we should idle + * + */ +struct rowq_idling_data { + ktime_t last_insert_time; + bool begin_idling; +}; + +/** + * struct row_queue - requests grouping structure + * @rdata: parent row_data structure + * @fifo: fifo of requests + * @prio: queue priority (enum row_queue_prio) + * @nr_dispatched: number of requests already dispatched in + * the current dispatch cycle + * @slice: number of requests to dispatch in a cycle + * @nr_req: number of requests in queue + * @dispatch quantum: number of requests this queue may + * dispatch in a dispatch cycle + * @idle_data: data for idling on queues + * + */ +struct row_queue { + struct row_data *rdata; + struct list_head fifo; + enum row_queue_prio prio; + + unsigned int nr_dispatched; + unsigned int slice; + + unsigned int nr_req; + int disp_quantum; + + /* used only for READ queues */ + struct rowq_idling_data idle_data; +}; + +/** + * struct idling_data - data for idling on empty rqueue + * @idle_time: idling duration (jiffies) + * @freq: min time between two requests that + * triger idling (msec) + * @idle_work: pointer to struct delayed_work + * + */ +struct idling_data { + unsigned long idle_time; + u32 freq; + + struct workqueue_struct *idle_workqueue; + struct delayed_work idle_work; +}; + +/** + * struct row_queue - Per block device rqueue structure + * @dispatch_queue: dispatch rqueue + * @row_queues: array of priority request queues + * @curr_queue: index in the row_queues array of the + * currently serviced rqueue + * @read_idle: data for idling after READ request + * @nr_reqs: nr_reqs[0] holds the number of all READ requests in + * scheduler, nr_reqs[1] holds the number of all WRITE + * requests in scheduler + * @cycle_flags: used for marking unserved queueus + * + */ +struct row_data { + struct request_queue *dispatch_queue; + + struct row_queue row_queues[ROWQ_MAX_PRIO]; + + enum row_queue_prio curr_queue; + + struct idling_data read_idle; + unsigned int nr_reqs[2]; + + unsigned int cycle_flags; +}; + +#define RQ_ROWQ(rq) ((struct row_queue *) ((rq)->elv.priv[0])) + +#define row_log(q, fmt, args...) \ + blk_add_trace_msg(q, "%s():" fmt , __func__, ##args) +#define row_log_rowq(rdata, rowq_id, fmt, args...) \ + blk_add_trace_msg(rdata->dispatch_queue, "rowq%d " fmt, \ + rowq_id, ##args) + +static inline void row_mark_rowq_unserved(struct row_data *rd, + enum row_queue_prio qnum) +{ + rd->cycle_flags |= (1 << qnum); +} + +static inline void row_clear_rowq_unserved(struct row_data *rd, + enum row_queue_prio qnum) +{ + rd->cycle_flags &= ~(1 << qnum); +} + +static inline int row_rowq_unserved(struct row_data *rd, + enum row_queue_prio qnum) +{ + return rd->cycle_flags & (1 << qnum); +} + +static inline void __maybe_unused row_dump_queues_stat(struct row_data *rd) +{ + int i; + + row_log(rd->dispatch_queue, " Queues status:"); + for (i = 0; i < ROWQ_MAX_PRIO; i++) + row_log(rd->dispatch_queue, + "queue%d: dispatched= %d, nr_req=%d", i, + rd->row_queues[i].nr_dispatched, + rd->row_queues[i].nr_req); +} + +/******************** Static helper functions ***********************/ +/* + * kick_queue() - Wake up device driver queue thread + * @work: pointer to struct work_struct + * + * This is a idling delayed work function. It's purpose is to wake up the + * device driver in order for it to start fetching requests. + * + */ +static void kick_queue(struct work_struct *work) +{ + struct delayed_work *idle_work = to_delayed_work(work); + struct idling_data *read_data = + container_of(idle_work, struct idling_data, idle_work); + struct row_data *rd = + container_of(read_data, struct row_data, read_idle); + + row_log_rowq(rd, rd->curr_queue, "Performing delayed work"); + /* Mark idling process as done */ + rd->row_queues[rd->curr_queue].idle_data.begin_idling = false; + + if (!(rd->nr_reqs[0] + rd->nr_reqs[1])) + row_log(rd->dispatch_queue, "No requests in scheduler"); + else { + spin_lock_irq(rd->dispatch_queue->queue_lock); + __blk_run_queue(rd->dispatch_queue); + spin_unlock_irq(rd->dispatch_queue->queue_lock); + } +} + +/* + * row_restart_disp_cycle() - Restart the dispatch cycle + * @rd: pointer to struct row_data + * + * This function restarts the dispatch cycle by: + * - Setting current queue to ROWQ_PRIO_HIGH_READ + * - For each queue: reset the number of requests dispatched in + * the cycle + */ +static inline void row_restart_disp_cycle(struct row_data *rd) +{ + int i; + + for (i = 0; i < ROWQ_MAX_PRIO; i++) + rd->row_queues[i].nr_dispatched = 0; + + rd->curr_queue = ROWQ_PRIO_HIGH_READ; + row_log(rd->dispatch_queue, "Restarting cycle"); +} + +static inline void row_get_next_queue(struct row_data *rd) +{ + rd->curr_queue++; + if (rd->curr_queue == ROWQ_MAX_PRIO) + row_restart_disp_cycle(rd); +} + +/******************* Elevator callback functions *********************/ + +/* + * row_add_request() - Add request to the scheduler + * @q: requests queue + * @rq: request to add + * + */ +static void row_add_request(struct request_queue *q, + struct request *rq) +{ + struct row_data *rd = (struct row_data *)q->elevator->elevator_data; + struct row_queue *rqueue = RQ_ROWQ(rq); + + list_add_tail(&rq->queuelist, &rqueue->fifo); + rd->nr_reqs[rq_data_dir(rq)]++; + rqueue->nr_req++; + rq_set_fifo_time(rq, jiffies); /* for statistics*/ + + if (row_queues_def[rqueue->prio].idling_enabled) { + if (delayed_work_pending(&rd->read_idle.idle_work)) + (void)cancel_delayed_work( + &rd->read_idle.idle_work); + if (ktime_to_ms(ktime_sub(ktime_get(), + rqueue->idle_data.last_insert_time)) < + rd->read_idle.freq) { + rqueue->idle_data.begin_idling = true; + row_log_rowq(rd, rqueue->prio, "Enable idling"); + } else { + rqueue->idle_data.begin_idling = false; + row_log_rowq(rd, rqueue->prio, "Disable idling"); + } + + rqueue->idle_data.last_insert_time = ktime_get(); + } + if (row_queues_def[rqueue->prio].is_urgent && + row_rowq_unserved(rd, rqueue->prio)) { + row_log_rowq(rd, rqueue->prio, + "added urgent request (total on queue=%d)", + rqueue->nr_req); + } else + row_log_rowq(rd, rqueue->prio, + "added request (total on queue=%d)", rqueue->nr_req); +} + +/** + * row_reinsert_req() - Reinsert request back to the scheduler + * @q: requests queue + * @rq: request to add + * + * Reinsert the given request back to the queue it was + * dispatched from as if it was never dispatched. + * + * Returns 0 on success, error code otherwise + */ +static int row_reinsert_req(struct request_queue *q, + struct request *rq) +{ + struct row_data *rd = q->elevator->elevator_data; + struct row_queue *rqueue = RQ_ROWQ(rq); + + /* Verify rqueue is legitimate */ + if (rqueue->prio >= ROWQ_MAX_PRIO) { + pr_err("\n\nROW BUG: row_reinsert_req() rqueue->prio = %d\n", + rqueue->prio); + blk_dump_rq_flags(rq, ""); + return -EIO; + } + + list_add(&rq->queuelist, &rqueue->fifo); + rd->nr_reqs[rq_data_dir(rq)]++; + rqueue->nr_req++; + + row_log_rowq(rd, rqueue->prio, + "request reinserted (total on queue=%d)", rqueue->nr_req); + + return 0; +} + +/** + * row_urgent_pending() - Return TRUE if there is an urgent + * request on scheduler + * @q: requests queue + */ +static bool row_urgent_pending(struct request_queue *q) +{ + struct row_data *rd = q->elevator->elevator_data; + int i; + + for (i = 0; i < ROWQ_MAX_PRIO; i++) + if (row_queues_def[i].is_urgent && row_rowq_unserved(rd, i) && + !list_empty(&rd->row_queues[i].fifo)) { + row_log_rowq(rd, i, + "Urgent request pending (curr=%i)", + rd->curr_queue); + return true; + } + + return false; +} + +/** + * row_remove_request() - Remove given request from scheduler + * @q: requests queue + * @rq: request to remove + * + */ +static void row_remove_request(struct request_queue *q, + struct request *rq) +{ + struct row_data *rd = (struct row_data *)q->elevator->elevator_data; + struct row_queue *rqueue = RQ_ROWQ(rq); + + rq_fifo_clear(rq); + rqueue->nr_req--; + rd->nr_reqs[rq_data_dir(rq)]--; +} + +/* + * row_dispatch_insert() - move request to dispatch queue + * @rd: pointer to struct row_data + * + * This function moves the next request to dispatch from + * rd->curr_queue to the dispatch queue + * + */ +static void row_dispatch_insert(struct row_data *rd) +{ + struct request *rq; + + rq = rq_entry_fifo(rd->row_queues[rd->curr_queue].fifo.next); + row_remove_request(rd->dispatch_queue, rq); + elv_dispatch_add_tail(rd->dispatch_queue, rq); + rd->row_queues[rd->curr_queue].nr_dispatched++; + row_clear_rowq_unserved(rd, rd->curr_queue); + row_log_rowq(rd, rd->curr_queue, " Dispatched request nr_disp = %d", + rd->row_queues[rd->curr_queue].nr_dispatched); +} + +/* + * row_choose_queue() - choose the next queue to dispatch from + * @rd: pointer to struct row_data + * + * Updates rd->curr_queue. Returns 1 if there are requests to + * dispatch, 0 if there are no requests in scheduler + * + */ +static int row_choose_queue(struct row_data *rd) +{ + int prev_curr_queue = rd->curr_queue; + + if (!(rd->nr_reqs[0] + rd->nr_reqs[1])) { + row_log(rd->dispatch_queue, "No more requests in scheduler"); + return 0; + } + + row_get_next_queue(rd); + + /* + * Loop over all queues to find the next queue that is not empty. + * Stop when you get back to curr_queue + */ + while (list_empty(&rd->row_queues[rd->curr_queue].fifo) + && rd->curr_queue != prev_curr_queue) { + /* Mark rqueue as unserved */ + row_mark_rowq_unserved(rd, rd->curr_queue); + row_get_next_queue(rd); + } + + return 1; +} + +/* + * row_dispatch_requests() - selects the next request to dispatch + * @q: requests queue + * @force: ignored + * + * Return 0 if no requests were moved to the dispatch queue. + * 1 otherwise + * + */ +static int row_dispatch_requests(struct request_queue *q, int force) +{ + struct row_data *rd = (struct row_data *)q->elevator->elevator_data; + int ret = 0, currq, i; + + currq = rd->curr_queue; + + /* + * Find the first unserved queue (with higher priority then currq) + * that is not empty + */ + for (i = 0; i < currq; i++) { + if (row_rowq_unserved(rd, i) && + !list_empty(&rd->row_queues[i].fifo)) { + row_log_rowq(rd, currq, + " Preemting for unserved rowq%d. (nr_req=%u)", + i, rd->row_queues[currq].nr_req); + rd->curr_queue = i; + row_dispatch_insert(rd); + ret = 1; + goto done; + } + } + + if (rd->row_queues[currq].nr_dispatched >= + rd->row_queues[currq].disp_quantum) { + rd->row_queues[currq].nr_dispatched = 0; + row_log_rowq(rd, currq, "Expiring rqueue"); + ret = row_choose_queue(rd); + if (ret) + row_dispatch_insert(rd); + goto done; + } + + /* Dispatch from curr_queue */ + if (list_empty(&rd->row_queues[currq].fifo)) { + /* check idling */ + if (delayed_work_pending(&rd->read_idle.idle_work)) { + if (force) { + (void)cancel_delayed_work( + &rd->read_idle.idle_work); + row_log_rowq(rd, currq, + "Canceled delayed work - forced dispatch"); + } else { + row_log_rowq(rd, currq, + "Delayed work pending. Exiting"); + goto done; + } + } + + if (!force && row_queues_def[currq].idling_enabled && + rd->row_queues[currq].idle_data.begin_idling) { + if (!queue_delayed_work(rd->read_idle.idle_workqueue, + &rd->read_idle.idle_work, + rd->read_idle.idle_time)) { + row_log_rowq(rd, currq, + "Work already on queue!"); + pr_err("ROW_BUG: Work already on queue!"); + } else + row_log_rowq(rd, currq, + "Scheduled delayed work. exiting"); + goto done; + } else { + row_log_rowq(rd, currq, + "Currq empty. Choose next queue"); + ret = row_choose_queue(rd); + if (!ret) + goto done; + } + } + + ret = 1; + row_dispatch_insert(rd); + +done: + return ret; +} + +/* + * row_init_queue() - Init scheduler data structures + * @q: requests queue + * + * Return pointer to struct row_data to be saved in elevator for + * this dispatch queue + * + */ +static void *row_init_queue(struct request_queue *q) +{ + + struct row_data *rdata; + int i; + + rdata = kmalloc_node(sizeof(*rdata), + GFP_KERNEL | __GFP_ZERO, q->node); + if (!rdata) + return NULL; + + for (i = 0; i < ROWQ_MAX_PRIO; i++) { + INIT_LIST_HEAD(&rdata->row_queues[i].fifo); + rdata->row_queues[i].disp_quantum = row_queues_def[i].quantum; + rdata->row_queues[i].rdata = rdata; + rdata->row_queues[i].prio = i; + rdata->row_queues[i].idle_data.begin_idling = false; + rdata->row_queues[i].idle_data.last_insert_time = + ktime_set(0, 0); + } + + /* + * Currently idling is enabled only for READ queues. If we want to + * enable it for write queues also, note that idling frequency will + * be the same in both cases + */ + rdata->read_idle.idle_time = msecs_to_jiffies(ROW_IDLE_TIME_MSEC); + /* Maybe 0 on some platforms */ + if (!rdata->read_idle.idle_time) + rdata->read_idle.idle_time = 1; + rdata->read_idle.freq = ROW_READ_FREQ_MSEC; + rdata->read_idle.idle_workqueue = alloc_workqueue("row_idle_work", + WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); + if (!rdata->read_idle.idle_workqueue) + panic("Failed to create idle workqueue\n"); + INIT_DELAYED_WORK(&rdata->read_idle.idle_work, kick_queue); + + rdata->curr_queue = ROWQ_PRIO_HIGH_READ; + rdata->dispatch_queue = q; + + rdata->nr_reqs[READ] = rdata->nr_reqs[WRITE] = 0; + + return rdata; +} + +/* + * row_exit_queue() - called on unloading the RAW scheduler + * @e: poiner to struct elevator_queue + * + */ +static void row_exit_queue(struct elevator_queue *e) +{ + struct row_data *rd = (struct row_data *)e->elevator_data; + int i; + + for (i = 0; i < ROWQ_MAX_PRIO; i++) + BUG_ON(!list_empty(&rd->row_queues[i].fifo)); + (void)cancel_delayed_work_sync(&rd->read_idle.idle_work); + BUG_ON(delayed_work_pending(&rd->read_idle.idle_work)); + destroy_workqueue(rd->read_idle.idle_workqueue); + kfree(rd); +} + +/* + * row_merged_requests() - Called when 2 requests are merged + * @q: requests queue + * @rq: request the two requests were merged into + * @next: request that was merged + */ +static void row_merged_requests(struct request_queue *q, struct request *rq, + struct request *next) +{ + struct row_queue *rqueue = RQ_ROWQ(next); + + list_del_init(&next->queuelist); + rqueue->nr_req--; + + rqueue->rdata->nr_reqs[rq_data_dir(rq)]--; +} + +/* + * get_queue_type() - Get queue type for a given request + * + * This is a helping function which purpose is to determine what + * ROW queue the given request should be added to (and + * dispatched from leter on) + * + * TODO: Right now only 3 queues are used REG_READ, REG_WRITE + * and REG_SWRITE + */ +static enum row_queue_prio get_queue_type(struct request *rq) +{ + const int data_dir = rq_data_dir(rq); + const bool is_sync = rq_is_sync(rq); + + if (data_dir == READ) + return ROWQ_PRIO_REG_READ; + else if (is_sync) + return ROWQ_PRIO_REG_SWRITE; + else + return ROWQ_PRIO_REG_WRITE; +} + +/* + * row_set_request() - Set ROW data structures associated with this request. + * @q: requests queue + * @rq: pointer to the request + * @gfp_mask: ignored + * + */ +static int +row_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask) +{ + struct row_data *rd = (struct row_data *)q->elevator->elevator_data; + unsigned long flags; + + spin_lock_irqsave(q->queue_lock, flags); + rq->elv.priv[0] = + (void *)(&rd->row_queues[get_queue_type(rq)]); + spin_unlock_irqrestore(q->queue_lock, flags); + + return 0; +} + +/********** Helping sysfs functions/defenitions for ROW attributes ******/ +static ssize_t row_var_show(int var, char *page) +{ + return snprintf(page, 100, "%d\n", var); +} + +static ssize_t row_var_store(int *var, const char *page, size_t count) +{ + int err; + err = kstrtoul(page, 10, (unsigned long *)var); + + return count; +} + +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, char *page) \ +{ \ + struct row_data *rowd = e->elevator_data; \ + int __data = __VAR; \ + if (__CONV) \ + __data = jiffies_to_msecs(__data); \ + return row_var_show(__data, (page)); \ +} +SHOW_FUNCTION(row_hp_read_quantum_show, + rowd->row_queues[ROWQ_PRIO_HIGH_READ].disp_quantum, 0); +SHOW_FUNCTION(row_rp_read_quantum_show, + rowd->row_queues[ROWQ_PRIO_REG_READ].disp_quantum, 0); +SHOW_FUNCTION(row_hp_swrite_quantum_show, + rowd->row_queues[ROWQ_PRIO_HIGH_SWRITE].disp_quantum, 0); +SHOW_FUNCTION(row_rp_swrite_quantum_show, + rowd->row_queues[ROWQ_PRIO_REG_SWRITE].disp_quantum, 0); +SHOW_FUNCTION(row_rp_write_quantum_show, + rowd->row_queues[ROWQ_PRIO_REG_WRITE].disp_quantum, 0); +SHOW_FUNCTION(row_lp_read_quantum_show, + rowd->row_queues[ROWQ_PRIO_LOW_READ].disp_quantum, 0); +SHOW_FUNCTION(row_lp_swrite_quantum_show, + rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum, 0); +SHOW_FUNCTION(row_read_idle_show, rowd->read_idle.idle_time, 0); +SHOW_FUNCTION(row_read_idle_freq_show, rowd->read_idle.freq, 0); +#undef SHOW_FUNCTION + +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, \ + const char *page, size_t count) \ +{ \ + struct row_data *rowd = e->elevator_data; \ + int __data; \ + int ret = row_var_store(&__data, (page), count); \ + if (__CONV) \ + __data = (int)msecs_to_jiffies(__data); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + *(__PTR) = __data; \ + return ret; \ +} +STORE_FUNCTION(row_hp_read_quantum_store, +&rowd->row_queues[ROWQ_PRIO_HIGH_READ].disp_quantum, 1, INT_MAX, 0); +STORE_FUNCTION(row_rp_read_quantum_store, + &rowd->row_queues[ROWQ_PRIO_REG_READ].disp_quantum, + 1, INT_MAX, 0); +STORE_FUNCTION(row_hp_swrite_quantum_store, + &rowd->row_queues[ROWQ_PRIO_HIGH_SWRITE].disp_quantum, + 1, INT_MAX, 0); +STORE_FUNCTION(row_rp_swrite_quantum_store, + &rowd->row_queues[ROWQ_PRIO_REG_SWRITE].disp_quantum, + 1, INT_MAX, 0); +STORE_FUNCTION(row_rp_write_quantum_store, + &rowd->row_queues[ROWQ_PRIO_REG_WRITE].disp_quantum, + 1, INT_MAX, 0); +STORE_FUNCTION(row_lp_read_quantum_store, + &rowd->row_queues[ROWQ_PRIO_LOW_READ].disp_quantum, + 1, INT_MAX, 0); +STORE_FUNCTION(row_lp_swrite_quantum_store, + &rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum, + 1, INT_MAX, 1); +STORE_FUNCTION(row_read_idle_store, &rowd->read_idle.idle_time, 1, INT_MAX, 0); +STORE_FUNCTION(row_read_idle_freq_store, &rowd->read_idle.freq, 1, INT_MAX, 0); + +#undef STORE_FUNCTION + +#define ROW_ATTR(name) \ + __ATTR(name, S_IRUGO|S_IWUSR, row_##name##_show, \ + row_##name##_store) + +static struct elv_fs_entry row_attrs[] = { + ROW_ATTR(hp_read_quantum), + ROW_ATTR(rp_read_quantum), + ROW_ATTR(hp_swrite_quantum), + ROW_ATTR(rp_swrite_quantum), + ROW_ATTR(rp_write_quantum), + ROW_ATTR(lp_read_quantum), + ROW_ATTR(lp_swrite_quantum), + ROW_ATTR(read_idle), + ROW_ATTR(read_idle_freq), + __ATTR_NULL +}; + +static struct elevator_type iosched_row = { + .ops = { + .elevator_merge_req_fn = row_merged_requests, + .elevator_dispatch_fn = row_dispatch_requests, + .elevator_add_req_fn = row_add_request, + .elevator_reinsert_req_fn = row_reinsert_req, + .elevator_is_urgent_fn = row_urgent_pending, + .elevator_former_req_fn = elv_rb_former_request, + .elevator_latter_req_fn = elv_rb_latter_request, + .elevator_set_req_fn = row_set_request, + .elevator_init_fn = row_init_queue, + .elevator_exit_fn = row_exit_queue, + }, + + .elevator_attrs = row_attrs, + .elevator_name = "row", + .elevator_owner = THIS_MODULE, +}; + +static int __init row_init(void) +{ + elv_register(&iosched_row); + return 0; +} + +static void __exit row_exit(void) +{ + elv_unregister(&iosched_row); +} + +module_init(row_init); +module_exit(row_exit); + +MODULE_LICENSE("GPLv2"); +MODULE_DESCRIPTION("Read Over Write IO scheduler"); diff --git a/block/sio-iosched.c b/block/sio-iosched.c new file mode 100755 index 00000000..8861872b --- /dev/null +++ b/block/sio-iosched.c @@ -0,0 +1,399 @@ +/* + * Simple IO scheduler + * Based on Noop, Deadline and V(R) IO schedulers. + * + * Copyright (C) 2012 Miguel Boton + * + * + * This algorithm does not do any kind of sorting, as it is aimed for + * aleatory access devices, but it does some basic merging. We try to + * keep minimum overhead to achieve low latency. + * + * Asynchronous and synchronous requests are not treated separately, but + * we relay on deadlines to ensure fairness. + * + */ +#include +#include +#include +#include +#include +#include + +enum { ASYNC, SYNC }; + +/* Tunables */ +static const int sync_read_expire = HZ / 2; /* max time before a sync read is submitted. */ +static const int sync_write_expire = 2 * HZ; /* max time before a sync write is submitted. */ + +static const int async_read_expire = 4 * HZ; /* ditto for async, these limits are SOFT! */ +static const int async_write_expire = 16 * HZ; /* ditto for async, these limits are SOFT! */ + +static const int writes_starved = 1; /* max times reads can starve a write */ +static const int fifo_batch = 1; /* # of sequential requests treated as one + by the above parameters. For throughput. */ + +/* Elevator data */ +struct sio_data { + /* Request queues */ + struct list_head fifo_list[2][2]; + + /* Attributes */ + unsigned int batched; + unsigned int starved; + + /* Settings */ + int fifo_expire[2][2]; + int fifo_batch; + int writes_starved; +}; + +static void +sio_merged_requests(struct request_queue *q, struct request *rq, + struct request *next) +{ + /* + * If next expires before rq, assign its expire time to rq + * and move into next position (next will be deleted) in fifo. + */ + if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist)) { + if (time_before(rq_fifo_time(next), rq_fifo_time(rq))) { + list_move(&rq->queuelist, &next->queuelist); + rq_set_fifo_time(rq, rq_fifo_time(next)); + } + } + + /* Delete next request */ + rq_fifo_clear(next); +} + +static void +sio_add_request(struct request_queue *q, struct request *rq) +{ + struct sio_data *sd = q->elevator->elevator_data; + const int sync = rq_is_sync(rq); + const int data_dir = rq_data_dir(rq); + + /* + * Add request to the proper fifo list and set its + * expire time. + */ + rq_set_fifo_time(rq, jiffies + sd->fifo_expire[sync][data_dir]); + list_add_tail(&rq->queuelist, &sd->fifo_list[sync][data_dir]); +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,38) +static int +sio_queue_empty(struct request_queue *q) +{ + struct sio_data *sd = q->elevator->elevator_data; + + /* Check if fifo lists are empty */ + return list_empty(&sd->fifo_list[SYNC][READ]) && list_empty(&sd->fifo_list[SYNC][WRITE]) && + list_empty(&sd->fifo_list[ASYNC][READ]) && list_empty(&sd->fifo_list[ASYNC][WRITE]); +} +#endif + +static struct request * +sio_expired_request(struct sio_data *sd, int sync, int data_dir) +{ + struct list_head *list = &sd->fifo_list[sync][data_dir]; + struct request *rq; + + if (list_empty(list)) + return NULL; + + /* Retrieve request */ + rq = rq_entry_fifo(list->next); + + /* Request has expired */ + if (time_after(jiffies, rq_fifo_time(rq))) + return rq; + + return NULL; +} + +static struct request * +sio_choose_expired_request(struct sio_data *sd) +{ + struct request *rq; + + /* + * Check expired requests. + * Asynchronous requests have priority over synchronous. + * Write requests have priority over read. + */ + rq = sio_expired_request(sd, ASYNC, WRITE); + if (rq) + return rq; + rq = sio_expired_request(sd, ASYNC, READ); + if (rq) + return rq; + + rq = sio_expired_request(sd, SYNC, WRITE); + if (rq) + return rq; + rq = sio_expired_request(sd, SYNC, READ); + if (rq) + return rq; + + return NULL; +} + +static struct request * +sio_choose_request(struct sio_data *sd, int data_dir) +{ + struct list_head *sync = sd->fifo_list[SYNC]; + struct list_head *async = sd->fifo_list[ASYNC]; + + /* + * Retrieve request from available fifo list. + * Synchronous requests have priority over asynchronous. + * Read requests have priority over write. + */ + if (!list_empty(&sync[data_dir])) + return rq_entry_fifo(sync[data_dir].next); + if (!list_empty(&async[data_dir])) + return rq_entry_fifo(async[data_dir].next); + + if (!list_empty(&sync[!data_dir])) + return rq_entry_fifo(sync[!data_dir].next); + if (!list_empty(&async[!data_dir])) + return rq_entry_fifo(async[!data_dir].next); + + return NULL; +} + +static inline void +sio_dispatch_request(struct sio_data *sd, struct request *rq) +{ + /* + * Remove the request from the fifo list + * and dispatch it. + */ + rq_fifo_clear(rq); + elv_dispatch_add_tail(rq->q, rq); + + sd->batched++; + + if (rq_data_dir(rq)) + sd->starved = 0; + else + sd->starved++; +} + +static int +sio_dispatch_requests(struct request_queue *q, int force) +{ + struct sio_data *sd = q->elevator->elevator_data; + struct request *rq = NULL; + int data_dir = READ; + + /* + * Retrieve any expired request after a batch of + * sequential requests. + */ + if (sd->batched > sd->fifo_batch) { + sd->batched = 0; + rq = sio_choose_expired_request(sd); + } + + /* Retrieve request */ + if (!rq) { + if (sd->starved > sd->writes_starved) + data_dir = WRITE; + + rq = sio_choose_request(sd, data_dir); + if (!rq) + return 0; + } + + /* Dispatch request */ + sio_dispatch_request(sd, rq); + + return 1; +} + +static struct request * +sio_former_request(struct request_queue *q, struct request *rq) +{ + struct sio_data *sd = q->elevator->elevator_data; + const int sync = rq_is_sync(rq); + const int data_dir = rq_data_dir(rq); + + if (rq->queuelist.prev == &sd->fifo_list[sync][data_dir]) + return NULL; + + /* Return former request */ + return list_entry(rq->queuelist.prev, struct request, queuelist); +} + +static struct request * +sio_latter_request(struct request_queue *q, struct request *rq) +{ + struct sio_data *sd = q->elevator->elevator_data; + const int sync = rq_is_sync(rq); + const int data_dir = rq_data_dir(rq); + + if (rq->queuelist.next == &sd->fifo_list[sync][data_dir]) + return NULL; + + /* Return latter request */ + return list_entry(rq->queuelist.next, struct request, queuelist); +} + +static void * +sio_init_queue(struct request_queue *q) +{ + struct sio_data *sd; + + /* Allocate structure */ + sd = kmalloc_node(sizeof(*sd), GFP_KERNEL, q->node); + if (!sd) + return NULL; + + /* Initialize fifo lists */ + INIT_LIST_HEAD(&sd->fifo_list[SYNC][READ]); + INIT_LIST_HEAD(&sd->fifo_list[SYNC][WRITE]); + INIT_LIST_HEAD(&sd->fifo_list[ASYNC][READ]); + INIT_LIST_HEAD(&sd->fifo_list[ASYNC][WRITE]); + + /* Initialize data */ + sd->batched = 0; + sd->fifo_expire[SYNC][READ] = sync_read_expire; + sd->fifo_expire[SYNC][WRITE] = sync_write_expire; + sd->fifo_expire[ASYNC][READ] = async_read_expire; + sd->fifo_expire[ASYNC][WRITE] = async_write_expire; + sd->fifo_batch = fifo_batch; + + return sd; +} + +static void +sio_exit_queue(struct elevator_queue *e) +{ + struct sio_data *sd = e->elevator_data; + + BUG_ON(!list_empty(&sd->fifo_list[SYNC][READ])); + BUG_ON(!list_empty(&sd->fifo_list[SYNC][WRITE])); + BUG_ON(!list_empty(&sd->fifo_list[ASYNC][READ])); + BUG_ON(!list_empty(&sd->fifo_list[ASYNC][WRITE])); + + /* Free structure */ + kfree(sd); +} + +/* + * sysfs code + */ + +static ssize_t +sio_var_show(int var, char *page) +{ + return sprintf(page, "%d\n", var); +} + +static ssize_t +sio_var_store(int *var, const char *page, size_t count) +{ + char *p = (char *) page; + + *var = simple_strtol(p, &p, 10); + return count; +} + +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, char *page) \ +{ \ + struct sio_data *sd = e->elevator_data; \ + int __data = __VAR; \ + if (__CONV) \ + __data = jiffies_to_msecs(__data); \ + return sio_var_show(__data, (page)); \ +} +SHOW_FUNCTION(sio_sync_read_expire_show, sd->fifo_expire[SYNC][READ], 1); +SHOW_FUNCTION(sio_sync_write_expire_show, sd->fifo_expire[SYNC][WRITE], 1); +SHOW_FUNCTION(sio_async_read_expire_show, sd->fifo_expire[ASYNC][READ], 1); +SHOW_FUNCTION(sio_async_write_expire_show, sd->fifo_expire[ASYNC][WRITE], 1); +SHOW_FUNCTION(sio_fifo_batch_show, sd->fifo_batch, 0); +SHOW_FUNCTION(sio_writes_starved_show, sd->writes_starved, 0); +#undef SHOW_FUNCTION + +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \ +{ \ + struct sio_data *sd = e->elevator_data; \ + int __data; \ + int ret = sio_var_store(&__data, (page), count); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + if (__CONV) \ + *(__PTR) = msecs_to_jiffies(__data); \ + else \ + *(__PTR) = __data; \ + return ret; \ +} +STORE_FUNCTION(sio_sync_read_expire_store, &sd->fifo_expire[SYNC][READ], 0, INT_MAX, 1); +STORE_FUNCTION(sio_sync_write_expire_store, &sd->fifo_expire[SYNC][WRITE], 0, INT_MAX, 1); +STORE_FUNCTION(sio_async_read_expire_store, &sd->fifo_expire[ASYNC][READ], 0, INT_MAX, 1); +STORE_FUNCTION(sio_async_write_expire_store, &sd->fifo_expire[ASYNC][WRITE], 0, INT_MAX, 1); +STORE_FUNCTION(sio_fifo_batch_store, &sd->fifo_batch, 0, INT_MAX, 0); +STORE_FUNCTION(sio_writes_starved_store, &sd->writes_starved, 0, INT_MAX, 0); +#undef STORE_FUNCTION + +#define DD_ATTR(name) \ + __ATTR(name, S_IRUGO|S_IWUSR, sio_##name##_show, \ + sio_##name##_store) + +static struct elv_fs_entry sio_attrs[] = { + DD_ATTR(sync_read_expire), + DD_ATTR(sync_write_expire), + DD_ATTR(async_read_expire), + DD_ATTR(async_write_expire), + DD_ATTR(fifo_batch), + DD_ATTR(writes_starved), + __ATTR_NULL +}; + +static struct elevator_type iosched_sio = { + .ops = { + .elevator_merge_req_fn = sio_merged_requests, + .elevator_dispatch_fn = sio_dispatch_requests, + .elevator_add_req_fn = sio_add_request, +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,38) + .elevator_queue_empty_fn = sio_queue_empty, +#endif + .elevator_former_req_fn = sio_former_request, + .elevator_latter_req_fn = sio_latter_request, + .elevator_init_fn = sio_init_queue, + .elevator_exit_fn = sio_exit_queue, + }, + + .elevator_attrs = sio_attrs, + .elevator_name = "sio", + .elevator_owner = THIS_MODULE, +}; + +static int __init sio_init(void) +{ + /* Register elevator */ + elv_register(&iosched_sio); + + return 0; +} + +static void __exit sio_exit(void) +{ + /* Unregister elevator */ + elv_unregister(&iosched_sio); +} + +module_init(sio_init); +module_exit(sio_exit); + +MODULE_AUTHOR("Miguel Boton"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Simple IO scheduler"); +MODULE_VERSION("0.2"); diff --git a/block/sioplus-iosched.c b/block/sioplus-iosched.c new file mode 100644 index 00000000..7e54137a --- /dev/null +++ b/block/sioplus-iosched.c @@ -0,0 +1,409 @@ +/* +* Simple IO scheduler plus +* Based on Noop, Deadline and V(R) IO schedulers. +* +* Copyright (C) 2012 Miguel Boton +* (C) 2013 Boy Petersen +* +* +* This algorithm does not do any kind of sorting, as it is aimed for +* aleatory access devices, but it does some basic merging. We try to +* keep minimum overhead to achieve low latency. +* +* Asynchronous and synchronous requests are not treated separately, but +* we relay on deadlines to ensure fairness. +* +* The plus version fixes writes_starved not being initialized on startup +* and also modifies the write starvation counting logic. +* +*/ +#include +#include +#include +#include +#include +#include +#include + +enum { ASYNC, SYNC }; + +/* Tunables */ +static const int sync_read_expire = (HZ / 16) * 5; /* max time before a sync read is submitted. */ +static const int sync_write_expire = (HZ / 2) * 5; /* max time before a sync write is submitted. */ + +static const int async_read_expire = HZ * 4; /* ditto for async, these limits are SOFT! */ +static const int async_write_expire = HZ * 16; /* ditto for async, these limits are SOFT! */ + +static const int writes_starved = 2; /* max times reads can starve a write */ +static const int fifo_batch = 1; /* # of sequential requests treated as one +by the above parameters. For throughput. */ + +/* Elevator data */ +struct sio_data { +/* Request queues */ +struct list_head fifo_list[2][2]; + +/* Attributes */ +unsigned int batched; +unsigned int starved; + +/* Settings */ +int fifo_expire[2][2]; +int fifo_batch; +int writes_starved; +}; + +static void +sio_merged_requests(struct request_queue *q, struct request *rq, +struct request *next) +{ +/* +* If next expires before rq, assign its expire time to rq +* and move into next position (next will be deleted) in fifo. +*/ +if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist)) { +if (time_before(rq_fifo_time(next), rq_fifo_time(rq))) { +list_move(&rq->queuelist, &next->queuelist); +rq_set_fifo_time(rq, rq_fifo_time(next)); +} +} + +/* Delete next request */ +rq_fifo_clear(next); +} + +static void +sio_add_request(struct request_queue *q, struct request *rq) +{ +struct sio_data *sd = q->elevator->elevator_data; +const int sync = rq_is_sync(rq); +const int data_dir = rq_data_dir(rq); + +/* +* Add request to the proper fifo list and set its +* expire time. +*/ +rq_set_fifo_time(rq, jiffies + sd->fifo_expire[sync][data_dir]); +list_add_tail(&rq->queuelist, &sd->fifo_list[sync][data_dir]); +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,38) +static int +sio_queue_empty(struct request_queue *q) +{ +struct sio_data *sd = q->elevator->elevator_data; + +/* Check if fifo lists are empty */ +return list_empty(&sd->fifo_list[SYNC][READ]) && list_empty(&sd->fifo_list[SYNC][WRITE]) && +list_empty(&sd->fifo_list[ASYNC][READ]) && list_empty(&sd->fifo_list[ASYNC][WRITE]); +} +#endif + +static struct request * +sio_expired_request(struct sio_data *sd, int sync, int data_dir) +{ +struct list_head *list = &sd->fifo_list[sync][data_dir]; +struct request *rq; + +if (list_empty(list)) +return NULL; + +/* Retrieve request */ +rq = rq_entry_fifo(list->next); + +/* Request has expired */ +if (time_after_eq(jiffies, rq_fifo_time(rq))) +return rq; + +return NULL; +} + +static struct request * +sio_choose_expired_request(struct sio_data *sd) +{ +struct request *rq; + +/* +* Check expired requests. +* Asynchronous requests have priority over synchronous. +* Write requests have priority over read. +*/ +rq = sio_expired_request(sd, ASYNC, WRITE); +if (rq) +return rq; +rq = sio_expired_request(sd, ASYNC, READ); +if (rq) +return rq; + +rq = sio_expired_request(sd, SYNC, WRITE); +if (rq) +return rq; +rq = sio_expired_request(sd, SYNC, READ); +if (rq) +return rq; + + +return NULL; +} + +static struct request * +sio_choose_request(struct sio_data *sd, int data_dir) +{ +struct list_head *sync = sd->fifo_list[SYNC]; +struct list_head *async = sd->fifo_list[ASYNC]; + +/* +* Retrieve request from available fifo list. +* Synchronous requests have priority over asynchronous. +* Read requests have priority over write. +*/ +if (!list_empty(&sync[data_dir])) +return rq_entry_fifo(sync[data_dir].next); +if (!list_empty(&async[data_dir])) +return rq_entry_fifo(async[data_dir].next); + +if (!list_empty(&sync[!data_dir])) +return rq_entry_fifo(sync[!data_dir].next); +if (!list_empty(&async[!data_dir])) +return rq_entry_fifo(async[!data_dir].next); + +return NULL; +} + +static inline void +sio_dispatch_request(struct sio_data *sd, struct request *rq) +{ + +/* +* Remove the request from the fifo list +* and dispatch it. +*/ +rq_fifo_clear(rq); +elv_dispatch_add_tail(rq->q, rq); + +sd->batched++; + +if (rq_data_dir(rq)) { +sd->starved = 0; +} else { +if (!list_empty(&sd->fifo_list[SYNC][WRITE]) || +!list_empty(&sd->fifo_list[ASYNC][WRITE])) +sd->starved++; +} +} + +static int +sio_dispatch_requests(struct request_queue *q, int force) +{ +struct sio_data *sd = q->elevator->elevator_data; +struct request *rq = NULL; +int data_dir = READ; + +/* +* Retrieve any expired request after a batch of +* sequential requests. +*/ +if (sd->batched > sd->fifo_batch) { +sd->batched = 0; +rq = sio_choose_expired_request(sd); +} + +/* Retrieve request */ +if (!rq) { +if (sd->starved > sd->writes_starved) +data_dir = WRITE; + +rq = sio_choose_request(sd, data_dir); +if (!rq) +return 0; +} + +/* Dispatch request */ +sio_dispatch_request(sd, rq); + +return 1; +} + +static struct request * +sio_former_request(struct request_queue *q, struct request *rq) +{ +struct sio_data *sd = q->elevator->elevator_data; +const int sync = rq_is_sync(rq); +const int data_dir = rq_data_dir(rq); + +if (rq->queuelist.prev == &sd->fifo_list[sync][data_dir]) +return NULL; + +/* Return former request */ +return list_entry(rq->queuelist.prev, struct request, queuelist); +} + +static struct request * +sio_latter_request(struct request_queue *q, struct request *rq) +{ +struct sio_data *sd = q->elevator->elevator_data; +const int sync = rq_is_sync(rq); +const int data_dir = rq_data_dir(rq); + +if (rq->queuelist.next == &sd->fifo_list[sync][data_dir]) +return NULL; + +/* Return latter request */ +return list_entry(rq->queuelist.next, struct request, queuelist); +} + +static void * +sio_init_queue(struct request_queue *q) +{ +struct sio_data *sd; + +/* Allocate structure */ +sd = kmalloc_node(sizeof(*sd), GFP_KERNEL, q->node); +if (!sd) +return NULL; + +/* Initialize fifo lists */ +INIT_LIST_HEAD(&sd->fifo_list[SYNC][READ]); +INIT_LIST_HEAD(&sd->fifo_list[SYNC][WRITE]); +INIT_LIST_HEAD(&sd->fifo_list[ASYNC][READ]); +INIT_LIST_HEAD(&sd->fifo_list[ASYNC][WRITE]); + +/* Initialize data */ +sd->batched = 0; +sd->fifo_expire[SYNC][READ] = sync_read_expire; +sd->fifo_expire[SYNC][WRITE] = sync_write_expire; +sd->fifo_expire[ASYNC][READ] = async_read_expire; +sd->fifo_expire[ASYNC][WRITE] = async_write_expire; +sd->fifo_batch = fifo_batch; +sd->writes_starved = writes_starved; + +return sd; +} + +static void +sio_exit_queue(struct elevator_queue *e) +{ +struct sio_data *sd = e->elevator_data; + +BUG_ON(!list_empty(&sd->fifo_list[SYNC][READ])); +BUG_ON(!list_empty(&sd->fifo_list[SYNC][WRITE])); +BUG_ON(!list_empty(&sd->fifo_list[ASYNC][READ])); +BUG_ON(!list_empty(&sd->fifo_list[ASYNC][WRITE])); + +/* Free structure */ +kfree(sd); +} + +/* +* sysfs code +*/ + +static ssize_t +sio_var_show(int var, char *page) +{ +return sprintf(page, "%d\n", var); +} + +static ssize_t +sio_var_store(int *var, const char *page, size_t count) +{ +char *p = (char *) page; + +*var = simple_strtol(p, &p, 10); +return count; +} + +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, char *page) \ +{ \ +struct sio_data *sd = e->elevator_data; \ +int __data = __VAR; \ +if (__CONV) \ +__data = jiffies_to_msecs(__data); \ +return sio_var_show(__data, (page)); \ +} +SHOW_FUNCTION(sio_sync_read_expire_show, sd->fifo_expire[SYNC][READ], 1); +SHOW_FUNCTION(sio_sync_write_expire_show, sd->fifo_expire[SYNC][WRITE], 1); +SHOW_FUNCTION(sio_async_read_expire_show, sd->fifo_expire[ASYNC][READ], 1); +SHOW_FUNCTION(sio_async_write_expire_show, sd->fifo_expire[ASYNC][WRITE], 1); +SHOW_FUNCTION(sio_fifo_batch_show, sd->fifo_batch, 0); +SHOW_FUNCTION(sio_writes_starved_show, sd->writes_starved, 0); +#undef SHOW_FUNCTION + +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \ +{ \ +struct sio_data *sd = e->elevator_data; \ +int __data; \ +int ret = sio_var_store(&__data, (page), count); \ +if (__data < (MIN)) \ +__data = (MIN); \ +else if (__data > (MAX)) \ +__data = (MAX); \ +if (__CONV) \ +*(__PTR) = msecs_to_jiffies(__data); \ +else \ +*(__PTR) = __data; \ +return ret; \ +} +STORE_FUNCTION(sio_sync_read_expire_store, &sd->fifo_expire[SYNC][READ], 0, INT_MAX, 1); +STORE_FUNCTION(sio_sync_write_expire_store, &sd->fifo_expire[SYNC][WRITE], 0, INT_MAX, 1); +STORE_FUNCTION(sio_async_read_expire_store, &sd->fifo_expire[ASYNC][READ], 0, INT_MAX, 1); +STORE_FUNCTION(sio_async_write_expire_store, &sd->fifo_expire[ASYNC][WRITE], 0, INT_MAX, 1); +STORE_FUNCTION(sio_fifo_batch_store, &sd->fifo_batch, 0, INT_MAX, 0); +STORE_FUNCTION(sio_writes_starved_store, &sd->writes_starved, 0, INT_MAX, 0); +#undef STORE_FUNCTION + +#define DD_ATTR(name) \ +__ATTR(name, S_IRUGO|S_IWUSR, sio_##name##_show, \ +sio_##name##_store) + +static struct elv_fs_entry sio_attrs[] = { +DD_ATTR(sync_read_expire), +DD_ATTR(sync_write_expire), +DD_ATTR(async_read_expire), +DD_ATTR(async_write_expire), +DD_ATTR(fifo_batch), +DD_ATTR(writes_starved), +__ATTR_NULL +}; + +static struct elevator_type iosched_sioplus = { +.ops = { +.elevator_merge_req_fn = sio_merged_requests, +.elevator_dispatch_fn = sio_dispatch_requests, +.elevator_add_req_fn = sio_add_request, +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,38) +.elevator_queue_empty_fn = sio_queue_empty, +#endif +.elevator_former_req_fn = sio_former_request, +.elevator_latter_req_fn = sio_latter_request, +.elevator_init_fn = sio_init_queue, +.elevator_exit_fn = sio_exit_queue, +}, + +.elevator_attrs = sio_attrs, +.elevator_name = "sioplus", +.elevator_owner = THIS_MODULE, +}; + +static int __init sioplus_init(void) +{ +/* Register elevator */ +elv_register(&iosched_sioplus); + +return 0; +} + +static void __exit sioplus_exit(void) +{ +/* Unregister elevator */ +elv_unregister(&iosched_sioplus); +} + +module_init(sioplus_init); +module_exit(sioplus_exit); + +MODULE_AUTHOR("Miguel Boton"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Simple IO scheduler plus"); diff --git a/block/vr-iosched.c b/block/vr-iosched.c new file mode 100644 index 00000000..f94de99c --- /dev/null +++ b/block/vr-iosched.c @@ -0,0 +1,434 @@ +/* + * V(R) I/O Scheduler + * + * Copyright (C) 2007 Aaron Carroll + * + * + * The algorithm: + * + * The next request is decided based on its distance from the last + * request, with a multiplicative penalty of `rev_penalty' applied + * for reversing the head direction. A rev_penalty of 1 means SSTF + * behaviour. As this variable is increased, the algorithm approaches + * pure SCAN. Setting rev_penalty to 0 forces SCAN. + * + * Async and synch requests are not treated seperately. Instead we + * rely on deadlines to ensure fairness. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +enum vr_data_dir { + ASYNC, + SYNC, +}; + +enum vr_head_dir { + FORWARD, + BACKWARD, +}; + +static const int sync_expire = HZ / 2; /* max time before a sync is submitted. */ +static const int async_expire = 5 * HZ; /* ditto for async, these limits are SOFT! */ +static const int fifo_batch = 1; +static const int rev_penalty = 10; /* penalty for reversing head direction */ + +struct vr_data { + struct rb_root sort_list; + struct list_head fifo_list[2]; + + struct request *next_rq; + struct request *prev_rq; + + unsigned int nbatched; + sector_t last_sector; /* head position */ + int head_dir; + + /* tunables */ + int fifo_expire[2]; + int fifo_batch; + int rev_penalty; +}; + +static void vr_move_request(struct vr_data *, struct request *); + +static inline struct vr_data * +vr_get_data(struct request_queue *q) +{ + return q->elevator->elevator_data; +} + +static void +vr_add_rq_rb(struct vr_data *vd, struct request *rq) +{ + elv_rb_add(&vd->sort_list, rq); + + if (blk_rq_pos(rq) >= vd->last_sector) { + if (!vd->next_rq || blk_rq_pos(vd->next_rq) > blk_rq_pos(rq)) + vd->next_rq = rq; + } + else { + if (!vd->prev_rq || blk_rq_pos(vd->prev_rq) < blk_rq_pos(rq)) + vd->prev_rq = rq; + } + + BUG_ON(vd->next_rq && vd->next_rq == vd->prev_rq); + BUG_ON(vd->next_rq && vd->prev_rq && blk_rq_pos(vd->next_rq) < blk_rq_pos(vd->prev_rq)); +} + +static void +vr_del_rq_rb(struct vr_data *vd, struct request *rq) +{ + /* + * We might be deleting our cached next request. + * If so, find its sucessor. + */ + + if (vd->next_rq == rq) + vd->next_rq = elv_rb_latter_request(NULL, rq); + else if (vd->prev_rq == rq) + vd->prev_rq = elv_rb_former_request(NULL, rq); + + BUG_ON(vd->next_rq && vd->next_rq == vd->prev_rq); + BUG_ON(vd->next_rq && vd->prev_rq && blk_rq_pos(vd->next_rq) < blk_rq_pos(vd->prev_rq)); + + elv_rb_del(&vd->sort_list, rq); +} + +/* + * add rq to rbtree and fifo + */ +static void +vr_add_request(struct request_queue *q, struct request *rq) +{ + struct vr_data *vd = vr_get_data(q); + const int dir = rq_is_sync(rq); + + vr_add_rq_rb(vd, rq); + + if (vd->fifo_expire[dir]) { + rq_set_fifo_time(rq, jiffies + vd->fifo_expire[dir]); + list_add_tail(&rq->queuelist, &vd->fifo_list[dir]); + } +} + +/* + * remove rq from rbtree and fifo. + */ +static void +vr_remove_request(struct request_queue *q, struct request *rq) +{ + struct vr_data *vd = vr_get_data(q); + + rq_fifo_clear(rq); + vr_del_rq_rb(vd, rq); +} + +static int +vr_merge(struct request_queue *q, struct request **rqp, struct bio *bio) +{ + sector_t sector = bio->bi_sector + bio_sectors(bio); + struct vr_data *vd = vr_get_data(q); + struct request *rq = elv_rb_find(&vd->sort_list, sector); + + if (rq && elv_rq_merge_ok(rq, bio)) { + *rqp = rq; + return ELEVATOR_FRONT_MERGE; + } + return ELEVATOR_NO_MERGE; +} + +static void +vr_merged_request(struct request_queue *q, struct request *req, int type) +{ + struct vr_data *vd = vr_get_data(q); + + /* + * if the merge was a front merge, we need to reposition request + */ + if (type == ELEVATOR_FRONT_MERGE) { + vr_del_rq_rb(vd, req); + vr_add_rq_rb(vd, req); + } +} + +static void +vr_merged_requests(struct request_queue *q, struct request *rq, + struct request *next) +{ + /* + * if next expires before rq, assign its expire time to rq + * and move into next position (next will be deleted) in fifo + */ + if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist)) { + if (time_before(rq_fifo_time(next), rq_fifo_time(rq))) { + list_move(&rq->queuelist, &next->queuelist); + rq_set_fifo_time(rq, rq_fifo_time(next)); + } + } + + vr_remove_request(q, next); +} + +/* + * move an entry to dispatch queue + */ +static void +vr_move_request(struct vr_data *vd, struct request *rq) +{ + struct request_queue *q = rq->q; + + if (blk_rq_pos(rq) > vd->last_sector) + vd->head_dir = FORWARD; + else + vd->head_dir = BACKWARD; + + vd->last_sector = blk_rq_pos(rq); + vd->next_rq = elv_rb_latter_request(NULL, rq); + vd->prev_rq = elv_rb_former_request(NULL, rq); + + BUG_ON(vd->next_rq && vd->next_rq == vd->prev_rq); + + vr_remove_request(q, rq); + elv_dispatch_add_tail(q, rq); + vd->nbatched++; +} + +/* + * get the first expired request in direction ddir + */ +static struct request * +vr_expired_request(struct vr_data *vd, int ddir) +{ + struct request *rq; + + if (list_empty(&vd->fifo_list[ddir])) + return NULL; + + rq = rq_entry_fifo(vd->fifo_list[ddir].next); + if (time_after(jiffies, rq_fifo_time(rq))) + return rq; + + return NULL; +} + +/* + * Returns the oldest expired request + */ +static struct request * +vr_check_fifo(struct vr_data *vd) +{ + struct request *rq_sync = vr_expired_request(vd, SYNC); + struct request *rq_async = vr_expired_request(vd, ASYNC); + + if (rq_async && rq_sync) { + if (time_after(rq_fifo_time(rq_async), rq_fifo_time(rq_sync))) + return rq_sync; + } + else if (rq_sync) + return rq_sync; + + return rq_async; +} + +/* + * Return the request with the lowest penalty + */ +static struct request * +vr_choose_request(struct vr_data *vd) +{ + int penalty = (vd->rev_penalty) ? : INT_MAX; + struct request *next = vd->next_rq; + struct request *prev = vd->prev_rq; + sector_t next_pen, prev_pen; + + BUG_ON(prev && prev == next); + + if (!prev) + return next; + else if (!next) + return prev; + + /* At this point both prev and next are defined and distinct */ + + next_pen = blk_rq_pos(next) - vd->last_sector; + prev_pen = vd->last_sector - blk_rq_pos(prev); + + if (vd->head_dir == FORWARD) + next_pen = do_div(next_pen, penalty); + else + prev_pen = do_div(prev_pen, penalty); + + if (next_pen <= prev_pen) + return next; + + return prev; +} + +static int +vr_dispatch_requests(struct request_queue *q, int force) +{ + struct vr_data *vd = vr_get_data(q); + struct request *rq = NULL; + + /* Check for and issue expired requests */ + if (vd->nbatched > vd->fifo_batch) { + vd->nbatched = 0; + rq = vr_check_fifo(vd); + } + + if (!rq) { + rq = vr_choose_request(vd); + if (!rq) + return 0; + } + + vr_move_request(vd, rq); + + return 1; +} + + +static void +vr_exit_queue(struct elevator_queue *e) +{ + struct vr_data *vd = e->elevator_data; + BUG_ON(!RB_EMPTY_ROOT(&vd->sort_list)); + kfree(vd); +} + +/* + * initialize elevator private data (vr_data). + */ +static void *vr_init_queue(struct request_queue *q) +{ + struct vr_data *vd; + + vd = kmalloc_node(sizeof(*vd), GFP_KERNEL | __GFP_ZERO, q->node); + if (!vd) + return NULL; + + INIT_LIST_HEAD(&vd->fifo_list[SYNC]); + INIT_LIST_HEAD(&vd->fifo_list[ASYNC]); + vd->sort_list = RB_ROOT; + vd->fifo_expire[SYNC] = sync_expire; + vd->fifo_expire[ASYNC] = async_expire; + vd->fifo_batch = fifo_batch; + vd->rev_penalty = rev_penalty; + return vd; +} + +/* + * sysfs parts below + */ + +static ssize_t +vr_var_show(int var, char *page) +{ + return sprintf(page, "%d\n", var); +} + +static ssize_t +vr_var_store(int *var, const char *page, size_t count) +{ + *var = simple_strtol(page, NULL, 10); + return count; +} + +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ + static ssize_t __FUNC(struct elevator_queue *e, char *page) \ + { \ + struct vr_data *vd = e->elevator_data; \ + int __data = __VAR; \ + if (__CONV) \ + __data = jiffies_to_msecs(__data); \ + return vr_var_show(__data, (page)); \ + } +SHOW_FUNCTION(vr_sync_expire_show, vd->fifo_expire[SYNC], 1); +SHOW_FUNCTION(vr_async_expire_show, vd->fifo_expire[ASYNC], 1); +SHOW_FUNCTION(vr_fifo_batch_show, vd->fifo_batch, 0); +SHOW_FUNCTION(vr_rev_penalty_show, vd->rev_penalty, 0); +#undef SHOW_FUNCTION + +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ + static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \ + { \ + struct vr_data *vd = e->elevator_data; \ + int __data; \ + int ret = vr_var_store(&__data, (page), count); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + if (__CONV) \ + *(__PTR) = msecs_to_jiffies(__data); \ + else \ + *(__PTR) = __data; \ + return ret; \ + } +STORE_FUNCTION(vr_sync_expire_store, &vd->fifo_expire[SYNC], 0, INT_MAX, 1); +STORE_FUNCTION(vr_async_expire_store, &vd->fifo_expire[ASYNC], 0, INT_MAX, 1); +STORE_FUNCTION(vr_fifo_batch_store, &vd->fifo_batch, 0, INT_MAX, 0); +STORE_FUNCTION(vr_rev_penalty_store, &vd->rev_penalty, 0, INT_MAX, 0); +#undef STORE_FUNCTION + +#define DD_ATTR(name) \ + __ATTR(name, S_IRUGO|S_IWUSR, vr_##name##_show, \ + vr_##name##_store) + +static struct elv_fs_entry vr_attrs[] = { + DD_ATTR(sync_expire), + DD_ATTR(async_expire), + DD_ATTR(fifo_batch), + DD_ATTR(rev_penalty), + __ATTR_NULL +}; + +static struct elevator_type iosched_vr = { + .ops = { + .elevator_merge_fn = vr_merge, + .elevator_merged_fn = vr_merged_request, + .elevator_merge_req_fn = vr_merged_requests, + .elevator_dispatch_fn = vr_dispatch_requests, + .elevator_add_req_fn = vr_add_request, + .elevator_former_req_fn = elv_rb_former_request, + .elevator_latter_req_fn = elv_rb_latter_request, + .elevator_init_fn = vr_init_queue, + .elevator_exit_fn = vr_exit_queue, + }, + + .elevator_attrs = vr_attrs, + .elevator_name = "vr", + .elevator_owner = THIS_MODULE, +}; + +static int __init vr_init(void) +{ + elv_register(&iosched_vr); + + return 0; +} + +static void __exit vr_exit(void) +{ + elv_unregister(&iosched_vr); +} + +module_init(vr_init); +module_exit(vr_exit); + +MODULE_AUTHOR("Aaron Carroll"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("V(R) IO scheduler"); + diff --git a/block/zen-iosched.c b/block/zen-iosched.c new file mode 100644 index 00000000..77145de8 --- /dev/null +++ b/block/zen-iosched.c @@ -0,0 +1,277 @@ +/* + * Zen IO scheduler + * Primarily based on Noop, deadline, and SIO IO schedulers. + * + * Copyright (C) 2012 Brandon Berhent + * + * FCFS, dispatches are back-inserted, deadlines ensure fairness. + * Should work best with devices where there is no travel delay. + */ +#include +#include +#include +#include +#include +#include + +enum zen_data_dir { ASYNC, SYNC }; + +static const int sync_expire = HZ / 4; /* max time before a sync is submitted. */ +static const int async_expire = 2 * HZ; /* ditto for async, these limits are SOFT! */ +static const int fifo_batch = 1; + +struct zen_data { + /* Runtime Data */ + /* Requests are only present on fifo_list */ + struct list_head fifo_list[2]; + + unsigned int batching; /* number of sequential requests made */ + + /* tunables */ + int fifo_expire[2]; + int fifo_batch; +}; + +static inline struct zen_data * +zen_get_data(struct request_queue *q) { + return q->elevator->elevator_data; +} + +static void zen_dispatch(struct zen_data *, struct request *); + +static void +zen_merged_requests(struct request_queue *q, struct request *rq, + struct request *next) +{ + /* + * if next expires before rq, assign its expire time to arq + * and move into next position (next will be deleted) in fifo + */ + if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist)) { + if (time_before(rq_fifo_time(next), rq_fifo_time(rq))) { + list_move(&rq->queuelist, &next->queuelist); + rq_set_fifo_time(rq, rq_fifo_time(next)); + } + } + + /* next request is gone */ + rq_fifo_clear(next); +} + +static void zen_add_request(struct request_queue *q, struct request *rq) +{ + struct zen_data *zdata = zen_get_data(q); + const int dir = rq_data_dir(rq); + + if (zdata->fifo_expire[dir]) { + rq_set_fifo_time(rq, jiffies + zdata->fifo_expire[dir]); + list_add_tail(&rq->queuelist, &zdata->fifo_list[dir]); + } +} + +static void zen_dispatch(struct zen_data *zdata, struct request *rq) +{ + /* Remove request from list and dispatch it */ + rq_fifo_clear(rq); + elv_dispatch_add_tail(rq->q, rq); + + /* Increment # of sequential requests */ + zdata->batching++; +} + +/* + * get the first expired request in direction ddir + */ +static struct request * +zen_expired_request(struct zen_data *zdata, int ddir) +{ + struct request *rq; + + if (list_empty(&zdata->fifo_list[ddir])) + return NULL; + + rq = rq_entry_fifo(zdata->fifo_list[ddir].next); + if (time_after(jiffies, rq_fifo_time(rq))) + return rq; + + return NULL; +} + +/* + * zen_check_fifo returns 0 if there are no expired requests on the fifo, + * otherwise it returns the next expired request + */ +static struct request * +zen_check_fifo(struct zen_data *zdata) +{ + struct request *rq_sync = zen_expired_request(zdata, SYNC); + struct request *rq_async = zen_expired_request(zdata, ASYNC); + + if (rq_async && rq_sync) { + if (time_after(rq_fifo_time(rq_async), rq_fifo_time(rq_sync))) + return rq_sync; + } else if (rq_sync) { + return rq_sync; + } else if (rq_async) { + return rq_async; + } + + return 0; +} + +static struct request * +zen_choose_request(struct zen_data *zdata) +{ + /* + * Retrieve request from available fifo list. + * Synchronous requests have priority over asynchronous. + */ + if (!list_empty(&zdata->fifo_list[SYNC])) + return rq_entry_fifo(zdata->fifo_list[SYNC].next); + if (!list_empty(&zdata->fifo_list[ASYNC])) + return rq_entry_fifo(zdata->fifo_list[ASYNC].next); + + return NULL; +} + +static int zen_dispatch_requests(struct request_queue *q, int force) +{ + struct zen_data *zdata = zen_get_data(q); + struct request *rq = NULL; + + /* Check for and issue expired requests */ + if (zdata->batching > zdata->fifo_batch) { + zdata->batching = 0; + rq = zen_check_fifo(zdata); + } + + if (!rq) { + rq = zen_choose_request(zdata); + if (!rq) + return 0; + } + + zen_dispatch(zdata, rq); + + return 1; +} + +static void *zen_init_queue(struct request_queue *q) +{ + struct zen_data *zdata; + + zdata = kmalloc_node(sizeof(*zdata), GFP_KERNEL, q->node); + if (!zdata) + return NULL; + INIT_LIST_HEAD(&zdata->fifo_list[SYNC]); + INIT_LIST_HEAD(&zdata->fifo_list[ASYNC]); + zdata->fifo_expire[SYNC] = sync_expire; + zdata->fifo_expire[ASYNC] = async_expire; + zdata->fifo_batch = fifo_batch; + return zdata; +} + +static void zen_exit_queue(struct elevator_queue *e) +{ + struct zen_data *zdata = e->elevator_data; + + BUG_ON(!list_empty(&zdata->fifo_list[SYNC])); + BUG_ON(!list_empty(&zdata->fifo_list[ASYNC])); + kfree(zdata); +} + +/* Sysfs */ +static ssize_t +zen_var_show(int var, char *page) +{ + return sprintf(page, "%d\n", var); +} + +static ssize_t +zen_var_store(int *var, const char *page, size_t count) +{ + *var = simple_strtol(page, NULL, 10); + return count; +} + +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, char *page) \ +{ \ + struct zen_data *zdata = e->elevator_data; \ + int __data = __VAR; \ + if (__CONV) \ + __data = jiffies_to_msecs(__data); \ + return zen_var_show(__data, (page)); \ +} +SHOW_FUNCTION(zen_sync_expire_show, zdata->fifo_expire[SYNC], 1); +SHOW_FUNCTION(zen_async_expire_show, zdata->fifo_expire[ASYNC], 1); +SHOW_FUNCTION(zen_fifo_batch_show, zdata->fifo_batch, 0); +#undef SHOW_FUNCTION + +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \ +{ \ + struct zen_data *zdata = e->elevator_data; \ + int __data; \ + int ret = zen_var_store(&__data, (page), count); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + if (__CONV) \ + *(__PTR) = msecs_to_jiffies(__data); \ + else \ + *(__PTR) = __data; \ + return ret; \ +} +STORE_FUNCTION(zen_sync_expire_store, &zdata->fifo_expire[SYNC], 0, INT_MAX, 1); +STORE_FUNCTION(zen_async_expire_store, &zdata->fifo_expire[ASYNC], 0, INT_MAX, 1); +STORE_FUNCTION(zen_fifo_batch_store, &zdata->fifo_batch, 0, INT_MAX, 0); +#undef STORE_FUNCTION + +#define DD_ATTR(name) \ + __ATTR(name, S_IRUGO|S_IWUSR, zen_##name##_show, \ + zen_##name##_store) + +static struct elv_fs_entry zen_attrs[] = { + DD_ATTR(sync_expire), + DD_ATTR(async_expire), + DD_ATTR(fifo_batch), + __ATTR_NULL +}; + +static struct elevator_type iosched_zen = { + .ops = { + .elevator_merge_req_fn = zen_merged_requests, + .elevator_dispatch_fn = zen_dispatch_requests, + .elevator_add_req_fn = zen_add_request, + .elevator_former_req_fn = elv_rb_former_request, + .elevator_latter_req_fn = elv_rb_latter_request, + .elevator_init_fn = zen_init_queue, + .elevator_exit_fn = zen_exit_queue, + }, + .elevator_attrs = zen_attrs, + .elevator_name = "zen", + .elevator_owner = THIS_MODULE, +}; + +static int __init zen_init(void) +{ + elv_register(&iosched_zen); + + return 0; +} + +static void __exit zen_exit(void) +{ + elv_unregister(&iosched_zen); +} + +module_init(zen_init); +module_exit(zen_exit); + + +MODULE_AUTHOR("Brandon Berhent"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Zen IO scheduler"); +MODULE_VERSION("1.0"); diff --git a/drivers/base/genlock.c b/drivers/base/genlock.c index 8b7259a1..1d59c745 100644 --- a/drivers/base/genlock.c +++ b/drivers/base/genlock.c @@ -116,7 +116,6 @@ static const struct file_operations genlock_fops = { struct genlock *genlock_create_lock(struct genlock_handle *handle) { struct genlock *lock; - void *ret; if (IS_ERR_OR_NULL(handle)) { GENLOCK_LOG_ERR("Invalid handle\n"); @@ -146,13 +145,8 @@ struct genlock *genlock_create_lock(struct genlock_handle *handle) * other processes */ - ret = anon_inode_getfile("genlock", &genlock_fops, lock, O_RDWR); - if (IS_ERR_OR_NULL(ret)) { - GENLOCK_LOG_ERR("Unable to create lock inode\n"); - kfree(lock); - return ret; - } - lock->file = ret; + lock->file = anon_inode_getfile("genlock", &genlock_fops, + lock, O_RDWR); /* Attach the new lock to the handle */ handle->lock = lock; @@ -666,19 +660,12 @@ static struct genlock_handle *_genlock_get_handle(void) struct genlock_handle *genlock_get_handle(void) { - void *ret; struct genlock_handle *handle = _genlock_get_handle(); if (IS_ERR(handle)) return handle; - ret = anon_inode_getfile("genlock-handle", + handle->file = anon_inode_getfile("genlock-handle", &genlock_handle_fops, handle, O_RDWR); - if (IS_ERR_OR_NULL(ret)) { - GENLOCK_LOG_ERR("Unable to create handle inode\n"); - kfree(handle); - return ret; - } - handle->file = ret; return handle; } @@ -712,6 +699,50 @@ struct genlock_handle *genlock_get_handle_fd(int fd) } EXPORT_SYMBOL(genlock_get_handle_fd); +/* + * Get a file descriptor reference to a lock suitable for sharing with + * other processes + */ + +int genlock_get_fd_handle(struct genlock_handle *handle) +{ + int ret; + struct genlock *lock; + + if (IS_ERR_OR_NULL(handle)) + return -EINVAL; + + lock = handle->lock; + + if (IS_ERR(lock)) + return PTR_ERR(lock); + + if (!lock->file) { + GENLOCK_LOG_ERR("No file attached to the lock\n"); + return -EINVAL; + } + + ret = get_unused_fd_flags(0); + + if (ret < 0) + return ret; + + fd_install(ret, lock->file); + + /* + * Taking a reference for lock file. + * This is required as now we have two file descriptor + * pointing to same file. If one FD is closed, lock file + * will be closed. Taking this reference will make sure + * that file doesn't get close. This refrence will go + * when client will call close on this FD. + */ + fget(ret); + + return ret; +} +EXPORT_SYMBOL(genlock_get_fd_handle); + #ifdef CONFIG_GENLOCK_MISCDEVICE static long genlock_dev_ioctl(struct file *filep, unsigned int cmd, diff --git a/drivers/base/sync.c b/drivers/base/sync.c index 9bc0da50..abcd58b9 100644 --- a/drivers/base/sync.c +++ b/drivers/base/sync.c @@ -28,8 +28,14 @@ #include +#define CREATE_TRACE_POINTS +#include +#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(struct sync_fence *fence); static LIST_HEAD(sync_timeline_list_head); static DEFINE_SPINLOCK(sync_timeline_list_lock); @@ -50,6 +56,7 @@ struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, if (obj == NULL) return NULL; + kref_init(&obj->kref); obj->ops = ops; strlcpy(obj->name, name, sizeof(obj->name)); @@ -67,34 +74,33 @@ struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, } EXPORT_SYMBOL(sync_timeline_create); -static void sync_timeline_free(struct sync_timeline *obj) +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); + if (obj->ops->release_obj) + obj->ops->release_obj(obj); + kfree(obj); } void sync_timeline_destroy(struct sync_timeline *obj) { - unsigned long flags; - bool needs_freeing; - - spin_lock_irqsave(&obj->child_list_lock, flags); obj->destroyed = true; - needs_freeing = list_empty(&obj->child_list_head); - spin_unlock_irqrestore(&obj->child_list_lock, flags); + smp_wmb(); - if (needs_freeing) - sync_timeline_free(obj); - else - sync_timeline_signal(obj); + /* + * signal any children that their parent is going away. + */ + sync_timeline_signal(obj); + + kref_put(&obj->kref, sync_timeline_free); } EXPORT_SYMBOL(sync_timeline_destroy); @@ -113,7 +119,6 @@ static void sync_timeline_remove_pt(struct sync_pt *pt) { struct sync_timeline *obj = pt->parent; unsigned long flags; - bool needs_freeing; spin_lock_irqsave(&obj->active_list_lock, flags); if (!list_empty(&pt->active_list)) @@ -121,12 +126,10 @@ static void sync_timeline_remove_pt(struct sync_pt *pt) spin_unlock_irqrestore(&obj->active_list_lock, flags); spin_lock_irqsave(&obj->child_list_lock, flags); - list_del(&pt->child_list); - needs_freeing = obj->destroyed && list_empty(&obj->child_list_head); + if (!list_empty(&pt->child_list)) { + list_del_init(&pt->child_list); + } spin_unlock_irqrestore(&obj->child_list_lock, flags); - - if (needs_freeing) - sync_timeline_free(obj); } void sync_timeline_signal(struct sync_timeline *obj) @@ -135,24 +138,30 @@ void sync_timeline_signal(struct sync_timeline *obj) 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_move(pos, &signaled_pts); + 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, active_list); + 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); @@ -169,6 +178,7 @@ struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size) return NULL; INIT_LIST_HEAD(&pt->active_list); + kref_get(&parent->kref); sync_timeline_add_pt(parent, pt); return pt; @@ -182,6 +192,8 @@ void sync_pt_free(struct sync_pt *pt) sync_timeline_remove_pt(pt); + kref_put(&pt->parent->kref, sync_timeline_free); + kfree(pt); } EXPORT_SYMBOL(sync_pt_free); @@ -253,12 +265,13 @@ static struct sync_fence *sync_fence_alloc(const char *name) 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); - + trace_sync_alloc(fence,current->pid); init_waitqueue_head(&fence->wq); spin_lock_irqsave(&sync_fence_list_lock, flags); @@ -288,6 +301,12 @@ struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt) 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); @@ -306,12 +325,68 @@ static int sync_fence_copy_pts(struct sync_fence *dst, struct sync_fence *src) new_pt->fence = dst; list_add(&new_pt->pt_list, &dst->pt_list_head); - sync_pt_activate(new_pt); } 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; @@ -376,6 +451,7 @@ 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); @@ -386,11 +462,23 @@ struct sync_fence *sync_fence_merge(const char *name, if (err < 0) goto err; - err = sync_fence_copy_pts(fence, b); + err = sync_fence_merge_pts(fence, b); if (err < 0) goto err; - fence->status = sync_fence_get_status(fence); + 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: @@ -489,44 +577,91 @@ int sync_fence_cancel_async(struct sync_fence *fence, } 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; + 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) { + if (timeout > 0) { timeout = msecs_to_jiffies(timeout); err = wait_event_interruptible_timeout(fence->wq, - fence->status != 0, + sync_fence_check(fence), timeout); - } else { - err = wait_event_interruptible(fence->wq, fence->status != 0); + } 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) + if (fence->status < 0) { + pr_info("fence error %d on [%p]\n", fence->status, fence); + sync_dump(fence); return fence->status; + } - if (fence->status == 0) + if (fence->status == 0) { + if (timeout > 0) { + pr_info("fence timeout on [%p] after %dms\n", fence, + jiffies_to_msecs(timeout)); + sync_dump(fence); + } 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); + + trace_sync_free(fence, current->pid); + + 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; - sync_fence_free_pts(fence); - + /* + * 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); - kfree(fence); + /* + * 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; } @@ -537,6 +672,12 @@ static unsigned int sync_fence_poll(struct file *file, poll_table *wait) 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) @@ -547,7 +688,7 @@ static unsigned int sync_fence_poll(struct file *file, poll_table *wait) static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg) { - __u32 value; + __s32 value; if (copy_from_user(&value, (void __user *)arg, sizeof(value))) return -EFAULT; @@ -562,8 +703,13 @@ static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg) struct sync_fence *fence2, *fence3; struct sync_merge_data data; - if (copy_from_user(&data, (void __user *)arg, sizeof(data))) - return -EFAULT; + 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) { @@ -720,7 +866,17 @@ static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence) seq_printf(s, "@%ld.%06ld", tv.tv_sec, tv.tv_usec); } - if (pt->parent->ops->print_pt) { + 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); } @@ -735,7 +891,11 @@ static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj) seq_printf(s, "%s %s", obj->name, obj->ops->driver_name); - if (obj->ops->print_obj) { + 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); } @@ -756,7 +916,8 @@ static void sync_print_fence(struct seq_file *s, struct sync_fence *fence) struct list_head *pos; unsigned long flags; - seq_printf(s, "%s: %s\n", fence->name, sync_status_str(fence->status)); + 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 = @@ -824,7 +985,36 @@ 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]; +static void sync_dump(struct sync_fence *fence) +{ + struct seq_file s = { + .buf = sync_dump_buf, + .size = sizeof(sync_dump_buf) - 1, + }; + int i; + + seq_printf(&s, "fence:\n--------------\n"); + sync_print_fence(&s, fence); + seq_printf(&s, "\n"); + + 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(struct sync_fence *fence) +{ +} #endif diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index a7964071..e93e2ded 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -118,6 +118,8 @@ source "drivers/block/paride/Kconfig" source "drivers/block/mtip32xx/Kconfig" +source "drivers/block/zram/Kconfig" + config BLK_CPQ_DA tristate "Compaq SMART2 support" depends on PCI && VIRT_TO_BUS diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 5b795059..4fd9bdf5 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -42,4 +42,6 @@ obj-$(CONFIG_BLK_DEV_DRBD) += drbd/ obj-$(CONFIG_BLK_DEV_RBD) += rbd.o obj-$(CONFIG_BLK_DEV_PCIESSD_MTIP32XX) += mtip32xx/ +obj-$(CONFIG_ZRAM) += zram/ + swim_mod-y := swim.o swim_asm.o diff --git a/drivers/staging/zram/Kconfig b/drivers/block/zram/Kconfig similarity index 58% rename from drivers/staging/zram/Kconfig rename to drivers/block/zram/Kconfig index 5c12faa3..75f46cb7 100644 --- a/drivers/staging/zram/Kconfig +++ b/drivers/block/zram/Kconfig @@ -1,7 +1,9 @@ config ZRAM tristate "Compressed RAM block device support" depends on BLOCK && SYSFS && ZSMALLOC - default n + select LZO_COMPRESS + select LZO_DECOMPRESS + default y help Creates virtual block devices called /dev/zramX (X = 0, 1, ...). Pages written to these disks are compressed and stored in memory @@ -12,7 +14,16 @@ config ZRAM disks and maybe many more. See zram.txt for more information. - Project home: + +config ZRAM_LZ4_COMPRESS + bool "Enable LZ4 algorithm support" + depends on ZRAM + select LZ4_COMPRESS + select LZ4_DECOMPRESS + default y + help + This option enables LZ4 compression algorithm support. Compression + algorithm can be changed using `comp_algorithm' device attribute. config ZRAM_DEBUG bool "Compressed RAM block device debug support" @@ -21,29 +32,3 @@ config ZRAM_DEBUG help This option adds additional debugging code to the compressed RAM block device driver. - -choice ZRAM_COMPRESS - prompt "compression method" - depends on ZRAM - default ZRAM_LZO - help - Select the compression method used by zram. - LZO is the default. Snappy compresses a bit worse (around ~2%) but - much (~2x) faster, at least on x86-64. - -config ZRAM_LZO - bool "LZO compression" - select LZO_COMPRESS - select LZO_DECOMPRESS - -#config ZRAM_LZ4 -# bool "Snappy compression" -# select LZ4_COMPRESS -# select LZ4_DECOMPRESS - -config ZRAM_SNAPPY - bool "Snappy compression" - select SNAPPY_COMPRESS - select SNAPPY_DECOMPRESS - -endchoice diff --git a/drivers/block/zram/Makefile b/drivers/block/zram/Makefile new file mode 100644 index 00000000..be0763ff --- /dev/null +++ b/drivers/block/zram/Makefile @@ -0,0 +1,5 @@ +zram-y := zcomp_lzo.o zcomp.o zram_drv.o + +zram-$(CONFIG_ZRAM_LZ4_COMPRESS) += zcomp_lz4.o + +obj-$(CONFIG_ZRAM) += zram.o diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c new file mode 100644 index 00000000..f1ff39a3 --- /dev/null +++ b/drivers/block/zram/zcomp.c @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + * + * 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 "zcomp.h" +#include "zcomp_lzo.h" +#ifdef CONFIG_ZRAM_LZ4_COMPRESS +#include "zcomp_lz4.h" +#endif + +/* + * single zcomp_strm backend + */ +struct zcomp_strm_single { + struct mutex strm_lock; + struct zcomp_strm *zstrm; +}; + +/* + * multi zcomp_strm backend + */ +struct zcomp_strm_multi { + /* protect strm list */ + spinlock_t strm_lock; + /* max possible number of zstrm streams */ + int max_strm; + /* number of available zstrm streams */ + int avail_strm; + /* list of available strms */ + struct list_head idle_strm; + wait_queue_head_t strm_wait; +}; + +static struct zcomp_backend *backends[] = { + &zcomp_lzo, +#ifdef CONFIG_ZRAM_LZ4_COMPRESS + &zcomp_lz4, +#endif + NULL +}; + +static struct zcomp_backend *find_backend(const char *compress) +{ + int i = 0; + while (backends[i]) { + if (sysfs_streq(compress, backends[i]->name)) + break; + i++; + } + return backends[i]; +} + +static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm) +{ + if (zstrm->private) + comp->backend->destroy(zstrm->private); + free_pages((unsigned long)zstrm->buffer, 1); + kfree(zstrm); +} + +/* + * allocate new zcomp_strm structure with ->private initialized by + * backend, return NULL on error + */ +static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp) +{ + struct zcomp_strm *zstrm = kmalloc(sizeof(*zstrm), GFP_KERNEL); + if (!zstrm) + return NULL; + + zstrm->private = comp->backend->create(); + /* + * allocate 2 pages. 1 for compressed data, plus 1 extra for the + * case when compressed size is larger than the original one + */ + zstrm->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); + if (!zstrm->private || !zstrm->buffer) { + zcomp_strm_free(comp, zstrm); + zstrm = NULL; + } + return zstrm; +} + +/* + * get idle zcomp_strm or wait until other process release + * (zcomp_strm_release()) one for us + */ +static struct zcomp_strm *zcomp_strm_multi_find(struct zcomp *comp) +{ + struct zcomp_strm_multi *zs = comp->stream; + struct zcomp_strm *zstrm; + + while (1) { + spin_lock(&zs->strm_lock); + if (!list_empty(&zs->idle_strm)) { + zstrm = list_entry(zs->idle_strm.next, + struct zcomp_strm, list); + list_del(&zstrm->list); + spin_unlock(&zs->strm_lock); + return zstrm; + } + /* zstrm streams limit reached, wait for idle stream */ + if (zs->avail_strm >= zs->max_strm) { + spin_unlock(&zs->strm_lock); + wait_event(zs->strm_wait, !list_empty(&zs->idle_strm)); + continue; + } + /* allocate new zstrm stream */ + zs->avail_strm++; + spin_unlock(&zs->strm_lock); + + zstrm = zcomp_strm_alloc(comp); + if (!zstrm) { + spin_lock(&zs->strm_lock); + zs->avail_strm--; + spin_unlock(&zs->strm_lock); + wait_event(zs->strm_wait, !list_empty(&zs->idle_strm)); + continue; + } + break; + } + return zstrm; +} + +/* add stream back to idle list and wake up waiter or free the stream */ +static void zcomp_strm_multi_release(struct zcomp *comp, struct zcomp_strm *zstrm) +{ + struct zcomp_strm_multi *zs = comp->stream; + + spin_lock(&zs->strm_lock); + if (zs->avail_strm <= zs->max_strm) { + list_add(&zstrm->list, &zs->idle_strm); + spin_unlock(&zs->strm_lock); + wake_up(&zs->strm_wait); + return; + } + + zs->avail_strm--; + spin_unlock(&zs->strm_lock); + zcomp_strm_free(comp, zstrm); +} + +/* change max_strm limit */ +static bool zcomp_strm_multi_set_max_streams(struct zcomp *comp, int num_strm) +{ + struct zcomp_strm_multi *zs = comp->stream; + struct zcomp_strm *zstrm; + + spin_lock(&zs->strm_lock); + zs->max_strm = num_strm; + /* + * if user has lowered the limit and there are idle streams, + * immediately free as much streams (and memory) as we can. + */ + while (zs->avail_strm > num_strm && !list_empty(&zs->idle_strm)) { + zstrm = list_entry(zs->idle_strm.next, + struct zcomp_strm, list); + list_del(&zstrm->list); + zcomp_strm_free(comp, zstrm); + zs->avail_strm--; + } + spin_unlock(&zs->strm_lock); + return true; +} + +static void zcomp_strm_multi_destroy(struct zcomp *comp) +{ + struct zcomp_strm_multi *zs = comp->stream; + struct zcomp_strm *zstrm; + + while (!list_empty(&zs->idle_strm)) { + zstrm = list_entry(zs->idle_strm.next, + struct zcomp_strm, list); + list_del(&zstrm->list); + zcomp_strm_free(comp, zstrm); + } + kfree(zs); +} + +static int zcomp_strm_multi_create(struct zcomp *comp, int max_strm) +{ + struct zcomp_strm *zstrm; + struct zcomp_strm_multi *zs; + + comp->destroy = zcomp_strm_multi_destroy; + comp->strm_find = zcomp_strm_multi_find; + comp->strm_release = zcomp_strm_multi_release; + comp->set_max_streams = zcomp_strm_multi_set_max_streams; + zs = kmalloc(sizeof(struct zcomp_strm_multi), GFP_KERNEL); + if (!zs) + return -ENOMEM; + + comp->stream = zs; + spin_lock_init(&zs->strm_lock); + INIT_LIST_HEAD(&zs->idle_strm); + init_waitqueue_head(&zs->strm_wait); + zs->max_strm = max_strm; + zs->avail_strm = 1; + + zstrm = zcomp_strm_alloc(comp); + if (!zstrm) { + kfree(zs); + return -ENOMEM; + } + list_add(&zstrm->list, &zs->idle_strm); + return 0; +} + +static struct zcomp_strm *zcomp_strm_single_find(struct zcomp *comp) +{ + struct zcomp_strm_single *zs = comp->stream; + mutex_lock(&zs->strm_lock); + return zs->zstrm; +} + +static void zcomp_strm_single_release(struct zcomp *comp, + struct zcomp_strm *zstrm) +{ + struct zcomp_strm_single *zs = comp->stream; + mutex_unlock(&zs->strm_lock); +} + +static bool zcomp_strm_single_set_max_streams(struct zcomp *comp, int num_strm) +{ + /* zcomp_strm_single support only max_comp_streams == 1 */ + return false; +} + +static void zcomp_strm_single_destroy(struct zcomp *comp) +{ + struct zcomp_strm_single *zs = comp->stream; + zcomp_strm_free(comp, zs->zstrm); + kfree(zs); +} + +static int zcomp_strm_single_create(struct zcomp *comp) +{ + struct zcomp_strm_single *zs; + + comp->destroy = zcomp_strm_single_destroy; + comp->strm_find = zcomp_strm_single_find; + comp->strm_release = zcomp_strm_single_release; + comp->set_max_streams = zcomp_strm_single_set_max_streams; + zs = kmalloc(sizeof(struct zcomp_strm_single), GFP_KERNEL); + if (!zs) + return -ENOMEM; + + comp->stream = zs; + mutex_init(&zs->strm_lock); + zs->zstrm = zcomp_strm_alloc(comp); + if (!zs->zstrm) { + kfree(zs); + return -ENOMEM; + } + return 0; +} + +/* show available compressors */ +ssize_t zcomp_available_show(const char *comp, char *buf) +{ + ssize_t sz = 0; + int i = 0; + + while (backends[i]) { + if (sysfs_streq(comp, backends[i]->name)) + sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, + "[%s] ", backends[i]->name); + else + sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, + "%s ", backends[i]->name); + i++; + } + sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); + return sz; +} + +bool zcomp_set_max_streams(struct zcomp *comp, int num_strm) +{ + return comp->set_max_streams(comp, num_strm); +} + +struct zcomp_strm *zcomp_strm_find(struct zcomp *comp) +{ + return comp->strm_find(comp); +} + +void zcomp_strm_release(struct zcomp *comp, struct zcomp_strm *zstrm) +{ + comp->strm_release(comp, zstrm); +} + +int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm, + const unsigned char *src, size_t *dst_len) +{ + return comp->backend->compress(src, zstrm->buffer, dst_len, + zstrm->private); +} + +int zcomp_decompress(struct zcomp *comp, const unsigned char *src, + size_t src_len, unsigned char *dst) +{ + return comp->backend->decompress(src, src_len, dst); +} + +void zcomp_destroy(struct zcomp *comp) +{ + comp->destroy(comp); + kfree(comp); +} + +/* + * search available compressors for requested algorithm. + * allocate new zcomp and initialize it. return compressing + * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL) + * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in + * case of allocation error. + */ +struct zcomp *zcomp_create(const char *compress, int max_strm) +{ + struct zcomp *comp; + struct zcomp_backend *backend; + + backend = find_backend(compress); + if (!backend) + return ERR_PTR(-EINVAL); + + comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL); + if (!comp) + return ERR_PTR(-ENOMEM); + + comp->backend = backend; + if (max_strm > 1) + zcomp_strm_multi_create(comp, max_strm); + else + zcomp_strm_single_create(comp); + if (!comp->stream) { + kfree(comp); + return ERR_PTR(-ENOMEM); + } + return comp; +} diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h new file mode 100644 index 00000000..c59d1fca --- /dev/null +++ b/drivers/block/zram/zcomp.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + * + * 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. + */ + +#ifndef _ZCOMP_H_ +#define _ZCOMP_H_ + +#include + +struct zcomp_strm { + /* compression/decompression buffer */ + void *buffer; + /* + * The private data of the compression stream, only compression + * stream backend can touch this (e.g. compression algorithm + * working memory) + */ + void *private; + /* used in multi stream backend, protected by backend strm_lock */ + struct list_head list; +}; + +/* static compression backend */ +struct zcomp_backend { + int (*compress)(const unsigned char *src, unsigned char *dst, + size_t *dst_len, void *private); + + int (*decompress)(const unsigned char *src, size_t src_len, + unsigned char *dst); + + void *(*create)(void); + void (*destroy)(void *private); + + const char *name; +}; + +/* dynamic per-device compression frontend */ +struct zcomp { + void *stream; + struct zcomp_backend *backend; + + struct zcomp_strm *(*strm_find)(struct zcomp *comp); + void (*strm_release)(struct zcomp *comp, struct zcomp_strm *zstrm); + bool (*set_max_streams)(struct zcomp *comp, int num_strm); + void (*destroy)(struct zcomp *comp); +}; + +ssize_t zcomp_available_show(const char *comp, char *buf); + +struct zcomp *zcomp_create(const char *comp, int max_strm); +void zcomp_destroy(struct zcomp *comp); + +struct zcomp_strm *zcomp_strm_find(struct zcomp *comp); +void zcomp_strm_release(struct zcomp *comp, struct zcomp_strm *zstrm); + +int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm, + const unsigned char *src, size_t *dst_len); + +int zcomp_decompress(struct zcomp *comp, const unsigned char *src, + size_t src_len, unsigned char *dst); + +bool zcomp_set_max_streams(struct zcomp *comp, int num_strm); +#endif /* _ZCOMP_H_ */ diff --git a/drivers/block/zram/zcomp_lz4.c b/drivers/block/zram/zcomp_lz4.c new file mode 100644 index 00000000..f2afb7e9 --- /dev/null +++ b/drivers/block/zram/zcomp_lz4.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + * + * 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 "zcomp_lz4.h" + +static void *zcomp_lz4_create(void) +{ + return kzalloc(LZ4_MEM_COMPRESS, GFP_KERNEL); +} + +static void zcomp_lz4_destroy(void *private) +{ + kfree(private); +} + +static int zcomp_lz4_compress(const unsigned char *src, unsigned char *dst, + size_t *dst_len, void *private) +{ + /* return : Success if return 0 */ + return lz4_compress(src, PAGE_SIZE, dst, dst_len, private); +} + +static int zcomp_lz4_decompress(const unsigned char *src, size_t src_len, + unsigned char *dst) +{ + size_t dst_len = PAGE_SIZE; + /* return : Success if return 0 */ + return lz4_decompress_unknownoutputsize(src, src_len, dst, &dst_len); +} + +struct zcomp_backend zcomp_lz4 = { + .compress = zcomp_lz4_compress, + .decompress = zcomp_lz4_decompress, + .create = zcomp_lz4_create, + .destroy = zcomp_lz4_destroy, + .name = "lz4", +}; diff --git a/drivers/block/zram/zcomp_lz4.h b/drivers/block/zram/zcomp_lz4.h new file mode 100644 index 00000000..60613fb2 --- /dev/null +++ b/drivers/block/zram/zcomp_lz4.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + * + * 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. + */ + +#ifndef _ZCOMP_LZ4_H_ +#define _ZCOMP_LZ4_H_ + +#include "zcomp.h" + +extern struct zcomp_backend zcomp_lz4; + +#endif /* _ZCOMP_LZ4_H_ */ diff --git a/drivers/block/zram/zcomp_lzo.c b/drivers/block/zram/zcomp_lzo.c new file mode 100644 index 00000000..da1bc47d --- /dev/null +++ b/drivers/block/zram/zcomp_lzo.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + * + * 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 "zcomp_lzo.h" + +static void *lzo_create(void) +{ + return kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); +} + +static void lzo_destroy(void *private) +{ + kfree(private); +} + +static int lzo_compress(const unsigned char *src, unsigned char *dst, + size_t *dst_len, void *private) +{ + int ret = lzo1x_1_compress(src, PAGE_SIZE, dst, dst_len, private); + return ret == LZO_E_OK ? 0 : ret; +} + +static int lzo_decompress(const unsigned char *src, size_t src_len, + unsigned char *dst) +{ + size_t dst_len = PAGE_SIZE; + int ret = lzo1x_decompress_safe(src, src_len, dst, &dst_len); + return ret == LZO_E_OK ? 0 : ret; +} + +struct zcomp_backend zcomp_lzo = { + .compress = lzo_compress, + .decompress = lzo_decompress, + .create = lzo_create, + .destroy = lzo_destroy, + .name = "lzo", +}; diff --git a/drivers/block/zram/zcomp_lzo.h b/drivers/block/zram/zcomp_lzo.h new file mode 100644 index 00000000..128c5807 --- /dev/null +++ b/drivers/block/zram/zcomp_lzo.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + * + * 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. + */ + +#ifndef _ZCOMP_LZO_H_ +#define _ZCOMP_LZO_H_ + +#include "zcomp.h" + +extern struct zcomp_backend zcomp_lzo; + +#endif /* _ZCOMP_LZO_H_ */ diff --git a/drivers/staging/zram/zram_drv.c b/drivers/block/zram/zram_drv.c similarity index 66% rename from drivers/staging/zram/zram_drv.c rename to drivers/block/zram/zram_drv.c index 02953802..e67160e3 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -2,6 +2,7 @@ * Compressed RAM block device * * Copyright (C) 2008, 2009, 2010 Nitin Gupta + * 2012, 2013 Minchan Kim * * This code is released using a dual license strategy: BSD/GPL * You can choose the licence that better fits your requirements. @@ -9,7 +10,6 @@ * Released under the terms of 3-clause BSD License * Released under the terms of GNU General Public License Version 2.0 * - * Project home: http://compcache.googlecode.com */ #define KMSG_COMPONENT "zram" @@ -31,57 +31,15 @@ #include #include #include +#include #include #include "zram_drv.h" -#if defined(CONFIG_ZRAM_LZO) -#include -#define WMSIZE LZO1X_MEM_COMPRESS -#define COMPRESS(s, sl, d, dl, wm) \ - lzo1x_1_compress(s, sl, d, dl, wm) -#define DECOMPRESS(s, sl, d, dl) \ - lzo1x_decompress_safe(s, sl, d, dl) -#elif defined(CONFIG_ZRAM_SNAPPY) -#include "../snappy/csnappy.h" /* if built in drivers/staging */ -#define WMSIZE_ORDER ((PAGE_SHIFT > 14) ? (15) : (PAGE_SHIFT+1)) -#define WMSIZE (1 << WMSIZE_ORDER) -static int -snappy_compress_( - const unsigned char *src, - size_t src_len, - unsigned char *dst, - size_t *dst_len, - void *workmem) -{ - const unsigned char *end = csnappy_compress_fragment( - src, (uint32_t)src_len, dst, workmem, WMSIZE_ORDER); - *dst_len = end - dst; - return 0; -} -static int -snappy_decompress_( - const unsigned char *src, - size_t src_len, - unsigned char *dst, - size_t *dst_len) -{ - uint32_t dst_len_ = (uint32_t)*dst_len; - int ret = csnappy_decompress_noheader(src, src_len, dst, &dst_len_); - *dst_len = (size_t)dst_len_; - return ret; -} -#define COMPRESS(s, sl, d, dl, wm) \ - snappy_compress_(s, sl, d, dl, wm) -#define DECOMPRESS(s, sl, d, dl) \ - snappy_decompress_(s, sl, d, dl) -#else -#error either CONFIG_ZRAM_LZO or CONFIG_ZRAM_SNAPPY must be defined -#endif - /* Globals */ static int zram_major; static struct zram *zram_devices; +static const char *default_compressor = "lzo"; /* * We don't need to see memory allocation errors more than once every 1 @@ -92,6 +50,22 @@ static struct zram *zram_devices; /* Module params (documentation at end) */ static unsigned int num_devices = 1; +#define ZRAM_ATTR_RO(name) \ +static ssize_t zram_attr_##name##_show(struct device *d, \ + struct device_attribute *attr, char *b) \ +{ \ + struct zram *zram = dev_to_zram(d); \ + return scnprintf(b, PAGE_SIZE, "%llu\n", \ + (u64)atomic64_read(&zram->stats.name)); \ +} \ +static struct device_attribute dev_attr_##name = \ + __ATTR(name, S_IRUGO, zram_attr_##name##_show, NULL); + +static inline int init_done(struct zram *zram) +{ + return zram->meta != NULL; +} + static inline struct zram *dev_to_zram(struct device *dev) { return (struct zram *)dev_to_disk(dev)->private_data; @@ -102,110 +76,146 @@ static ssize_t disksize_show(struct device *dev, { struct zram *zram = dev_to_zram(dev); - return sprintf(buf, "%llu\n", zram->disksize); + return scnprintf(buf, PAGE_SIZE, "%llu\n", zram->disksize); } static ssize_t initstate_show(struct device *dev, struct device_attribute *attr, char *buf) { + u32 val; struct zram *zram = dev_to_zram(dev); - return sprintf(buf, "%u\n", zram->init_done); -} - -static ssize_t num_reads_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); + down_read(&zram->init_lock); + val = init_done(zram); + up_read(&zram->init_lock); - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.num_reads)); + return scnprintf(buf, PAGE_SIZE, "%u\n", val); } -static ssize_t num_writes_show(struct device *dev, +static ssize_t orig_data_size_show(struct device *dev, struct device_attribute *attr, char *buf) { struct zram *zram = dev_to_zram(dev); - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.num_writes)); + return scnprintf(buf, PAGE_SIZE, "%llu\n", + (u64)(atomic64_read(&zram->stats.pages_stored)) << PAGE_SHIFT); } -static ssize_t invalid_io_show(struct device *dev, +static ssize_t mem_used_total_show(struct device *dev, struct device_attribute *attr, char *buf) { + u64 val = 0; struct zram *zram = dev_to_zram(dev); + struct zram_meta *meta = zram->meta; - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.invalid_io)); -} - -static ssize_t notify_free_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); + down_read(&zram->init_lock); + if (init_done(zram)) + val = zs_get_total_size_bytes(meta->mem_pool); + up_read(&zram->init_lock); - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.notify_free)); + return scnprintf(buf, PAGE_SIZE, "%llu\n", val); } -static ssize_t zero_pages_show(struct device *dev, +static ssize_t max_comp_streams_show(struct device *dev, struct device_attribute *attr, char *buf) { + int val; struct zram *zram = dev_to_zram(dev); - return sprintf(buf, "%u\n", zram->stats.pages_zero); + down_read(&zram->init_lock); + val = zram->max_comp_streams; + up_read(&zram->init_lock); + + return scnprintf(buf, PAGE_SIZE, "%d\n", val); } -static ssize_t orig_data_size_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t max_comp_streams_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) { + int num; struct zram *zram = dev_to_zram(dev); + int ret; - return sprintf(buf, "%llu\n", - (u64)(zram->stats.pages_stored) << PAGE_SHIFT); -} + ret = kstrtoint(buf, 0, &num); + if (ret < 0) + return ret; + if (num < 1) + return -EINVAL; -static ssize_t compr_data_size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); + down_write(&zram->init_lock); + if (init_done(zram)) { + if (!zcomp_set_max_streams(zram->comp, num)) { + pr_info("Cannot change max compression streams\n"); + ret = -EINVAL; + goto out; + } + } - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.compr_size)); + zram->max_comp_streams = num; + ret = len; +out: + up_write(&zram->init_lock); + return ret; } -static ssize_t mem_used_total_show(struct device *dev, +static ssize_t comp_algorithm_show(struct device *dev, struct device_attribute *attr, char *buf) { - u64 val = 0; + size_t sz; struct zram *zram = dev_to_zram(dev); - struct zram_meta *meta = zram->meta; down_read(&zram->init_lock); - if (zram->init_done) - val = zs_get_total_size_bytes(meta->mem_pool); + sz = zcomp_available_show(zram->compressor, buf); up_read(&zram->init_lock); - return sprintf(buf, "%llu\n", val); + return sz; } +static ssize_t comp_algorithm_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + down_write(&zram->init_lock); + if (init_done(zram)) { + up_write(&zram->init_lock); + pr_info("Can't change algorithm for initialized device\n"); + return -EBUSY; + } + strlcpy(zram->compressor, buf, sizeof(zram->compressor)); + up_write(&zram->init_lock); + return len; +} + +/* flag operations needs meta->tb_lock */ static int zram_test_flag(struct zram_meta *meta, u32 index, enum zram_pageflags flag) { - return meta->table[index].flags & BIT(flag); + return meta->table[index].value & BIT(flag); } static void zram_set_flag(struct zram_meta *meta, u32 index, enum zram_pageflags flag) { - meta->table[index].flags |= BIT(flag); + meta->table[index].value |= BIT(flag); } static void zram_clear_flag(struct zram_meta *meta, u32 index, enum zram_pageflags flag) { - meta->table[index].flags &= ~BIT(flag); + meta->table[index].value &= ~BIT(flag); +} + +static size_t zram_get_obj_size(struct zram_meta *meta, u32 index) +{ + return meta->table[index].value & (BIT(ZRAM_FLAG_SHIFT) - 1); +} + +static void zram_set_obj_size(struct zram_meta *meta, + u32 index, size_t size) +{ + unsigned long flags = meta->table[index].value >> ZRAM_FLAG_SHIFT; + + meta->table[index].value = (flags << ZRAM_FLAG_SHIFT) | size; } static inline int is_partial_io(struct bio_vec *bvec) @@ -240,8 +250,6 @@ static inline int valid_io_request(struct zram *zram, struct bio *bio) static void zram_meta_free(struct zram_meta *meta) { zs_destroy_pool(meta->mem_pool); - kfree(meta->compress_workmem); - free_pages((unsigned long)meta->compress_buffer, 1); vfree(meta->table); kfree(meta); } @@ -253,22 +261,11 @@ static struct zram_meta *zram_meta_alloc(u64 disksize) if (!meta) goto out; - meta->compress_workmem = kzalloc(WMSIZE, GFP_KERNEL); - if (!meta->compress_workmem) - goto free_meta; - - meta->compress_buffer = - (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); - if (!meta->compress_buffer) { - pr_err("Error allocating compressor buffer space\n"); - goto free_workmem; - } - num_pages = disksize >> PAGE_SHIFT; meta->table = vzalloc(num_pages * sizeof(*meta->table)); if (!meta->table) { pr_err("Error allocating zram address table\n"); - goto free_buffer; + goto free_meta; } meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM | @@ -282,10 +279,6 @@ static struct zram_meta *zram_meta_alloc(u64 disksize) free_table: vfree(meta->table); -free_buffer: - free_pages((unsigned long)meta->compress_buffer, 1); -free_workmem: - kfree(meta->compress_workmem); free_meta: kfree(meta); meta = NULL; @@ -330,11 +323,16 @@ static void handle_zero_page(struct bio_vec *bvec) flush_dcache_page(page); } + +/* + * To protect concurrent access to the same index entry, + * caller should hold this table index entry's bit_spinlock to + * indicate this index entry is accessing. + */ static void zram_free_page(struct zram *zram, size_t index) { struct zram_meta *meta = zram->meta; unsigned long handle = meta->table[index].handle; - u16 size = meta->table[index].size; if (unlikely(!handle)) { /* @@ -343,51 +341,50 @@ static void zram_free_page(struct zram *zram, size_t index) */ if (zram_test_flag(meta, index, ZRAM_ZERO)) { zram_clear_flag(meta, index, ZRAM_ZERO); - zram->stats.pages_zero--; + atomic64_dec(&zram->stats.zero_pages); } return; } - if (unlikely(size > max_zpage_size)) - zram->stats.bad_compress--; - zs_free(meta->mem_pool, handle); - if (size <= PAGE_SIZE / 2) - zram->stats.good_compress--; - - atomic64_sub(meta->table[index].size, &zram->stats.compr_size); - zram->stats.pages_stored--; + atomic64_sub(zram_get_obj_size(meta, index), + &zram->stats.compr_data_size); + atomic64_dec(&zram->stats.pages_stored); meta->table[index].handle = 0; - meta->table[index].size = 0; + zram_set_obj_size(meta, index, 0); } static int zram_decompress_page(struct zram *zram, char *mem, u32 index) { int ret = 0; - size_t clen = PAGE_SIZE; unsigned char *cmem; struct zram_meta *meta = zram->meta; - unsigned long handle = meta->table[index].handle; + unsigned long handle; + size_t size; + + bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); + handle = meta->table[index].handle; + size = zram_get_obj_size(meta, index); if (!handle || zram_test_flag(meta, index, ZRAM_ZERO)) { + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); clear_page(mem); return 0; } cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO); - if (meta->table[index].size == PAGE_SIZE) + if (size == PAGE_SIZE) copy_page(mem, cmem); else - ret = DECOMPRESS(cmem, meta->table[index].size, - mem, &clen); + ret = zcomp_decompress(zram->comp, cmem, size, mem); zs_unmap_object(meta->mem_pool, handle); + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); /* Should NEVER happen. Return bio error if it does. */ if (unlikely(ret)) { pr_err("Decompression failed! err=%d, page=%u\n", ret, index); - atomic64_inc(&zram->stats.failed_reads); return ret; } @@ -403,11 +400,14 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, struct zram_meta *meta = zram->meta; page = bvec->bv_page; + bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); if (unlikely(!meta->table[index].handle) || zram_test_flag(meta, index, ZRAM_ZERO)) { + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); handle_zero_page(bvec); return 0; } + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); if (is_partial_io(bvec)) /* Use a temporary buffer to decompress the page */ @@ -450,11 +450,11 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, struct page *page; unsigned char *user_mem, *cmem, *src, *uncmem = NULL; struct zram_meta *meta = zram->meta; + struct zcomp_strm *zstrm; + bool locked = false; static unsigned long zram_rs_time; page = bvec->bv_page; - src = meta->compress_buffer; - if (is_partial_io(bvec)) { /* * This is a partial IO. We need to read the full page @@ -470,6 +470,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, goto out; } + zstrm = zcomp_strm_find(zram->comp); + locked = true; user_mem = kmap_atomic(page); if (is_partial_io(bvec)) { @@ -484,35 +486,30 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, if (page_zero_filled(uncmem)) { kunmap_atomic(user_mem); /* Free memory associated with this sector now. */ + bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); zram_free_page(zram, index); - - zram->stats.pages_zero++; zram_set_flag(meta, index, ZRAM_ZERO); + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); + + atomic64_inc(&zram->stats.zero_pages); ret = 0; goto out; } - /* - * zram_slot_free_notify could miss free so that let's - * double check. - */ - if (unlikely(meta->table[index].handle || - zram_test_flag(meta, index, ZRAM_ZERO))) - zram_free_page(zram, index); - - COMPRESS(user_mem, PAGE_SIZE, src, &clen, - meta->compress_workmem); - + ret = zcomp_compress(zram->comp, zstrm, uncmem, &clen); if (!is_partial_io(bvec)) { kunmap_atomic(user_mem); user_mem = NULL; uncmem = NULL; } + if (unlikely(ret)) { + pr_err("Compression failed! err=%d\n", ret); + goto out; + } + src = zstrm->buffer; if (unlikely(clen > max_zpage_size)) { - zram->stats.bad_compress++; clen = PAGE_SIZE; - src = NULL; if (is_partial_io(bvec)) src = uncmem; } @@ -536,82 +533,106 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, memcpy(cmem, src, clen); } + zcomp_strm_release(zram->comp, zstrm); + locked = false; zs_unmap_object(meta->mem_pool, handle); /* * Free memory associated with this sector * before overwriting unused sectors. */ + bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); zram_free_page(zram, index); meta->table[index].handle = handle; - meta->table[index].size = clen; + zram_set_obj_size(meta, index, clen); + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); /* Update stats */ - atomic64_add(clen, &zram->stats.compr_size); - zram->stats.pages_stored++; - if (clen <= PAGE_SIZE / 2) - zram->stats.good_compress++; - + atomic64_add(clen, &zram->stats.compr_data_size); + atomic64_inc(&zram->stats.pages_stored); out: + if (locked) + zcomp_strm_release(zram->comp, zstrm); if (is_partial_io(bvec)) kfree(uncmem); - - if (ret) - atomic64_inc(&zram->stats.failed_writes); return ret; } -static void handle_pending_slot_free(struct zram *zram) -{ - struct zram_slot_free *free_rq; - - spin_lock(&zram->slot_free_lock); - while (zram->slot_free_rq) { - free_rq = zram->slot_free_rq; - zram->slot_free_rq = free_rq->next; - zram_free_page(zram, free_rq->index); - kfree(free_rq); - } - spin_unlock(&zram->slot_free_lock); -} - static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, - int offset, struct bio *bio, int rw) + int offset, struct bio *bio) { int ret; + int rw = bio_data_dir(bio); if (rw == READ) { - down_read(&zram->lock); - handle_pending_slot_free(zram); + atomic64_inc(&zram->stats.num_reads); ret = zram_bvec_read(zram, bvec, index, offset, bio); - up_read(&zram->lock); } else { - down_write(&zram->lock); - handle_pending_slot_free(zram); + atomic64_inc(&zram->stats.num_writes); ret = zram_bvec_write(zram, bvec, index, offset); - up_write(&zram->lock); + } + + if (unlikely(ret)) { + if (rw == READ) + atomic64_inc(&zram->stats.failed_reads); + else + atomic64_inc(&zram->stats.failed_writes); } return ret; } +/* + * zram_bio_discard - handler on discard request + * @index: physical block index in PAGE_SIZE units + * @offset: byte offset within physical block + */ +static void zram_bio_discard(struct zram *zram, u32 index, + int offset, struct bio *bio) +{ + size_t n = bio->bi_size; + struct zram_meta *meta = zram->meta; + + /* + * zram manages data in physical block size units. Because logical block + * size isn't identical with physical block size on some arch, we + * could get a discard request pointing to a specific offset within a + * certain physical block. Although we can handle this request by + * reading that physiclal block and decompressing and partially zeroing + * and re-compressing and then re-storing it, this isn't reasonable + * because our intent with a discard request is to save memory. So + * skipping this logical block is appropriate here. + */ + if (offset) { + if (n <= (PAGE_SIZE - offset)) + return; + + n -= (PAGE_SIZE - offset); + index++; + } + + while (n >= PAGE_SIZE) { + bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); + zram_free_page(zram, index); + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); + index++; + n -= PAGE_SIZE; + } +} + static void zram_reset_device(struct zram *zram, bool reset_capacity) { size_t index; struct zram_meta *meta; - flush_work(&zram->free_work); - down_write(&zram->init_lock); - if (!zram->init_done) { + if (!init_done(zram)) { up_write(&zram->init_lock); return; } meta = zram->meta; - zram->init_done = 0; - /* Free all pages that are still in this zram device */ for (index = 0; index < zram->disksize >> PAGE_SHIFT; index++) { unsigned long handle = meta->table[index].handle; @@ -621,6 +642,9 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity) zs_free(meta->mem_pool, handle); } + zcomp_destroy(zram->comp); + zram->max_comp_streams = 1; + zram_meta_free(zram->meta); zram->meta = NULL; /* Reset stats */ @@ -629,40 +653,26 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity) zram->disksize = 0; if (reset_capacity) set_capacity(zram->disk, 0); - up_write(&zram->init_lock); -} -static void zram_init_device(struct zram *zram, struct zram_meta *meta) -{ - if (zram->disksize > 2 * (totalram_pages << PAGE_SHIFT)) { - pr_info( - "There is little point creating a zram of greater than " - "twice the size of memory since we expect a 2:1 compression " - "ratio. Note that zram uses about 0.1%% of the size of " - "the disk when not in use so a huge zram is " - "wasteful.\n" - "\tMemory Size: %lu kB\n" - "\tSize you selected: %llu kB\n" - "Continuing anyway ...\n", - (totalram_pages << PAGE_SHIFT) >> 10, zram->disksize >> 10 - ); - } - - /* zram devices sort of resembles non-rotational disks */ - queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); - - zram->meta = meta; - zram->init_done = 1; + up_write(&zram->init_lock); - pr_debug("Initialization done!\n"); + /* + * Revalidate disk out of the init_lock to avoid lockdep splat. + * It's okay because disk's capacity is protected by init_lock + * so that revalidate_disk always sees up-to-date capacity. + */ + if (reset_capacity) + revalidate_disk(zram->disk); } static ssize_t disksize_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { u64 disksize; + struct zcomp *comp; struct zram_meta *meta; struct zram *zram = dev_to_zram(dev); + int err; disksize = memparse(buf, NULL); if (!disksize) @@ -670,20 +680,45 @@ static ssize_t disksize_store(struct device *dev, disksize = PAGE_ALIGN(disksize); meta = zram_meta_alloc(disksize); + if (!meta) + return -ENOMEM; + + comp = zcomp_create(zram->compressor, zram->max_comp_streams); + if (IS_ERR(comp)) { + pr_info("Cannot initialise %s compressing backend\n", + zram->compressor); + err = PTR_ERR(comp); + goto out_free_meta; + } + down_write(&zram->init_lock); - if (zram->init_done) { - up_write(&zram->init_lock); - zram_meta_free(meta); + if (init_done(zram)) { pr_info("Cannot change disksize for initialized device\n"); - return -EBUSY; + err = -EBUSY; + goto out_destroy_comp; } + zram->meta = meta; + zram->comp = comp; zram->disksize = disksize; set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); - zram_init_device(zram, meta); up_write(&zram->init_lock); + /* + * Revalidate disk out of the init_lock to avoid lockdep splat. + * It's okay because disk's capacity is protected by init_lock + * so that revalidate_disk always sees up-to-date capacity. + */ + revalidate_disk(zram->disk); + return len; + +out_destroy_comp: + up_write(&zram->init_lock); + zcomp_destroy(comp); +out_free_meta: + zram_meta_free(meta); + return err; } static ssize_t reset_store(struct device *dev, @@ -727,24 +762,21 @@ static ssize_t reset_store(struct device *dev, return ret; } -static void __zram_make_request(struct zram *zram, struct bio *bio, int rw) +static void __zram_make_request(struct zram *zram, struct bio *bio) { int i, offset; u32 index; struct bio_vec *bvec; - switch (rw) { - case READ: - atomic64_inc(&zram->stats.num_reads); - break; - case WRITE: - atomic64_inc(&zram->stats.num_writes); - break; - } - index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; offset = (bio->bi_sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; + if (unlikely(bio->bi_rw & REQ_DISCARD)) { + zram_bio_discard(zram, index, offset, bio); + bio_endio(bio, 0); + return; + } + bio_for_each_segment(bvec, bio, i) { int max_transfer_size = PAGE_SIZE - offset; @@ -759,16 +791,15 @@ static void __zram_make_request(struct zram *zram, struct bio *bio, int rw) bv.bv_len = max_transfer_size; bv.bv_offset = bvec->bv_offset; - if (zram_bvec_rw(zram, &bv, index, offset, bio, rw) < 0) + if (zram_bvec_rw(zram, &bv, index, offset, bio) < 0) goto out; bv.bv_len = bvec->bv_len - max_transfer_size; bv.bv_offset += max_transfer_size; - if (zram_bvec_rw(zram, &bv, index+1, 0, bio, rw) < 0) + if (zram_bvec_rw(zram, &bv, index + 1, 0, bio) < 0) goto out; } else - if (zram_bvec_rw(zram, bvec, index, offset, bio, rw) - < 0) + if (zram_bvec_rw(zram, bvec, index, offset, bio) < 0) goto out; update_position(&index, &offset, bvec); @@ -790,7 +821,7 @@ static void zram_make_request(struct request_queue *queue, struct bio *bio) struct zram *zram = queue->queuedata; down_read(&zram->init_lock); - if (unlikely(!zram->init_done)) + if (unlikely(!init_done(zram))) goto error; if (!valid_io_request(zram, bio)) { @@ -798,7 +829,7 @@ static void zram_make_request(struct request_queue *queue, struct bio *bio) goto error; } - __zram_make_request(zram, bio, bio_data_dir(bio)); + __zram_make_request(zram, bio); up_read(&zram->init_lock); return; @@ -808,40 +839,19 @@ static void zram_make_request(struct request_queue *queue, struct bio *bio) bio_io_error(bio); } -static void zram_slot_free(struct work_struct *work) -{ - struct zram *zram; - - zram = container_of(work, struct zram, free_work); - down_write(&zram->lock); - handle_pending_slot_free(zram); - up_write(&zram->lock); -} - -static void add_slot_free(struct zram *zram, struct zram_slot_free *free_rq) -{ - spin_lock(&zram->slot_free_lock); - free_rq->next = zram->slot_free_rq; - zram->slot_free_rq = free_rq; - spin_unlock(&zram->slot_free_lock); -} - static void zram_slot_free_notify(struct block_device *bdev, unsigned long index) { struct zram *zram; - struct zram_slot_free *free_rq; + struct zram_meta *meta; zram = bdev->bd_disk->private_data; - atomic64_inc(&zram->stats.notify_free); - - free_rq = kmalloc(sizeof(struct zram_slot_free), GFP_ATOMIC); - if (!free_rq) - return; + meta = zram->meta; - free_rq->index = index; - add_slot_free(zram, free_rq); - schedule_work(&zram->free_work); + bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); + zram_free_page(zram, index); + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); + atomic64_inc(&zram->stats.notify_free); } static const struct block_device_operations zram_devops = { @@ -853,14 +863,21 @@ static DEVICE_ATTR(disksize, S_IRUGO | S_IWUSR, disksize_show, disksize_store); static DEVICE_ATTR(initstate, S_IRUGO, initstate_show, NULL); static DEVICE_ATTR(reset, S_IWUSR, NULL, reset_store); -static DEVICE_ATTR(num_reads, S_IRUGO, num_reads_show, NULL); -static DEVICE_ATTR(num_writes, S_IRUGO, num_writes_show, NULL); -static DEVICE_ATTR(invalid_io, S_IRUGO, invalid_io_show, NULL); -static DEVICE_ATTR(notify_free, S_IRUGO, notify_free_show, NULL); -static DEVICE_ATTR(zero_pages, S_IRUGO, zero_pages_show, NULL); static DEVICE_ATTR(orig_data_size, S_IRUGO, orig_data_size_show, NULL); -static DEVICE_ATTR(compr_data_size, S_IRUGO, compr_data_size_show, NULL); static DEVICE_ATTR(mem_used_total, S_IRUGO, mem_used_total_show, NULL); +static DEVICE_ATTR(max_comp_streams, S_IRUGO | S_IWUSR, + max_comp_streams_show, max_comp_streams_store); +static DEVICE_ATTR(comp_algorithm, S_IRUGO | S_IWUSR, + comp_algorithm_show, comp_algorithm_store); + +ZRAM_ATTR_RO(num_reads); +ZRAM_ATTR_RO(num_writes); +ZRAM_ATTR_RO(failed_reads); +ZRAM_ATTR_RO(failed_writes); +ZRAM_ATTR_RO(invalid_io); +ZRAM_ATTR_RO(notify_free); +ZRAM_ATTR_RO(zero_pages); +ZRAM_ATTR_RO(compr_data_size); static struct attribute *zram_disk_attrs[] = { &dev_attr_disksize.attr, @@ -868,12 +885,16 @@ static struct attribute *zram_disk_attrs[] = { &dev_attr_reset.attr, &dev_attr_num_reads.attr, &dev_attr_num_writes.attr, + &dev_attr_failed_reads.attr, + &dev_attr_failed_writes.attr, &dev_attr_invalid_io.attr, &dev_attr_notify_free.attr, &dev_attr_zero_pages.attr, &dev_attr_orig_data_size.attr, &dev_attr_compr_data_size.attr, &dev_attr_mem_used_total.attr, + &dev_attr_max_comp_streams.attr, + &dev_attr_comp_algorithm.attr, NULL, }; @@ -885,13 +906,8 @@ static int create_device(struct zram *zram, int device_id) { int ret = -ENOMEM; - init_rwsem(&zram->lock); init_rwsem(&zram->init_lock); - INIT_WORK(&zram->free_work, zram_slot_free); - spin_lock_init(&zram->slot_free_lock); - zram->slot_free_rq = NULL; - zram->queue = blk_alloc_queue(GFP_KERNEL); if (!zram->queue) { pr_err("Error allocating disk queue for device %d\n", @@ -919,7 +935,8 @@ static int create_device(struct zram *zram, int device_id) /* Actual capacity set using syfs (/sys/block/zram/disksize */ set_capacity(zram->disk, 0); - + /* zram devices sort of resembles non-rotational disks */ + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); /* * To ensure that we always get PAGE_SIZE aligned * and n*PAGE_SIZED sized I/O requests. @@ -929,6 +946,21 @@ static int create_device(struct zram *zram, int device_id) ZRAM_LOGICAL_BLOCK_SIZE); blk_queue_io_min(zram->disk->queue, PAGE_SIZE); blk_queue_io_opt(zram->disk->queue, PAGE_SIZE); + zram->disk->queue->limits.discard_granularity = PAGE_SIZE; + zram->disk->queue->limits.max_discard_sectors = UINT_MAX; + /* + * zram_bio_discard() will clear all logical blocks if logical block + * size is identical with physical block size(PAGE_SIZE). But if it is + * different, we will skip discarding some parts of logical blocks in + * the part of the request range which isn't aligned to physical block + * size. So we can't ensure that all discarded logical blocks are + * zeroed. + */ + if (ZRAM_LOGICAL_BLOCK_SIZE == PAGE_SIZE) + zram->disk->queue->limits.discard_zeroes_data = 1; + else + zram->disk->queue->limits.discard_zeroes_data = 0; + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, zram->disk->queue); add_disk(zram->disk); @@ -938,8 +970,9 @@ static int create_device(struct zram *zram, int device_id) pr_warn("Error creating sysfs group"); goto out_free_disk; } - - zram->init_done = 0; + strlcpy(zram->compressor, default_compressor, sizeof(zram->compressor)); + zram->meta = NULL; + zram->max_comp_streams = 1; return 0; out_free_disk: @@ -956,13 +989,10 @@ static void destroy_device(struct zram *zram) sysfs_remove_group(&disk_to_dev(zram->disk)->kobj, &zram_disk_attr_group); - if (zram->disk) { - del_gendisk(zram->disk); - put_disk(zram->disk); - } + del_gendisk(zram->disk); + put_disk(zram->disk); - if (zram->queue) - blk_cleanup_queue(zram->queue); + blk_cleanup_queue(zram->queue); } static int __init zram_init(void) diff --git a/drivers/staging/zram/zram_drv.h b/drivers/block/zram/zram_drv.h similarity index 61% rename from drivers/staging/zram/zram_drv.h rename to drivers/block/zram/zram_drv.h index 78464562..b3b68acc 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -2,6 +2,7 @@ * Compressed RAM block device * * Copyright (C) 2008, 2009, 2010 Nitin Gupta + * 2012, 2013 Minchan Kim * * This code is released using a dual license strategy: BSD/GPL * You can choose the licence that better fits your requirements. @@ -9,16 +10,15 @@ * Released under the terms of 3-clause BSD License * Released under the terms of GNU General Public License Version 2.0 * - * Project home: http://compcache.googlecode.com */ #ifndef _ZRAM_DRV_H_ #define _ZRAM_DRV_H_ #include -#include +#include -#include "../zsmalloc/zsmalloc.h" +#include "zcomp.h" /* * Some arbitrary value. This is just to catch @@ -43,7 +43,6 @@ static const size_t max_zpage_size = PAGE_SIZE / 10 * 9; /*-- End of configurable params */ #define SECTOR_SHIFT 9 -#define SECTOR_SIZE (1 << SECTOR_SHIFT) #define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) #define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT) #define ZRAM_LOGICAL_BLOCK_SHIFT 12 @@ -51,10 +50,24 @@ static const size_t max_zpage_size = PAGE_SIZE / 10 * 9; #define ZRAM_SECTOR_PER_LOGICAL_BLOCK \ (1 << (ZRAM_LOGICAL_BLOCK_SHIFT - SECTOR_SHIFT)) -/* Flags for zram pages (table[page_no].flags) */ + +/* + * The lower ZRAM_FLAG_SHIFT bits of table.value is for + * object size (excluding header), the higher bits is for + * zram_pageflags. + * + * zram is mainly used for memory efficiency so we want to keep memory + * footprint small so we can squeeze size and flags into a field. + * The lower ZRAM_FLAG_SHIFT bits is for object size (excluding header), + * the higher bits is for zram_pageflags. + */ +#define ZRAM_FLAG_SHIFT 24 + +/* Flags for zram pages (table[page_no].value) */ enum zram_pageflags { /* Page consists entirely of zeros */ - ZRAM_ZERO, + ZRAM_ZERO = ZRAM_FLAG_SHIFT + 1, + ZRAM_ACCESS, /* page in now accessed */ __NR_ZRAM_PAGEFLAGS, }; @@ -62,55 +75,34 @@ enum zram_pageflags { /*-- Data structures */ /* Allocated for each disk page */ -struct table { +struct zram_table_entry { unsigned long handle; - u16 size; /* object size (excluding header) */ - u8 count; /* object ref count (not yet used) */ - u8 flags; -} __aligned(4); + unsigned long value; +}; -/* - * All 64bit fields should only be manipulated by 64bit atomic accessors. - * All modifications to 32bit counter should be protected by zram->lock. - */ struct zram_stats { - atomic64_t compr_size; /* compressed size of pages stored */ + atomic64_t compr_data_size; /* compressed size of pages stored */ atomic64_t num_reads; /* failed + successful */ atomic64_t num_writes; /* --do-- */ - atomic64_t failed_reads; /* should NEVER! happen */ + atomic64_t failed_reads; /* can happen when memory is too low */ atomic64_t failed_writes; /* can happen when memory is too low */ atomic64_t invalid_io; /* non-page-aligned I/O requests */ atomic64_t notify_free; /* no. of swap slot free notifications */ - u32 pages_zero; /* no. of zero filled pages */ - u32 pages_stored; /* no. of pages currently stored */ - u32 good_compress; /* % of pages with compression ratio<=50% */ - u32 bad_compress; /* % of pages with compression ratio>=75% */ + atomic64_t zero_pages; /* no. of zero filled pages */ + atomic64_t pages_stored; /* no. of pages currently stored */ }; struct zram_meta { - void *compress_workmem; - void *compress_buffer; - struct table *table; + struct zram_table_entry *table; struct zs_pool *mem_pool; }; -struct zram_slot_free { - unsigned long index; - struct zram_slot_free *next; -}; - struct zram { struct zram_meta *meta; - struct rw_semaphore lock; /* protect compression buffers, table, - * 32bit stat counters against concurrent - * notifications, reads and writes */ - - struct work_struct free_work; /* handle pending free request */ - struct zram_slot_free *slot_free_rq; /* list head of free request */ - struct request_queue *queue; struct gendisk *disk; - int init_done; + struct zcomp *comp; + /* Prevent concurrent execution of device init, reset and R/W request */ struct rw_semaphore init_lock; /* @@ -118,9 +110,8 @@ struct zram { * we can store in a disk. */ u64 disksize; /* bytes */ - spinlock_t slot_free_lock; - + int max_comp_streams; struct zram_stats stats; + char compressor[10]; }; #endif - diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index ea1c27a3..fcf49012 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -266,4 +266,11 @@ config BT_WILINK Say Y here to compile support for Texas Instrument's WiLink7 driver into the kernel or say M to compile it as module. + +config HUAWEI_BT_WCN2243 + bool "Qualcomm WCN2243 Bluetooth" + default y + help + Select Y to use Qualcomm's BlueZ stack. Used by WCN2243. Not used in Huawei devices. + endmenu diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 497abe16..43447a2a 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -61,57 +61,17 @@ static struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x0CF3, 0x3000) }, /* Atheros AR3011 with sflash firmware*/ - { USB_DEVICE(0x0489, 0xE027) }, - { USB_DEVICE(0x0489, 0xE03D) }, - { USB_DEVICE(0x04F2, 0xAFF1) }, - { USB_DEVICE(0x0930, 0x0215) }, { USB_DEVICE(0x0CF3, 0x3002) }, - { USB_DEVICE(0x0CF3, 0xE019) }, - { USB_DEVICE(0x13d3, 0x3304) }, /* Atheros AR9285 Malbec with sflash firmware */ { USB_DEVICE(0x03F0, 0x311D) }, /* Atheros AR3012 with sflash firmware*/ - { USB_DEVICE(0x0489, 0xe04d) }, - { USB_DEVICE(0x0489, 0xe04e) }, - { USB_DEVICE(0x0489, 0xe057) }, - { USB_DEVICE(0x0489, 0xe056) }, - { USB_DEVICE(0x0489, 0xe078) }, - { USB_DEVICE(0x04c5, 0x1330) }, - { USB_DEVICE(0x04CA, 0x3004) }, - { USB_DEVICE(0x04CA, 0x3005) }, - { USB_DEVICE(0x04CA, 0x3006) }, - { USB_DEVICE(0x04CA, 0x3008) }, - { USB_DEVICE(0x04CA, 0x3010) }, - { USB_DEVICE(0x0930, 0x0219) }, - { USB_DEVICE(0x0930, 0x0227) }, - { USB_DEVICE(0x0b05, 0x17d0) }, - { USB_DEVICE(0x0CF3, 0x0036) }, { USB_DEVICE(0x0CF3, 0x3004) }, - { USB_DEVICE(0x0CF3, 0x3008) }, - { USB_DEVICE(0x0CF3, 0x311D) }, - { USB_DEVICE(0x0cf3, 0x3121) }, - { USB_DEVICE(0x0CF3, 0x817a) }, - { USB_DEVICE(0x0cf3, 0xe003) }, - { USB_DEVICE(0x0CF3, 0xE004) }, - { USB_DEVICE(0x0CF3, 0xE005) }, - { USB_DEVICE(0x13d3, 0x3362) }, - { USB_DEVICE(0x13d3, 0x3375) }, - { USB_DEVICE(0x13d3, 0x3393) }, - { USB_DEVICE(0x13d3, 0x3402) }, - { USB_DEVICE(0x13d3, 0x3408) }, - { USB_DEVICE(0x13d3, 0x3423) }, - { USB_DEVICE(0x13d3, 0x3432) }, - { USB_DEVICE(0x13d3, 0x3474) }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xE02C) }, - /* Atheros AR5BBU22 with sflash firmware */ - { USB_DEVICE(0x0489, 0xE036) }, - { USB_DEVICE(0x0489, 0xE03C) }, - { } /* Terminating entry */ }; @@ -123,41 +83,7 @@ MODULE_DEVICE_TABLE(usb, ath3k_table); static struct usb_device_id ath3k_blist_tbl[] = { /* Atheros AR3012 with sflash firmware*/ - { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 }, - - /* Atheros AR5BBU22 with sflash firmware */ - { USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 }, { } /* Terminating entry */ }; diff --git a/drivers/bluetooth/bluesleep.c b/drivers/bluetooth/bluesleep.c index 6dc98624..a7f14ff5 100644 --- a/drivers/bluetooth/bluesleep.c +++ b/drivers/bluetooth/bluesleep.c @@ -1,3 +1,4 @@ +#if (defined(CONFIG_HUAWEI_BT_BCM43XX) && defined(CONFIG_HUAWEI_KERNEL)) /* This program is free software; you can redistribute it and/or modify @@ -15,7 +16,7 @@ Copyright (C) 2006-2007 - Motorola - Copyright (c) 2008-2010, The Linux Foundation. All rights reserved. + Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. Date Author Comment ----------- -------------- -------------------------------- @@ -44,7 +45,7 @@ #include #include #include -#include +#include #include #include @@ -133,7 +134,837 @@ struct proc_dir_entry *bluetooth_dir, *sleep_dir; static void hsuart_power(int on) { - if (on) { + if(NULL == bsi->uport) + { + BT_ERR("bsi->uport is null"); + return; + } + if (on) { + msm_hs_request_clock_on(bsi->uport); + msm_hs_set_mctrl(bsi->uport, TIOCM_RTS); + } else { + msm_hs_set_mctrl(bsi->uport, 0); + msm_hs_request_clock_off(bsi->uport); + } +} + + +void bluesleep_uart_open(struct uart_port *uport) +{ + BT_DBG("bluesleep_uart_open"); + if(bsi->uport == NULL) { + BT_DBG("bluesleep_uart_open done"); + bsi->uport = uport; + } +} + + +void bluesleep_uart_close(struct uart_port *uport) +{ + BT_DBG("bluesleep_uart_close"); + if(bsi->uport == uport) { + BT_DBG("bluesleep_uart_close done"); + bsi->uport = NULL; + } +} + +/** + * @return 1 if the Host can go to sleep, 0 otherwise. + */ +static inline int bluesleep_can_sleep(void) +{ + /* check if MSM_WAKE_BT_GPIO and BT_WAKE_MSM_GPIO are both deasserted */ + return gpio_get_value(bsi->ext_wake) && + gpio_get_value(bsi->host_wake) && + (bsi->uport != NULL); +} + +void bluesleep_sleep_wakeup(void) +{ + if (test_bit(BT_ASLEEP, &flags)) { + BT_DBG("waking up..."); + /* Start the timer */ + mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL * HZ)); + gpio_set_value(bsi->ext_wake, 0); + clear_bit(BT_ASLEEP, &flags); + /*Activating UART */ + hsuart_power(1); + } + #ifdef CONFIG_HUAWEI_KERNEL + else + { + /*Tx idle, Rx busy, we must also make host_wake asserted, that is low + * 1 means bt chip can sleep, in bluesleep.c + */ + /* Here we depend on the status of MSM gpio, for stability */ + if(1 == gpio_get_value(bsi->ext_wake)) + { + printk(KERN_ERR "-bluesleep_sleep_wakeup wakeup bt chip\n"); + /*0 means wakup bt chip */ + gpio_set_value(bsi->ext_wake, 0); + mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL * HZ)); + } + } + #endif +} + +/** + * @brief@ main sleep work handling function which update the flags + * and activate and deactivate UART ,check FIFO. + */ +static void bluesleep_sleep_work(struct work_struct *work) +{ + if (bluesleep_can_sleep()) { + /* already asleep, this is an error case */ + if (test_bit(BT_ASLEEP, &flags)) { + BT_DBG("already asleep"); + return; + } + + if (msm_hs_tx_empty(bsi->uport)) { + BT_DBG("going to sleep..."); + set_bit(BT_ASLEEP, &flags); + /*Deactivating UART */ + hsuart_power(0); + } else { + + mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL * HZ)); + return; + } + } else if(gpio_get_value(bsi->ext_wake) && !test_bit(BT_ASLEEP, &flags)){ + mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL * HZ)); + gpio_set_value(bsi->ext_wake, 0); + } else { + bluesleep_sleep_wakeup(); + } +} + +/** + * A tasklet function that runs in tasklet context and reads the value + * of the HOST_WAKE GPIO pin and further defer the work. + * @param data Not used. + */ +static void bluesleep_hostwake_task(unsigned long data) +{ + BT_DBG("hostwake line change"); + + spin_lock(&rw_lock); + + if (gpio_get_value(bsi->host_wake)) + bluesleep_rx_busy(); + else + bluesleep_rx_idle(); + + spin_unlock(&rw_lock); +} + +/** + * Handles proper timer action when outgoing data is delivered to the + * HCI line discipline. Sets BT_TXDATA. + */ +void bluesleep_outgoing_data(void) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&rw_lock, irq_flags); + + /* log data passing by */ + set_bit(BT_TXDATA, &flags); + + /* if the tx side is sleeping... */ + if (gpio_get_value(bsi->ext_wake)) { + + BT_DBG("tx was sleeping"); + bluesleep_sleep_wakeup(); + } + + spin_unlock_irqrestore(&rw_lock, irq_flags); +} + +/** + * Handles HCI device events. + * @param this Not used. + * @param event The event that occurred. + * @param data The HCI device associated with the event. + * @return NOTIFY_DONE. + */ +static int bluesleep_hci_event(struct notifier_block *this, + unsigned long event, void *data) +{ + struct hci_dev *hdev = (struct hci_dev *) data; + struct hci_uart *hu; + struct uart_state *state; + + if (!hdev) + return NOTIFY_DONE; + + switch (event) { + case HCI_DEV_REG: + if (!bluesleep_hdev) { + bluesleep_hdev = hdev; + hu = (struct hci_uart *) hdev->driver_data; + state = (struct uart_state *) hu->tty->driver_data; + bsi->uport = state->uart_port; + } + break; + case HCI_DEV_UNREG: + bluesleep_hdev = NULL; + bsi->uport = NULL; + break; + case HCI_DEV_WRITE: + bluesleep_outgoing_data(); + break; + } + + return NOTIFY_DONE; +} + +/** + * Handles transmission timer expiration. + * @param data Not used. + */ +static void bluesleep_tx_timer_expire(unsigned long data) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&rw_lock, irq_flags); + + BT_DBG("Tx timer expired"); + + /* were we silent during the last timeout? */ + if (!test_bit(BT_TXDATA, &flags)) { + BT_DBG("Tx has been idle"); + gpio_set_value(bsi->ext_wake, 1); + bluesleep_tx_idle(); + } else { + BT_DBG("Tx data during last period"); + mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL*HZ)); + } + + /* clear the incoming data flag */ + clear_bit(BT_TXDATA, &flags); + + spin_unlock_irqrestore(&rw_lock, irq_flags); +} + +/** + * Schedules a tasklet to run when receiving an interrupt on the + * HOST_WAKE GPIO pin. + * @param irq Not used. + * @param dev_id Not used. + */ +static irqreturn_t bluesleep_hostwake_isr(int irq, void *dev_id) +{ + /* schedule a tasklet to handle the change in the host wake line */ + tasklet_schedule(&hostwake_task); + return IRQ_HANDLED; +} + +/** + * Starts the Sleep-Mode Protocol on the Host. + * @return On success, 0. On error, -1, and errno is set + * appropriately. + */ +static int bluesleep_start(void) +{ + int retval; + unsigned long irq_flags; + + spin_lock_irqsave(&rw_lock, irq_flags); + + if (test_bit(BT_PROTO, &flags)) { + spin_unlock_irqrestore(&rw_lock, irq_flags); + return 0; + } + + spin_unlock_irqrestore(&rw_lock, irq_flags); + + if (!atomic_dec_and_test(&open_count)) { + atomic_inc(&open_count); + return -EBUSY; + } + + /* start the timer */ + + mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL*HZ)); + + /* assert BT_WAKE */ + gpio_set_value(bsi->ext_wake, 0); +/* add RISING condition to invoke MSM7230 to update the SLEEP FLAG */ +#ifndef CONFIG_HUAWEI_KERNEL + retval = request_irq(bsi->host_wake_irq, bluesleep_hostwake_isr, + IRQF_DISABLED | IRQF_TRIGGER_FALLING, + "bluetooth hostwake", NULL); +#else + retval = request_irq(bsi->host_wake_irq, bluesleep_hostwake_isr, + IRQF_DISABLED | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "bluetooth hostwake", NULL); +#endif + if (retval < 0) { + BT_ERR("Couldn't acquire BT_HOST_WAKE IRQ"); + goto fail; + } + + retval = enable_irq_wake(bsi->host_wake_irq); + if (retval < 0) { + BT_ERR("Couldn't enable BT_HOST_WAKE as wakeup interrupt"); + free_irq(bsi->host_wake_irq, NULL); + goto fail; + } + + set_bit(BT_PROTO, &flags); + return 0; +fail: + del_timer(&tx_timer); + atomic_inc(&open_count); + + return retval; +} + +/** + * Stops the Sleep-Mode Protocol on the Host. + */ +static void bluesleep_stop(void) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&rw_lock, irq_flags); + + if (!test_bit(BT_PROTO, &flags)) { + spin_unlock_irqrestore(&rw_lock, irq_flags); + return; + } + + /* assert BT_WAKE */ + gpio_set_value(bsi->ext_wake, 0); + del_timer(&tx_timer); + clear_bit(BT_PROTO, &flags); + + if (test_bit(BT_ASLEEP, &flags)) { + clear_bit(BT_ASLEEP, &flags); + hsuart_power(1); + } + + atomic_inc(&open_count); + + spin_unlock_irqrestore(&rw_lock, irq_flags); + if (disable_irq_wake(bsi->host_wake_irq)) + BT_ERR("Couldn't disable hostwake IRQ wakeup mode\n"); + free_irq(bsi->host_wake_irq, NULL); +} +/** + * Read the BT_WAKE GPIO pin value via the proc interface. + * When this function returns, page will contain a 1 if the + * pin is high, 0 otherwise. + * @param page Buffer for writing data. + * @param start Not used. + * @param offset Not used. + * @param count Not used. + * @param eof Whether or not there is more data to be read. + * @param data Not used. + * @return The number of bytes written. + */ +static int bluepower_read_proc_btwake(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + *eof = 1; + return sprintf(page, "btwake:%u\n", gpio_get_value(bsi->ext_wake)); +} + +/** + * Write the BT_WAKE GPIO pin value via the proc interface. + * @param file Not used. + * @param buffer The buffer to read from. + * @param count The number of bytes to be written. + * @param data Not used. + * @return On success, the number of bytes written. On error, -1, and + * errno is set appropriately. + */ +static int bluepower_write_proc_btwake(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char *buf; + + if (count < 1) + return -EINVAL; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, buffer, count)) { + kfree(buf); + return -EFAULT; + } + + if (buf[0] == '0') { + gpio_set_value(bsi->ext_wake, 0); + } else if (buf[0] == '1') { + gpio_set_value(bsi->ext_wake, 1); + } else { + kfree(buf); + return -EINVAL; + } + + kfree(buf); + return count; +} + +/** + * Read the BT_HOST_WAKE GPIO pin value via the proc interface. + * When this function returns, page will contain a 1 if the pin + * is high, 0 otherwise. + * @param page Buffer for writing data. + * @param start Not used. + * @param offset Not used. + * @param count Not used. + * @param eof Whether or not there is more data to be read. + * @param data Not used. + * @return The number of bytes written. + */ +static int bluepower_read_proc_hostwake(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + *eof = 1; + return sprintf(page, "hostwake: %u \n", gpio_get_value(bsi->host_wake)); +} + + +/** + * Read the low-power status of the Host via the proc interface. + * When this function returns, page contains a 1 if the Host + * is asleep, 0 otherwise. + * @param page Buffer for writing data. + * @param start Not used. + * @param offset Not used. + * @param count Not used. + * @param eof Whether or not there is more data to be read. + * @param data Not used. + * @return The number of bytes written. + */ +static int bluesleep_read_proc_asleep(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + unsigned int asleep; + + asleep = test_bit(BT_ASLEEP, &flags) ? 1 : 0; + *eof = 1; + return sprintf(page, "asleep: %u\n", asleep); +} + +/** + * Read the low-power protocol being used by the Host via the proc interface. + * When this function returns, page will contain a 1 if the Host + * is using the Sleep Mode Protocol, 0 otherwise. + * @param page Buffer for writing data. + * @param start Not used. + * @param offset Not used. + * @param count Not used. + * @param eof Whether or not there is more data to be read. + * @param data Not used. + * @return The number of bytes written. + */ +static int bluesleep_read_proc_proto(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + unsigned int proto; + + proto = test_bit(BT_PROTO, &flags) ? 1 : 0; + *eof = 1; + return sprintf(page, "proto: %u\n", proto); +} + +/** + * Modify the low-power protocol used by the Host via the proc interface. + * @param file Not used. + * @param buffer The buffer to read from. + * @param count The number of bytes to be written. + * @param data Not used. + * @return On success, the number of bytes written. On error, -1, and + * errno is set appropriately. + */ +static int bluesleep_write_proc_proto(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char proto; + + if (count < 1) + return -EINVAL; + + if (copy_from_user(&proto, buffer, 1)) + return -EFAULT; + + if (proto == '0') + bluesleep_stop(); + else + bluesleep_start(); + + /* claim that we wrote everything */ + return count; +} + +static int __init bluesleep_probe(struct platform_device *pdev) +{ + int ret; + struct resource *res; + + bsi = kzalloc(sizeof(struct bluesleep_info), GFP_KERNEL); + if (!bsi) + return -ENOMEM; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "gpio_host_wake"); + if (!res) { + BT_ERR("couldn't find host_wake gpio\n"); + ret = -ENODEV; + goto free_bsi; + } + bsi->host_wake = res->start; + + ret = gpio_request(bsi->host_wake, "bt_host_wake"); + if (ret) + goto free_bsi; + ret = gpio_direction_input(bsi->host_wake); + if (ret) + goto free_bt_host_wake; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "gpio_ext_wake"); + if (!res) { + BT_ERR("couldn't find ext_wake gpio\n"); + ret = -ENODEV; + goto free_bt_host_wake; + } + bsi->ext_wake = res->start; + + ret = gpio_request(bsi->ext_wake, "bt_ext_wake"); + if (ret) + goto free_bt_host_wake; + /* assert bt wake */ + ret = gpio_direction_output(bsi->ext_wake, 0); + if (ret) + goto free_bt_ext_wake; + + bsi->host_wake_irq = platform_get_irq_byname(pdev, "host_wake"); + if ((int)bsi->host_wake_irq < 0) { + BT_ERR("couldn't find host_wake irq\n"); + ret = -ENODEV; + goto free_bt_ext_wake; + } + + + return 0; + +free_bt_ext_wake: + gpio_free(bsi->ext_wake); +free_bt_host_wake: + gpio_free(bsi->host_wake); +free_bsi: + kfree(bsi); + return ret; +} + +static int bluesleep_remove(struct platform_device *pdev) +{ + /* assert bt wake */ + gpio_set_value(bsi->ext_wake, 0); + if (test_bit(BT_PROTO, &flags)) { + if (disable_irq_wake(bsi->host_wake_irq)) + BT_ERR("Couldn't disable hostwake IRQ wakeup mode \n"); + free_irq(bsi->host_wake_irq, NULL); + del_timer(&tx_timer); + if (test_bit(BT_ASLEEP, &flags)) + hsuart_power(1); + } + + gpio_free(bsi->host_wake); + gpio_free(bsi->ext_wake); + kfree(bsi); + return 0; +} + +static struct platform_driver bluesleep_driver = { + .remove = bluesleep_remove, + .driver = { + .name = "bluesleep", + .owner = THIS_MODULE, + }, +}; +/** + * Initializes the module. + * @return On success, 0. On error, -1, and errno is set + * appropriately. + */ +static int __init bluesleep_init(void) +{ + int retval; + struct proc_dir_entry *ent; + + BT_INFO("MSM Sleep Mode Driver Ver %s", VERSION); + + retval = platform_driver_probe(&bluesleep_driver, bluesleep_probe); + if (retval) + return retval; + + bluesleep_hdev = NULL; + + bluetooth_dir = proc_mkdir("bluetooth", NULL); + if (bluetooth_dir == NULL) { + BT_ERR("Unable to create /proc/bluetooth directory"); + return -ENOMEM; + } + + sleep_dir = proc_mkdir("sleep", bluetooth_dir); + if (sleep_dir == NULL) { + BT_ERR("Unable to create /proc/%s directory", PROC_DIR); + return -ENOMEM; + } + + /* Creating read/write "btwake" entry */ + ent = create_proc_entry("btwake", 0, sleep_dir); + if (ent == NULL) { + BT_ERR("Unable to create /proc/%s/btwake entry", PROC_DIR); + retval = -ENOMEM; + goto fail; + } + ent->read_proc = bluepower_read_proc_btwake; + ent->write_proc = bluepower_write_proc_btwake; + + /* read only proc entries */ + if (create_proc_read_entry("hostwake", 0, sleep_dir, + bluepower_read_proc_hostwake, NULL) == NULL) { + BT_ERR("Unable to create /proc/%s/hostwake entry", PROC_DIR); + retval = -ENOMEM; + goto fail; + } + + /* read/write proc entries */ + ent = create_proc_entry("proto", 0, sleep_dir); + if (ent == NULL) { + BT_ERR("Unable to create /proc/%s/proto entry", PROC_DIR); + retval = -ENOMEM; + goto fail; + } + ent->read_proc = bluesleep_read_proc_proto; + ent->write_proc = bluesleep_write_proc_proto; + + /* read only proc entries */ + if (create_proc_read_entry("asleep", 0, + sleep_dir, bluesleep_read_proc_asleep, NULL) == NULL) { + BT_ERR("Unable to create /proc/%s/asleep entry", PROC_DIR); + retval = -ENOMEM; + goto fail; + } + + flags = 0; /* clear all status bits */ + + /* Initialize spinlock. */ + spin_lock_init(&rw_lock); + + /* Initialize timer */ + init_timer(&tx_timer); + tx_timer.function = bluesleep_tx_timer_expire; + tx_timer.data = 0; + + /* initialize host wake tasklet */ + tasklet_init(&hostwake_task, bluesleep_hostwake_task, 0); + + hci_register_notifier(&hci_event_nblock); + + return 0; + +fail: + remove_proc_entry("asleep", sleep_dir); + remove_proc_entry("proto", sleep_dir); + remove_proc_entry("hostwake", sleep_dir); + remove_proc_entry("btwake", sleep_dir); + remove_proc_entry("sleep", bluetooth_dir); + remove_proc_entry("bluetooth", 0); + return retval; +} + +/** + * Cleans up the module. + */ +static void __exit bluesleep_exit(void) +{ + hci_unregister_notifier(&hci_event_nblock); + platform_driver_unregister(&bluesleep_driver); + + remove_proc_entry("asleep", sleep_dir); + remove_proc_entry("proto", sleep_dir); + remove_proc_entry("hostwake", sleep_dir); + remove_proc_entry("btwake", sleep_dir); + remove_proc_entry("sleep", bluetooth_dir); + remove_proc_entry("bluetooth", 0); +} + +module_init(bluesleep_init); +module_exit(bluesleep_exit); + +MODULE_DESCRIPTION("Bluetooth Sleep Mode Driver ver %s " VERSION); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +#endif +#if (defined(CONFIG_HUAWEI_BT_WCN2243) || (!defined(CONFIG_HUAWEI_KERNEL))) +/* + + 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. + + 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 + + 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. + + + Copyright (C) 2006-2007 - Motorola + Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. + + Date Author Comment + ----------- -------------- -------------------------------- + 2006-Apr-28 Motorola The kernel module for running the Bluetooth(R) + Sleep-Mode Protocol from the Host side + 2006-Sep-08 Motorola Added workqueue for handling sleep work. + 2007-Jan-24 Motorola Added mbm_handle_ioi() call to ISR. + +*/ + +#include /* kernel module definitions */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* event notifications */ +#include "hci_uart.h" + +#define BT_SLEEP_DBG +#ifndef BT_SLEEP_DBG +#define BT_DBG(fmt, arg...) +#endif +/* + * Defines + */ + +#define VERSION "1.1" +#define PROC_DIR "bluetooth/sleep" + +#ifdef CONFIG_HUAWEI_KERNEL +#define BT_BLUEDROID_SUPPORT 1 +#else +#define BT_BLUEDROID_SUPPORT 1 +#endif + +struct bluesleep_info { + unsigned host_wake; + unsigned ext_wake; + unsigned host_wake_irq; + struct uart_port *uport; +}; + +/* work function */ +static void bluesleep_sleep_work(struct work_struct *work); + +/* work queue */ +DECLARE_DELAYED_WORK(sleep_workqueue, bluesleep_sleep_work); + +/* Macros for handling sleep work */ +#define bluesleep_rx_busy() schedule_delayed_work(&sleep_workqueue, 0) +#define bluesleep_tx_busy() schedule_delayed_work(&sleep_workqueue, 0) +#define bluesleep_rx_idle() schedule_delayed_work(&sleep_workqueue, 0) +#define bluesleep_tx_idle() schedule_delayed_work(&sleep_workqueue, 0) + +/* 1 second timeout */ +#define TX_TIMER_INTERVAL 1 + +/* state variable names and bit positions */ +#define BT_PROTO 0x01 +#define BT_TXDATA 0x02 +#define BT_ASLEEP 0x04 + +#if BT_BLUEDROID_SUPPORT +static bool has_lpm_enabled = false; +#else +/* global pointer to a single hci device. */ +static struct hci_dev *bluesleep_hdev; +#endif + +static struct platform_device *bluesleep_uart_dev; +static struct bluesleep_info *bsi; + +/* module usage */ +static atomic_t open_count = ATOMIC_INIT(1); + +/* + * Local function prototypes + */ + +#if !BT_BLUEDROID_SUPPORT +static int bluesleep_hci_event(struct notifier_block *this, + unsigned long event, void *data); +#endif + +/* + * Global variables + */ + +/** Global state flags */ +static unsigned long flags; + +/** Tasklet to respond to change in hostwake line */ +static struct tasklet_struct hostwake_task; + +/** Transmission timer */ +static struct timer_list tx_timer; + +/** Lock for state transitions */ +static spinlock_t rw_lock; + +#if !BT_BLUEDROID_SUPPORT +/** Notifier block for HCI events */ +struct notifier_block hci_event_nblock = { + .notifier_call = bluesleep_hci_event, +}; +#endif + +struct proc_dir_entry *bluetooth_dir, *sleep_dir; + +/* + * Local functions + */ + +static void hsuart_power(int on) +{ + if(NULL == bsi->uport) + { + BT_ERR("bsi->uport is null"); + return; + } + if (on) { msm_hs_request_clock_on(bsi->uport); msm_hs_set_mctrl(bsi->uport, TIOCM_RTS); } else { @@ -165,6 +996,22 @@ void bluesleep_sleep_wakeup(void) /*Activating UART */ hsuart_power(1); } + #if defined(CONFIG_HUAWEI_KERNEL) || defined(CONFIG_WERITOS_KERNEL) + else + { + /*Tx idle, Rx busy, we must also make host_wake asserted, that is low + * 1 means bt chip can sleep, in bluesleep.c + */ + /* Here we depend on the status of MSM gpio, for stability */ + if(1 == gpio_get_value(bsi->ext_wake)) + { + printk(KERN_ERR "-bluesleep_sleep_wakeup wakeup bt chip\n"); + /*0 means wakup bt chip */ + gpio_set_value(bsi->ext_wake, 0); + mod_timer(&tx_timer, jiffies + (TX_TIMER_INTERVAL * HZ)); + } + } + #endif } /** @@ -237,6 +1084,7 @@ static void bluesleep_outgoing_data(void) spin_unlock_irqrestore(&rw_lock, irq_flags); } +#if !BT_BLUEDROID_SUPPORT /** * Handles HCI device events. * @param this Not used. @@ -274,6 +1122,7 @@ static int bluesleep_hci_event(struct notifier_block *this, return NOTIFY_DONE; } +#endif /** * Handles transmission timer expiration. @@ -346,9 +1195,16 @@ static int bluesleep_start(void) /* assert BT_WAKE */ gpio_set_value(bsi->ext_wake, 0); +/* add RISING condition to invoke MSM7230 to update the SLEEP FLAG */ +#ifndef CONFIG_HUAWEI_KERNEL + retval = request_irq(bsi->host_wake_irq, bluesleep_hostwake_isr, + IRQF_DISABLED | IRQF_TRIGGER_FALLING, + "bluetooth hostwake", NULL); +#else retval = request_irq(bsi->host_wake_irq, bluesleep_hostwake_isr, - IRQF_DISABLED | IRQF_TRIGGER_FALLING, + IRQF_DISABLED | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "bluetooth hostwake", NULL); +#endif if (retval < 0) { BT_ERR("Couldn't acquire BT_HOST_WAKE IRQ"); goto fail; @@ -401,6 +1257,80 @@ static void bluesleep_stop(void) BT_ERR("Couldn't disable hostwake IRQ wakeup mode\n"); free_irq(bsi->host_wake_irq, NULL); } + +#if BT_BLUEDROID_SUPPORT +static struct uart_port *bluesleep_get_uart_port(void) +{ + struct uart_port *uport = NULL; + if (bluesleep_uart_dev) + uport = (struct uart_port *)platform_get_drvdata(bluesleep_uart_dev); + + return uport; +} + +static int bluesleep_read_proc_lpm(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + *eof = 1; + return sprintf(page, "unsupported to read\n"); +} + +static int bluesleep_write_proc_lpm(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char b; + + if (count < 1) + return -EINVAL; + + if (copy_from_user(&b, buffer, 1)) + return -EFAULT; + + if (b == '0') { + /* HCI_DEV_UNREG */ + bluesleep_stop(); + has_lpm_enabled = false; + bsi->uport = NULL; + } else { + /* HCI_DEV_REG */ + if (!has_lpm_enabled) { + has_lpm_enabled = true; + bsi->uport = bluesleep_get_uart_port(); + /* if bluetooth started, start bluesleep*/ + bluesleep_start(); + } + } + + return count; +} + +static int bluesleep_read_proc_btwrite(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + *eof = 1; + return sprintf(page, "unsupported to read\n"); +} + +static int bluesleep_write_proc_btwrite(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char b; + + if (count < 1) + return -EINVAL; + + if (copy_from_user(&b, buffer, 1)) + return -EFAULT; + + /* HCI_DEV_WRITE */ + if (b != '0') { + bluesleep_outgoing_data(); + } + + return count; +} +#endif + /** * Read the BT_WAKE GPIO pin value via the proc interface. * When this function returns, page will contain a 1 if the @@ -478,6 +1408,10 @@ static int bluepower_read_proc_hostwake(char *page, char **start, off_t offset, return sprintf(page, "hostwake: %u \n", gpio_get_value(bsi->host_wake)); } +void bluesleep_setup_uart_port(struct platform_device *uart_dev) +{ + bluesleep_uart_dev = uart_dev; +} /** * Read the low-power status of the Host via the proc interface. @@ -655,7 +1589,9 @@ static int __init bluesleep_init(void) if (retval) return retval; - bluesleep_hdev = NULL; +#if !BT_BLUEDROID_SUPPORT + bluesleep_hdev = NULL; +#endif bluetooth_dir = proc_mkdir("bluetooth", NULL); if (bluetooth_dir == NULL) { @@ -705,6 +1641,28 @@ static int __init bluesleep_init(void) goto fail; } +#if BT_BLUEDROID_SUPPORT + /* read/write proc entries */ + ent = create_proc_entry("lpm", 0, sleep_dir); + if (ent == NULL) { + BT_ERR("Unable to create /proc/%s/lpm entry", PROC_DIR); + retval = -ENOMEM; + goto fail; + } + ent->read_proc = bluesleep_read_proc_lpm; + ent->write_proc = bluesleep_write_proc_lpm; + + /* read/write proc entries */ + ent = create_proc_entry("btwrite", 0, sleep_dir); + if (ent == NULL) { + BT_ERR("Unable to create /proc/%s/btwrite entry", PROC_DIR); + retval = -ENOMEM; + goto fail; + } + ent->read_proc = bluesleep_read_proc_btwrite; + ent->write_proc = bluesleep_write_proc_btwrite; +#endif + flags = 0; /* clear all status bits */ /* Initialize spinlock. */ @@ -718,11 +1676,17 @@ static int __init bluesleep_init(void) /* initialize host wake tasklet */ tasklet_init(&hostwake_task, bluesleep_hostwake_task, 0); +#if !BT_BLUEDROID_SUPPORT hci_register_notifier(&hci_event_nblock); +#endif return 0; fail: +#if BT_BLUEDROID_SUPPORT + remove_proc_entry("btwrite", sleep_dir); + remove_proc_entry("lpm", sleep_dir); +#endif remove_proc_entry("asleep", sleep_dir); remove_proc_entry("proto", sleep_dir); remove_proc_entry("hostwake", sleep_dir); @@ -737,9 +1701,15 @@ static int __init bluesleep_init(void) */ static void __exit bluesleep_exit(void) { +#if !BT_BLUEDROID_SUPPORT hci_unregister_notifier(&hci_event_nblock); +#endif platform_driver_unregister(&bluesleep_driver); +#if BT_BLUEDROID_SUPPORT + remove_proc_entry("btwrite", sleep_dir); + remove_proc_entry("lpm", sleep_dir); +#endif remove_proc_entry("asleep", sleep_dir); remove_proc_entry("proto", sleep_dir); remove_proc_entry("hostwake", sleep_dir); @@ -755,3 +1725,4 @@ MODULE_DESCRIPTION("Bluetooth Sleep Mode Driver ver %s " VERSION); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif +#endif diff --git a/drivers/bluetooth/bluetooth-power.c b/drivers/bluetooth/bluetooth-power.c index 718df024..3bf49d1b 100644 --- a/drivers/bluetooth/bluetooth-power.c +++ b/drivers/bluetooth/bluetooth-power.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2010, The Linux Foundation. All rights reserved. +/* Copyright (c) 2009-2010, Code Aurora Forum. 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 and diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 650d7b30..66e5544b 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -55,7 +55,6 @@ static struct usb_driver btusb_driver; #define BTUSB_BROKEN_ISOC 0x20 #define BTUSB_WRONG_SCO_MTU 0x40 #define BTUSB_ATH3012 0x80 -#define BTUSB_INTEL_BOOT 0x200 static struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ @@ -64,9 +63,6 @@ static struct usb_device_id btusb_table[] = { /* Apple-specific (Broadcom) devices */ { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) }, - /* MediaTek MT76x0E */ - { USB_DEVICE(0x0e8d, 0x763f) }, - /* Broadcom SoftSailing reporting vendor specific */ { USB_DEVICE(0x05ac, 0x21e1) }, @@ -107,26 +103,6 @@ static struct usb_device_id btusb_table[] = { /* Canyon CN-BTU1 with HID interfaces */ { USB_DEVICE(0x0c10, 0x0000) }, - /* Broadcom BCM20702A0 */ - { USB_DEVICE(0x0489, 0xe042) }, - { USB_DEVICE(0x04ca, 0x2003) }, - { USB_DEVICE(0x0b05, 0x17b5) }, - { USB_DEVICE(0x0b05, 0x17cb) }, - { USB_DEVICE(0x413c, 0x8197) }, - - /* Foxconn - Hon Hai */ - { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) }, - - /* Broadcom devices with vendor specific id */ - { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) }, - - /* ASUSTek Computer - Broadcom based */ - { USB_VENDOR_AND_INTERFACE_INFO(0x0b05, 0xff, 0x01, 0x01) }, - - /* Intel Bluetooth USB Bootloader (RAM module) */ - { USB_DEVICE(0x8087, 0x0a5a), - .driver_info = BTUSB_INTEL_BOOT | BTUSB_BROKEN_ISOC }, - { } /* Terminating entry */ }; @@ -140,61 +116,21 @@ static struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE }, /* Atheros 3011 with sflash firmware */ - { USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE }, - { USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE }, - { USB_DEVICE(0x04f2, 0xaff1), .driver_info = BTUSB_IGNORE }, - { USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE }, { USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE }, - { USB_DEVICE(0x0cf3, 0xe019), .driver_info = BTUSB_IGNORE }, - { USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE }, /* Atheros AR9285 Malbec with sflash firmware */ { USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE }, /* Atheros 3012 with sflash firmware */ - { USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0x817a), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE }, - /* Atheros AR5BBU12 with sflash firmware */ - { USB_DEVICE(0x0489, 0xe036), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 }, - /* Broadcom BCM2035 */ - { USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 }, - { USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU }, { USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU }, + { USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU }, + { USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 }, /* Broadcom BCM2045 */ { USB_DEVICE(0x0a5c, 0x2039), .driver_info = BTUSB_WRONG_SCO_MTU }, @@ -1075,9 +1011,6 @@ static int btusb_probe(struct usb_interface *intf, hdev->owner = THIS_MODULE; - if (id->driver_info & BTUSB_INTEL_BOOT) - set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); - /* Interface numbers are hardcoded in the specification */ data->isoc = usb_ifnum_to_if(data->udev, 1); diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c index 65d27aff..e4c1edd2 100644 --- a/drivers/bluetooth/btwilink.c +++ b/drivers/bluetooth/btwilink.c @@ -22,8 +22,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#define DEBUG +#include #include +#include #include #include #include @@ -113,7 +114,7 @@ static long st_receive(void *priv_data, struct sk_buff *skb) err = hci_recv_frame(skb); if (err < 0) { BT_ERR("Unable to push skb to HCI core(%d)", err); - return err; + return 0; } lhst->hdev->stat.byte_rx += skb->len; diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index da4cfb00..2fcd8b38 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -2,7 +2,7 @@ * * Bluetooth HCI UART driver * - * Copyright (C) 2000-2001 Qualcomm Technologies, Inc. + * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2004-2005 Marcel Holtmann * diff --git a/drivers/bluetooth/hci_ibs.c b/drivers/bluetooth/hci_ibs.c index 84b253ba..33d210c1 100644 --- a/drivers/bluetooth/hci_ibs.c +++ b/drivers/bluetooth/hci_ibs.c @@ -5,7 +5,7 @@ * protocol extension to H4. * * Copyright (C) 2007 Texas Instruments, Inc. - * Copyright (c) 2010, 2012 The Linux Foundation. All rights reserved. + * Copyright (c) 2010, 2012 Code Aurora Forum. All rights reserved. * * Acknowledgements: * This file is based on hci_ll.c, which was... @@ -745,7 +745,7 @@ static inline int ibs_check_data_len(struct ibs_struct *ibs, int len) static int ibs_recv(struct hci_uart *hu, void *data, int count) { struct ibs_struct *ibs = hu->priv; - register char *ptr; + register unsigned char *ptr; struct hci_event_hdr *eh; struct hci_acl_hdr *ah; struct hci_sco_hdr *sh; diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index f094df3a..d1bc1e9b 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -4,7 +4,7 @@ * * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2004-2005 Marcel Holtmann - * Copyright (c) 2000-2001, 2010-2012, The Linux Foundation. All rights reserved. + * Copyright (c) 2000-2001, 2010-2012, Code Aurora Forum. All rights reserved. * * * This program is free software; you can redistribute it and/or modify @@ -121,6 +121,10 @@ static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu) int hci_uart_tx_wakeup(struct hci_uart *hu) { + struct tty_struct *tty = hu->tty; + struct hci_dev *hdev = hu->hdev; + struct sk_buff *skb; + if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) { set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); return 0; @@ -128,22 +132,6 @@ int hci_uart_tx_wakeup(struct hci_uart *hu) BT_DBG(""); - schedule_work(&hu->write_work); - - return 0; -} - -static void hci_uart_write_work(struct work_struct *work) -{ - struct hci_uart *hu = container_of(work, struct hci_uart, write_work); - struct tty_struct *tty = hu->tty; - struct hci_dev *hdev = hu->hdev; - struct sk_buff *skb; - - /* REVISIT: should we cope with bad skbs or ->write() returning - * and error value ? - */ - restart: clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); @@ -168,6 +156,7 @@ static void hci_uart_write_work(struct work_struct *work) goto restart; clear_bit(HCI_UART_SENDING, &hu->tx_state); + return 0; } /* ------- Interface to HCI layer ------ */ @@ -287,8 +276,6 @@ static int hci_uart_tty_open(struct tty_struct *tty) hu->tty = tty; tty->receive_room = 65536; - INIT_WORK(&hu->write_work, hci_uart_write_work); - spin_lock_init(&hu->rx_lock); tasklet_init(&hu->tty_wakeup_task, hci_uart_tty_wakeup_action, (unsigned long)hu); @@ -327,8 +314,6 @@ static void hci_uart_tty_close(struct tty_struct *tty) tasklet_kill(&hu->tty_wakeup_task); - cancel_work_sync(&hu->write_work); - if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) { hu->proto->close(hu); if (hdev) { @@ -604,16 +589,26 @@ static int __init hci_uart_init(void) #ifdef CONFIG_BT_HCIUART_BCSP bcsp_init(); #endif -#ifdef CONFIG_BT_HCIUART_LL +#if defined(CONFIG_BT_HCIUART_LL) && defined(CONFIG_HUAWEI_BT_BCM43XX) ll_init(); #endif + +/*default without Huawei modification*/ +#if defined(CONFIG_BT_HCIUART_LL) && (!defined(CONFIG_HUAWEI_KERNEL)) + ll_init(); +#endif #ifdef CONFIG_BT_HCIUART_ATH3K ath_init(); #endif -#ifdef CONFIG_BT_HCIUART_IBS +#if defined(CONFIG_BT_HCIUART_IBS) && defined(CONFIG_HUAWEI_BT_WCN2243) ibs_init(); #endif +/*default without Huawei modification*/ +#if defined(CONFIG_BT_HCIUART_IBS) && (!defined(CONFIG_HUAWEI_KERNEL)) + ibs_init(); +#endif + return 0; } @@ -627,13 +622,23 @@ static void __exit hci_uart_exit(void) #ifdef CONFIG_BT_HCIUART_BCSP bcsp_deinit(); #endif -#ifdef CONFIG_BT_HCIUART_LL +#if defined(CONFIG_BT_HCIUART_LL) && defined(CONFIG_HUAWEI_BT_BCM43XX) + ll_deinit(); +#endif + +/*default without Huawei modification*/ +#if defined(CONFIG_BT_HCIUART_LL) && (!defined(CONFIG_HUAWEI_KERNEL)) ll_deinit(); #endif #ifdef CONFIG_BT_HCIUART_ATH3K ath_deinit(); #endif -#ifdef CONFIG_BT_HCIUART_IBS +#if defined(CONFIG_BT_HCIUART_IBS) && defined(CONFIG_HUAWEI_BT_WCN2243) + ibs_deinit(); +#endif + +/*default without Huawei modification*/ +#if defined(CONFIG_BT_HCIUART_IBS) && (!defined(CONFIG_HUAWEI_KERNEL)) ibs_deinit(); #endif diff --git a/drivers/bluetooth/hci_smd.c b/drivers/bluetooth/hci_smd.c index c0708d7c..60305204 100644 --- a/drivers/bluetooth/hci_smd.c +++ b/drivers/bluetooth/hci_smd.c @@ -2,7 +2,7 @@ * HCI_SMD (HCI Shared Memory Driver) is Qualcomm's Shared memory driver * for the BT HCI protocol. * - * Copyright (c) 2000-2001, 2011-2012 The Linux Foundation. All rights reserved. + * Copyright (c) 2000-2001, 2011-2012 Code Aurora Forum. All rights reserved. * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2004-2006 Marcel Holtmann * diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 09f3497f..123fc24b 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -4,7 +4,7 @@ * * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2004-2005 Marcel Holtmann - * Copyright (c) 2000-2001, 2010, 2012 The Linux Foundation. All rights reserved. + * Copyright (c) 2000-2001, 2010, 2012 Code Aurora Forum. All rights reserved. * * * This program is free software; you can redistribute it and/or modify @@ -65,8 +65,6 @@ struct hci_uart { unsigned long flags; unsigned long hdev_flags; - struct work_struct write_work; - struct hci_uart_proto *proto; struct tasklet_struct tty_wakeup_task; void *priv; diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 0dd54ddf..67c180c2 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -2,7 +2,7 @@ * * Bluetooth virtual HCI driver * - * Copyright (C) 2000-2001 Qualcomm Technologies, Inc. + * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2004-2006 Marcel Holtmann * diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 2c4b546c..06dc9189 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -109,9 +109,61 @@ config CPU_FREQ_DEFAULT_GOV_INTERACTIVE loading your cpufreq low-level hardware driver, using the 'interactive' governor for latency-sensitive workloads. -config CPU_FREQ_DEFAULT_GOV_HOTPLUG - bool "hotplug" - select CPU_FREQ_GOV_HOTPLUG +config CPU_FREQ_DEFAULT_GOV_SMARTASS2 + bool "smartass2" + select CPU_FREQ_GOV_SMARTASS2 + help + Use the CPUFreq governor 'smartassV2' as default. + +config CPU_FREQ_DEFAULT_GOV_SMARTASSH3 + bool "SmartassH3" + select CPU_FREQ_GOV_SMARTASSH3 + help + Use the CPUFreq governor 'SmartassH3' as default. + +config CPU_FREQ_DEFAULT_GOV_INTELLIACTIVE + bool "intelliactive" + select CPU_FREQ_GOV_INTELLIACTIVE + help + Use the CPUFreq governor 'intelliactive' 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. + +config CPU_FREQ_DEFAULT_GOV_ADAPTIVE + bool "adaptive" + select CPU_FREQ_GOV_ADAPTIVE + ---help--- + Use the CPUFreq governor 'adaptive' 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 + 'adaptive' governor for latency-sensitive workloads and demanding + performance. + + +config CPU_FREQ_DEFAULT_GOV_INTELLIMM + bool "intelliminmax" + select CPU_FREQ_GOV_INTELLIMM + help + Use the CPUFreq governor 'intelliminmax' as default. This is + governor will use only 3 different frequencies, idle, UI and max + +config CPU_FREQ_DEFAULT_GOV_STOCKDEMAND + bool "stockdemand" + select CPU_FREQ_GOV_STOCKDEMAND + select CPU_FREQ_GOV_PERFORMANCE + help + Use the CPUFreq governor 'stockdemand' as default. This allows + you to get a full dynamic frequency capable system by simply + loading your cpufreq low-level hardware driver. + Be aware that not all cpufreq drivers support the ondemand + governor. If unsure have a look at the help section of the + driver. Fallback governor will be the performance governor. + This is default LG ondemand cpu gov. + +config CPU_FREQ_DEFAULT_GOV_YANKDEMAND + bool "yankdemand" + select CPU_FREQ_GOV_YANKDEMAND select CPU_FREQ_GOV_PERFORMANCE help Use the CPUFreq governor 'hotplug' as default. This allows @@ -121,6 +173,57 @@ config CPU_FREQ_DEFAULT_GOV_HOTPLUG 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_INTELLIDEMAND + bool "intellidemand" + select CPU_FREQ_GOV_INTELLIDEMAND + help + Use the CPUFreq governor 'intellidemand' as default. This is + based on Ondemand with browsing detection based on GPU loading + +config CPU_FREQ_DEFAULT_GOV_LIONHEART + bool "lionheart" + select CPU_FREQ_GOV_LIONHEART + help + Use the CPUFreq governor 'Lionheart' as default. + +config CPU_FREQ_DEFAULT_GOV_HYPER + bool "hyper" + select CPU_FREQ_GOV_HYPER + ---help--- + Use the CPUFreq governor 'hyper' 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 + 'hyper' governor for latency-sensitive workloads. + +config CPU_FREQ_DEFAULT_GOV_ONDEMANDX + bool "ondemandX" + select CPU_FREQ_GOV_ONDEMANDX + select CPU_FREQ_GOV_PERFORMANCE + help + Use the CPUFreq governor 'ondemand' as default. This allows + you to get a full dynamic frequency capable system by simply + loading your cpufreq low-level hardware driver. + Be aware that not all cpufreq drivers support the ondemand + governor. If unsure have a look at the help section of the + driver. Fallback governor will be the performance governor. + OndemandX has built in sleep profile, but not working Sysfs + interface. + +config CPU_FREQ_DEFAULT_GOV_INTERACTIVEX + bool "interactiveX" + select CPU_FREQ_GOV_INTERACTIVEX + help + Use the CPUFreq governor 'interactiveX' 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 + 'interactiveX' governor for latency-sensitive workloads. + +config CPU_FREQ_DEFAULT_GOV_ALUCARD + bool "alucard" + select CPU_FREQ_GOV_ALUCARD + help + Use the CPUFreq governor 'alucard' as default. + endchoice config CPU_FREQ_GOV_PERFORMANCE @@ -178,6 +281,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 @@ -201,10 +321,26 @@ config CPU_FREQ_GOV_CONSERVATIVE If in doubt, say N. -config CPU_FREQ_GOV_INTERACTIVE - tristate "'interactive' cpufreq policy governor" +config CPU_FREQ_GOV_SMARTASS2 + tristate "'smartassV2' cpufreq governor" + depends on CPU_FREQ help - 'interactive' - This driver adds a dynamic cpufreq policy governor + 'smartassV2' - a "smart" optimized governor! + + If in doubt, say N. + +config CPU_FREQ_GOV_SMARTASSH3 + tristate "'SmartassH3' cpufreq governor" + depends on CPU_FREQ + help + 'SmartassH3' - a "smartassV2 with tweaks by H3ROS" governor! + + If in doubt, say N. + +config CPU_FREQ_GOV_INTELLIACTIVE + tristate "'intelliactive' cpufreq policy governor" + help + 'intelliactive' - This driver adds a dynamic cpufreq policy governor designed for latency-sensitive workloads. This governor attempts to reduce the latency of clock @@ -218,26 +354,115 @@ config CPU_FREQ_GOV_INTERACTIVE If in doubt, say N. -config CPU_FREQ_GOV_HOTPLUG - tristate "'hotplug' cpufreq governor" - depends on CPU_FREQ && NO_HZ && HOTPLUG_CPU +config CPU_FREQ_GOV_ADAPTIVE + tristate "Adaptive" + ---help--- + 'adaptive' - This driver adds a dynamic cpufreq policy governor + designed for latency-sensitive workloads and also for demanding + performance. + + This governor attempts to reduce the latency of clock + increases so that the system is more responsive to + interactive workloads in loweset steady-state but to + to reduce power consumption in middle operation level level up + will be done in step by step to prohibit system from going to + max operation level. + + To compile this driver as a module, choose M here: the + module will be called cpufreq_adaptive. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + + +config CPU_FREQ_GOV_INTELLIMM + tristate "intelliminmax" help - 'hotplug' - this driver mimics the frequency scaling behavior - in 'ondemand', but with several key differences. First is - that frequency transitions use the CPUFreq table directly, - instead of incrementing in a percentage of the maximum - available frequency. Second 'hotplug' will offline auxillary - CPUs when the system is idle, and online those CPUs once the - system becomes busy again. This last feature is needed for - architectures which transition to low power states when only - the "master" CPU is online, or for thermally constrained - devices. - - If you don't have one of these architectures or devices, use - 'ondemand' instead. + To compile this driver as a module, choose M here: the + module will be called cpufreq_interactive. + + If in doubt, say N. + +config CPU_FREQ_GOV_STOCKDEMAND + tristate "'stockdemand' cpufreq policy governor" + select CPU_FREQ_TABLE + help + this is stock LG ondemand GOV. for users that like STOCK. + + +config CPU_FREQ_GOV_YANKDEMAND + tristate "'yankdemand' cpufreq policy governor" + select CPU_FREQ_TABLE + help + 'yankdemand' - This driver adds a dynamic cpufreq policy governor. + The governor is based on ondemand with just different default settings + and can be achieved using ondemand with a script. This is added for + simplification purposes for myself. + +config CPU_FREQ_GOV_INTELLIDEMAND + tristate "'intellidemand' cpufreq policy governor" + select CPU_FREQ_TABLE + help + 'intellidemand' - This driver adds a dynamic cpufreq policy governor. + The governor does a periodic polling and + changes frequency based on the CPU utilization. + The support for this governor depends on CPU capability to + do fast frequency switching (i.e, very low latency frequency + transitions). with browsing detection based on GPU loading + + To compile this driver as a module, choose M here: the + module will be called cpufreq_ondemand. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + +config CPU_FREQ_GOV_LIONHEART + tristate "'lionheart' cpufreq governor" + depends on CPU_FREQ + help + 'Lionheart' - A brave and agile conservative-based governor. + +config CPU_FREQ_GOV_HYPER + tristate "'hyper' cpufreq governor" + depends on CPU_FREQ + ---help--- + 'hyper' - A tweaked "ondemand" based smart and smooth optimized governor! + + If in doubt, say N. + +config CPU_FREQ_GOV_ONDEMANDX + tristate "'ondemandx' cpufreq policy governor" + select CPU_FREQ_TABLE + help + 'ondemandx' - This driver adds a dynamic cpufreq policy governor. + The governor does a periodic polling and + changes frequency based on the CPU utilization. + The support for this governor depends on CPU capability to + do fast frequency switching (i.e, very low latency frequency + transitions). + + To compile this driver as a module, choose M here: the + module will be called cpufreq_ondemand. + + For details, take a look at linux/Documentation/cpu-freq. If in doubt, say N. +config CPU_FREQ_GOV_INTERACTIVEX + tristate "'interactiveX' cpufreq policy governor" + help + 'interactiveX' - Modified version of interactive with sleep+wake code. + + If in doubt, say N. + +config CPU_FREQ_GOV_ALUCARD + tristate "'alucard' cpufreq governor" + default y + depends on CPU_FREQ + help + menu "x86 CPU frequency scaling drivers" depends on X86 source "drivers/cpufreq/Kconfig.x86" diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 0dd9c736..4258fecf 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -3,14 +3,28 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq.o # CPUfreq stats obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o -# CPUfreq governors +# CPUfreq governors +obj-$(CONFIG_CPU_FREQ_GOV_ALUCARD) += cpufreq_alucard.o obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE) += cpufreq_performance.o 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_ONDEMANDX) += cpufreq_ondemandx.o +obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o +obj-$(CONFIG_CPU_FREQ_GOV_LIONHEART) += cpufreq_lionheart.o +obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVEX) += cpufreq_interactivex.o +obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o -obj-$(CONFIG_CPU_FREQ_GOV_HOTPLUG) += cpufreq_hotplug.o +obj-$(CONFIG_CPU_FREQ_GOV_SMARTASS2) += cpufreq_smartass2.o +obj-$(CONFIG_CPU_FREQ_GOV_SMARTASSH3) += cpufreq_smartassH3.o +obj-$(CONFIG_CPU_FREQ_GOV_INTELLIACTIVE) += cpufreq_intelliactive.o +obj-$(CONFIG_CPU_FREQ_GOV_ADAPTIVE) += cpufreq_adaptive.o +obj-$(CONFIG_CPU_FREQ_GOV_HYPER) += cpufreq_hyper.o +obj-$(CONFIG_CPU_FREQ_GOV_INTELLIMM) += cpufreq_intellimm.o +obj-$(CONFIG_CPU_FREQ_GOV_STOCKDEMAND) += cpufreq_stockdemand.o +obj-$(CONFIG_CPU_FREQ_GOV_YANKDEMAND) += cpufreq_yankdemand.o +obj-$(CONFIG_CPU_FREQ_GOV_INTELLIDEMAND) += cpufreq_intellidemand.o # CPUfreq cross-arch helpers obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o diff --git a/drivers/cpufreq/cpufreq_adaptive.c b/drivers/cpufreq/cpufreq_adaptive.c new file mode 100644 index 00000000..c88eed41 --- /dev/null +++ b/drivers/cpufreq/cpufreq_adaptive.c @@ -0,0 +1,947 @@ +/* + * drivers/cpufreq/cpufreq_adaptive.c + * + * Copyright (C) 2001 Russell King + * (C) 2003 Venkatesh Pallipadi . + * Jun Nakajima + * + * 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 + +/* + * dbs is used in this file as a shortform for demandbased switching + * It helps to keep variable names smaller, simpler + */ + +#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) +#define DEF_FREQUENCY_UP_THRESHOLD (80) +#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) +#define MICRO_FREQUENCY_UP_THRESHOLD (95) +#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) +#define MIN_FREQUENCY_UP_THRESHOLD (11) +#define MAX_FREQUENCY_UP_THRESHOLD (100) +#define MIN_ONDEMAND_THRESHOLD (4) +/* + * The polling frequency of this governor depends on the capability of + * the processor. Default polling frequency is 1000 times the transition + * latency of the processor. The governor will work on any processor with + * transition latency <= 10mS, using appropriate sampling + * rate. + * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL) + * this governor will not work. + * All times here are in uS. + */ +#define MIN_SAMPLING_RATE_RATIO (2) + +static unsigned int min_sampling_rate; + +#define LATENCY_MULTIPLIER (1000) +#define MIN_LATENCY_MULTIPLIER (100) +#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) + +static void (*pm_idle_old)(void); +static void do_dbs_timer(struct work_struct *work); +static int cpufreq_governor_dbs(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_ADAPTIVE +static +#endif +struct cpufreq_governor cpufreq_gov_adaptive = { + .name = "adaptive", + .governor = cpufreq_governor_dbs, + .max_transition_latency = TRANSITION_LATENCY_LIMIT, + .owner = THIS_MODULE, +}; + +/* Sampling types */ +enum {DBS_NORMAL_SAMPLE, DBS_SUB_SAMPLE}; + +struct cpu_dbs_info_s { + cputime64_t prev_cpu_idle; + cputime64_t prev_cpu_iowait; + cputime64_t prev_cpu_wall; + cputime64_t prev_cpu_nice; + struct cpufreq_policy *cur_policy; + struct delayed_work work; + struct cpufreq_frequency_table *freq_table; + unsigned int freq_hi_jiffies; + int cpu; + unsigned int sample_type:1; + bool ondemand; + /* + * percpu mutex that serializes governor limit change with + * do_dbs_timer invocation. We do not want do_dbs_timer to run + * when user is changing the governor or limits. + */ + struct mutex timer_mutex; +}; +static DEFINE_PER_CPU(struct cpu_dbs_info_s, od_cpu_dbs_info); + +static unsigned int dbs_enable; /* number of CPUs using this policy */ + +/* + * dbs_mutex protects data in dbs_tuners_ins from concurrent changes on + * different CPUs. It protects dbs_enable in governor start/stop. + */ +static DEFINE_MUTEX(dbs_mutex); +static struct task_struct *up_task; +static struct workqueue_struct *down_wq; +static struct work_struct freq_scale_down_work; +static cpumask_t up_cpumask; +static spinlock_t up_cpumask_lock; +static cpumask_t down_cpumask; +static spinlock_t down_cpumask_lock; + +static DEFINE_PER_CPU(cputime64_t, idle_in_idle); +static DEFINE_PER_CPU(cputime64_t, idle_exit_wall); + +static struct timer_list cpu_timer; +static unsigned int target_freq; +static DEFINE_MUTEX(short_timer_mutex); + +/* Go to max speed when CPU load at or above this value. */ +#define DEFAULT_GO_MAXSPEED_LOAD 70 +static unsigned long go_maxspeed_load; + +#define DEFAULT_KEEP_MINSPEED_LOAD 30 +static unsigned long keep_minspeed_load; + +#define DEFAULT_STEPUP_LOAD 10 +static unsigned long step_up_load; + +static struct dbs_tuners { + unsigned int sampling_rate; + unsigned int up_threshold; + unsigned int down_differential; + unsigned int ignore_nice; + unsigned int io_is_busy; +} dbs_tuners_ins = { + .up_threshold = DEF_FREQUENCY_UP_THRESHOLD, + .down_differential = DEF_FREQUENCY_DOWN_DIFFERENTIAL, + .ignore_nice = 0, +}; + +static inline cputime64_t get_cpu_iowait_time(unsigned int cpu, cputime64_t *wall) +{ + u64 iowait_time = get_cpu_iowait_time_us(cpu, wall); + + if (iowait_time == -1ULL) + return 0; + + return iowait_time; +} + +static void adaptive_init_cpu(int cpu) +{ + struct cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, cpu); + dbs_info->freq_table = cpufreq_frequency_get_table(cpu); +} + +/************************** sysfs interface ************************/ + +static ssize_t show_sampling_rate_max(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + printk_once(KERN_INFO "CPUFREQ: adaptive sampling_rate_max " + "sysfs file is deprecated - used by: %s\n", current->comm); + return sprintf(buf, "%u\n", -1U); +} + +static ssize_t show_sampling_rate_min(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", min_sampling_rate); +} + +define_one_global_ro(sampling_rate_max); +define_one_global_ro(sampling_rate_min); + +/* cpufreq_adaptive Governor Tunables */ +#define show_one(file_name, object) \ +static ssize_t show_##file_name \ +(struct kobject *kobj, struct attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%u\n", dbs_tuners_ins.object); \ +} +show_one(sampling_rate, sampling_rate); +show_one(io_is_busy, io_is_busy); +show_one(up_threshold, up_threshold); +show_one(ignore_nice_load, ignore_nice); + +/*** delete after deprecation time ***/ + +#define DEPRECATION_MSG(file_name) \ + printk_once(KERN_INFO "CPUFREQ: Per core adaptive sysfs " \ + "interface is deprecated - " #file_name "\n"); + +#define show_one_old(file_name) \ +static ssize_t show_##file_name##_old \ +(struct cpufreq_policy *unused, char *buf) \ +{ \ + printk_once(KERN_INFO "CPUFREQ: Per core adaptive sysfs " \ + "interface is deprecated - " #file_name "\n"); \ + return show_##file_name(NULL, NULL, buf); \ +} + +/*** delete after deprecation time ***/ + +static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + mutex_lock(&dbs_mutex); + dbs_tuners_ins.sampling_rate = max(input, min_sampling_rate); + mutex_unlock(&dbs_mutex); + + return count; +} + +static ssize_t store_io_is_busy(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + mutex_lock(&dbs_mutex); + dbs_tuners_ins.io_is_busy = !!input; + mutex_unlock(&dbs_mutex); + + return count; +} + +static ssize_t store_up_threshold(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > MAX_FREQUENCY_UP_THRESHOLD || + input < MIN_FREQUENCY_UP_THRESHOLD) { + return -EINVAL; + } + + mutex_lock(&dbs_mutex); + dbs_tuners_ins.up_threshold = input; + mutex_unlock(&dbs_mutex); + + return count; +} + +static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + unsigned int j; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + if (input > 1) + input = 1; + + mutex_lock(&dbs_mutex); + if (input == dbs_tuners_ins.ignore_nice) { /* nothing to do */ + mutex_unlock(&dbs_mutex); + return count; + } + dbs_tuners_ins.ignore_nice = input; + + /* we need to re-evaluate prev_cpu_idle */ + for_each_online_cpu(j) { + struct cpu_dbs_info_s *dbs_info; + dbs_info = &per_cpu(od_cpu_dbs_info, j); + dbs_info->prev_cpu_idle = get_cpu_idle_time_us(j, + &dbs_info->prev_cpu_wall); + if (dbs_tuners_ins.ignore_nice) + dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + + } + mutex_unlock(&dbs_mutex); + + return count; +} + +define_one_global_rw(sampling_rate); +define_one_global_rw(io_is_busy); +define_one_global_rw(up_threshold); +define_one_global_rw(ignore_nice_load); + +static struct attribute *dbs_attributes[] = { + &sampling_rate_max.attr, + &sampling_rate_min.attr, + &sampling_rate.attr, + &up_threshold.attr, + &ignore_nice_load.attr, + &io_is_busy.attr, + NULL +}; + +static struct attribute_group dbs_attr_group = { + .attrs = dbs_attributes, + .name = "adaptive", +}; + +/*** delete after deprecation time ***/ + +#define write_one_old(file_name) \ +static ssize_t store_##file_name##_old \ +(struct cpufreq_policy *unused, const char *buf, size_t count) \ +{ \ + printk_once(KERN_INFO "CPUFREQ: Per core adaptive sysfs " \ + "interface is deprecated - " #file_name "\n"); \ + return store_##file_name(NULL, NULL, buf, count); \ +} + +static void cpufreq_adaptive_timer(unsigned long data) +{ + cputime64_t cur_idle; + cputime64_t cur_wall; + unsigned int delta_idle; + unsigned int delta_time; + int short_load; + unsigned int new_freq; + unsigned long flags; + struct cpu_dbs_info_s *this_dbs_info; + struct cpufreq_policy *policy; + unsigned int j; + unsigned int index; + unsigned int max_load = 0; + + this_dbs_info = &per_cpu(od_cpu_dbs_info, 0); + + policy = this_dbs_info->cur_policy; + + for_each_online_cpu(j) { + cur_idle = get_cpu_idle_time_us(j, &cur_wall); + + delta_idle = (unsigned int) (cur_idle - per_cpu(idle_in_idle, j)); + delta_time = (unsigned int) (cur_wall - per_cpu(idle_exit_wall, j)); + + /* + * If timer ran less than 1ms after short-term sample started, retry. + */ + if (delta_time < 1000) + goto do_nothing; + + if (delta_idle > delta_time) + short_load = 0; + else + short_load = 100 * (delta_time - delta_idle) / delta_time; + + if (short_load > max_load) + max_load = short_load; + } + + if (this_dbs_info->ondemand) + goto do_nothing; + + if (max_load >= go_maxspeed_load) + new_freq = policy->max; + else + new_freq = policy->max * max_load / 100; + + if ((max_load <= keep_minspeed_load) && + (policy->cur == policy->min)) + new_freq = policy->cur; + + if (cpufreq_frequency_table_target(policy, this_dbs_info->freq_table, + new_freq, CPUFREQ_RELATION_L, + &index)) { + goto do_nothing; + } + + new_freq = this_dbs_info->freq_table[index].frequency; + + target_freq = new_freq; + + if (new_freq < this_dbs_info->cur_policy->cur) { + spin_lock_irqsave(&down_cpumask_lock, flags); + cpumask_set_cpu(0, &down_cpumask); + spin_unlock_irqrestore(&down_cpumask_lock, flags); + queue_work(down_wq, &freq_scale_down_work); + } else { + spin_lock_irqsave(&up_cpumask_lock, flags); + cpumask_set_cpu(0, &up_cpumask); + spin_unlock_irqrestore(&up_cpumask_lock, flags); + wake_up_process(up_task); + } + + return; + +do_nothing: + for_each_online_cpu(j) { + per_cpu(idle_in_idle, j) = + get_cpu_idle_time_us(j, + &per_cpu(idle_exit_wall, j)); + } + mod_timer(&cpu_timer, jiffies + 2); + schedule_delayed_work_on(0, &this_dbs_info->work, 10); + + if (mutex_is_locked(&short_timer_mutex)) + mutex_unlock(&short_timer_mutex); + return; +} + +/*** delete after deprecation time ***/ + +/************************** sysfs end ************************/ + +static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq) +{ +#ifndef CONFIG_ARCH_EXYNOS4 + if (p->cur == p->max) + return; +#endif + __cpufreq_driver_target(p, freq, CPUFREQ_RELATION_H); +} + +static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) +{ + unsigned int max_load_freq; + + struct cpufreq_policy *policy; + unsigned int j; + + unsigned int index, new_freq; + unsigned int longterm_load = 0; + + policy = this_dbs_info->cur_policy; + + /* + * Every sampling_rate, we check, if current idle time is less + * than 20% (default), then we try to increase frequency + * Every sampling_rate, we look for a the lowest + * frequency which can sustain the load while keeping idle time over + * 30%. If such a frequency exist, we try to decrease to this frequency. + * + * Any frequency increase takes it to the maximum frequency. + * Frequency reduction happens at minimum steps of + * 5% (default) of current frequency + */ + + /* Get Absolute Load - in terms of freq */ + max_load_freq = 0; + + for_each_cpu(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + cputime64_t cur_wall_time, cur_idle_time, cur_iowait_time; + unsigned int idle_time, wall_time, iowait_time; + unsigned int load, load_freq; + int freq_avg; + + j_dbs_info = &per_cpu(od_cpu_dbs_info, j); + + cur_idle_time = get_cpu_idle_time_us(j, &cur_wall_time); + cur_iowait_time = get_cpu_iowait_time(j, &cur_wall_time); + + wall_time = (unsigned int) + (cur_wall_time - j_dbs_info->prev_cpu_wall); + j_dbs_info->prev_cpu_wall = cur_wall_time; + + idle_time = (unsigned int) + (cur_idle_time - j_dbs_info->prev_cpu_idle); + j_dbs_info->prev_cpu_idle = cur_idle_time; + + iowait_time = (unsigned int) + (cur_iowait_time - j_dbs_info->prev_cpu_iowait); + j_dbs_info->prev_cpu_iowait = cur_iowait_time; + + if (dbs_tuners_ins.ignore_nice) { + u64 cur_nice; + unsigned long cur_nice_jiffies; + + cur_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE] - + j_dbs_info->prev_cpu_nice; + /* + * Assumption: nice time between sampling periods will + * be less than 2^32 jiffies for 32 bit sys + */ + cur_nice_jiffies = (unsigned long) + cputime64_to_jiffies64(cur_nice); + + j_dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + idle_time += jiffies_to_usecs(cur_nice_jiffies); + } + + /* + * For the purpose of adaptive, waiting for disk IO is an + * indication that you're performance critical, and not that + * the system is actually idle. So subtract the iowait time + * from the cpu idle time. + */ + + if (dbs_tuners_ins.io_is_busy && idle_time >= iowait_time) + idle_time -= iowait_time; + + if (unlikely(!wall_time || wall_time < idle_time)) + continue; + + load = 100 * (wall_time - idle_time) / wall_time; + + if (load > longterm_load) + longterm_load = load; + + freq_avg = __cpufreq_driver_getavg(policy, j); + if (freq_avg <= 0) + freq_avg = policy->cur; + + load_freq = load * freq_avg; + + if (load_freq > max_load_freq) + max_load_freq = load_freq; + } + + if (longterm_load >= MIN_ONDEMAND_THRESHOLD) + this_dbs_info->ondemand = true; + else + this_dbs_info->ondemand = false; + + /* Check for frequency increase */ + if (max_load_freq > (dbs_tuners_ins.up_threshold * policy->cur)) { + cpufreq_frequency_table_target(policy, + this_dbs_info->freq_table, + (policy->cur + step_up_load), + CPUFREQ_RELATION_L, &index); + + new_freq = this_dbs_info->freq_table[index].frequency; + dbs_freq_increase(policy, new_freq); + return; + } + + /* Check for frequency decrease */ + /* if we cannot reduce the frequency anymore, break out early */ +#ifndef CONFIG_ARCH_EXYNOS4 + if (policy->cur == policy->min) + return; +#endif + /* + * The optimal frequency is the frequency that is the lowest that + * can support the current CPU usage without triggering the up + * policy. To be safe, we focus 10 points under the threshold. + */ + if (max_load_freq < + (dbs_tuners_ins.up_threshold - dbs_tuners_ins.down_differential) * + policy->cur) { + unsigned int freq_next; + freq_next = max_load_freq / + (dbs_tuners_ins.up_threshold - + dbs_tuners_ins.down_differential); + + if (freq_next < policy->min) + freq_next = policy->min; + + __cpufreq_driver_target(policy, freq_next, + CPUFREQ_RELATION_L); + } +} + +static void do_dbs_timer(struct work_struct *work) +{ + struct cpu_dbs_info_s *dbs_info = + container_of(work, struct cpu_dbs_info_s, work.work); + unsigned int cpu = dbs_info->cpu; + + int delay; + + mutex_lock(&dbs_info->timer_mutex); + + /* Common NORMAL_SAMPLE setup */ + dbs_info->sample_type = DBS_NORMAL_SAMPLE; + dbs_check_cpu(dbs_info); + + /* We want all CPUs to do sampling nearly on + * same jiffy + */ + delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + + schedule_delayed_work_on(cpu, &dbs_info->work, delay); + + mutex_unlock(&dbs_info->timer_mutex); +} + +static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) +{ + /* We want all CPUs to do sampling nearly on same jiffy */ + int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + + dbs_info->sample_type = DBS_NORMAL_SAMPLE; + INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer); + schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, delay); +} + +static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info) +{ + cancel_delayed_work_sync(&dbs_info->work); +} + +/* + * Not all CPUs want IO time to be accounted as busy; this dependson how + * efficient idling at a higher frequency/voltage is. + * Pavel Machek says this is not so for various generations of AMD and old + * Intel systems. + * Mike Chan (androidlcom) calis this is also not true for ARM. + * Because of this, whitelist specific known (series) of CPUs by default, and + * leave all others up to the user. + */ +static int should_io_be_busy(void) +{ +#if defined(CONFIG_X86) + /* + * For Intel, Core 2 (model 15) andl later have an efficient idle. + */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && + boot_cpu_data.x86 == 6 && + boot_cpu_data.x86_model >= 15) + return 1; +#endif + return 0; +} + +static void cpufreq_adaptive_idle(void) +{ + int i; + struct cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, 0); + struct cpufreq_policy *policy; + + policy = dbs_info->cur_policy; + + pm_idle_old(); + + if ((policy->cur == policy->min) || + (policy->cur == policy->max)) { + + if (timer_pending(&cpu_timer)) + return; + + if (mutex_trylock(&short_timer_mutex)) { + for_each_online_cpu(i) { + per_cpu(idle_in_idle, i) = + get_cpu_idle_time_us(i, + &per_cpu(idle_exit_wall, i)); + } + + mod_timer(&cpu_timer, jiffies + 2); + cancel_delayed_work(&dbs_info->work); + } + } else { + if (timer_pending(&cpu_timer)) + del_timer(&cpu_timer); + + } +} + +static int cpufreq_governor_dbs(struct cpufreq_policy *policy, + unsigned int event) +{ + unsigned int cpu = policy->cpu; + struct cpu_dbs_info_s *this_dbs_info; + unsigned int j; + int rc; + + this_dbs_info = &per_cpu(od_cpu_dbs_info, cpu); + + switch (event) { + case CPUFREQ_GOV_START: + if ((!cpu_online(cpu)) || (!policy->cur)) + return -EINVAL; + + mutex_lock(&dbs_mutex); + + rc = sysfs_create_group(&policy->kobj, &dbs_attr_group); + if (rc) { + mutex_unlock(&dbs_mutex); + return rc; + } + + dbs_enable++; + for_each_cpu(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + j_dbs_info = &per_cpu(od_cpu_dbs_info, j); + j_dbs_info->cur_policy = policy; + + j_dbs_info->prev_cpu_idle = get_cpu_idle_time_us(j, + &j_dbs_info->prev_cpu_wall); + if (dbs_tuners_ins.ignore_nice) + j_dbs_info->prev_cpu_nice = + kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + } + this_dbs_info->cpu = cpu; + adaptive_init_cpu(cpu); + + /* + * Start the timerschedule work, when this governor + * is used for first time + */ + if (dbs_enable == 1) { + unsigned int latency; + + rc = sysfs_create_group(cpufreq_global_kobject, + &dbs_attr_group); + if (rc) { + mutex_unlock(&dbs_mutex); + return rc; + } + + /* policy latency is in nS. Convert it to uS first */ + latency = policy->cpuinfo.transition_latency / 1000; + if (latency == 0) + latency = 1; + /* Bring kernel and HW constraints together */ + min_sampling_rate = max(min_sampling_rate, + MIN_LATENCY_MULTIPLIER * latency); + dbs_tuners_ins.sampling_rate = + max(min_sampling_rate, + latency * LATENCY_MULTIPLIER); + dbs_tuners_ins.io_is_busy = should_io_be_busy(); + } + mutex_unlock(&dbs_mutex); + + mutex_init(&this_dbs_info->timer_mutex); + dbs_timer_init(this_dbs_info); + + pm_idle_old = pm_idle; + pm_idle = cpufreq_adaptive_idle; + break; + + case CPUFREQ_GOV_STOP: + dbs_timer_exit(this_dbs_info); + + mutex_lock(&dbs_mutex); + sysfs_remove_group(&policy->kobj, &dbs_attr_group); + mutex_destroy(&this_dbs_info->timer_mutex); + dbs_enable--; + mutex_unlock(&dbs_mutex); + if (!dbs_enable) + sysfs_remove_group(cpufreq_global_kobject, + &dbs_attr_group); + + pm_idle = pm_idle_old; + break; + + case CPUFREQ_GOV_LIMITS: + mutex_lock(&this_dbs_info->timer_mutex); + if (policy->max < this_dbs_info->cur_policy->cur) + __cpufreq_driver_target(this_dbs_info->cur_policy, + policy->max, CPUFREQ_RELATION_H); + else if (policy->min > this_dbs_info->cur_policy->cur) + __cpufreq_driver_target(this_dbs_info->cur_policy, + policy->min, CPUFREQ_RELATION_L); + mutex_unlock(&this_dbs_info->timer_mutex); + break; + } + return 0; +} + +static inline void cpufreq_adaptive_update_time(void) +{ + struct cpu_dbs_info_s *this_dbs_info; + struct cpufreq_policy *policy; + int j; + + this_dbs_info = &per_cpu(od_cpu_dbs_info, 0); + policy = this_dbs_info->cur_policy; + + for_each_cpu(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + cputime64_t cur_wall_time, cur_idle_time, cur_iowait_time; + + j_dbs_info = &per_cpu(od_cpu_dbs_info, j); + + cur_idle_time = get_cpu_idle_time_us(j, &cur_wall_time); + cur_iowait_time = get_cpu_iowait_time(j, &cur_wall_time); + + j_dbs_info->prev_cpu_wall = cur_wall_time; + + j_dbs_info->prev_cpu_idle = cur_idle_time; + + j_dbs_info->prev_cpu_iowait = cur_iowait_time; + + if (dbs_tuners_ins.ignore_nice) + j_dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + + } + +} + +static int cpufreq_adaptive_up_task(void *data) +{ + unsigned long flags; + struct cpu_dbs_info_s *this_dbs_info; + struct cpufreq_policy *policy; + int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + + this_dbs_info = &per_cpu(od_cpu_dbs_info, 0); + policy = this_dbs_info->cur_policy; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&up_cpumask_lock, flags); + + if (cpumask_empty(&up_cpumask)) { + spin_unlock_irqrestore(&up_cpumask_lock, flags); + schedule(); + + if (kthread_should_stop()) + break; + + spin_lock_irqsave(&up_cpumask_lock, flags); + } + + set_current_state(TASK_RUNNING); + + cpumask_clear(&up_cpumask); + spin_unlock_irqrestore(&up_cpumask_lock, flags); + + __cpufreq_driver_target(this_dbs_info->cur_policy, + target_freq, + CPUFREQ_RELATION_H); + if (policy->cur != policy->max) { + mutex_lock(&this_dbs_info->timer_mutex); + + schedule_delayed_work_on(0, &this_dbs_info->work, delay); + mutex_unlock(&this_dbs_info->timer_mutex); + cpufreq_adaptive_update_time(); + } + if (mutex_is_locked(&short_timer_mutex)) + mutex_unlock(&short_timer_mutex); + } + + return 0; +} + +static void cpufreq_adaptive_freq_down(struct work_struct *work) +{ + unsigned long flags; + struct cpu_dbs_info_s *this_dbs_info; + struct cpufreq_policy *policy; + int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + + spin_lock_irqsave(&down_cpumask_lock, flags); + cpumask_clear(&down_cpumask); + spin_unlock_irqrestore(&down_cpumask_lock, flags); + + this_dbs_info = &per_cpu(od_cpu_dbs_info, 0); + policy = this_dbs_info->cur_policy; + + __cpufreq_driver_target(this_dbs_info->cur_policy, + target_freq, + CPUFREQ_RELATION_H); + + if (policy->cur != policy->min) { + mutex_lock(&this_dbs_info->timer_mutex); + + schedule_delayed_work_on(0, &this_dbs_info->work, delay); + mutex_unlock(&this_dbs_info->timer_mutex); + cpufreq_adaptive_update_time(); + } + + if (mutex_is_locked(&short_timer_mutex)) + mutex_unlock(&short_timer_mutex); +} + +static int __init cpufreq_gov_dbs_init(void) +{ + cputime64_t wall; + u64 idle_time; + int cpu = get_cpu(); + + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + go_maxspeed_load = DEFAULT_GO_MAXSPEED_LOAD; + keep_minspeed_load = DEFAULT_KEEP_MINSPEED_LOAD; + step_up_load = DEFAULT_STEPUP_LOAD; + + idle_time = get_cpu_idle_time_us(cpu, &wall); + put_cpu(); + if (idle_time != -1ULL) { + /* Idle micro accounting is supported. Use finer thresholds */ + dbs_tuners_ins.up_threshold = MICRO_FREQUENCY_UP_THRESHOLD; + dbs_tuners_ins.down_differential = + MICRO_FREQUENCY_DOWN_DIFFERENTIAL; + /* + * In no_hz/micro accounting case we set the minimum frequency + * not depending on HZ, but fixed (very low). The deferred + * timer might skip some samples if idle/sleeping as needed. + */ + min_sampling_rate = MICRO_FREQUENCY_MIN_SAMPLE_RATE; + } else { + /* For correct statistics, we need 10 ticks for each measure */ + min_sampling_rate = + MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10); + } + + init_timer(&cpu_timer); + cpu_timer.function = cpufreq_adaptive_timer; + + up_task = kthread_create(cpufreq_adaptive_up_task, NULL, + "kadaptiveup"); + + if (IS_ERR(up_task)) + return PTR_ERR(up_task); + + sched_setscheduler_nocheck(up_task, SCHED_FIFO, ¶m); + get_task_struct(up_task); + + /* No rescuer thread, bind to CPU queuing the work for possibly + warm cache (probably doesn't matter much). */ + down_wq = alloc_workqueue("kadaptive_down", 0, 1); + + if (!down_wq) + goto err_freeuptask; + + INIT_WORK(&freq_scale_down_work, cpufreq_adaptive_freq_down); + + + return cpufreq_register_governor(&cpufreq_gov_adaptive); +err_freeuptask: + put_task_struct(up_task); + return -ENOMEM; +} + +static void __exit cpufreq_gov_dbs_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_adaptive); +} + + +MODULE_AUTHOR("Venkatesh Pallipadi "); +MODULE_AUTHOR("Alexey Starikovskiy "); +MODULE_DESCRIPTION("'cpufreq_adaptive' - A dynamic cpufreq governor for " + "Low Latency Frequency Transition capable processors"); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_ADAPTIVE +fs_initcall(cpufreq_gov_dbs_init); +#else +module_init(cpufreq_gov_dbs_init); +#endif +module_exit(cpufreq_gov_dbs_exit); diff --git a/drivers/cpufreq/cpufreq_alucard.c b/drivers/cpufreq/cpufreq_alucard.c new file mode 100644 index 00000000..180e2050 --- /dev/null +++ b/drivers/cpufreq/cpufreq_alucard.c @@ -0,0 +1,599 @@ +/* + * drivers/cpufreq/cpufreq_alucard.c + * + * Copyright (C) 2011 Samsung Electronics co. ltd + * ByungChang Cha + * + * Based on ondemand governor + * Copyright (C) 2001 Russell King + * (C) 2003 Venkatesh Pallipadi . + * Jun Nakajima + * + * 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. + * + * Created by Alucard_24@xda + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * dbs is used in this file as a shortform for demandbased switching + * It helps to keep variable names smaller, simpler + */ + +static void do_alucard_timer(struct work_struct *work); +static int cpufreq_governor_alucard(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_ALUCARD +static +#endif +struct cpufreq_governor cpufreq_gov_alucard = { + .name = "alucard", + .governor = cpufreq_governor_alucard, + .owner = THIS_MODULE, +}; + +struct cpufreq_alucard_cpuinfo { + cputime64_t prev_cpu_wall; + cputime64_t prev_cpu_idle; + struct cpufreq_frequency_table *freq_table; + struct delayed_work work; + struct cpufreq_policy *cur_policy; + int cpu; + unsigned int enable:1; + /* + * mutex that serializes governor limit change with + * do_alucard_timer invocation. We do not want do_alucard_timer to run + * when user is changing the governor or limits. + */ + struct mutex timer_mutex; +}; + +static DEFINE_PER_CPU(struct cpufreq_alucard_cpuinfo, od_alucard_cpuinfo); + +static unsigned int alucard_enable; /* number of CPUs using this policy */ +/* + * alucard_mutex protects alucard_enable in governor start/stop. + */ +static DEFINE_MUTEX(alucard_mutex); + +/* alucard tuners */ +static struct alucard_tuners { + atomic_t sampling_rate; + atomic_t inc_cpu_load_at_min_freq; + atomic_t inc_cpu_load; + atomic_t dec_cpu_load_at_min_freq; + atomic_t dec_cpu_load; + atomic_t freq_responsiveness; + atomic_t pump_inc_step; + atomic_t pump_dec_step; +} alucard_tuners_ins = { + .sampling_rate = ATOMIC_INIT(60000), + .inc_cpu_load_at_min_freq = ATOMIC_INIT(70), + .inc_cpu_load = ATOMIC_INIT(70), + .dec_cpu_load_at_min_freq = ATOMIC_INIT(70), + .dec_cpu_load = ATOMIC_INIT(70), +#ifdef CONFIG_CPU_EXYNOS4210 + .freq_responsiveness = ATOMIC_INIT(600000), +#else + .freq_responsiveness = ATOMIC_INIT(600000), +#endif + .pump_inc_step = ATOMIC_INIT(1), + .pump_dec_step = ATOMIC_INIT(1), +}; + +/************************** sysfs interface ************************/ + +/* cpufreq_alucard Governor Tunables */ +#define show_one(file_name, object) \ +static ssize_t show_##file_name \ +(struct kobject *kobj, struct attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%d\n", atomic_read(&alucard_tuners_ins.object)); \ +} +show_one(sampling_rate, sampling_rate); +show_one(inc_cpu_load_at_min_freq, inc_cpu_load_at_min_freq); +show_one(inc_cpu_load, inc_cpu_load); +show_one(dec_cpu_load_at_min_freq, dec_cpu_load_at_min_freq); +show_one(dec_cpu_load, dec_cpu_load); +show_one(freq_responsiveness, freq_responsiveness); +show_one(pump_inc_step, pump_inc_step); +show_one(pump_dec_step, pump_dec_step); + +/** + * update_sampling_rate - update sampling rate effective immediately if needed. + * @new_rate: new sampling rate + * + * If new rate is smaller than the old, simply updaing + * alucard_tuners_ins.sampling_rate might not be appropriate. For example, + * if the original sampling_rate was 1 second and the requested new sampling + * rate is 10 ms because the user needs immediate reaction from ondemand + * governor, but not sure if higher frequency will be required or not, + * then, the governor may change the sampling rate too late; up to 1 second + * later. Thus, if we are reducing the sampling rate, we need to make the + * new value effective immediately. + */ +static void update_sampling_rate(unsigned int new_rate) +{ + int cpu; + + atomic_set(&alucard_tuners_ins.sampling_rate,new_rate); + + get_online_cpus(); + for_each_online_cpu(cpu) { + struct cpufreq_policy *policy; + struct cpufreq_alucard_cpuinfo *alucard_cpuinfo; + unsigned long next_sampling, appointed_at; + + policy = cpufreq_cpu_get(cpu); + if (!policy) + continue; + alucard_cpuinfo = &per_cpu(od_alucard_cpuinfo, policy->cpu); + cpufreq_cpu_put(policy); + + mutex_lock(&alucard_cpuinfo->timer_mutex); + + if (!delayed_work_pending(&alucard_cpuinfo->work)) { + mutex_unlock(&alucard_cpuinfo->timer_mutex); + continue; + } + + next_sampling = jiffies + usecs_to_jiffies(new_rate); + appointed_at = alucard_cpuinfo->work.timer.expires; + + + if (time_before(next_sampling, appointed_at)) { + + mutex_unlock(&alucard_cpuinfo->timer_mutex); + cancel_delayed_work_sync(&alucard_cpuinfo->work); + mutex_lock(&alucard_cpuinfo->timer_mutex); + + #ifdef CONFIG_CPU_EXYNOS4210 + mod_delayed_work_on(alucard_cpuinfo->cpu, system_wq, &alucard_cpuinfo->work, usecs_to_jiffies(new_rate)); + #else + queue_delayed_work_on(alucard_cpuinfo->cpu, system_wq, &alucard_cpuinfo->work, usecs_to_jiffies(new_rate)); + #endif + } + mutex_unlock(&alucard_cpuinfo->timer_mutex); + } + put_online_cpus(); +} + +/* sampling_rate */ +static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + int input; + int ret; + + ret = sscanf(buf, "%d", &input); + if (ret != 1) + return -EINVAL; + + input = max(input,10000); + + if (input == atomic_read(&alucard_tuners_ins.sampling_rate)) + return count; + + update_sampling_rate(input); + + return count; +} + +/* inc_cpu_load_at_min_freq */ +static ssize_t store_inc_cpu_load_at_min_freq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + int input; + int ret; + + ret = sscanf(buf, "%d", &input); + if (ret != 1) { + return -EINVAL; + } + + input = min(input,atomic_read(&alucard_tuners_ins.inc_cpu_load)); + + if (input == atomic_read(&alucard_tuners_ins.inc_cpu_load_at_min_freq)) + return count; + + atomic_set(&alucard_tuners_ins.inc_cpu_load_at_min_freq,input); + + return count; +} + +/* inc_cpu_load */ +static ssize_t store_inc_cpu_load(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + int input; + int ret; + + ret = sscanf(buf, "%d", &input); + if (ret != 1) + return -EINVAL; + + input = max(min(input,100),0); + + if (input == atomic_read(&alucard_tuners_ins.inc_cpu_load)) + return count; + + atomic_set(&alucard_tuners_ins.inc_cpu_load,input); + + return count; +} + +/* dec_cpu_load_at_min_freq */ +static ssize_t store_dec_cpu_load_at_min_freq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + int input; + int ret; + + ret = sscanf(buf, "%d", &input); + if (ret != 1) { + return -EINVAL; + } + + input = min(input,atomic_read(&alucard_tuners_ins.dec_cpu_load)); + + if (input == atomic_read(&alucard_tuners_ins.dec_cpu_load_at_min_freq)) + return count; + + atomic_set(&alucard_tuners_ins.dec_cpu_load_at_min_freq,input); + + return count; +} + +/* dec_cpu_load */ +static ssize_t store_dec_cpu_load(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + int input; + int ret; + + ret = sscanf(buf, "%d", &input); + if (ret != 1) + return -EINVAL; + + input = max(min(input,95),5); + + if (input == atomic_read(&alucard_tuners_ins.dec_cpu_load)) + return count; + + atomic_set(&alucard_tuners_ins.dec_cpu_load,input); + + return count; +} + +/* freq_responsiveness */ +static ssize_t store_freq_responsiveness(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + int input; + int ret; + + ret = sscanf(buf, "%d", &input); + if (ret != 1) + return -EINVAL; + + if (input == atomic_read(&alucard_tuners_ins.freq_responsiveness)) + return count; + + atomic_set(&alucard_tuners_ins.freq_responsiveness,input); + + return count; +} + +/* pump_inc_step */ +static ssize_t store_pump_inc_step(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + int input; + int ret; + + ret = sscanf(buf, "%d", &input); + if (ret != 1) + return -EINVAL; + + input = max(min(input,3),1); + + if (input == atomic_read(&alucard_tuners_ins.pump_inc_step)) + return count; + + atomic_set(&alucard_tuners_ins.pump_inc_step,input); + + return count; +} + +/* pump_dec_step */ +static ssize_t store_pump_dec_step(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + int input; + int ret; + + ret = sscanf(buf, "%d", &input); + if (ret != 1) + return -EINVAL; + + input = max(min(input,3),1); + + if (input == atomic_read(&alucard_tuners_ins.pump_dec_step)) + return count; + + atomic_set(&alucard_tuners_ins.pump_dec_step,input); + + return count; +} + +define_one_global_rw(sampling_rate); +define_one_global_rw(inc_cpu_load_at_min_freq); +define_one_global_rw(inc_cpu_load); +define_one_global_rw(dec_cpu_load_at_min_freq); +define_one_global_rw(dec_cpu_load); +define_one_global_rw(freq_responsiveness); +define_one_global_rw(pump_inc_step); +define_one_global_rw(pump_dec_step); + +static struct attribute *alucard_attributes[] = { + &sampling_rate.attr, + &inc_cpu_load_at_min_freq.attr, + &inc_cpu_load.attr, + &dec_cpu_load_at_min_freq.attr, + &dec_cpu_load.attr, + &freq_responsiveness.attr, + &pump_inc_step.attr, + &pump_dec_step.attr, + NULL +}; + +static struct attribute_group alucard_attr_group = { + .attrs = alucard_attributes, + .name = "alucard", +}; + +/************************** sysfs end ************************/ + +static void alucard_check_cpu(struct cpufreq_alucard_cpuinfo *this_alucard_cpuinfo) +{ + struct cpufreq_policy *cpu_policy; + unsigned int min_freq; + unsigned int max_freq; + unsigned int freq_responsiveness; + int dec_cpu_load; + int inc_cpu_load; + int pump_inc_step; + int pump_dec_step; + cputime64_t cur_wall_time, cur_idle_time; + unsigned int wall_time, idle_time; +#ifndef CONFIG_CPU_EXYNOS4210 + unsigned int index = 0; + unsigned int tmp_freq = 0; +#endif + unsigned int next_freq = 0; + int cur_load = -1; + unsigned int cpu; + + cpu = this_alucard_cpuinfo->cpu; + cpu_policy = this_alucard_cpuinfo->cur_policy; + + cur_idle_time = get_cpu_idle_time_us(cpu, NULL); + cur_idle_time += get_cpu_iowait_time_us(cpu, &cur_wall_time); + + wall_time = (unsigned int) + (cur_wall_time - this_alucard_cpuinfo->prev_cpu_wall); + this_alucard_cpuinfo->prev_cpu_wall = cur_wall_time; + + idle_time = (unsigned int) + (cur_idle_time - this_alucard_cpuinfo->prev_cpu_idle); + this_alucard_cpuinfo->prev_cpu_idle = cur_idle_time; + + if (!cpu_policy) + return; + + /*printk(KERN_ERR "TIMER CPU[%u], wall[%u], idle[%u]\n",cpu, wall_time, idle_time);*/ + if (wall_time >= idle_time) { /*if wall_time < idle_time, evaluate cpu load next time*/ + cur_load = wall_time > idle_time ? (100 * (wall_time - idle_time)) / wall_time : 1;/*if wall_time is equal to idle_time cpu_load is equal to 1*/ + tmp_freq = cpu_policy->cur; + /* Checking Frequency Limit */ + min_freq = cpu_policy->min; + max_freq = cpu_policy->max; + + freq_responsiveness = atomic_read(&alucard_tuners_ins.freq_responsiveness); + + pump_inc_step = atomic_read(&alucard_tuners_ins.pump_inc_step); + pump_dec_step = atomic_read(&alucard_tuners_ins.pump_dec_step); + + /* CPUs Online Scale Frequency*/ + if (cpu_policy->cur < freq_responsiveness) { + inc_cpu_load = atomic_read(&alucard_tuners_ins.inc_cpu_load_at_min_freq); + dec_cpu_load = atomic_read(&alucard_tuners_ins.dec_cpu_load_at_min_freq); + } else { + inc_cpu_load = atomic_read(&alucard_tuners_ins.inc_cpu_load); + dec_cpu_load = atomic_read(&alucard_tuners_ins.dec_cpu_load); + } + /* Check for frequency increase or for frequency decrease */ +#ifdef CONFIG_CPU_EXYNOS4210 + if (cur_load >= inc_cpu_load && cpu_policy->cur < max_freq) { + next_freq = min(cpu_policy->cur + (pump_inc_step * 100000), max_freq); + } else if (cur_load < dec_cpu_load && cpu_policy->cur > min_freq) { + next_freq = max(cpu_policy->cur - (pump_dec_step * 100000), min_freq); + } else { + next_freq = cpu_policy->cur; + } +#else + if (cur_load >= inc_cpu_load && cpu_policy->cur < max_freq) { + tmp_freq = min(cpu_policy->cur + (pump_inc_step * 108000), max_freq); + } else if (cur_load < dec_cpu_load && cpu_policy->cur > min_freq) { + tmp_freq = max(cpu_policy->cur - (pump_dec_step * 108000), min_freq); + } else { + tmp_freq = cpu_policy->cur; + } + cpufreq_frequency_table_target(cpu_policy, this_alucard_cpuinfo->freq_table, tmp_freq, + CPUFREQ_RELATION_H, &index); + if (this_alucard_cpuinfo->freq_table[index].frequency != cpu_policy->cur) { + cpufreq_frequency_table_target(cpu_policy, this_alucard_cpuinfo->freq_table, tmp_freq, + CPUFREQ_RELATION_L, &index); + } + next_freq = this_alucard_cpuinfo->freq_table[index].frequency; +#endif + /*printk(KERN_ERR "FREQ CALC.: CPU[%u], load[%d], target freq[%u], cur freq[%u], min freq[%u], max_freq[%u]\n",cpu, cur_load, next_freq, cpu_policy->cur, cpu_policy->min, max_freq);*/ + if (next_freq != cpu_policy->cur && cpu_online(cpu)) { + __cpufreq_driver_target(cpu_policy, next_freq, CPUFREQ_RELATION_L); + } + } + +} + +static void do_alucard_timer(struct work_struct *work) +{ + struct cpufreq_alucard_cpuinfo *alucard_cpuinfo; + int delay; + unsigned int cpu; + + alucard_cpuinfo = container_of(work, struct cpufreq_alucard_cpuinfo, work.work); + cpu = alucard_cpuinfo->cpu; + + mutex_lock(&alucard_cpuinfo->timer_mutex); + alucard_check_cpu(alucard_cpuinfo); + /* We want all CPUs to do sampling nearly on + * same jiffy + */ + delay = usecs_to_jiffies(atomic_read(&alucard_tuners_ins.sampling_rate)); + if (num_online_cpus() > 1) { + delay -= jiffies % delay; + } + +#ifdef CONFIG_CPU_EXYNOS4210 + mod_delayed_work_on(cpu, system_wq, &alucard_cpuinfo->work, delay); +#else + queue_delayed_work_on(cpu, system_wq, &alucard_cpuinfo->work, delay); +#endif + mutex_unlock(&alucard_cpuinfo->timer_mutex); +} + +static int cpufreq_governor_alucard(struct cpufreq_policy *policy, + unsigned int event) +{ + unsigned int cpu; + struct cpufreq_alucard_cpuinfo *this_alucard_cpuinfo; + int rc, delay; + + cpu = policy->cpu; + this_alucard_cpuinfo = &per_cpu(od_alucard_cpuinfo, cpu); + + switch (event) { + case CPUFREQ_GOV_START: + if ((!cpu_online(cpu)) || (!policy->cur)) + return -EINVAL; + + mutex_lock(&alucard_mutex); + + this_alucard_cpuinfo->cur_policy = policy; + + this_alucard_cpuinfo->prev_cpu_idle = get_cpu_idle_time_us(cpu, NULL); + this_alucard_cpuinfo->prev_cpu_idle += get_cpu_iowait_time_us(cpu, &this_alucard_cpuinfo->prev_cpu_wall); + + this_alucard_cpuinfo->freq_table = cpufreq_frequency_get_table(cpu); + this_alucard_cpuinfo->cpu = cpu; + + mutex_init(&this_alucard_cpuinfo->timer_mutex); + + alucard_enable++; + /* + * Start the timerschedule work, when this governor + * is used for first time + */ + if (alucard_enable == 1) { + rc = sysfs_create_group(cpufreq_global_kobject, + &alucard_attr_group); + if (rc) { + mutex_unlock(&alucard_mutex); + return rc; + } + } + + mutex_unlock(&alucard_mutex); + + delay=usecs_to_jiffies(atomic_read(&alucard_tuners_ins.sampling_rate)); + if (num_online_cpus() > 1) { + delay -= jiffies % delay; + } + + this_alucard_cpuinfo->enable = 1; +#ifdef CONFIG_CPU_EXYNOS4210 + INIT_DEFERRABLE_WORK(&this_alucard_cpuinfo->work, do_alucard_timer); + mod_delayed_work_on(this_alucard_cpuinfo->cpu, system_wq, &this_alucard_cpuinfo->work, delay); +#else + INIT_DELAYED_WORK_DEFERRABLE(&this_alucard_cpuinfo->work, do_alucard_timer); + queue_delayed_work_on(this_alucard_cpuinfo->cpu, system_wq, &this_alucard_cpuinfo->work, delay); +#endif + + break; + + case CPUFREQ_GOV_STOP: + this_alucard_cpuinfo->enable = 0; + cancel_delayed_work_sync(&this_alucard_cpuinfo->work); + + mutex_lock(&alucard_mutex); + alucard_enable--; + mutex_destroy(&this_alucard_cpuinfo->timer_mutex); + + if (!alucard_enable) { + sysfs_remove_group(cpufreq_global_kobject, + &alucard_attr_group); + } + mutex_unlock(&alucard_mutex); + + break; + + case CPUFREQ_GOV_LIMITS: + mutex_lock(&this_alucard_cpuinfo->timer_mutex); + if (policy->max < this_alucard_cpuinfo->cur_policy->cur) + __cpufreq_driver_target(this_alucard_cpuinfo->cur_policy, + policy->max, CPUFREQ_RELATION_H); + else if (policy->min > this_alucard_cpuinfo->cur_policy->cur) + __cpufreq_driver_target(this_alucard_cpuinfo->cur_policy, + policy->min, CPUFREQ_RELATION_L); + mutex_unlock(&this_alucard_cpuinfo->timer_mutex); + + break; + } + return 0; +} + +static int __init cpufreq_gov_alucard_init(void) +{ + return cpufreq_register_governor(&cpufreq_gov_alucard); +} + +static void __exit cpufreq_gov_alucard_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_alucard); +} + +MODULE_AUTHOR("Alucard24@XDA"); +MODULE_DESCRIPTION("'cpufreq_alucard' - A dynamic cpufreq governor v1.0 (SnapDragon)"); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_ALUCARD +fs_initcall(cpufreq_gov_alucard_init); +#else +module_init(cpufreq_gov_alucard_init); +#endif +module_exit(cpufreq_gov_alucard_exit); diff --git a/drivers/cpufreq/cpufreq_gov_msm.c b/drivers/cpufreq/cpufreq_gov_msm.c index 8f086aaa..84446d3c 100644 --- a/drivers/cpufreq/cpufreq_gov_msm.c +++ b/drivers/cpufreq/cpufreq_gov_msm.c @@ -18,114 +18,20 @@ #include #include #include -#include -#include -#include -#include #include -struct cpu_idle_info { - int enabled; - int dcvs_core_id; - struct pm_qos_request pm_qos_req; -}; - -static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_idle_info, cpu_idle_info); -static DEFINE_PER_CPU_SHARED_ALIGNED(u64, iowait_on_cpu); -static uint32_t latency; - -static int msm_dcvs_idle_notifier(int core_num, - enum msm_core_control_event event) -{ - struct cpu_idle_info *info = &per_cpu(cpu_idle_info, core_num); - - switch (event) { - case MSM_DCVS_ENABLE_IDLE_PULSE: - info->enabled = true; - break; - - case MSM_DCVS_DISABLE_IDLE_PULSE: - info->enabled = false; - break; - - case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES: - pm_qos_update_request(&info->pm_qos_req, PM_QOS_DEFAULT_VALUE); - break; - - case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES: - pm_qos_update_request(&info->pm_qos_req, latency); - break; - } - - return 0; -} - -static int msm_cpuidle_notifier(struct notifier_block *self, unsigned long cmd, - void *v) -{ - struct cpu_idle_info *info = - &per_cpu(cpu_idle_info, smp_processor_id()); - u64 io_wait_us = 0; - u64 prev_io_wait_us = 0; - u64 last_update_time = 0; - u64 val = 0; - uint32_t iowaited = 0; - - if (!info->enabled) - return NOTIFY_OK; - - switch (cmd) { - case CPU_PM_ENTER: - val = get_cpu_iowait_time_us(smp_processor_id(), - &last_update_time); - /* val could be -1 when NOHZ is not enabled */ - if (val == (u64)-1) - val = 0; - per_cpu(iowait_on_cpu, smp_processor_id()) = val; - msm_dcvs_idle(info->dcvs_core_id, MSM_DCVS_IDLE_ENTER, 0); - break; - - case CPU_PM_EXIT: - prev_io_wait_us = per_cpu(iowait_on_cpu, smp_processor_id()); - val = get_cpu_iowait_time_us(smp_processor_id(), - &last_update_time); - if (val == (u64)-1) - val = 0; - io_wait_us = val; - iowaited = (io_wait_us - prev_io_wait_us); - msm_dcvs_idle(info->dcvs_core_id, MSM_DCVS_IDLE_EXIT, iowaited); - break; - } - - return NOTIFY_OK; -} - -static struct notifier_block idle_nb = { - .notifier_call = msm_cpuidle_notifier, -}; - -static void msm_gov_idle_source_init(int cpu, int dcvs_core_id) -{ - struct cpu_idle_info *info = NULL; - - info = &per_cpu(cpu_idle_info, cpu); - info->dcvs_core_id = dcvs_core_id; - - pm_qos_add_request(&info->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, - PM_QOS_DEFAULT_VALUE); -} - struct msm_gov { - int cpu; - unsigned int cur_freq; - unsigned int min_freq; - unsigned int max_freq; - struct cpufreq_policy *policy; - int dcvs_core_id; + int cpu; + unsigned int cur_freq; + unsigned int min_freq; + unsigned int max_freq; + struct msm_dcvs_freq gov_notifier; + struct cpufreq_policy *policy; }; static DEFINE_PER_CPU_SHARED_ALIGNED(struct mutex, gov_mutex); static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_gov, msm_gov_info); +static char core_name[NR_CPUS][10]; static void msm_gov_check_limits(struct cpufreq_policy *policy) { @@ -134,7 +40,7 @@ static void msm_gov_check_limits(struct cpufreq_policy *policy) if (policy->max < gov->cur_freq) __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H); - else if (policy->min > gov->cur_freq) + else if (policy->min > gov->min_freq) __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); else @@ -144,14 +50,14 @@ static void msm_gov_check_limits(struct cpufreq_policy *policy) gov->cur_freq = policy->cur; gov->min_freq = policy->min; gov->max_freq = policy->max; - msm_dcvs_update_limits(gov->dcvs_core_id); } -static int msm_dcvs_freq_set(int core_num, +static int msm_dcvs_freq_set(struct msm_dcvs_freq *self, unsigned int freq) { int ret = -EINVAL; - struct msm_gov *gov = &per_cpu(msm_gov_info, core_num); + struct msm_gov *gov = + container_of(self, struct msm_gov, gov_notifier); mutex_lock(&per_cpu(gov_mutex, gov->cpu)); @@ -160,30 +66,23 @@ static int msm_dcvs_freq_set(int core_num, if (freq > gov->max_freq) freq = gov->max_freq; - mutex_unlock(&per_cpu(gov_mutex, gov->cpu)); + ret = __cpufreq_driver_target(gov->policy, freq, CPUFREQ_RELATION_L); + gov->cur_freq = gov->policy->cur; - ret = cpufreq_driver_target(gov->policy, freq, CPUFREQ_RELATION_L); + mutex_unlock(&per_cpu(gov_mutex, gov->cpu)); - if (!ret) { - gov->cur_freq = cpufreq_quick_get(gov->cpu); - if (freq != gov->cur_freq) - pr_err("cpu %d freq %u gov->cur_freq %u didn't match", - gov->cpu, freq, gov->cur_freq); - } - ret = gov->cur_freq; + if (!ret) + return gov->cur_freq; return ret; } -static unsigned int msm_dcvs_freq_get(int core_num) +static unsigned int msm_dcvs_freq_get(struct msm_dcvs_freq *self) { - struct msm_gov *gov = &per_cpu(msm_gov_info, core_num); - /* - * the rw_sem in cpufreq is always held when this is called. - * The policy->cur won't be updated in this case - so it is safe to - * access policy->cur - */ - return gov->policy->cur; + struct msm_gov *gov = + container_of(self, struct msm_gov, gov_notifier); + + return gov->cur_freq; } static int cpufreq_governor_msm(struct cpufreq_policy *policy, @@ -193,6 +92,8 @@ static int cpufreq_governor_msm(struct cpufreq_policy *policy, int ret = 0; int handle = 0; struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu); + struct msm_dcvs_freq *dcvs_notifier = + &(per_cpu(msm_gov_info, cpu).gov_notifier); switch (event) { case CPUFREQ_GOV_START: @@ -202,14 +103,19 @@ static int cpufreq_governor_msm(struct cpufreq_policy *policy, mutex_lock(&per_cpu(gov_mutex, cpu)); per_cpu(msm_gov_info, cpu).cpu = cpu; gov->policy = policy; - handle = msm_dcvs_freq_sink_start(gov->dcvs_core_id); + dcvs_notifier->core_name = core_name[cpu]; + dcvs_notifier->set_frequency = msm_dcvs_freq_set; + dcvs_notifier->get_frequency = msm_dcvs_freq_get; + handle = msm_dcvs_freq_sink_register(dcvs_notifier); BUG_ON(handle < 0); msm_gov_check_limits(policy); mutex_unlock(&per_cpu(gov_mutex, cpu)); break; case CPUFREQ_GOV_STOP: - msm_dcvs_freq_sink_stop(gov->dcvs_core_id); + mutex_lock(&per_cpu(gov_mutex, cpu)); + msm_dcvs_freq_sink_unregister(dcvs_notifier); + mutex_unlock(&per_cpu(gov_mutex, cpu)); break; case CPUFREQ_GOV_LIMITS: @@ -230,41 +136,21 @@ struct cpufreq_governor cpufreq_gov_msm = { static int __devinit msm_gov_probe(struct platform_device *pdev) { + int ret = 0; int cpu; + uint32_t group_id = 0x43505530; /* CPU0 */ struct msm_dcvs_core_info *core = NULL; - struct msm_dcvs_core_info *core_info = NULL; - struct msm_gov_platform_data *pdata = pdev->dev.platform_data; - int sensor = 0; core = pdev->dev.platform_data; - core_info = pdata->info; - latency = pdata->latency; for_each_possible_cpu(cpu) { - struct msm_gov *gov = &per_cpu(msm_gov_info, cpu); - mutex_init(&per_cpu(gov_mutex, cpu)); - if (cpu < core->num_cores) - sensor = core_info->sensors[cpu]; - gov->dcvs_core_id = msm_dcvs_register_core( - MSM_DCVS_CORE_TYPE_CPU, - cpu, - core_info, - msm_dcvs_freq_set, - msm_dcvs_freq_get, - msm_dcvs_idle_notifier, - NULL, - sensor); - if (gov->dcvs_core_id < 0) { + snprintf(core_name[cpu], 10, "cpu%d", cpu); + ret = msm_dcvs_register_core(core_name[cpu], group_id, core); + if (ret) pr_err("Unable to register core for %d\n", cpu); - return -EINVAL; - } - - msm_gov_idle_source_init(cpu, gov->dcvs_core_id); } - cpu_pm_register_notifier(&idle_nb); - return cpufreq_register_governor(&cpufreq_gov_msm); } diff --git a/drivers/cpufreq/cpufreq_hyper.c b/drivers/cpufreq/cpufreq_hyper.c new file mode 100644 index 00000000..76326db1 --- /dev/null +++ b/drivers/cpufreq/cpufreq_hyper.c @@ -0,0 +1,898 @@ +/* + * drivers/cpufreq/cpufreq_hyper.c + * + * Copyright (C) 2001 Russell King + * (C) 2003 Venkatesh Pallipadi . + * Jun Nakajima + * + * 2012 Minor Edits by Sar Castillo + * 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 + +/* + * dbs is used in this file as a shortform for demandbased switching + * It helps to keep variable names smaller, simpler + */ + +#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) +#define DEF_FREQUENCY_UP_THRESHOLD (65) +#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) +#define MICRO_FREQUENCY_UP_THRESHOLD (85) +#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) +#define MIN_FREQUENCY_UP_THRESHOLD (11) +#define MAX_FREQUENCY_UP_THRESHOLD (100) +#define DEF_SUSPEND_FREQ (500000) + +/* + * The polling frequency of this governor depends on the capability of + * the processor. Default polling frequency is 1000 times the transition + * latency of the processor. The governor will work on any processor with + * transition latency <= 10mS, using appropriate sampling + * rate. + * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL) + * this governor will not work. + * All times here are in uS. + */ +#define MIN_SAMPLING_RATE_RATIO (2) + +static unsigned int min_sampling_rate; + +static DEFINE_PER_CPU(unsigned int, idle_start); +static DEFINE_PER_CPU(unsigned int, idle_end); + +static int idle_event_handler(struct notifier_block *n, + unsigned long cmd, void *p) +{ + switch (cmd) { + case IDLE_START: + percpu_write(idle_start, jiffies); + break; + case IDLE_END: + percpu_write(idle_end, jiffies); + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block idle_notifier_block = { + .notifier_call = idle_event_handler, +}; + +#define LATENCY_MULTIPLIER (350) +#define MIN_LATENCY_MULTIPLIER (100) +#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) + +static void do_dbs_timer(struct work_struct *work); +static int cpufreq_governor_dbs(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_HYPER +static +#endif +struct cpufreq_governor cpufreq_gov_hyper = { + .name = "hyper", + .governor = cpufreq_governor_dbs, + .max_transition_latency = TRANSITION_LATENCY_LIMIT, + .owner = THIS_MODULE, +}; + +/* Sampling types */ +enum {DBS_NORMAL_SAMPLE, DBS_SUB_SAMPLE}; + +struct cpu_dbs_info_s { + cputime64_t prev_cpu_idle; + cputime64_t prev_cpu_iowait; + cputime64_t prev_cpu_wall; + cputime64_t prev_cpu_nice; + struct cpufreq_policy *cur_policy; + struct delayed_work work; + struct cpufreq_frequency_table *freq_table; + unsigned int freq_lo; + unsigned int freq_lo_jiffies; + unsigned int freq_hi_jiffies; + int cpu; + unsigned int sample_type:1; + /* + * percpu mutex that serializes governor limit change with + * do_dbs_timer invocation. We do not want do_dbs_timer to run + * when user is changing the governor or limits. + */ + struct mutex timer_mutex; +}; +static DEFINE_PER_CPU(struct cpu_dbs_info_s, od_cpu_dbs_info); + +static unsigned int dbs_enable; /* number of CPUs using this policy */ + +/* + * dbs_mutex protects data in dbs_tuners_ins from concurrent changes on + * different CPUs. It protects dbs_enable in governor start/stop. + */ +static DEFINE_MUTEX(dbs_mutex); + +static struct workqueue_struct *khyper_wq; + +static struct dbs_tuners { + unsigned int sampling_rate; + unsigned int up_threshold; + unsigned int down_differential; + unsigned int ignore_nice; + unsigned int powersave_bias; + unsigned int io_is_busy; + unsigned int fast_start; + unsigned int deep_sleep; + unsigned int suspend_freq; +} dbs_tuners_ins = { + .up_threshold = DEF_FREQUENCY_UP_THRESHOLD, + .down_differential = DEF_FREQUENCY_DOWN_DIFFERENTIAL, + .ignore_nice = 0, + .powersave_bias = 0, + .deep_sleep = 1, + .fast_start = 1, + .suspend_freq = DEF_SUSPEND_FREQ, +}; + +static unsigned int dbs_enable=0; /* number of CPUs using this policy */ + +// hyper suspend mods (Thanks to Imoseyon) +static unsigned int suspended = 0; +static void hyper_suspend(int suspend) +{ + struct cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, smp_processor_id()); + if (dbs_enable==0) return; + if (!suspend) { // resume at max speed: + suspended = 0; + __cpufreq_driver_target(dbs_info->cur_policy, dbs_info->cur_policy->max, + CPUFREQ_RELATION_L); + pr_info("[hyper] hyper awake at %d\n", dbs_info->cur_policy->cur); + } else { + suspended = 1; + // let's give it a little breathing room + __cpufreq_driver_target(dbs_info->cur_policy, dbs_tuners_ins.suspend_freq, CPUFREQ_RELATION_H); + pr_info("[hyper] hyper suspended at %d\n", dbs_info->cur_policy->cur); + } +} + +static void hyper_early_suspend(struct early_suspend *handler) { + hyper_suspend(1); +} + +static void hyper_late_resume(struct early_suspend *handler) { + hyper_suspend(0); +} + +static struct early_suspend hyper_power_suspend = { + .suspend = hyper_early_suspend, + .resume = hyper_late_resume, + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, +}; + +static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, + cputime64_t *wall) +{ + cputime64_t idle_time; + cputime64_t cur_wall_time; + cputime64_t busy_time; + + cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); + + busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; + + idle_time = cur_wall_time - busy_time; + if (wall) + *wall = (cputime64_t)jiffies_to_usecs(cur_wall_time); + + return (cputime64_t)jiffies_to_usecs(idle_time); +} + +static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall) +{ + u64 idle_time = get_cpu_idle_time_us(cpu, wall); + + if (idle_time == -1ULL) + return get_cpu_idle_time_jiffy(cpu, wall); + + return idle_time; +} + +static inline cputime64_t get_cpu_iowait_time(unsigned int cpu, cputime64_t *wall) +{ + u64 iowait_time = get_cpu_iowait_time_us(cpu, wall); + + if (iowait_time == -1ULL) + return 0; + + return iowait_time; +} + +/* + * Find right freq to be set now with powersave_bias on. + * Returns the freq_hi to be used right now and will set freq_hi_jiffies, + * freq_lo, and freq_lo_jiffies in percpu area for averaging freqs. + */ +static unsigned int powersave_bias_target(struct cpufreq_policy *policy, + unsigned int freq_next, + unsigned int relation) +{ + unsigned int freq_req, freq_reduc, freq_avg; + unsigned int freq_hi, freq_lo; + unsigned int index = 0; + unsigned int jiffies_total, jiffies_hi, jiffies_lo; + struct cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, + policy->cpu); + + if (!dbs_info->freq_table) { + dbs_info->freq_lo = 0; + dbs_info->freq_lo_jiffies = 0; + return freq_next; + } + + cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_next, + relation, &index); + freq_req = dbs_info->freq_table[index].frequency; + freq_reduc = freq_req * dbs_tuners_ins.powersave_bias / 1000; + freq_avg = freq_req - freq_reduc; + + /* Find freq bounds for freq_avg in freq_table */ + index = 0; + cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_avg, + CPUFREQ_RELATION_H, &index); + freq_lo = dbs_info->freq_table[index].frequency; + index = 0; + cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_avg, + CPUFREQ_RELATION_L, &index); + freq_hi = dbs_info->freq_table[index].frequency; + + /* Find out how long we have to be in hi and lo freqs */ + if (freq_hi == freq_lo) { + dbs_info->freq_lo = 0; + dbs_info->freq_lo_jiffies = 0; + return freq_lo; + } + jiffies_total = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + jiffies_hi = (freq_avg - freq_lo) * jiffies_total; + jiffies_hi += ((freq_hi - freq_lo) / 2); + jiffies_hi /= (freq_hi - freq_lo); + jiffies_lo = jiffies_total - jiffies_hi; + dbs_info->freq_lo = freq_lo; + dbs_info->freq_lo_jiffies = jiffies_lo; + dbs_info->freq_hi_jiffies = jiffies_hi; + return freq_hi; +} + +static void hyper_powersave_bias_init_cpu(int cpu) +{ + struct cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, cpu); + dbs_info->freq_table = cpufreq_frequency_get_table(cpu); + dbs_info->freq_lo = 0; +} + +static void hyper_powersave_bias_init(void) +{ + int i; + for_each_online_cpu(i) { + hyper_powersave_bias_init_cpu(i); + } +} + +/************************** sysfs interface ************************/ + +static ssize_t show_sampling_rate_min(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", min_sampling_rate); +} + +define_one_global_ro(sampling_rate_min); + +/* cpufreq_hyper Governor Tunables */ +#define show_one(file_name, object) \ + static ssize_t show_##file_name \ + (struct kobject *kobj, struct attribute *attr, char *buf) \ + { \ + return sprintf(buf, "%u\n", dbs_tuners_ins.object); \ + } + +show_one(sampling_rate, sampling_rate); +show_one(io_is_busy, io_is_busy); +show_one(fast_start, fast_start); +show_one(deep_sleep, deep_sleep); +show_one(up_threshold, up_threshold); +show_one(ignore_nice_load, ignore_nice); +show_one(powersave_bias, powersave_bias); +show_one(suspend_freq, suspend_freq); + +static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + dbs_tuners_ins.sampling_rate = max(input, min_sampling_rate); + return count; +} + +static ssize_t store_io_is_busy(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + dbs_tuners_ins.io_is_busy = !!input; + return count; +} + +static ssize_t store_fast_start(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + dbs_tuners_ins.fast_start = !!input; + + return count; +} + +static ssize_t store_deep_sleep(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + dbs_tuners_ins.deep_sleep = !!input; + + return count; +} + +static ssize_t store_up_threshold(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > MAX_FREQUENCY_UP_THRESHOLD || + input < MIN_FREQUENCY_UP_THRESHOLD) { + return -EINVAL; + } + + dbs_tuners_ins.up_threshold = input; + + return count; +} + +static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + unsigned int j; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + if (input > 1) + input = 1; + + if (input == dbs_tuners_ins.ignore_nice) { /* nothing to do */ + return count; + } + dbs_tuners_ins.ignore_nice = input; + + /* we need to re-evaluate prev_cpu_idle */ + for_each_online_cpu(j) { + struct cpu_dbs_info_s *dbs_info; + dbs_info = &per_cpu(od_cpu_dbs_info, j); + dbs_info->prev_cpu_idle = get_cpu_idle_time(j, + &dbs_info->prev_cpu_wall); + if (dbs_tuners_ins.ignore_nice) + dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + + } + + return count; +} + +static ssize_t store_powersave_bias(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1) + return -EINVAL; + + if (input > 1000) + input = 1000; + + dbs_tuners_ins.powersave_bias = input; + hyper_powersave_bias_init(); + + return count; +} + +static ssize_t store_suspend_freq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1) + return -EINVAL; + + if (input > 1200000) + input = 1200000; + + if (input < 100000) + input = 100000; + + dbs_tuners_ins.suspend_freq = input; + + return count; +} + +define_one_global_rw(sampling_rate); +define_one_global_rw(io_is_busy); +define_one_global_rw(deep_sleep); +define_one_global_rw(fast_start); +define_one_global_rw(up_threshold); +define_one_global_rw(ignore_nice_load); +define_one_global_rw(powersave_bias); +define_one_global_rw(suspend_freq); + +static struct attribute *dbs_attributes[] = { + &sampling_rate_min.attr, + &sampling_rate.attr, + &up_threshold.attr, + &ignore_nice_load.attr, + &powersave_bias.attr, + &io_is_busy.attr, + &deep_sleep.attr, + &fast_start.attr, + &suspend_freq.attr, + NULL +}; + +static struct attribute_group dbs_attr_group = { + .attrs = dbs_attributes, + .name = "hyper", +}; + +/************************** sysfs end ************************/ + +static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) +{ + unsigned int max_load_freq; + + struct cpufreq_policy *policy; + unsigned int j; + + this_dbs_info->freq_lo = 0; + policy = this_dbs_info->cur_policy; + + /* + * Every sampling_rate, we check, if current idle time is less + * than 20% (default), then we try to increase frequency + * Every sampling_rate, we look for a the lowest + * frequency which can sustain the load while keeping idle time over + * 30%. If such a frequency exist, we try to decrease to this frequency. + * + * Any frequency increase takes it to the maximum frequency. + * Frequency reduction happens at minimum steps of + * 5% (default) of current frequency + */ + + /* Get Absolute Load - in terms of freq */ + max_load_freq = 0; + + for_each_cpu(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + cputime64_t cur_wall_time, cur_idle_time, cur_iowait_time; + unsigned int idle_time, wall_time, iowait_time; + unsigned int load, load_freq; + int freq_avg; + unsigned long start, end, delta, sampling_delta; + + j_dbs_info = &per_cpu(od_cpu_dbs_info, j); + + start = per_cpu(idle_start, j); + end = per_cpu(idle_end, j); + if (time_after(end, start)) + delta = (long)end - (long)start; + else + delta = 0; + sampling_delta = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + + /* + * Deep sleep detection. + * + * If the previous low-power idle interval was 4x the sampling + * rate or more, we're probably exiting or entering from a deep + * sleep state. + * + * In both cases ignore the cpu activity, because probably + * there's a big chance to get back to the deep sleep again. + */ + if (dbs_tuners_ins.deep_sleep) { + if (delta > 4 * sampling_delta) + continue; + } + + /* + * Fast start detection. + * + * If in the last 4 samples the cpu did not enter in low-power + * idle state, probably the cpu is busy; try to jump to the + * maximum speed. + */ + if (dbs_tuners_ins.fast_start) { + if (delta && time_is_before_jiffies(end + + 4 * sampling_delta)) { + max_load_freq = UINT_MAX; + break; + } + } + + cur_idle_time = get_cpu_idle_time(j, &cur_wall_time); + cur_iowait_time = get_cpu_iowait_time(j, &cur_wall_time); + + wall_time = (unsigned int) + (cur_wall_time - j_dbs_info->prev_cpu_wall); + j_dbs_info->prev_cpu_wall = cur_wall_time; + + idle_time = (unsigned int) + (cur_idle_time - j_dbs_info->prev_cpu_idle); + j_dbs_info->prev_cpu_idle = cur_idle_time; + + iowait_time = (unsigned int) + (cur_iowait_time - j_dbs_info->prev_cpu_iowait); + j_dbs_info->prev_cpu_iowait = cur_iowait_time; + + if (dbs_tuners_ins.ignore_nice) { + u64 cur_nice; + unsigned long cur_nice_jiffies; + + cur_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE] - + j_dbs_info->prev_cpu_nice; + /* + * Assumption: nice time between sampling periods will + * be less than 2^32 jiffies for 32 bit sys + */ + cur_nice_jiffies = (unsigned long) + cputime64_to_jiffies64(cur_nice); + + j_dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + idle_time += jiffies_to_usecs(cur_nice_jiffies); + } + + /* + * For the purpose of hyper, waiting for disk IO is an + * indication that you're performance critical, and not that + * the system is actually idle. So subtract the iowait time + * from the cpu idle time. + */ + + if (dbs_tuners_ins.io_is_busy && idle_time >= iowait_time) + idle_time -= iowait_time; + + if (unlikely(!wall_time || wall_time < idle_time)) + continue; + + load = 100 * (wall_time - idle_time) / wall_time; + + freq_avg = __cpufreq_driver_getavg(policy, j); + if (freq_avg <= 0) + freq_avg = policy->cur; + + load_freq = load * freq_avg; + if (load_freq > max_load_freq) + max_load_freq = load_freq; + } + + /* Check for frequency increase */ + if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) { + /* if we are already at full speed then break out early */ + if (!dbs_tuners_ins.powersave_bias) { + //if (policy->cur == policy->max) + // return; + + __cpufreq_driver_target(policy, policy->max, + CPUFREQ_RELATION_H); + } else { + int freq = powersave_bias_target(policy, policy->max, + CPUFREQ_RELATION_H); + __cpufreq_driver_target(policy, freq, + CPUFREQ_RELATION_L); + } + return; + } + + /* Check for frequency decrease */ + /* if we cannot reduce the frequency anymore, break out early */ + if (policy->cur == policy->min) { + __cpufreq_driver_target(policy, policy->min, + CPUFREQ_RELATION_L); + return; + } + + /* + * The optimal frequency is the frequency that is the lowest that + * can support the current CPU usage without triggering the up + * policy. To be safe, we focus 10 points under the threshold. + */ + if (max_load_freq < + (dbs_tuners_ins.up_threshold - dbs_tuners_ins.down_differential) * + policy->cur) { + unsigned int freq_next; + freq_next = max_load_freq / + (dbs_tuners_ins.up_threshold - + dbs_tuners_ins.down_differential); + + if (freq_next < policy->min) + freq_next = policy->min; + + if (!dbs_tuners_ins.powersave_bias) { + __cpufreq_driver_target(policy, freq_next, + CPUFREQ_RELATION_L); + } else { + int freq = powersave_bias_target(policy, freq_next, + CPUFREQ_RELATION_L); + __cpufreq_driver_target(policy, freq, + CPUFREQ_RELATION_L); + } + } +} + +static void do_dbs_timer(struct work_struct *work) +{ + struct cpu_dbs_info_s *dbs_info = + container_of(work, struct cpu_dbs_info_s, work.work); + unsigned int cpu = dbs_info->cpu; + int sample_type = dbs_info->sample_type; + + /* We want all CPUs to do sampling nearly on same jiffy */ + int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + + mutex_lock(&dbs_info->timer_mutex); + + /* Common NORMAL_SAMPLE setup */ + dbs_info->sample_type = DBS_NORMAL_SAMPLE; + if (!dbs_tuners_ins.powersave_bias || + sample_type == DBS_NORMAL_SAMPLE) { + dbs_check_cpu(dbs_info); + if (dbs_info->freq_lo) { + /* Setup timer for SUB_SAMPLE */ + dbs_info->sample_type = DBS_SUB_SAMPLE; + delay = dbs_info->freq_hi_jiffies; + } + } else { + if (!suspended) + __cpufreq_driver_target(dbs_info->cur_policy, + dbs_info->freq_lo, CPUFREQ_RELATION_H); + delay = dbs_info->freq_lo_jiffies; + } + queue_delayed_work_on(cpu, khyper_wq, &dbs_info->work, delay); + mutex_unlock(&dbs_info->timer_mutex); +} + +static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) +{ + int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + + dbs_info->sample_type = DBS_NORMAL_SAMPLE; + INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer); + queue_delayed_work_on(dbs_info->cpu, khyper_wq, &dbs_info->work, delay); +} + +static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info) +{ + cancel_delayed_work_sync(&dbs_info->work); +} + +/* + * Not all CPUs want IO time to be accounted as busy; this dependson how + * efficient idling at a higher frequency/voltage is. + * Pavel Machek says this is not so for various generations of AMD and old + * Intel systems. + * Mike Chan (androidlcom) calis this is also not true for ARM. + * Because of this, whitelist specific known (series) of CPUs by default, and + * leave all others up to the user. + */ +static int should_io_be_busy(void) +{ + return 0; +} + +static int cpufreq_governor_dbs(struct cpufreq_policy *policy, + unsigned int event) +{ + unsigned int cpu = policy->cpu; + struct cpu_dbs_info_s *this_dbs_info; + unsigned int j; + int rc; + + this_dbs_info = &per_cpu(od_cpu_dbs_info, cpu); + + switch (event) { + case CPUFREQ_GOV_START: + if ((!cpu_online(cpu)) || (!policy->cur)) + return -EINVAL; + + mutex_lock(&dbs_mutex); + + dbs_enable++; + for_each_cpu(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + j_dbs_info = &per_cpu(od_cpu_dbs_info, j); + j_dbs_info->cur_policy = policy; + + j_dbs_info->prev_cpu_idle = get_cpu_idle_time(j, + &j_dbs_info->prev_cpu_wall); + if (dbs_tuners_ins.ignore_nice) + j_dbs_info->prev_cpu_nice = + kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + } + this_dbs_info->cpu = cpu; + hyper_powersave_bias_init_cpu(cpu); + /* + * Start the timerschedule work, when this governor + * is used for first time + */ + if (dbs_enable == 1) { + unsigned int latency; + + rc = sysfs_create_group(cpufreq_global_kobject, + &dbs_attr_group); + if (rc) { + mutex_unlock(&dbs_mutex); + return rc; + } + + /* policy latency is in nS. Convert it to uS first */ + latency = policy->cpuinfo.transition_latency / 1000; + if (latency == 0) + latency = 1; + /* Bring kernel and HW constraints together */ + min_sampling_rate = max(min_sampling_rate, + MIN_LATENCY_MULTIPLIER * latency); + dbs_tuners_ins.sampling_rate = + max(min_sampling_rate, + latency * LATENCY_MULTIPLIER); + dbs_tuners_ins.io_is_busy = should_io_be_busy(); + } + mutex_unlock(&dbs_mutex); + + mutex_init(&this_dbs_info->timer_mutex); + dbs_timer_init(this_dbs_info); + register_early_suspend(&hyper_power_suspend); + pr_info("[hyper] hyper active\n"); + break; + + case CPUFREQ_GOV_STOP: + dbs_timer_exit(this_dbs_info); + + mutex_lock(&dbs_mutex); + mutex_destroy(&this_dbs_info->timer_mutex); + dbs_enable--; + mutex_unlock(&dbs_mutex); + if (!dbs_enable) + sysfs_remove_group(cpufreq_global_kobject, + &dbs_attr_group); + unregister_early_suspend(&hyper_power_suspend); + pr_info("[hyper] hyper inactive\n"); + break; + + case CPUFREQ_GOV_LIMITS: + mutex_lock(&this_dbs_info->timer_mutex); + if (policy->max < this_dbs_info->cur_policy->cur) + __cpufreq_driver_target(this_dbs_info->cur_policy, + policy->max, CPUFREQ_RELATION_H); + else if (policy->min > this_dbs_info->cur_policy->cur) + __cpufreq_driver_target(this_dbs_info->cur_policy, + policy->min, CPUFREQ_RELATION_L); + mutex_unlock(&this_dbs_info->timer_mutex); + break; + } + return 0; +} + +static int __init cpufreq_gov_dbs_init(void) +{ + cputime64_t wall; + u64 idle_time; + int cpu = get_cpu(); + int err = 0; + + idle_time = get_cpu_idle_time_us(cpu, &wall); + put_cpu(); + if (idle_time != -1ULL) { + /* Idle micro accounting is supported. Use finer thresholds */ + dbs_tuners_ins.up_threshold = MICRO_FREQUENCY_UP_THRESHOLD; + dbs_tuners_ins.down_differential = + MICRO_FREQUENCY_DOWN_DIFFERENTIAL; + /* + * In no_hz/micro accounting case we set the minimum frequency + * not depending on HZ, but fixed (very low). The deferred + * timer might skip some samples if idle/sleeping as needed. + */ + min_sampling_rate = MICRO_FREQUENCY_MIN_SAMPLE_RATE; + } else { + /* For correct statistics, we need 10 ticks for each measure */ + min_sampling_rate = + MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10); + } + + khyper_wq = create_workqueue("khyper"); + if (!khyper_wq) { + printk(KERN_ERR "Creation of khyper failed\n"); + return -EFAULT; + } + + idle_notifier_register(&idle_notifier_block); + + err = cpufreq_register_governor(&cpufreq_gov_hyper); + if (err) + destroy_workqueue(khyper_wq); + + return err; +} + +static void __exit cpufreq_gov_dbs_exit(void) +{ + idle_notifier_unregister(&idle_notifier_block); + cpufreq_unregister_governor(&cpufreq_gov_hyper); + destroy_workqueue(khyper_wq); +} + +MODULE_AUTHOR("Venkatesh Pallipadi "); +MODULE_AUTHOR("Alexey Starikovskiy "); +MODULE_DESCRIPTION("'cpufreq_hyper' - A dynamic cpufreq governor with a sleep profile based on ondemand for " + "Low Latency Frequency Transition capable processors"); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_HYPER +fs_initcall(cpufreq_gov_dbs_init); +#else +module_init(cpufreq_gov_dbs_init); +#endif +module_exit(cpufreq_gov_dbs_exit); diff --git a/drivers/cpufreq/cpufreq_intelliactive.c b/drivers/cpufreq/cpufreq_intelliactive.c new file mode 100644 index 00000000..9af81527 --- /dev/null +++ b/drivers/cpufreq/cpufreq_intelliactive.c @@ -0,0 +1,1544 @@ +/* + * drivers/cpufreq/cpufreq_intelliactive.c + * + * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2014 Paul Reioux + * + * 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) + * Author: Paul Reioux (reioux@gmail.com) Modified for intelliactive + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CORES (2) + +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; + int prev_load; + unsigned int two_phase_freq; +}; + +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; + +/* Sampling down factor to be applied to min_sample_time at max freq */ +static unsigned int sampling_down_factor; + +/* 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; + +/* Busy SDF parameters*/ +#define MIN_BUSY_TIME (100 * USEC_PER_MSEC) + +/* + * Wait this long before raising speed above hispeed, by default a single + * timer interval. + */ +#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE +static unsigned int default_above_hispeed_delay[] = { + DEFAULT_ABOVE_HISPEED_DELAY }; +static spinlock_t above_hispeed_delay_lock; +static unsigned int *above_hispeed_delay = default_above_hispeed_delay; +static int nabove_hispeed_delay = ARRAY_SIZE(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 bool io_is_busy = 1; + +/* + * If the max load among other CPUs is higher than up_threshold_any_cpu_load + * and if the highest frequency among the other CPUs is higher than + * up_threshold_any_cpu_freq then do not let the frequency to drop below + * sync_freq + */ +static unsigned int up_threshold_any_cpu_load = 95; +static unsigned int sync_freq = 245000; +static unsigned int up_threshold_any_cpu_freq = 320000; + +static int two_phase_freq_array[MAX_CORES] = {[0 ... MAX_CORES-1] = 700000} ; + +static int cpufreq_governor_intelliactive(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIACTIVE +static +#endif +struct cpufreq_governor cpufreq_gov_intelliactive = { + .name = "intelliactive", + .governor = cpufreq_governor_intelliactive, + .max_transition_latency = 10000000, + .owner = THIS_MODULE, +}; + +static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, + cputime64_t *wall) +{ + u64 idle_time; + u64 cur_wall_time; + u64 busy_time; + + cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); + + busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; + + idle_time = cur_wall_time - busy_time; + if (wall) + *wall = jiffies_to_usecs(cur_wall_time); + + return jiffies_to_usecs(idle_time); +} + +static inline cputime64_t get_cpu_idle_time(unsigned int cpu, + cputime64_t *wall) +{ + u64 idle_time = get_cpu_idle_time_us(cpu, wall); + + if (idle_time == -1ULL) + idle_time = get_cpu_idle_time_jiffy(cpu, wall); + else if (!io_is_busy) + idle_time += get_cpu_iowait_time_us(cpu, wall); + + return idle_time; +} + +static void cpufreq_interactive_timer_resched( + struct cpufreq_interactive_cpuinfo *pcpu) +{ + unsigned long expires; + unsigned long flags; + + spin_lock_irqsave(&pcpu->load_lock, flags); + pcpu->time_in_idle = + get_cpu_idle_time(smp_processor_id(), + &pcpu->time_in_idle_timestamp); + pcpu->cputime_speedadj = 0; + pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; + expires = jiffies + usecs_to_jiffies(timer_rate); + 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_unlock_irqrestore(&pcpu->load_lock, flags); +} + +/* The caller shall take enable_sem write semaphore to avoid any timer race. + * The cpu_timer and cpu_slack_timer must be deactivated when calling this + * function. + */ +static void cpufreq_interactive_timer_start(int cpu) +{ + struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); + unsigned long expires = jiffies + usecs_to_jiffies(timer_rate); + unsigned long flags; + + pcpu->cpu_timer.expires = expires; + if (cpu_online(cpu)) { + add_timer_on(&pcpu->cpu_timer, cpu); + if (timer_slack_val >= 0 && pcpu->target_freq > + pcpu->policy->min) { + expires += usecs_to_jiffies(timer_slack_val); + pcpu->cpu_slack_timer.expires = expires; + add_timer_on(&pcpu->cpu_slack_timer, cpu); + } + } + + spin_lock_irqsave(&pcpu->load_lock, flags); + pcpu->time_in_idle = + get_cpu_idle_time(cpu, &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_above_hispeed_delay(unsigned int freq) +{ + int i; + unsigned int ret; + unsigned long flags; + + spin_lock_irqsave(&above_hispeed_delay_lock, flags); + + for (i = 0; i < nabove_hispeed_delay - 1 && + freq >= above_hispeed_delay[i+1]; i += 2) + ; + + ret = above_hispeed_delay[i]; + ret = (ret > (1 * USEC_PER_MSEC)) ? (ret - (1 * USEC_PER_MSEC)) : ret; + + spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); + return ret; +} + +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. + */ + + if (cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, loadadjfreq / tl, + CPUFREQ_RELATION_L, &index)) + break; + 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. + */ + if (cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, + freqmax - 1, CPUFREQ_RELATION_H, + &index)) + break; + 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. + */ + if (cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, + freqmin + 1, CPUFREQ_RELATION_L, + &index)) + break; + 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(cpu, &now); + delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); + delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); + + if (delta_time <= delta_idle) + active_time = 0; + else + 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; + unsigned long mod_min_sample_time; + int i, max_load; + unsigned int max_freq; + struct cpufreq_interactive_cpuinfo *picpu; + static unsigned int phase = 0; + static unsigned int counter = 0; + unsigned int nr_cpus; + + if (!down_read_trylock(&pcpu->enable_sem)) + return; + if (!pcpu->governor_enabled) + goto exit; + + if (cpu_is_offline(data)) + 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; + pcpu->prev_load = cpu_load; + boosted = boost_val || now < boostpulse_endtime; + + // HACK HACK HACK BEGIN + if (counter < 5) { + counter++; + if (counter > 2) { + phase = 1; + } + } + + if (cpu_load >= go_hispeed_load || boosted) { + if (pcpu->target_freq < hispeed_freq) { + nr_cpus = num_online_cpus(); + + pcpu->two_phase_freq = two_phase_freq_array[nr_cpus-1]; + if (pcpu->two_phase_freq < pcpu->policy->cur) + phase = 1; + if (pcpu->two_phase_freq != 0 && phase == 0) { + new_freq = pcpu->two_phase_freq; + } else + 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 (sync_freq && new_freq < sync_freq) { + + max_load = 0; + max_freq = 0; + + for_each_online_cpu(i) { + picpu = &per_cpu(cpuinfo, i); + + if (i == data || picpu->prev_load < + up_threshold_any_cpu_load) + continue; + + max_load = max(max_load, picpu->prev_load); + max_freq = max(max_freq, picpu->target_freq); + } + + if (max_freq > up_threshold_any_cpu_freq && + max_load >= up_threshold_any_cpu_load) + new_freq = sync_freq; + } + } + + if (counter > 0) { + counter--; + if (counter == 0) { + phase = 0; + } + } + + if (pcpu->target_freq >= hispeed_freq && + new_freq > pcpu->target_freq && + now - pcpu->hispeed_validate_time < + freq_to_above_hispeed_delay(pcpu->target_freq)) { + goto rearm; + } + + pcpu->hispeed_validate_time = now; + + if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, + new_freq, CPUFREQ_RELATION_L, + &index)) + 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 (sampling_down_factor && pcpu->policy->cur == pcpu->policy->max) + mod_min_sample_time = sampling_down_factor; + else + mod_min_sample_time = min_sample_time; + + if (new_freq < pcpu->floor_freq) { + if (now - pcpu->floor_validate_time < mod_min_sample_time) { + 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) { + goto rearm_if_notmax; + } + + 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) +{ + int cpu = smp_processor_id(); + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, smp_processor_id()); + int pending; + u64 now; + + if (!down_read_trylock(&pcpu->enable_sem)) + return; + if (!pcpu->governor_enabled) + goto exit; + + /* Cancel the timer if cpu is offline */ + if (cpu_is_offline(cpu)) { + del_timer(&pcpu->cpu_timer); + del_timer(&pcpu->cpu_slack_timer); + goto exit; + } + + 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); + + now = ktime_to_us(ktime_get()); + if ((pcpu->policy->cur == pcpu->policy->max) && + (now - pcpu->hispeed_validate_time) > + MIN_BUSY_TIME) { + pcpu->floor_validate_time = now; + } + + } + } +exit: + 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); + + 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); + if (cpu != freq->cpu) { + if (!down_read_trylock(&pjcpu->enable_sem)) + continue; + if (!pjcpu->governor_enabled) { + up_read(&pjcpu->enable_sem); + continue; + } + } + spin_lock_irqsave(&pjcpu->load_lock, flags); + update_load(cpu); + spin_unlock_irqrestore(&pjcpu->load_lock, flags); + if (cpu != freq->cpu) + up_read(&pjcpu->enable_sem); + } + + up_read(&pcpu->enable_sem); + } + return 0; +} + +static struct notifier_block cpufreq_notifier_block = { + .notifier_call = cpufreq_interactive_notifier, +}; + +static unsigned int *get_tokenized_data(const char *buf, int *num_tokens) +{ + const char *cp; + int i; + int ntokens = 1; + unsigned int *tokenized_data; + int err = -EINVAL; + + cp = buf; + while ((cp = strpbrk(cp + 1, " :"))) + ntokens++; + + if (!(ntokens & 0x1)) + goto err; + + tokenized_data = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL); + if (!tokenized_data) { + err = -ENOMEM; + goto err; + } + + cp = buf; + i = 0; + while (i < ntokens) { + if (sscanf(cp, "%u", &tokenized_data[i++]) != 1) + goto err_kfree; + + cp = strpbrk(cp, " :"); + if (!cp) + break; + cp++; + } + + if (i != ntokens) + goto err_kfree; + + *num_tokens = ntokens; + return tokenized_data; + +err_kfree: + kfree(tokenized_data); +err: + return ERR_PTR(err); +} + +static ssize_t show_two_phase_freq +(struct kobject *kobj, struct attribute *attr, char *buf) +{ + int i = 0 ; + int shift = 0 ; + char *buf_pos = buf; + for ( i = 0 ; i < MAX_CORES; i++) { + shift = sprintf(buf_pos,"%d,",two_phase_freq_array[i]); + buf_pos += shift; + } + *(buf_pos-1) = '\0'; + return strlen(buf); +} + +static ssize_t store_two_phase_freq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + + int ret = 0; + ret = sscanf(buf,"%u,%u",&two_phase_freq_array[0], + &two_phase_freq_array[1]); + if (ret < MAX_CORES) + return -EINVAL; + + return count; +} + +static struct global_attr two_phase_freq_attr = + __ATTR(two_phase_freq, S_IRUGO | S_IWUSR, + show_two_phase_freq, store_two_phase_freq); + +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; + 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 ntokens; + unsigned int *new_target_loads = NULL; + unsigned long flags; + + new_target_loads = get_tokenized_data(buf, &ntokens); + if (IS_ERR(new_target_loads)) + return PTR_RET(new_target_loads); + + 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; +} + +static struct global_attr target_loads_attr = + __ATTR(target_loads, S_IRUGO | S_IWUSR, + show_target_loads, store_target_loads); + +static ssize_t show_above_hispeed_delay( + struct kobject *kobj, struct attribute *attr, char *buf) +{ + int i; + ssize_t ret = 0; + unsigned long flags; + + spin_lock_irqsave(&above_hispeed_delay_lock, flags); + + for (i = 0; i < nabove_hispeed_delay; i++) + ret += sprintf(buf + ret, "%u%s", above_hispeed_delay[i], + i & 0x1 ? ":" : " "); + + --ret; + ret += sprintf(buf + ret, "\n"); + spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); + return ret; +} + +static ssize_t store_above_hispeed_delay( + struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + int ntokens; + unsigned int *new_above_hispeed_delay = NULL; + unsigned long flags; + + new_above_hispeed_delay = get_tokenized_data(buf, &ntokens); + if (IS_ERR(new_above_hispeed_delay)) + return PTR_RET(new_above_hispeed_delay); + + spin_lock_irqsave(&above_hispeed_delay_lock, flags); + if (above_hispeed_delay != default_above_hispeed_delay) + kfree(above_hispeed_delay); + above_hispeed_delay = new_above_hispeed_delay; + nabove_hispeed_delay = ntokens; + spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); + return count; + +} + +static struct global_attr above_hispeed_delay_attr = + __ATTR(above_hispeed_delay, S_IRUGO | S_IWUSR, + show_above_hispeed_delay, store_above_hispeed_delay); + +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_sampling_down_factor(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", sampling_down_factor); +} + +static ssize_t store_sampling_down_factor(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; + sampling_down_factor = val; + return count; +} + +static struct global_attr sampling_down_factor_attr = + __ATTR(sampling_down_factor, 0644, + show_sampling_down_factor, store_sampling_down_factor); + +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_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) { + cpufreq_interactive_boost(); + } + + 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; + 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 ssize_t show_io_is_busy(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", io_is_busy); +} + +static ssize_t store_io_is_busy(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; + io_is_busy = val; + return count; +} + +static struct global_attr io_is_busy_attr = __ATTR(io_is_busy, 0644, + show_io_is_busy, store_io_is_busy); + +static ssize_t show_sync_freq(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", sync_freq); +} + +static ssize_t store_sync_freq(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; + sync_freq = val; + return count; +} + +static struct global_attr sync_freq_attr = __ATTR(sync_freq, 0644, + show_sync_freq, store_sync_freq); + +static ssize_t show_up_threshold_any_cpu_load(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", up_threshold_any_cpu_load); +} + +static ssize_t store_up_threshold_any_cpu_load(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; + up_threshold_any_cpu_load = val; + return count; +} + +static struct global_attr up_threshold_any_cpu_load_attr = + __ATTR(up_threshold_any_cpu_load, 0644, + show_up_threshold_any_cpu_load, + store_up_threshold_any_cpu_load); + +static ssize_t show_up_threshold_any_cpu_freq(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", up_threshold_any_cpu_freq); +} + +static ssize_t store_up_threshold_any_cpu_freq(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; + up_threshold_any_cpu_freq = val; + return count; +} + +static struct global_attr up_threshold_any_cpu_freq_attr = + __ATTR(up_threshold_any_cpu_freq, 0644, + show_up_threshold_any_cpu_freq, + store_up_threshold_any_cpu_freq); + +static struct attribute *interactive_attributes[] = { + &target_loads_attr.attr, + &above_hispeed_delay_attr.attr, + &hispeed_freq_attr.attr, + &go_hispeed_load_attr.attr, + &min_sample_time_attr.attr, + &timer_rate_attr.attr, + &timer_slack.attr, + &boost.attr, + &boostpulse.attr, + &boostpulse_duration.attr, + &io_is_busy_attr.attr, + &sampling_down_factor_attr.attr, + &sync_freq_attr.attr, + &up_threshold_any_cpu_load_attr.attr, + &up_threshold_any_cpu_freq_attr.attr, + &two_phase_freq_attr.attr, + NULL, +}; + +static void interactive_input_event(struct input_handle *handle, + unsigned int type, + unsigned int code, int value) +{ + if (type == EV_SYN && code == SYN_REPORT) { + boostpulse_endtime = ktime_to_us(ktime_get()) + + boostpulse_duration_val; + cpufreq_interactive_boost(); + } +} + +static int interactive_input_connect(struct input_handler *handler, + struct input_dev *dev, const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "cpufreq"; + + error = input_register_handle(handle); + if (error) + goto err2; + + error = input_open_device(handle); + if (error) + goto err1; + + return 0; +err1: + input_unregister_handle(handle); +err2: + kfree(handle); + return error; +} + +static void interactive_input_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id interactive_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) }, + .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] = + BIT_MASK(ABS_MT_POSITION_X) | + BIT_MASK(ABS_MT_POSITION_Y) }, + }, /* multi-touch touchscreen */ + { + .flags = INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, + .absbit = { [BIT_WORD(ABS_X)] = + BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, + }, /* touchpad */ +}; + +static struct input_handler interactive_input_handler = { + .event = interactive_input_event, + .connect = interactive_input_connect, + .disconnect = interactive_input_disconnect, + .name = "intelliactive", + .id_table = interactive_ids, +}; + +static struct attribute_group interactive_attr_group = { + .attrs = interactive_attributes, + .name = "intelliactive", +}; + +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_intelliactive(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) { + 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); + del_timer_sync(&pcpu->cpu_timer); + del_timer_sync(&pcpu->cpu_slack_timer); + cpufreq_interactive_timer_start(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; + } + + if (!policy->cpu) + rc = input_register_handler + (&interactive_input_handler); + + 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; + pcpu->target_freq = 0; + del_timer_sync(&pcpu->cpu_timer); + del_timer_sync(&pcpu->cpu_slack_timer); + up_write(&pcpu->enable_sem); + } + + if (--active_count > 0) { + if (!policy->cpu) + input_unregister_handler(&interactive_input_handler); + 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); + for_each_cpu(j, policy->cpus) { + pcpu = &per_cpu(cpuinfo, j); + + /* hold write semaphore to avoid race */ + down_write(&pcpu->enable_sem); + if (pcpu->governor_enabled == 0) { + up_write(&pcpu->enable_sem); + continue; + } + + /* update target_freq firstly */ + if (policy->max < pcpu->target_freq) + pcpu->target_freq = policy->max; + else if (policy->min > pcpu->target_freq) + pcpu->target_freq = policy->min; + + /* Reschedule timer. + * Delete the timers, else the timer callback may + * return without re-arm the timer when failed + * acquire the semaphore. This race may cause timer + * stopped unexpectedly. + */ + del_timer_sync(&pcpu->cpu_timer); + del_timer_sync(&pcpu->cpu_slack_timer); + cpufreq_interactive_timer_start(j); + up_write(&pcpu->enable_sem); + } + break; + } + return 0; +} + +static void cpufreq_interactive_nop_timer(unsigned long data) +{ +} + +static int __init cpufreq_intelliactive_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); + spin_lock_init(&above_hispeed_delay_lock); + mutex_init(&gov_lock); + speedchange_task = + kthread_create(cpufreq_interactive_speedchange_task, NULL, + "cfintelliactive"); + 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_intelliactive); +} + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIACTIVE +fs_initcall(cpufreq_intelliactive_init); +#else +module_init(cpufreq_intelliactive_init); +#endif + +static void __exit cpufreq_interactive_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_intelliactive); + kthread_stop(speedchange_task); + put_task_struct(speedchange_task); +} + +module_exit(cpufreq_interactive_exit); + +MODULE_AUTHOR("Mike Chan "); +MODULE_AUTHOR("Paul Reioux "); +MODULE_DESCRIPTION("'cpufreq_intelliactive' - A cpufreq governor for " + "Latency sensitive workloads based on Google's Interactive"); +MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/cpufreq_intellidemand.c b/drivers/cpufreq/cpufreq_intellidemand.c new file mode 100644 index 00000000..4653db28 --- /dev/null +++ b/drivers/cpufreq/cpufreq_intellidemand.c @@ -0,0 +1,2469 @@ +/* + * drivers/cpufreq/cpufreq_intellidemand.c + * + * Copyright (C) 2001 Russell King + * (C) 2003 Venkatesh Pallipadi . + * Jun Nakajima + * (C) 2013 The Linux Foundation. All rights reserved. + * (C) 2013 Paul Reioux + * + * 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 + +#if 0 +#include +#endif + +#define INTELLIDEMAND_MAJOR_VERSION 5 +#define INTELLIDEMAND_MINOR_VERSION 5 + +/* + * dbs is used in this file as a shortform for demandbased switching + * It helps to keep variable names smaller, simpler + */ + +#define DEF_SAMPLING_RATE (120000) +#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) +#define DEF_FREQUENCY_UP_THRESHOLD (80) +#define DEF_SAMPLING_DOWN_FACTOR (1) +#define MAX_SAMPLING_DOWN_FACTOR (100000) +#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) +#define MICRO_FREQUENCY_UP_THRESHOLD (95) +#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) +#define MIN_FREQUENCY_UP_THRESHOLD (11) +#define MAX_FREQUENCY_UP_THRESHOLD (100) +#define MIN_FREQUENCY_DOWN_DIFFERENTIAL (1) +#define DBS_INPUT_EVENT_MIN_FREQ (700000) +#define DEF_UI_DYNAMIC_SAMPLING_RATE (30000) +#define DBS_UI_SAMPLING_MIN_TIMEOUT (30) +#define DBS_UI_SAMPLING_MAX_TIMEOUT (1000) +#define DBS_UI_SAMPLING_TIMEOUT (80) + +#define DEF_FREQ_STEP (10) +#define DEF_STEP_UP_EARLY_HISPEED (700000) +#define DEF_STEP_UP_INTERIM_HISPEED (1008000) +#define DEF_SAMPLING_EARLY_HISPEED_FACTOR (2) +#define DEF_SAMPLING_INTERIM_HISPEED_FACTOR (3) + +/* PATCH : SMART_UP */ +#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) + +#define SMART_UP_PLUS (0) +#define SMART_UP_SLOW_UP_AT_HIGH_FREQ (1) +#define SUP_MAX_STEP (3) +#define SUP_CORE_NUM (4) +#define SUP_SLOW_UP_DUR (5) +#define SUP_SLOW_UP_DUR_DEFAULT (2) + +#define SUP_HIGH_SLOW_UP_DUR (5) +#define SUP_FREQ_LEVEL (14) + +#if 0 +static unsigned long stored_sampling_rate; +#endif + +#if defined(SMART_UP_PLUS) +static unsigned int SUP_THRESHOLD_STEPS[SUP_MAX_STEP] = {85, 90, 95}; +static unsigned int SUP_FREQ_STEPS[SUP_MAX_STEP] = {4, 3, 2}; +//static unsigned int min_range = 108000; +typedef struct{ + unsigned int freq_idx; + unsigned int freq_value; +} freq_table_idx; +static freq_table_idx pre_freq_idx[SUP_CORE_NUM] = {}; + +#endif + + +#if defined(SMART_UP_SLOW_UP_AT_HIGH_FREQ) + +#define SUP_SLOW_UP_FREQUENCY (700000) +#define SUP_HIGH_SLOW_UP_FREQUENCY (1008000) +#define SUP_SLOW_UP_LOAD (95) + +typedef struct { + unsigned int hist_max_load[SUP_SLOW_UP_DUR]; + unsigned int hist_load_cnt; +} history_load; +static void reset_hist(history_load *hist_load); +static history_load hist_load[SUP_CORE_NUM] = {}; + +typedef struct { + unsigned int hist_max_load[SUP_HIGH_SLOW_UP_DUR]; + unsigned int hist_load_cnt; +} history_load_high; +static void reset_hist_high(history_load_high *hist_load); +static history_load_high hist_load_high[SUP_CORE_NUM] = {}; + +#endif + + +/* + * The polling frequency of this governor depends on the capability of + * the processor. Default polling frequency is 1000 times the transition + * latency of the processor. The governor will work on any processor with + * transition latency <= 10mS, using appropriate sampling + * rate. + * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL) + * this governor will not work. + * All times here are in uS. + */ +#define MIN_SAMPLING_RATE_RATIO (2) + +static unsigned int min_sampling_rate; +static unsigned int skip_intellidemand = 0; + +#define LATENCY_MULTIPLIER (1000) +#define MIN_LATENCY_MULTIPLIER (20) +#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) + +#define POWERSAVE_BIAS_MAXLEVEL (1000) +#define POWERSAVE_BIAS_MINLEVEL (-1000) + +static void do_dbs_timer(struct work_struct *work); +static int cpufreq_governor_dbs(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIDEMAND +static +#endif +struct cpufreq_governor cpufreq_gov_intellidemand = { + .name = "intellidemand", + .governor = cpufreq_governor_dbs, + .max_transition_latency = TRANSITION_LATENCY_LIMIT, + .owner = THIS_MODULE, +}; + +/* Sampling types */ +enum {DBS_NORMAL_SAMPLE, DBS_SUB_SAMPLE}; + +struct cpu_dbs_info_s { + cputime64_t prev_cpu_idle; + cputime64_t prev_cpu_iowait; + cputime64_t prev_cpu_wall; + cputime64_t prev_cpu_nice; + struct cpufreq_policy *cur_policy; + struct delayed_work work; + struct cpufreq_frequency_table *freq_table; + unsigned int freq_lo; + unsigned int freq_lo_jiffies; + unsigned int freq_hi_jiffies; + unsigned int rate_mult; + unsigned int prev_load; + unsigned int max_load; + int cpu; + unsigned int sample_type:1; + unsigned int freq_stay_count; + /* + * percpu mutex that serializes governor limit change with + * do_dbs_timer invocation. We do not want do_dbs_timer to run + * when user is changing the governor or limits. + */ + struct mutex timer_mutex; + + struct task_struct *sync_thread; + wait_queue_head_t sync_wq; + atomic_t src_sync_cpu; + atomic_t sync_enabled; +}; +static DEFINE_PER_CPU(struct cpu_dbs_info_s, id_cpu_dbs_info); + +static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info); +static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info); + +static unsigned int dbs_enable; /* number of CPUs using this policy */ + +static DEFINE_PER_CPU(struct task_struct *, up_task); +static spinlock_t input_boost_lock; +static bool input_event_boost = false; +static unsigned long ui_sampling_expired = 0; + +/* + * dbs_mutex protects dbs_enable and dbs_info during start/stop. + */ +static DEFINE_MUTEX(dbs_mutex); + +static struct workqueue_struct *dbs_wq; + +struct dbs_work_struct { + struct work_struct work; + unsigned int cpu; +}; + +static DEFINE_PER_CPU(struct dbs_work_struct, dbs_refresh_work); + +static struct dbs_tuners { + unsigned int sampling_rate; + unsigned int up_threshold; + unsigned int up_threshold_multi_core; + unsigned int down_differential; + unsigned int down_differential_multi_core; + unsigned int optimal_freq; + unsigned int up_threshold_any_cpu_load; + unsigned int sync_freq; + unsigned int ignore_nice; + unsigned int sampling_down_factor; + int powersave_bias; + unsigned int io_is_busy; + //20130711 smart_up + unsigned int smart_up; + unsigned int smart_slow_up_load; + unsigned int smart_slow_up_freq; + unsigned int smart_slow_up_dur; + unsigned int smart_high_slow_up_freq; + unsigned int smart_high_slow_up_dur; + unsigned int smart_each_off; + // end smart_up + unsigned int freq_step; + unsigned int step_up_early_hispeed; + unsigned int step_up_interim_hispeed; + unsigned int sampling_early_factor; + unsigned int sampling_interim_factor; + unsigned int two_phase_freq; + unsigned int origin_sampling_rate; + unsigned int ui_sampling_rate; + unsigned int ui_timeout; + unsigned int enable_boost_cpu; + +} dbs_tuners_ins = { + .up_threshold_multi_core = DEF_FREQUENCY_UP_THRESHOLD, + .up_threshold = DEF_FREQUENCY_UP_THRESHOLD, + .sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR, + .down_differential = DEF_FREQUENCY_DOWN_DIFFERENTIAL, + .down_differential_multi_core = MICRO_FREQUENCY_DOWN_DIFFERENTIAL, + .up_threshold_any_cpu_load = DEF_FREQUENCY_UP_THRESHOLD, + .ignore_nice = 0, + .powersave_bias = 0, + .sync_freq = 0, + .optimal_freq = 0, + //20130711 smart_up + .smart_up = SMART_UP_PLUS, + .smart_slow_up_load = SUP_SLOW_UP_LOAD, + .smart_slow_up_freq = SUP_SLOW_UP_FREQUENCY, + .smart_slow_up_dur = SUP_SLOW_UP_DUR_DEFAULT, + .smart_high_slow_up_freq = SUP_HIGH_SLOW_UP_FREQUENCY, + .smart_high_slow_up_dur = SUP_HIGH_SLOW_UP_DUR, + .smart_each_off = 0, + // end smart_up + .freq_step = DEF_FREQ_STEP, + .step_up_early_hispeed = DEF_STEP_UP_EARLY_HISPEED, + .step_up_interim_hispeed = DEF_STEP_UP_INTERIM_HISPEED, + .sampling_early_factor = DEF_SAMPLING_EARLY_HISPEED_FACTOR, + .sampling_interim_factor = DEF_SAMPLING_INTERIM_HISPEED_FACTOR, + .two_phase_freq = 0, + .ui_sampling_rate = DEF_UI_DYNAMIC_SAMPLING_RATE, + .ui_timeout = DBS_UI_SAMPLING_TIMEOUT, + .enable_boost_cpu = 1, + +}; + +static inline u64 get_cpu_idle_time_jiffy(unsigned int cpu, u64 *wall) +{ + u64 idle_time; + u64 cur_wall_time; + u64 busy_time; + + cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); + + busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; + + idle_time = cur_wall_time - busy_time; + if (wall) + *wall = jiffies_to_usecs(cur_wall_time); + + return jiffies_to_usecs(idle_time); +} + +static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall) +{ + u64 idle_time = get_cpu_idle_time_us(cpu, NULL); + + if (idle_time == -1ULL) + return get_cpu_idle_time_jiffy(cpu, wall); + else + idle_time += get_cpu_iowait_time_us(cpu, wall); + + return idle_time; +} + +static inline cputime64_t get_cpu_iowait_time(unsigned int cpu, cputime64_t *wall) +{ + u64 iowait_time = get_cpu_iowait_time_us(cpu, wall); + + if (iowait_time == -1ULL) + return 0; + + return iowait_time; +} + +/* + * Find right freq to be set now with powersave_bias on. + * Returns the freq_hi to be used right now and will set freq_hi_jiffies, + * freq_lo, and freq_lo_jiffies in percpu area for averaging freqs. + */ +static unsigned int powersave_bias_target(struct cpufreq_policy *policy, + unsigned int freq_next, + unsigned int relation) +{ + unsigned int freq_req, freq_avg; + unsigned int freq_hi, freq_lo; + unsigned int index = 0; + unsigned int jiffies_total, jiffies_hi, jiffies_lo; + int freq_reduc; + struct cpu_dbs_info_s *dbs_info = &per_cpu(id_cpu_dbs_info, + policy->cpu); + + if (!dbs_info->freq_table) { + dbs_info->freq_lo = 0; + dbs_info->freq_lo_jiffies = 0; + return freq_next; + } + + cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_next, + relation, &index); + freq_req = dbs_info->freq_table[index].frequency; + freq_reduc = freq_req * dbs_tuners_ins.powersave_bias / 1000; + freq_avg = freq_req - freq_reduc; + + /* Find freq bounds for freq_avg in freq_table */ + index = 0; + cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_avg, + CPUFREQ_RELATION_H, &index); + freq_lo = dbs_info->freq_table[index].frequency; + index = 0; + cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_avg, + CPUFREQ_RELATION_L, &index); + freq_hi = dbs_info->freq_table[index].frequency; + + /* Find out how long we have to be in hi and lo freqs */ + if (freq_hi == freq_lo) { + dbs_info->freq_lo = 0; + dbs_info->freq_lo_jiffies = 0; + return freq_lo; + } + jiffies_total = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + jiffies_hi = (freq_avg - freq_lo) * jiffies_total; + jiffies_hi += ((freq_hi - freq_lo) / 2); + jiffies_hi /= (freq_hi - freq_lo); + jiffies_lo = jiffies_total - jiffies_hi; + dbs_info->freq_lo = freq_lo; + dbs_info->freq_lo_jiffies = jiffies_lo; + dbs_info->freq_hi_jiffies = jiffies_hi; + return freq_hi; +} + +static int intellidemand_powersave_bias_setspeed(struct cpufreq_policy *policy, + struct cpufreq_policy *altpolicy, + int level) +{ + if (level == POWERSAVE_BIAS_MAXLEVEL) { + /* maximum powersave; set to lowest frequency */ + __cpufreq_driver_target(policy, + (altpolicy) ? altpolicy->min : policy->min, + CPUFREQ_RELATION_L); + return 1; + } else if (level == POWERSAVE_BIAS_MINLEVEL) { + /* minimum powersave; set to highest frequency */ + __cpufreq_driver_target(policy, + (altpolicy) ? altpolicy->max : policy->max, + CPUFREQ_RELATION_H); + return 1; + } + return 0; +} + +static void intellidemand_powersave_bias_init_cpu(int cpu) +{ + struct cpu_dbs_info_s *dbs_info = &per_cpu(id_cpu_dbs_info, cpu); + dbs_info->freq_table = cpufreq_frequency_get_table(cpu); + dbs_info->freq_lo = 0; +} + +static void intellidemand_powersave_bias_init(void) +{ + int i; + for_each_online_cpu(i) { + intellidemand_powersave_bias_init_cpu(i); + } +} + +void intellidemand_boost_cpu(int boost) +{ + int cpu; + + if (!dbs_tuners_ins.enable_boost_cpu) + return; + + for_each_online_cpu(cpu) { + struct cpufreq_policy *policy; + struct cpu_dbs_info_s *dbs_info; + + policy = cpufreq_cpu_get(cpu); + if (!policy) + continue; + dbs_info = &per_cpu(id_cpu_dbs_info, policy->cpu); + cpufreq_cpu_put(policy); + + mutex_lock(&dbs_info->timer_mutex); + if (boost) { + skip_intellidemand = 1; + __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H); + } else { + skip_intellidemand = 0; + } + mutex_unlock(&dbs_info->timer_mutex); + } +} +EXPORT_SYMBOL(intellidemand_boost_cpu); + +/************************** sysfs interface ************************/ + +static ssize_t show_sampling_rate_min(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", min_sampling_rate); +} + +define_one_global_ro(sampling_rate_min); + +/* cpufreq_intellidemand Governor Tunables */ +#define show_one(file_name, object) \ +static ssize_t show_##file_name \ +(struct kobject *kobj, struct attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%u\n", dbs_tuners_ins.object); \ +} +show_one(sampling_rate, sampling_rate); +show_one(io_is_busy, io_is_busy); +show_one(up_threshold, up_threshold); +show_one(up_threshold_multi_core, up_threshold_multi_core); +show_one(down_differential, down_differential); +show_one(sampling_down_factor, sampling_down_factor); +show_one(ignore_nice_load, ignore_nice); +show_one(optimal_freq, optimal_freq); +show_one(up_threshold_any_cpu_load, up_threshold_any_cpu_load); +show_one(sync_freq, sync_freq); +//20130711 smart_up +show_one(smart_up, smart_up); +show_one(smart_slow_up_load, smart_slow_up_load); +show_one(smart_slow_up_freq, smart_slow_up_freq); +show_one(smart_slow_up_dur, smart_slow_up_dur); +show_one(smart_high_slow_up_freq, smart_high_slow_up_freq); +show_one(smart_high_slow_up_dur, smart_high_slow_up_dur); +show_one(smart_each_off, smart_each_off); +// end smart_up +show_one(freq_step, freq_step); +show_one(step_up_early_hispeed, step_up_early_hispeed); +show_one(step_up_interim_hispeed, step_up_interim_hispeed); +show_one(sampling_early_factor, sampling_early_factor); +show_one(sampling_interim_factor, sampling_interim_factor); +show_one(enable_boost_cpu, enable_boost_cpu) + +static ssize_t show_powersave_bias +(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", dbs_tuners_ins.powersave_bias); +} + +/** + * update_sampling_rate - update sampling rate effective immediately if needed. + * @new_rate: new sampling rate + * + * If new rate is smaller than the old, simply updaing + * dbs_tuners_int.sampling_rate might not be appropriate. For example, + * if the original sampling_rate was 1 second and the requested new sampling + * rate is 10 ms because the user needs immediate reaction from intellidemand + * governor, but not sure if higher frequency will be required or not, + * then, the governor may change the sampling rate too late; up to 1 second + * later. Thus, if we are reducing the sampling rate, we need to make the + * new value effective immediately. + */ +static void update_sampling_rate(unsigned int new_rate) +{ + int cpu; + + dbs_tuners_ins.sampling_rate = new_rate + = max(new_rate, min_sampling_rate); + + get_online_cpus(); + for_each_online_cpu(cpu) { + struct cpufreq_policy *policy; + struct cpu_dbs_info_s *dbs_info; + unsigned long next_sampling, appointed_at; + + policy = cpufreq_cpu_get(cpu); + if (!policy) + continue; + dbs_info = &per_cpu(id_cpu_dbs_info, policy->cpu); + cpufreq_cpu_put(policy); + + mutex_lock(&dbs_info->timer_mutex); + + if (!delayed_work_pending(&dbs_info->work)) { + mutex_unlock(&dbs_info->timer_mutex); + continue; + } + + next_sampling = jiffies + usecs_to_jiffies(new_rate); + appointed_at = dbs_info->work.timer.expires; + + + if (time_before(next_sampling, appointed_at)) { + + mutex_unlock(&dbs_info->timer_mutex); + cancel_delayed_work_sync(&dbs_info->work); + mutex_lock(&dbs_info->timer_mutex); + + queue_delayed_work_on(dbs_info->cpu, dbs_wq, + &dbs_info->work, usecs_to_jiffies(new_rate)); + + } + mutex_unlock(&dbs_info->timer_mutex); + } + put_online_cpus(); +} + +show_one(ui_timeout, ui_timeout); + +static ssize_t store_ui_timeout(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + input = max(input, (unsigned int)DBS_UI_SAMPLING_MIN_TIMEOUT); + dbs_tuners_ins.ui_timeout = min(input, (unsigned int)DBS_UI_SAMPLING_MAX_TIMEOUT); + + return count; +} + +static int two_phase_freq_array[NR_CPUS] = {[0 ... NR_CPUS-1] = 700000} ; + +static ssize_t show_two_phase_freq +(struct kobject *kobj, struct attribute *attr, char *buf) +{ + int i = 0 ; + int shift = 0 ; + char *buf_pos = buf; + for ( i = 0 ; i < NR_CPUS; i++) { + shift = sprintf(buf_pos,"%d,",two_phase_freq_array[i]); + buf_pos += shift; + } + *(buf_pos-1) = '\0'; + return strlen(buf); +} + +static ssize_t store_two_phase_freq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + + int ret = 0; + if (NR_CPUS == 1) + ret = sscanf(buf,"%u",&two_phase_freq_array[0]); + else if (NR_CPUS == 2) + ret = sscanf(buf,"%u,%u",&two_phase_freq_array[0], + &two_phase_freq_array[1]); + else if (NR_CPUS == 4) + ret = sscanf(buf, "%u,%u,%u,%u", &two_phase_freq_array[0], + &two_phase_freq_array[1], + &two_phase_freq_array[2], + &two_phase_freq_array[3]); + if (ret < NR_CPUS) + return -EINVAL; + + return count; +} + +static int input_event_min_freq_array[NR_CPUS] = {[0 ... NR_CPUS-1] = DBS_INPUT_EVENT_MIN_FREQ} ; + +static ssize_t show_input_event_min_freq +(struct kobject *kobj, struct attribute *attr, char *buf) +{ + int i = 0 ; + int shift = 0 ; + char *buf_pos = buf; + for ( i = 0 ; i < NR_CPUS; i++) { + shift = sprintf(buf_pos,"%d,",input_event_min_freq_array[i]); + buf_pos += shift; + } + *(buf_pos-1) = '\0'; + return strlen(buf); +} + +static ssize_t store_input_event_min_freq(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + + int ret = 0; + if (NR_CPUS == 1) + ret = sscanf(buf,"%u",&input_event_min_freq_array[0]); + else if (NR_CPUS == 2) + ret = sscanf(buf,"%u,%u",&input_event_min_freq_array[0], + &input_event_min_freq_array[1]); + else if (NR_CPUS == 4) + ret = sscanf(buf, "%u,%u,%u,%u", &input_event_min_freq_array[0], + &input_event_min_freq_array[1], + &input_event_min_freq_array[2], + &input_event_min_freq_array[3]); + if (ret < NR_CPUS) + return -EINVAL; + + return count; +} + +show_one(ui_sampling_rate, ui_sampling_rate); + +static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + if (input == dbs_tuners_ins.origin_sampling_rate) + return count; + update_sampling_rate(input); + dbs_tuners_ins.origin_sampling_rate = dbs_tuners_ins.sampling_rate; + return count; +} + +static ssize_t store_ui_sampling_rate(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + dbs_tuners_ins.ui_sampling_rate = max(input, min_sampling_rate); + + return count; +} + +static ssize_t store_sync_freq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + dbs_tuners_ins.sync_freq = input; + return count; +} + +static ssize_t store_io_is_busy(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + dbs_tuners_ins.io_is_busy = !!input; + return count; +} + +static ssize_t store_optimal_freq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + dbs_tuners_ins.optimal_freq = input; + return count; +} + +static ssize_t store_up_threshold(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > MAX_FREQUENCY_UP_THRESHOLD || + input < MIN_FREQUENCY_UP_THRESHOLD) { + return -EINVAL; + } + dbs_tuners_ins.up_threshold = input; + return count; +} + +static ssize_t store_up_threshold_multi_core(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > MAX_FREQUENCY_UP_THRESHOLD || + input < MIN_FREQUENCY_UP_THRESHOLD) { + return -EINVAL; + } + dbs_tuners_ins.up_threshold_multi_core = input; + return count; +} + +static ssize_t store_up_threshold_any_cpu_load(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > MAX_FREQUENCY_UP_THRESHOLD || + input < MIN_FREQUENCY_UP_THRESHOLD) { + return -EINVAL; + } + dbs_tuners_ins.up_threshold_any_cpu_load = input; + return count; +} + +static ssize_t store_down_differential(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input >= dbs_tuners_ins.up_threshold || + input < MIN_FREQUENCY_DOWN_DIFFERENTIAL) { + return -EINVAL; + } + + dbs_tuners_ins.down_differential = input; + + return count; +} + +static ssize_t store_sampling_down_factor(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + unsigned int input, j; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1) + return -EINVAL; + dbs_tuners_ins.sampling_down_factor = input; + + /* Reset down sampling multiplier in case it was active */ + for_each_online_cpu(j) { + struct cpu_dbs_info_s *dbs_info; + dbs_info = &per_cpu(id_cpu_dbs_info, j); + dbs_info->rate_mult = 1; + } + return count; +} + +static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + unsigned int j; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + if (input > 1) + input = 1; + + if (input == dbs_tuners_ins.ignore_nice) { /* nothing to do */ + return count; + } + dbs_tuners_ins.ignore_nice = input; + + /* we need to re-evaluate prev_cpu_idle */ + for_each_online_cpu(j) { + struct cpu_dbs_info_s *dbs_info; + dbs_info = &per_cpu(id_cpu_dbs_info, j); + dbs_info->prev_cpu_idle = get_cpu_idle_time(j, + &dbs_info->prev_cpu_wall); + if (dbs_tuners_ins.ignore_nice) + dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + + } + return count; +} + +static ssize_t store_powersave_bias(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + int input = 0; + int bypass = 0; + int ret, cpu, reenable_timer, j; + struct cpu_dbs_info_s *dbs_info; + + struct cpumask cpus_timer_done; + cpumask_clear(&cpus_timer_done); + + ret = sscanf(buf, "%d", &input); + + if (ret != 1) + return -EINVAL; + + if (input >= POWERSAVE_BIAS_MAXLEVEL) { + input = POWERSAVE_BIAS_MAXLEVEL; + bypass = 1; + } else if (input <= POWERSAVE_BIAS_MINLEVEL) { + input = POWERSAVE_BIAS_MINLEVEL; + bypass = 1; + } + + if (input == dbs_tuners_ins.powersave_bias) { + /* no change */ + return count; + } + + reenable_timer = ((dbs_tuners_ins.powersave_bias == + POWERSAVE_BIAS_MAXLEVEL) || + (dbs_tuners_ins.powersave_bias == + POWERSAVE_BIAS_MINLEVEL)); + + dbs_tuners_ins.powersave_bias = input; + + get_online_cpus(); + mutex_lock(&dbs_mutex); + + if (!bypass) { + if (reenable_timer) { + /* reinstate dbs timer */ + for_each_online_cpu(cpu) { + if (lock_policy_rwsem_write(cpu) < 0) + continue; + + dbs_info = &per_cpu(id_cpu_dbs_info, cpu); + + for_each_cpu(j, &cpus_timer_done) { + if (!dbs_info->cur_policy) { + pr_err("Dbs policy is NULL\n"); + goto skip_this_cpu; + } + if (cpumask_test_cpu(j, dbs_info-> + cur_policy->cpus)) + goto skip_this_cpu; + } + + cpumask_set_cpu(cpu, &cpus_timer_done); + if (dbs_info->cur_policy) { + /* restart dbs timer */ + dbs_timer_init(dbs_info); + /* Enable frequency synchronization + * of CPUs */ + atomic_set(&dbs_info->sync_enabled, 1); + } +skip_this_cpu: + unlock_policy_rwsem_write(cpu); + } + } + intellidemand_powersave_bias_init(); + } else { + /* running at maximum or minimum frequencies; cancel + dbs timer as periodic load sampling is not necessary */ + for_each_online_cpu(cpu) { + if (lock_policy_rwsem_write(cpu) < 0) + continue; + + dbs_info = &per_cpu(id_cpu_dbs_info, cpu); + + for_each_cpu(j, &cpus_timer_done) { + if (!dbs_info->cur_policy) { + pr_err("Dbs policy is NULL\n"); + goto skip_this_cpu_bypass; + } + if (cpumask_test_cpu(j, dbs_info-> + cur_policy->cpus)) + goto skip_this_cpu_bypass; + } + + cpumask_set_cpu(cpu, &cpus_timer_done); + + if (dbs_info->cur_policy) { + /* cpu using intellidemand, cancel dbs timer */ + dbs_timer_exit(dbs_info); + /* Disable frequency synchronization of + * CPUs to avoid re-queueing of work from + * sync_thread */ + atomic_set(&dbs_info->sync_enabled, 0); + + mutex_lock(&dbs_info->timer_mutex); + intellidemand_powersave_bias_setspeed( + dbs_info->cur_policy, + NULL, + input); + mutex_unlock(&dbs_info->timer_mutex); + + } +skip_this_cpu_bypass: + unlock_policy_rwsem_write(cpu); + } + } + + mutex_unlock(&dbs_mutex); + put_online_cpus(); + + return count; +} + +static ssize_t store_enable_boost_cpu(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if(ret != 1) + return -EINVAL; + + dbs_tuners_ins.enable_boost_cpu = (input > 0 ? input : 0); + return count; +} + +/* PATCH : SMART_UP */ +#if defined(SMART_UP_SLOW_UP_AT_HIGH_FREQ) +static void reset_hist(history_load *hist_load) +{ int i; + + for (i = 0; i < SUP_SLOW_UP_DUR ; i++) + hist_load->hist_max_load[i] = 0; + + hist_load->hist_load_cnt = 0; +} + + +static void reset_hist_high(history_load_high *hist_load) +{ int i; + + for (i = 0; i < SUP_HIGH_SLOW_UP_DUR ; i++) + hist_load->hist_max_load[i] = 0; + + hist_load->hist_load_cnt = 0; +} + +#endif + +//20130711 smart_up +static ssize_t store_smart_up(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + unsigned int i; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + if (input > 1 ){ + input = 1; + }else if (input < 0 ){ + input = 0; + } + + // buffer reset + for_each_online_cpu(i){ + reset_hist(&hist_load[i]); + reset_hist_high(&hist_load_high[i]); + } + dbs_tuners_ins.smart_up = input; + return count; +} + +static ssize_t store_smart_slow_up_load(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + unsigned int i; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + if (input > 100 ){ + input = 100; + }else if (input < 0){ + input = 0; + } + // buffer reset + for_each_online_cpu(i){ + reset_hist(&hist_load[i]); + reset_hist_high(&hist_load_high[i]); + } + dbs_tuners_ins.smart_slow_up_load = input; + return count; +} +static ssize_t store_smart_slow_up_freq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + unsigned int i; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + if (input < 0) + input = 0; + // buffer reset + for_each_online_cpu(i){ + reset_hist(&hist_load[i]); + reset_hist_high(&hist_load_high[i]); + } + dbs_tuners_ins.smart_slow_up_freq = input; + return count; +} +static ssize_t store_smart_slow_up_dur(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + unsigned int i; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + if (input > SUP_SLOW_UP_DUR ){ + input = SUP_SLOW_UP_DUR; + }else if (input < 1 ){ + input = 1; + } + // buffer reset + for_each_online_cpu(i){ + reset_hist(&hist_load[i]); + reset_hist_high(&hist_load_high[i]); + } + dbs_tuners_ins.smart_slow_up_dur = input; + return count; +} +static ssize_t store_smart_high_slow_up_freq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + unsigned int i; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + if (input < 0) + input = 0; + // buffer reset + for_each_online_cpu(i){ + reset_hist(&hist_load[i]); + reset_hist_high(&hist_load_high[i]); + } + dbs_tuners_ins.smart_high_slow_up_freq = input; + return count; +} +static ssize_t store_smart_high_slow_up_dur(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + unsigned int i; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + if (input > SUP_HIGH_SLOW_UP_DUR ){ + input = SUP_HIGH_SLOW_UP_DUR; + }else if (input < 1 ){ + input = 1; + } + // buffer reset + for_each_online_cpu(i){ + reset_hist(&hist_load[i]); + reset_hist_high(&hist_load_high[i]); + } + dbs_tuners_ins.smart_high_slow_up_dur = input; + return count; +} +static ssize_t store_smart_each_off(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + unsigned int i; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + if (input > SUP_CORE_NUM){ + input = SUP_CORE_NUM; + }else if ( input < 0){ + input = 0; + } + // buffer reset + for_each_online_cpu(i){ + reset_hist(&hist_load[i]); + reset_hist_high(&hist_load_high[i]); + } + dbs_tuners_ins.smart_each_off = input; + + return count; +} +//end smart_up + +static ssize_t store_freq_step(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > 100 || + input < 0) { + return -EINVAL; + } + dbs_tuners_ins.freq_step = input; + return count; +} + +static ssize_t store_step_up_early_hispeed(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > 2265600 || + input < 0) { + return -EINVAL; + } + dbs_tuners_ins.step_up_early_hispeed = input; + return count; +} + +static ssize_t store_step_up_interim_hispeed(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > DEF_STEP_UP_INTERIM_HISPEED || + input < 0) { + return -EINVAL; + } + dbs_tuners_ins.step_up_interim_hispeed = input; + return count; +} + +static ssize_t store_sampling_early_factor(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input < 1) + return -EINVAL; + dbs_tuners_ins.sampling_early_factor = input; + return count; +} + +static ssize_t store_sampling_interim_factor(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input < 1) + return -EINVAL; + dbs_tuners_ins.sampling_interim_factor = input; + return count; +} + +define_one_global_rw(sampling_rate); +define_one_global_rw(io_is_busy); +define_one_global_rw(up_threshold); +define_one_global_rw(down_differential); +define_one_global_rw(sampling_down_factor); +define_one_global_rw(ignore_nice_load); +define_one_global_rw(powersave_bias); +define_one_global_rw(up_threshold_multi_core); +define_one_global_rw(optimal_freq); +define_one_global_rw(up_threshold_any_cpu_load); +define_one_global_rw(sync_freq); +//20130711 smart_up +define_one_global_rw(smart_up); +define_one_global_rw(smart_slow_up_load); +define_one_global_rw(smart_slow_up_freq); +define_one_global_rw(smart_slow_up_dur); +define_one_global_rw(smart_high_slow_up_freq); +define_one_global_rw(smart_high_slow_up_dur); +define_one_global_rw(smart_each_off); +// end smart_up +define_one_global_rw(freq_step); +define_one_global_rw(step_up_early_hispeed); +define_one_global_rw(step_up_interim_hispeed); +define_one_global_rw(sampling_early_factor); +define_one_global_rw(sampling_interim_factor); +define_one_global_rw(two_phase_freq); +define_one_global_rw(input_event_min_freq); +define_one_global_rw(ui_sampling_rate); +define_one_global_rw(ui_timeout); +define_one_global_rw(enable_boost_cpu); + +static struct attribute *dbs_attributes[] = { + &sampling_rate_min.attr, + &sampling_rate.attr, + &up_threshold.attr, + &down_differential.attr, + &sampling_down_factor.attr, + &ignore_nice_load.attr, + &powersave_bias.attr, + &io_is_busy.attr, + &up_threshold_multi_core.attr, + &optimal_freq.attr, + &up_threshold_any_cpu_load.attr, + &sync_freq.attr, + //20130711 smart_up + &smart_up.attr, + &smart_slow_up_load.attr, + &smart_slow_up_freq.attr, + &smart_slow_up_dur.attr, + &smart_high_slow_up_freq.attr, + &smart_high_slow_up_dur.attr, + &smart_each_off.attr, + // end smart_up + &freq_step.attr, + &step_up_early_hispeed.attr, + &step_up_interim_hispeed.attr, + &sampling_early_factor.attr, + &sampling_interim_factor.attr, + &two_phase_freq.attr, + &input_event_min_freq.attr, + &ui_sampling_rate.attr, + &ui_timeout.attr, + &enable_boost_cpu.attr, + + NULL +}; + +static struct attribute_group dbs_attr_group = { + .attrs = dbs_attributes, + .name = "intellidemand", +}; + +/************************** sysfs end ************************/ + +static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq) +{ + if (dbs_tuners_ins.powersave_bias) + freq = powersave_bias_target(p, freq, CPUFREQ_RELATION_H); + else if (p->cur == p->max) + return; + + __cpufreq_driver_target(p, freq, dbs_tuners_ins.powersave_bias ? + CPUFREQ_RELATION_L : CPUFREQ_RELATION_H); +} + +int set_two_phase_freq(int cpufreq) +{ + int i = 0; + for ( i = 0 ; i < NR_CPUS; i++) + two_phase_freq_array[i] = cpufreq; + return 0; +} + +void set_two_phase_freq_by_cpu ( int cpu_nr, int cpufreq){ + two_phase_freq_array[cpu_nr-1] = cpufreq; +} + +static int input_event_boosted(void) +{ + unsigned long flags; + + + spin_lock_irqsave(&input_boost_lock, flags); + if (input_event_boost) { + if (time_before(jiffies, ui_sampling_expired)) { + spin_unlock_irqrestore(&input_boost_lock, flags); + return 1; + } + input_event_boost = false; + dbs_tuners_ins.sampling_rate = dbs_tuners_ins.origin_sampling_rate; + } + spin_unlock_irqrestore(&input_boost_lock, flags); + + return 0; +} + +static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) +{ + +#if defined(SMART_UP_PLUS) + unsigned int max_load = 0; + unsigned int core_j = 0; +#endif + + /* Extrapolated load of this CPU */ + unsigned int load_at_max_freq = 0; + unsigned int max_load_freq; + /* Current load across this CPU */ + unsigned int cur_load = 0; + unsigned int max_load_other_cpu = 0; + struct cpufreq_policy *policy; + unsigned int j; + static unsigned int phase = 0; + static unsigned int counter = 0; + unsigned int nr_cpus; + + this_dbs_info->freq_lo = 0; + policy = this_dbs_info->cur_policy; + + /* + * Every sampling_rate, we check, if current idle time is less + * than 20% (default), then we try to increase frequency + * Every sampling_rate, we look for a the lowest + * frequency which can sustain the load while keeping idle time over + * 30%. If such a frequency exist, we try to decrease to this frequency. + * + * Any frequency increase takes it to the maximum frequency. + * Frequency reduction happens at minimum steps of + * 5% (default) of current frequency + */ + + /* Get Absolute Load - in terms of freq */ + max_load_freq = 0; + + for_each_cpu(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + cputime64_t cur_wall_time, cur_idle_time, cur_iowait_time; + unsigned int idle_time, wall_time, iowait_time; + unsigned int load_freq; + int freq_avg; + + j_dbs_info = &per_cpu(id_cpu_dbs_info, j); + + cur_idle_time = get_cpu_idle_time(j, &cur_wall_time); + cur_iowait_time = get_cpu_iowait_time(j, &cur_wall_time); + + wall_time = (unsigned int) + (cur_wall_time - j_dbs_info->prev_cpu_wall); + j_dbs_info->prev_cpu_wall = cur_wall_time; + + idle_time = (unsigned int) + (cur_idle_time - j_dbs_info->prev_cpu_idle); + j_dbs_info->prev_cpu_idle = cur_idle_time; + + iowait_time = (unsigned int) + (cur_iowait_time - j_dbs_info->prev_cpu_iowait); + j_dbs_info->prev_cpu_iowait = cur_iowait_time; + + if (dbs_tuners_ins.ignore_nice) { + u64 cur_nice; + unsigned long cur_nice_jiffies; + + cur_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE] - + j_dbs_info->prev_cpu_nice; + /* + * Assumption: nice time between sampling periods will + * be less than 2^32 jiffies for 32 bit sys + */ + cur_nice_jiffies = (unsigned long) + cputime64_to_jiffies64(cur_nice); + + j_dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + idle_time += jiffies_to_usecs(cur_nice_jiffies); + } + + /* + * For the purpose of intellidemand, waiting for disk IO is an + * indication that you're performance critical, and not that + * the system is actually idle. So subtract the iowait time + * from the cpu idle time. + */ + + if (dbs_tuners_ins.io_is_busy && idle_time >= iowait_time) + idle_time -= iowait_time; + + if (unlikely(!wall_time || wall_time < idle_time)) + continue; + + /* + * If the CPU had gone completely idle, and a task just woke up + * on this CPU now, it would be unfair to calculate 'load' the + * usual way for this elapsed time-window, because it will show + * near-zero load, irrespective of how CPU intensive the new + * task is. This is undesirable for latency-sensitive bursty + * workloads. + * + * To avoid this, we reuse the 'load' from the previous + * time-window and give this task a chance to start with a + * reasonably high CPU frequency. + * + * Detecting this situation is easy: the governor's deferrable + * timer would not have fired during CPU-idle periods. Hence + * an unusually large 'wall_time' indicates this scenario. + */ + if (unlikely(wall_time > (2 * dbs_tuners_ins.sampling_rate))) { + cur_load = j_dbs_info->prev_load; + } else { + cur_load = 100 * (wall_time - idle_time) / wall_time; + } + j_dbs_info->max_load = max(cur_load, j_dbs_info->prev_load); + j_dbs_info->prev_load = cur_load; + freq_avg = __cpufreq_driver_getavg(policy, j); + if (freq_avg <= 0) + freq_avg = policy->cur; + + load_freq = cur_load * freq_avg; + if (load_freq > max_load_freq) + max_load_freq = load_freq; + +#if defined(SMART_UP_PLUS) + max_load = cur_load; + core_j = j; +#endif + + } + + for_each_online_cpu(j) { + struct cpu_dbs_info_s *j_dbs_info; + j_dbs_info = &per_cpu(id_cpu_dbs_info, j); + + if (j == policy->cpu) + continue; + + if (max_load_other_cpu < j_dbs_info->max_load) + max_load_other_cpu = j_dbs_info->max_load; + /* + * The other cpu could be running at higher frequency + * but may not have completed it's sampling_down_factor. + * For that case consider other cpu is loaded so that + * frequency imbalance does not occur. + */ + + if ((j_dbs_info->cur_policy != NULL) + && (j_dbs_info->cur_policy->cur == + j_dbs_info->cur_policy->max)) { + + if (policy->cur >= dbs_tuners_ins.optimal_freq) + max_load_other_cpu = + dbs_tuners_ins.up_threshold_any_cpu_load; + } + } + + /* calculate the scaled load across CPU */ + load_at_max_freq = (cur_load * policy->cur)/policy->cpuinfo.max_freq; + + cpufreq_notify_utilization(policy, load_at_max_freq); + +/* PATCH : SMART_UP */ + if (dbs_tuners_ins.smart_up && ( core_j + 1 ) > dbs_tuners_ins.smart_each_off ){ + + if (max_load_freq > SUP_THRESHOLD_STEPS[0] * policy->cur) { + int smart_up_inc = + (policy->max - policy->cur) / SUP_FREQ_STEPS[0]; + int freq_next = 0; + int i = 0; + + //20130429 UPDATE + int check_idx = 0; + int check_freq = 0; + int temp_up_inc =0; + + if (counter < 5) { + counter++; + if (counter > 2) { + phase = 1; + } + } + + nr_cpus = num_online_cpus(); + dbs_tuners_ins.two_phase_freq = two_phase_freq_array[nr_cpus-1]; + if (dbs_tuners_ins.two_phase_freq < policy->cur) + phase = 1; + if (dbs_tuners_ins.two_phase_freq != 0 && phase == 0) { + dbs_freq_increase(policy, dbs_tuners_ins.two_phase_freq); + } else { + if (policy->cur < policy->max) + this_dbs_info->rate_mult = + dbs_tuners_ins.sampling_down_factor; + dbs_freq_increase(policy, policy->max); + } + + for (i = (SUP_MAX_STEP - 1); i > 0; i--) { + if (max_load_freq > SUP_THRESHOLD_STEPS[i] + * policy->cur) { + smart_up_inc = (policy->max - policy->cur) + / SUP_FREQ_STEPS[i]; + + break; + } + } + + //20130429 UPDATE + check_idx = pre_freq_idx[core_j].freq_idx; + check_freq = pre_freq_idx[core_j].freq_value; + if ( ( check_idx == 0) + || (this_dbs_info->freq_table[check_idx].frequency + != policy->cur) ) + { + int i = 0; + for( i =0; i < SUP_FREQ_LEVEL; i ++) + { + if (this_dbs_info->freq_table[i].frequency == policy->cur) + { + + pre_freq_idx[core_j].freq_idx = i; + pre_freq_idx[core_j].freq_value = policy->cur; + check_idx = i; + check_freq = policy->cur; + break; + } + } + + } + if( check_idx < SUP_FREQ_LEVEL-1 ){ + temp_up_inc = + this_dbs_info->freq_table[check_idx + 1].frequency + - check_freq; + } + + if (smart_up_inc < temp_up_inc ) + smart_up_inc = temp_up_inc; + + freq_next = MIN((policy->cur + smart_up_inc), policy->max); + + + if (policy->cur >= dbs_tuners_ins.smart_high_slow_up_freq){ + int idx = hist_load_high[core_j].hist_load_cnt; + int avg_hist_load = 0; + + if (idx >= dbs_tuners_ins.smart_high_slow_up_dur) + idx = 0; + + hist_load_high[core_j].hist_max_load[idx] = max_load; + hist_load_high[core_j].hist_load_cnt = idx + 1; + + /* note : check history_load and get_sum_hist_load */ + if (hist_load_high[core_j]. + hist_max_load[dbs_tuners_ins.smart_high_slow_up_dur - 1] > 0) { + int sum_hist_load_freq = 0; + int i = 0; + for (i = 0; i < dbs_tuners_ins.smart_high_slow_up_dur; i++) + sum_hist_load_freq += + hist_load_high[core_j].hist_max_load[i]; + + avg_hist_load = sum_hist_load_freq + /dbs_tuners_ins.smart_high_slow_up_dur; + + if (avg_hist_load > dbs_tuners_ins.smart_slow_up_load){ + reset_hist_high(&hist_load_high[core_j]); + freq_next = MIN((policy->cur + temp_up_inc), policy->max); + } else + freq_next = policy->cur; + } else { + freq_next = policy->cur; + } + + } else if (policy->cur >= dbs_tuners_ins.smart_slow_up_freq ) { + int idx = hist_load[core_j].hist_load_cnt; + int avg_hist_load = 0; + + if (idx >= dbs_tuners_ins.smart_slow_up_dur) + idx = 0; + + hist_load[core_j].hist_max_load[idx] = max_load; + hist_load[core_j].hist_load_cnt = idx + 1; + + /* note : check history_load and get_sum_hist_load */ + if (hist_load[core_j]. + hist_max_load[dbs_tuners_ins.smart_slow_up_dur - 1] > 0) { + int sum_hist_load_freq = 0; + int i = 0; + for (i = 0; i < dbs_tuners_ins.smart_slow_up_dur; i++) + sum_hist_load_freq += + hist_load[core_j].hist_max_load[i]; + + avg_hist_load = sum_hist_load_freq + / dbs_tuners_ins.smart_slow_up_dur ; + + if (avg_hist_load > dbs_tuners_ins.smart_slow_up_load){ + reset_hist(&hist_load[core_j]); + freq_next = MIN((policy->cur + temp_up_inc), policy->max); + } else + freq_next = policy->cur; + } else { + freq_next = policy->cur; + } + } else { + reset_hist(&hist_load[core_j]); + } + if (freq_next == policy->max) + this_dbs_info->rate_mult = + dbs_tuners_ins.sampling_down_factor; + + dbs_freq_increase(policy, freq_next); + + return; + } + }else{ + /* Check for frequency increase */ + if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) { + int target; + int inc; + + if (policy->cur < dbs_tuners_ins.step_up_early_hispeed) { + target = dbs_tuners_ins.step_up_early_hispeed; + }else if (policy->cur < dbs_tuners_ins.step_up_interim_hispeed) { + if(policy->cur == dbs_tuners_ins.step_up_early_hispeed) { + if(this_dbs_info->freq_stay_count < + dbs_tuners_ins.sampling_early_factor) { + this_dbs_info->freq_stay_count++; + return; + } + } + this_dbs_info->freq_stay_count = 1; + inc = (policy->max * dbs_tuners_ins.freq_step) / 100; + target = min(dbs_tuners_ins.step_up_interim_hispeed, + policy->cur + inc); + }else { + if(policy->cur == dbs_tuners_ins.step_up_interim_hispeed) { + if(this_dbs_info->freq_stay_count < + dbs_tuners_ins.sampling_interim_factor) { + this_dbs_info->freq_stay_count++; + return; + } + } + this_dbs_info->freq_stay_count = 1; + target = policy->max; + + //int inc = (policy->max * dbs_tuners_ins.freq_step) / 100; + //target = min(policy->max, policy->cur + inc); + } + + pr_debug("%s: cpu=%d, cur=%d, target=%d\n", + __func__, policy->cpu, policy->cur, target); + + /* If switching to max speed, apply sampling_down_factor */ + if (target == policy->max) + this_dbs_info->rate_mult = + dbs_tuners_ins.sampling_down_factor; + + dbs_freq_increase(policy, target); + return; + } + } + if (counter > 0) { + counter--; + if (counter == 0) { + phase = 0; + } + } + + if (num_online_cpus() > 1) { + + if (max_load_other_cpu > + dbs_tuners_ins.up_threshold_any_cpu_load) { + if (policy->cur < dbs_tuners_ins.sync_freq) + dbs_freq_increase(policy, + dbs_tuners_ins.sync_freq); + return; + } + + if (max_load_freq > dbs_tuners_ins.up_threshold_multi_core * + policy->cur) { + if (policy->cur < dbs_tuners_ins.optimal_freq) + dbs_freq_increase(policy, + dbs_tuners_ins.optimal_freq); + return; + } + } + + if (input_event_boosted()) { + return; + } + + /* Check for frequency decrease */ + /* if we cannot reduce the frequency anymore, break out early */ + if (policy->cur == policy->min) + return; + + /* + * The optimal frequency is the frequency that is the lowest that + * can support the current CPU usage without triggering the up + * policy. To be safe, we focus 10 points under the threshold. + */ + if (max_load_freq < + (dbs_tuners_ins.up_threshold - dbs_tuners_ins.down_differential) * + policy->cur) { + unsigned int freq_next; + freq_next = max_load_freq / + (dbs_tuners_ins.up_threshold - + dbs_tuners_ins.down_differential); + + pr_debug("%s: cpu=%d, cur=%d, target=%d (down)\n", + __func__, policy->cpu, policy->cur, freq_next); + +/* PATCH : SMART_UP */ + if (dbs_tuners_ins.smart_up && ( core_j + 1 ) > dbs_tuners_ins.smart_each_off ){ + + if (freq_next >= dbs_tuners_ins.smart_high_slow_up_freq){ + int idx = hist_load_high[core_j].hist_load_cnt; + + if (idx >= dbs_tuners_ins.smart_high_slow_up_dur ) + idx = 0; + + hist_load_high[core_j].hist_max_load[idx] = max_load; + hist_load_high[core_j].hist_load_cnt = idx + 1; + + + }else if (freq_next >= dbs_tuners_ins.smart_slow_up_freq) { + int idx = hist_load[core_j].hist_load_cnt; + + if (idx >= dbs_tuners_ins.smart_slow_up_dur) + idx = 0; + + hist_load[core_j].hist_max_load[idx] = max_load; + hist_load[core_j].hist_load_cnt = idx + 1; + + reset_hist_high(&hist_load_high[core_j]); + + + } else if (policy->cur >= dbs_tuners_ins.smart_slow_up_freq) { + reset_hist(&hist_load[core_j]); + reset_hist_high(&hist_load_high[core_j]); + + + } + } +//#endif + + /* No longer fully busy, reset rate_mult */ + this_dbs_info->rate_mult = 1; + this_dbs_info->freq_stay_count = 1; + + + if (freq_next < policy->min) + freq_next = policy->min; + + if (num_online_cpus() > 1) { + if (max_load_other_cpu > + (dbs_tuners_ins.up_threshold_multi_core - + dbs_tuners_ins.down_differential) && + freq_next < dbs_tuners_ins.sync_freq) + freq_next = dbs_tuners_ins.sync_freq; + + if (max_load_freq > + ((dbs_tuners_ins.up_threshold_multi_core - + dbs_tuners_ins.down_differential_multi_core) * + policy->cur) && + freq_next < dbs_tuners_ins.optimal_freq) + freq_next = dbs_tuners_ins.optimal_freq; + + } + if (!dbs_tuners_ins.powersave_bias) { + __cpufreq_driver_target(policy, freq_next, + CPUFREQ_RELATION_L); + } else { + int freq = powersave_bias_target(policy, freq_next, + CPUFREQ_RELATION_L); + __cpufreq_driver_target(policy, freq, + CPUFREQ_RELATION_L); + } + } +} + +static void do_dbs_timer(struct work_struct *work) +{ + struct cpu_dbs_info_s *dbs_info = + container_of(work, struct cpu_dbs_info_s, work.work); + unsigned int cpu = dbs_info->cpu; + int sample_type = dbs_info->sample_type; + int delay = msecs_to_jiffies(50); + + if (skip_intellidemand) + goto sched_wait; + + mutex_lock(&dbs_info->timer_mutex); + + /* Common NORMAL_SAMPLE setup */ + dbs_info->sample_type = DBS_NORMAL_SAMPLE; + if (!dbs_tuners_ins.powersave_bias || + sample_type == DBS_NORMAL_SAMPLE) { + dbs_check_cpu(dbs_info); + if (dbs_info->freq_lo) { + /* Setup timer for SUB_SAMPLE */ + dbs_info->sample_type = DBS_SUB_SAMPLE; + delay = dbs_info->freq_hi_jiffies; + } else { + /* We want all CPUs to do sampling nearly on + * same jiffy + */ + delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate + * dbs_info->rate_mult); + + if (num_online_cpus() > 1) + delay -= jiffies % delay; + } + } else { + if (input_event_boosted()) + goto sched_wait; + + __cpufreq_driver_target(dbs_info->cur_policy, + dbs_info->freq_lo, CPUFREQ_RELATION_H); + delay = dbs_info->freq_lo_jiffies; + } +sched_wait: + queue_delayed_work_on(cpu, dbs_wq, &dbs_info->work, delay); + mutex_unlock(&dbs_info->timer_mutex); +} + +static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) +{ + /* We want all CPUs to do sampling nearly on same jiffy */ + int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + + if (num_online_cpus() > 1) + delay -= jiffies % delay; + + dbs_info->sample_type = DBS_NORMAL_SAMPLE; + INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer); + queue_delayed_work_on(dbs_info->cpu, dbs_wq, &dbs_info->work, delay); +} + +static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info) +{ + cancel_delayed_work_sync(&dbs_info->work); +} + +/* + * Not all CPUs want IO time to be accounted as busy; this dependson how + * efficient idling at a higher frequency/voltage is. + * Pavel Machek says this is not so for various generations of AMD and old + * Intel systems. + * Mike Chan (androidlcom) calis this is also not true for ARM. + * Because of this, whitelist specific known (series) of CPUs by default, and + * leave all others up to the user. + */ +static int should_io_be_busy(void) +{ +#if defined(CONFIG_X86) + /* + * For Intel, Core 2 (model 15) andl later have an efficient idle. + */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && + boot_cpu_data.x86 == 6 && + boot_cpu_data.x86_model >= 15) + return 1; +#endif + return 0; +} + +static void dbs_refresh_callback(struct work_struct *work) +{ + struct cpufreq_policy *policy; + struct cpu_dbs_info_s *this_dbs_info; + struct dbs_work_struct *dbs_work; + unsigned int cpu; + + dbs_work = container_of(work, struct dbs_work_struct, work); + cpu = dbs_work->cpu; + + get_online_cpus(); + + if (lock_policy_rwsem_write(cpu) < 0) + goto bail_acq_sema_failed; + + this_dbs_info = &per_cpu(id_cpu_dbs_info, cpu); + policy = this_dbs_info->cur_policy; + if (!policy) { + /* CPU not using intellidemand governor */ + goto bail_incorrect_governor; + } + + if (policy->cur < policy->max) { + /* + * Arch specific cpufreq driver may fail. + * Don't update governor frequency upon failure. + */ + if (__cpufreq_driver_target(policy, policy->max, + CPUFREQ_RELATION_L) >= 0) + policy->cur = policy->max; + + this_dbs_info->prev_cpu_idle = get_cpu_idle_time(cpu, + &this_dbs_info->prev_cpu_wall); + } + +bail_incorrect_governor: + unlock_policy_rwsem_write(cpu); + +bail_acq_sema_failed: + put_online_cpus(); + return; +} + +static int dbs_migration_notify(struct notifier_block *nb, + unsigned long target_cpu, void *arg) +{ + struct cpu_dbs_info_s *target_dbs_info = + &per_cpu(id_cpu_dbs_info, target_cpu); + + atomic_set(&target_dbs_info->src_sync_cpu, (int)arg); + wake_up(&target_dbs_info->sync_wq); + + return NOTIFY_OK; +} + +static struct notifier_block dbs_migration_nb = { + .notifier_call = dbs_migration_notify, +}; + +static int sync_pending(struct cpu_dbs_info_s *this_dbs_info) +{ + return atomic_read(&this_dbs_info->src_sync_cpu) >= 0; +} + +static int dbs_sync_thread(void *data) +{ + int src_cpu, cpu = (int)data; + unsigned int src_freq, src_max_load; + struct cpu_dbs_info_s *this_dbs_info, *src_dbs_info; + struct cpufreq_policy *policy; + int delay; + + this_dbs_info = &per_cpu(id_cpu_dbs_info, cpu); + + while (1) { + wait_event(this_dbs_info->sync_wq, + sync_pending(this_dbs_info) || + kthread_should_stop()); + + if (kthread_should_stop()) + break; + + get_online_cpus(); + + src_cpu = atomic_read(&this_dbs_info->src_sync_cpu); + src_dbs_info = &per_cpu(id_cpu_dbs_info, src_cpu); + if (src_dbs_info != NULL && + src_dbs_info->cur_policy != NULL) { + src_freq = src_dbs_info->cur_policy->cur; + src_max_load = src_dbs_info->max_load; + } else { + src_freq = dbs_tuners_ins.sync_freq; + src_max_load = 0; + } + + if (lock_policy_rwsem_write(cpu) < 0) + goto bail_acq_sema_failed; + + if (!atomic_read(&this_dbs_info->sync_enabled)) { + atomic_set(&this_dbs_info->src_sync_cpu, -1); + put_online_cpus(); + unlock_policy_rwsem_write(cpu); + continue; + } + + policy = this_dbs_info->cur_policy; + if (!policy) { + /* CPU not using intellidemand governor */ + goto bail_incorrect_governor; + } + delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + + + if (policy->cur < src_freq) { + /* cancel the next intellidemand sample */ + cancel_delayed_work_sync(&this_dbs_info->work); + + /* + * Arch specific cpufreq driver may fail. + * Don't update governor frequency upon failure. + */ + if (__cpufreq_driver_target(policy, src_freq, + CPUFREQ_RELATION_L) >= 0) { + policy->cur = src_freq; + if (src_max_load > this_dbs_info->max_load) { + this_dbs_info->max_load = src_max_load; + this_dbs_info->prev_load = src_max_load; + } + } + + /* reschedule the next intellidemand sample */ + mutex_lock(&this_dbs_info->timer_mutex); + queue_delayed_work_on(cpu, dbs_wq, + &this_dbs_info->work, delay); + mutex_unlock(&this_dbs_info->timer_mutex); + } + +bail_incorrect_governor: + unlock_policy_rwsem_write(cpu); +bail_acq_sema_failed: + put_online_cpus(); + atomic_set(&this_dbs_info->src_sync_cpu, -1); + } + + return 0; +} + +static void dbs_input_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + int i; + struct cpu_dbs_info_s *dbs_info; + unsigned long flags; + int input_event_min_freq; + + if ((dbs_tuners_ins.powersave_bias == POWERSAVE_BIAS_MAXLEVEL) || + (dbs_tuners_ins.powersave_bias == POWERSAVE_BIAS_MINLEVEL)) { + /* nothing to do */ + return; + } + + if (type == EV_SYN && code == SYN_REPORT) { + spin_lock_irqsave(&input_boost_lock, flags); + input_event_boost = true; + ui_sampling_expired = jiffies + msecs_to_jiffies(dbs_tuners_ins.ui_timeout); + spin_unlock_irqrestore(&input_boost_lock, flags); + + input_event_min_freq = input_event_min_freq_array[num_online_cpus() - 1]; + for_each_online_cpu(i) { + dbs_info = &per_cpu(id_cpu_dbs_info, i); + if (dbs_info->cur_policy && + dbs_info->cur_policy->cur < input_event_min_freq) { + wake_up_process(per_cpu(up_task, i)); + } + } + } +} + +static int input_dev_filter(const char *input_dev_name) +{ + if (strstr(input_dev_name, "touchscreen") || + strstr(input_dev_name, "synaptics_dsx_i2c") || + strstr(input_dev_name, "elan-touchscreen") || + strstr(input_dev_name, "touch_dev") || + strstr(input_dev_name, "sec-touchscreen") || + strstr(input_dev_name, "keypad")) { + return 0; + } else { + return 1; + } +} + + +static int dbs_input_connect(struct input_handler *handler, + struct input_dev *dev, const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + if (input_dev_filter(dev->name)) + return -ENODEV; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "cpufreq"; + + error = input_register_handle(handle); + if (error) + goto err2; + + error = input_open_device(handle); + if (error) + goto err1; + + return 0; +err1: + input_unregister_handle(handle); +err2: + kfree(handle); + return error; +} + +static void dbs_input_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id dbs_ids[] = { + { .driver_info = 1 }, + { }, +}; + +static struct input_handler dbs_input_handler = { + .event = dbs_input_event, + .connect = dbs_input_connect, + .disconnect = dbs_input_disconnect, + .name = "cpufreq_id", + .id_table = dbs_ids, +}; + +int set_input_event_min_freq(int cpufreq) +{ + int i = 0; + for ( i = 0 ; i < NR_CPUS; i++) + input_event_min_freq_array[i] = cpufreq; + return 0; +} + +void set_input_event_min_freq_by_cpu ( int cpu_nr, int cpufreq){ + input_event_min_freq_array[cpu_nr-1] = cpufreq; +} + +static int cpufreq_governor_dbs(struct cpufreq_policy *policy, + unsigned int event) +{ + unsigned int cpu = policy->cpu; + struct cpu_dbs_info_s *this_dbs_info; + unsigned int j; + int rc; + + this_dbs_info = &per_cpu(id_cpu_dbs_info, cpu); + + switch (event) { + case CPUFREQ_GOV_START: + if ((!cpu_online(cpu)) || (!policy->cur)) + return -EINVAL; + + mutex_lock(&dbs_mutex); + + dbs_enable++; + for_each_cpu(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + cputime64_t tmp; + j_dbs_info = &per_cpu(id_cpu_dbs_info, j); + j_dbs_info->cur_policy = policy; + + j_dbs_info->prev_cpu_idle = get_cpu_idle_time(j, + &j_dbs_info->prev_cpu_wall); + tmp = j_dbs_info->prev_cpu_wall - + j_dbs_info->prev_cpu_idle; + do_div(tmp, j_dbs_info->prev_cpu_wall); + j_dbs_info->prev_load = 100 * tmp; + if (dbs_tuners_ins.ignore_nice) + j_dbs_info->prev_cpu_nice = + kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + set_cpus_allowed(j_dbs_info->sync_thread, + *cpumask_of(j)); + if (!dbs_tuners_ins.powersave_bias) + atomic_set(&j_dbs_info->sync_enabled, 1); + } + this_dbs_info->cpu = cpu; + this_dbs_info->rate_mult = 1; + this_dbs_info->freq_stay_count = 1; + intellidemand_powersave_bias_init_cpu(cpu); + /* + * Start the timerschedule work, when this governor + * is used for first time + */ + if (dbs_enable == 1) { + unsigned int latency; + + rc = sysfs_create_group(cpufreq_global_kobject, + &dbs_attr_group); + if (rc) { + mutex_unlock(&dbs_mutex); + return rc; + } + + /* policy latency is in nS. Convert it to uS first */ + latency = policy->cpuinfo.transition_latency / 1000; + if (latency == 0) + latency = 1; + /* Bring kernel and HW constraints together */ + min_sampling_rate = max(min_sampling_rate, + MIN_LATENCY_MULTIPLIER * latency); + dbs_tuners_ins.sampling_rate = + max(min_sampling_rate, + latency * LATENCY_MULTIPLIER); + if (dbs_tuners_ins.sampling_rate < DEF_SAMPLING_RATE) + dbs_tuners_ins.sampling_rate = DEF_SAMPLING_RATE; + dbs_tuners_ins.origin_sampling_rate = dbs_tuners_ins.sampling_rate; + dbs_tuners_ins.io_is_busy = should_io_be_busy(); + + if (dbs_tuners_ins.optimal_freq == 0) + dbs_tuners_ins.optimal_freq = policy->min; + + if (dbs_tuners_ins.sync_freq == 0) + dbs_tuners_ins.sync_freq = policy->min; + + atomic_notifier_chain_register(&migration_notifier_head, + &dbs_migration_nb); + } + if (!cpu) + rc = input_register_handler(&dbs_input_handler); + mutex_unlock(&dbs_mutex); + + + if (!intellidemand_powersave_bias_setspeed( + this_dbs_info->cur_policy, + NULL, + dbs_tuners_ins.powersave_bias)) + dbs_timer_init(this_dbs_info); + break; + + case CPUFREQ_GOV_STOP: + dbs_timer_exit(this_dbs_info); + + mutex_lock(&dbs_mutex); + dbs_enable--; + + for_each_cpu(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + j_dbs_info = &per_cpu(id_cpu_dbs_info, j); + atomic_set(&j_dbs_info->sync_enabled, 0); + } + + /* If device is being removed, policy is no longer + * valid. */ + this_dbs_info->cur_policy = NULL; + if (!cpu) + input_unregister_handler(&dbs_input_handler); + if (!dbs_enable) { + sysfs_remove_group(cpufreq_global_kobject, + &dbs_attr_group); + atomic_notifier_chain_unregister( + &migration_notifier_head, + &dbs_migration_nb); + } + + mutex_unlock(&dbs_mutex); + + break; + + case CPUFREQ_GOV_LIMITS: + if (this_dbs_info->cur_policy == NULL) { + pr_debug("Unable to limit cpu freq due to cur_policy == NULL\n"); + return -EPERM; + } + mutex_lock(&this_dbs_info->timer_mutex); + if (policy->max < this_dbs_info->cur_policy->cur) + __cpufreq_driver_target(this_dbs_info->cur_policy, + policy->max, CPUFREQ_RELATION_H); + else if (policy->min > this_dbs_info->cur_policy->cur) + __cpufreq_driver_target(this_dbs_info->cur_policy, + policy->min, CPUFREQ_RELATION_L); + else if (dbs_tuners_ins.powersave_bias != 0) + intellidemand_powersave_bias_setspeed( + this_dbs_info->cur_policy, + policy, + dbs_tuners_ins.powersave_bias); + mutex_unlock(&this_dbs_info->timer_mutex); + break; + } + return 0; +} + +static int cpufreq_gov_dbs_up_task(void *data) +{ + struct cpufreq_policy *policy; + struct cpu_dbs_info_s *this_dbs_info; + unsigned int cpu = smp_processor_id(); + int input_event_min_freq; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + + if (kthread_should_stop()) + break; + + set_current_state(TASK_RUNNING); + + get_online_cpus(); + + if (lock_policy_rwsem_write(cpu) < 0) + goto bail_acq_sema_failed; + + this_dbs_info = &per_cpu(id_cpu_dbs_info, cpu); + policy = this_dbs_info->cur_policy; + if (!policy) { + + goto bail_incorrect_governor; + } + + mutex_lock(&this_dbs_info->timer_mutex); + + input_event_min_freq = input_event_min_freq_array[num_online_cpus() - 1]; + if (policy->cur < input_event_min_freq) { + + dbs_tuners_ins.powersave_bias = 0; + dbs_freq_increase(policy, input_event_min_freq); + this_dbs_info->prev_cpu_idle = get_cpu_idle_time(cpu, &this_dbs_info->prev_cpu_wall); + } + + mutex_unlock(&this_dbs_info->timer_mutex); + +bail_incorrect_governor: + unlock_policy_rwsem_write(cpu); + +bail_acq_sema_failed: + put_online_cpus(); + + dbs_tuners_ins.sampling_rate = dbs_tuners_ins.ui_sampling_rate; + } + + return 0; +} + +#if 0 +static void cpufreq_intellidemand_power_suspend(struct power_suspend *h) +{ + mutex_lock(&dbs_mutex); + stored_sampling_rate = dbs_tuners_ins.sampling_rate; + dbs_tuners_ins.sampling_rate = DEF_SAMPLING_RATE * 6; + update_sampling_rate(dbs_tuners_ins.sampling_rate); + mutex_unlock(&dbs_mutex); +} + +static void cpufreq_intellidemand_power_resume(struct power_suspend *h) +{ + mutex_lock(&dbs_mutex); + dbs_tuners_ins.sampling_rate = stored_sampling_rate; + update_sampling_rate(dbs_tuners_ins.sampling_rate); + mutex_unlock(&dbs_mutex); +} + +static struct power_suspend cpufreq_intellidemand_power_suspend_info = { + .suspend = cpufreq_intellidemand_power_suspend, + .resume = cpufreq_intellidemand_power_resume, +}; +#endif + +static int __init cpufreq_gov_dbs_init(void) +{ + u64 idle_time; + unsigned int i; + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + struct task_struct *pthread; + int cpu = get_cpu(); + + idle_time = get_cpu_idle_time_us(cpu, NULL); + put_cpu(); + if (idle_time != -1ULL) { + /* Idle micro accounting is supported. Use finer thresholds */ + dbs_tuners_ins.up_threshold = MICRO_FREQUENCY_UP_THRESHOLD; + dbs_tuners_ins.down_differential = + MICRO_FREQUENCY_DOWN_DIFFERENTIAL; + /* + * In nohz/micro accounting case we set the minimum frequency + * not depending on HZ, but fixed (very low). The deferred + * timer might skip some samples if idle/sleeping as needed. + */ + min_sampling_rate = MICRO_FREQUENCY_MIN_SAMPLE_RATE; + } else { + /* For correct statistics, we need 10 ticks for each measure */ + min_sampling_rate = + MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10); + } + + dbs_wq = alloc_workqueue("intellidemand_dbs_wq", WQ_HIGHPRI, 0); + if (!dbs_wq) { + printk(KERN_ERR "Failed to create intellidemand_dbs_wq workqueue\n"); + return -EFAULT; + } + for_each_possible_cpu(i) { + struct cpu_dbs_info_s *this_dbs_info = + &per_cpu(id_cpu_dbs_info, i); + struct dbs_work_struct *dbs_work = + &per_cpu(dbs_refresh_work, i); + + pthread = kthread_create_on_node(cpufreq_gov_dbs_up_task, + NULL, cpu_to_node(i), + "kdbs_up/%d", i); + if (likely(!IS_ERR(pthread))) { + kthread_bind(pthread, i); + sched_setscheduler_nocheck(pthread, SCHED_FIFO, ¶m); + get_task_struct(pthread); + per_cpu(up_task, i) = pthread; + } + + mutex_init(&this_dbs_info->timer_mutex); + INIT_WORK(&dbs_work->work, dbs_refresh_callback); + dbs_work->cpu = i; + + atomic_set(&this_dbs_info->src_sync_cpu, -1); + init_waitqueue_head(&this_dbs_info->sync_wq); + + this_dbs_info->sync_thread = kthread_run(dbs_sync_thread, + (void *)i, + "dbs_sync/%d", i); + } + +#if 0 + register_power_suspend(&cpufreq_intellidemand_power_suspend_info); +#endif + return cpufreq_register_governor(&cpufreq_gov_intellidemand); +} + +static void __exit cpufreq_gov_dbs_exit(void) +{ + unsigned int i; + + cpufreq_unregister_governor(&cpufreq_gov_intellidemand); + for_each_possible_cpu(i) { + struct cpu_dbs_info_s *this_dbs_info = + &per_cpu(id_cpu_dbs_info, i); + mutex_destroy(&this_dbs_info->timer_mutex); + kthread_stop(this_dbs_info->sync_thread); + if (per_cpu(up_task, i)) { + kthread_stop(per_cpu(up_task, i)); + put_task_struct(per_cpu(up_task, i)); + } + } + destroy_workqueue(dbs_wq); +} + +MODULE_AUTHOR("Venkatesh Pallipadi "); +MODULE_AUTHOR("Alexey Starikovskiy "); +MODULE_AUTHOR("Paul Reioux "); +MODULE_DESCRIPTION("'cpufreq_intellidemand' - A dynamic cpufreq governor for " + "Low Latency Frequency Transition capable processors"); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIDEMAND +fs_initcall(cpufreq_gov_dbs_init); +#else +module_init(cpufreq_gov_dbs_init); +#endif +module_exit(cpufreq_gov_dbs_exit); + diff --git a/drivers/cpufreq/cpufreq_intellimm.c b/drivers/cpufreq/cpufreq_intellimm.c new file mode 100644 index 00000000..ce33e319 --- /dev/null +++ b/drivers/cpufreq/cpufreq_intellimm.c @@ -0,0 +1,1664 @@ +/* + * drivers/cpufreq/cpufreq_inteiiimm.c + * + * Copyright (C) 2001 Russell King + * (C) 2003 Venkatesh Pallipadi . + * Jun Nakajima + * (c) 2013 The Linux Foundation. All rights reserved. + * (c) 2014 Paul Reioux (aka Faux123) + * + * 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 + +#define DEF_SAMPLING_RATE (120000) +#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) +#define DEF_FREQUENCY_UP_THRESHOLD (80) +#define DEF_SAMPLING_DOWN_FACTOR (1) +#define MAX_SAMPLING_DOWN_FACTOR (100000) +#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) +#define MICRO_FREQUENCY_UP_THRESHOLD (95) +#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) +#define MIN_FREQUENCY_UP_THRESHOLD (11) +#define MAX_FREQUENCY_UP_THRESHOLD (100) +#define MIN_FREQUENCY_DOWN_DIFFERENTIAL (1) + +#define DEF_POWER_SAVE_FREQUENCY (700000) +#define DEF_TWO_PHASE_FREQUENCY (1008000) +#define DBS_INPUT_EVENT_MIN_FREQ (700000) +#define DEF_FREQUENCY_OPTIMAL (245000) +#define DEF_FREQ_DOWN_STEP (320000) +#define DEF_FREQ_DOWN_STEP_BARRIER (700000) + +#define DEF_INPUT_BOOST_DURATION (6) +#define CPU0 (0) + +#define MIN_SAMPLING_RATE_RATIO (2) + +static unsigned int min_sampling_rate; + +#define LATENCY_MULTIPLIER (1000) +#define MIN_LATENCY_MULTIPLIER (100) +#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) + +#define POWERSAVE_BIAS_MAXLEVEL (1000) +#define POWERSAVE_BIAS_MINLEVEL (-1000) + +static void do_dbs_timer(struct work_struct *work); +static int cpufreq_governor_dbs(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIMM +static +#endif +struct cpufreq_governor cpufreq_gov_intellimm = { + .name = "intellimm", + .governor = cpufreq_governor_dbs, + .max_transition_latency = TRANSITION_LATENCY_LIMIT, + .owner = THIS_MODULE, +}; + +enum {DBS_NORMAL_SAMPLE, DBS_SUB_SAMPLE}; + +struct cpu_dbs_info_s { + cputime64_t prev_cpu_idle; + cputime64_t prev_cpu_iowait; + cputime64_t prev_cpu_wall; + cputime64_t prev_cpu_nice; + struct cpufreq_policy *cur_policy; + struct delayed_work work; + struct cpufreq_frequency_table *freq_table; + unsigned int freq_lo; + unsigned int freq_lo_jiffies; + unsigned int freq_hi_jiffies; + unsigned int rate_mult; + unsigned int prev_load; + unsigned int cur_load; + unsigned int max_load; + int input_event_freq; + int cpu; + unsigned int sample_type:1; + struct mutex timer_mutex; + +}; +static DEFINE_PER_CPU(struct cpu_dbs_info_s, imm_cpu_dbs_info); + +static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info); +static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info); + +static unsigned int dbs_enable; + +static DEFINE_PER_CPU(struct task_struct *, up_task); +static spinlock_t input_boost_lock; +static bool input_event_boost = false; +static unsigned long input_event_boost_expired = 0; + +#define TABLE_SIZE 1 + +#define MAX(x,y) (x > y ? x : y) +#define MIN(x,y) (x < y ? x : y) +#define FREQ_NEED_BURST(x) (x < 600000 ? 1 : 0) + +static struct cpufreq_frequency_table *tbl = NULL; +static unsigned int *tblmap[TABLE_SIZE] __read_mostly; +static unsigned int tbl_select[4] = {0}; +static int input_event_counter = 0; + +static unsigned int up_threshold_level[2] __read_mostly = {90, 80}; +static void reset_freq_map_table(struct cpufreq_policy *); + +static DEFINE_MUTEX(dbs_mutex); + +static struct workqueue_struct *dbs_wq; + +static struct dbs_tuners { + unsigned int sampling_rate; + unsigned int up_threshold; + unsigned int up_threshold_multi_core; + unsigned int down_differential; + unsigned int down_differential_multi_core; + unsigned int optimal_freq; + unsigned int up_threshold_any_cpu_load; + unsigned int ignore_nice; + unsigned int sampling_down_factor; + int powersave_bias; + unsigned int io_is_busy; + unsigned int shortcut; + unsigned int power_save_freq; + unsigned int two_phase_freq; + unsigned int freq_down_step; + unsigned int freq_down_step_barrier; +} dbs_tuners_ins = { + .up_threshold_multi_core = DEF_FREQUENCY_UP_THRESHOLD, + .up_threshold = DEF_FREQUENCY_UP_THRESHOLD, + .sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR, + .down_differential = DEF_FREQUENCY_DOWN_DIFFERENTIAL, + .down_differential_multi_core = MICRO_FREQUENCY_DOWN_DIFFERENTIAL, + .up_threshold_any_cpu_load = DEF_FREQUENCY_UP_THRESHOLD, + .ignore_nice = 0, + .powersave_bias = 0, + .optimal_freq = 0, + .shortcut = 0, + .power_save_freq = DEF_POWER_SAVE_FREQUENCY, + .two_phase_freq = DEF_TWO_PHASE_FREQUENCY, + .freq_down_step = DEF_FREQ_DOWN_STEP, + .freq_down_step_barrier = DEF_FREQ_DOWN_STEP_BARRIER, +}; + +static inline u64 get_cpu_idle_time_jiffy(unsigned int cpu, u64 *wall) +{ + u64 idle_time; + u64 cur_wall_time; + u64 busy_time; + + cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); + + busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; + + idle_time = cur_wall_time - busy_time; + if (wall) + *wall = jiffies_to_usecs(cur_wall_time); + + return jiffies_to_usecs(idle_time); +} + +static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall) +{ + u64 idle_time = get_cpu_idle_time_us(cpu, NULL); + + if (idle_time == -1ULL) + return get_cpu_idle_time_jiffy(cpu, wall); + else + idle_time += get_cpu_iowait_time_us(cpu, wall); + + return idle_time; +} + +static inline cputime64_t get_cpu_iowait_time(unsigned int cpu, + cputime64_t *wall) +{ + u64 iowait_time = get_cpu_iowait_time_us(cpu, wall); + + if (iowait_time == -1ULL) + return 0; + + return iowait_time; +} + +static unsigned int powersave_bias_target(struct cpufreq_policy *policy, + unsigned int freq_next, + unsigned int relation) +{ + unsigned int freq_req, freq_avg; + unsigned int freq_hi, freq_lo; + unsigned int index = 0; + unsigned int jiffies_total, jiffies_hi, jiffies_lo; + int freq_reduc; + struct cpu_dbs_info_s *dbs_info = &per_cpu(imm_cpu_dbs_info, + policy->cpu); + + if (!dbs_info->freq_table) { + dbs_info->freq_lo = 0; + dbs_info->freq_lo_jiffies = 0; + return freq_next; + } + + cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_next, + relation, &index); + freq_req = dbs_info->freq_table[index].frequency; + freq_reduc = freq_req * dbs_tuners_ins.powersave_bias / 1000; + freq_avg = freq_req - freq_reduc; + + + index = 0; + cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_avg, + CPUFREQ_RELATION_H, &index); + freq_lo = dbs_info->freq_table[index].frequency; + index = 0; + cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_avg, + CPUFREQ_RELATION_L, &index); + freq_hi = dbs_info->freq_table[index].frequency; + + + if (freq_hi == freq_lo) { + dbs_info->freq_lo = 0; + dbs_info->freq_lo_jiffies = 0; + return freq_lo; + } + jiffies_total = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + jiffies_hi = (freq_avg - freq_lo) * jiffies_total; + jiffies_hi += ((freq_hi - freq_lo) / 2); + jiffies_hi /= (freq_hi - freq_lo); + jiffies_lo = jiffies_total - jiffies_hi; + dbs_info->freq_lo = freq_lo; + dbs_info->freq_lo_jiffies = jiffies_lo; + dbs_info->freq_hi_jiffies = jiffies_hi; + return freq_hi; +} + +static int intellimm_powersave_bias_setspeed(struct cpufreq_policy *policy, + struct cpufreq_policy *altpolicy, + int level) +{ + if (level == POWERSAVE_BIAS_MAXLEVEL) { + + __cpufreq_driver_target(policy, + (altpolicy) ? altpolicy->min : policy->min, + CPUFREQ_RELATION_L); + return 1; + } else if (level == POWERSAVE_BIAS_MINLEVEL) { + + __cpufreq_driver_target(policy, + (altpolicy) ? altpolicy->max : policy->max, + CPUFREQ_RELATION_H); + return 1; + } + return 0; +} + +static void intellimm_powersave_bias_init_cpu(int cpu) +{ + struct cpu_dbs_info_s *dbs_info = &per_cpu(imm_cpu_dbs_info, cpu); + dbs_info->freq_table = cpufreq_frequency_get_table(cpu); + dbs_info->freq_lo = 0; +} + +static void intellimm_powersave_bias_init(void) +{ + int i; + for_each_online_cpu(i) { + intellimm_powersave_bias_init_cpu(i); + } +} + + +static ssize_t show_sampling_rate_min(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", min_sampling_rate); +} + +define_one_global_ro(sampling_rate_min); + +#define show_one(file_name, object) \ +static ssize_t show_##file_name (struct kobject *kobj, \ + struct attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%u\n", dbs_tuners_ins.object); \ +} + +show_one(sampling_rate, sampling_rate); +show_one(io_is_busy, io_is_busy); +show_one(shortcut, shortcut); +show_one(up_threshold, up_threshold); +show_one(up_threshold_multi_core, up_threshold_multi_core); +show_one(down_differential, down_differential); +show_one(sampling_down_factor, sampling_down_factor); +show_one(ignore_nice_load, ignore_nice); +show_one(down_differential_multi_core, down_differential_multi_core); +show_one(optimal_freq, optimal_freq); +show_one(up_threshold_any_cpu_load, up_threshold_any_cpu_load); +show_one(freq_down_step, freq_down_step); +show_one(freq_down_step_barrier, freq_down_step_barrier); + +static ssize_t show_powersave_bias(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", dbs_tuners_ins.powersave_bias); +} + +static void update_sampling_rate(unsigned int new_rate) +{ + int cpu; + + dbs_tuners_ins.sampling_rate = new_rate + = max(new_rate, min_sampling_rate); + + get_online_cpus(); + for_each_online_cpu(cpu) { + struct cpufreq_policy *policy; + struct cpu_dbs_info_s *dbs_info; + unsigned long next_sampling, appointed_at; + + policy = cpufreq_cpu_get(cpu); + if (!policy) + continue; + dbs_info = &per_cpu(imm_cpu_dbs_info, policy->cpu); + cpufreq_cpu_put(policy); + + mutex_lock(&dbs_info->timer_mutex); + + if (!delayed_work_pending(&dbs_info->work)) { + mutex_unlock(&dbs_info->timer_mutex); + continue; + } + + next_sampling = jiffies + usecs_to_jiffies(new_rate); + appointed_at = dbs_info->work.timer.expires; + + if (time_before(next_sampling, appointed_at)) { + + mutex_unlock(&dbs_info->timer_mutex); + cancel_delayed_work_sync(&dbs_info->work); + mutex_lock(&dbs_info->timer_mutex); + + queue_delayed_work_on(dbs_info->cpu, dbs_wq, + &dbs_info->work, usecs_to_jiffies(new_rate)); + + } + mutex_unlock(&dbs_info->timer_mutex); + } + put_online_cpus(); +} + +static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + update_sampling_rate(input); + return count; +} + +static ssize_t store_io_is_busy(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + dbs_tuners_ins.io_is_busy = !!input; + return count; +} + +static ssize_t store_shortcut(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + if (dbs_tuners_ins.shortcut != input) { + dbs_tuners_ins.shortcut = input; + } + + return count; +} + +static ssize_t store_down_differential_multi_core(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + dbs_tuners_ins.down_differential_multi_core = input; + return count; +} + + +static ssize_t store_optimal_freq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + dbs_tuners_ins.optimal_freq = input; + return count; +} + +static ssize_t store_up_threshold(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > MAX_FREQUENCY_UP_THRESHOLD || + input < MIN_FREQUENCY_UP_THRESHOLD) { + return -EINVAL; + } + dbs_tuners_ins.up_threshold = input; + return count; +} + +static ssize_t store_up_threshold_multi_core(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > MAX_FREQUENCY_UP_THRESHOLD || + input < MIN_FREQUENCY_UP_THRESHOLD) { + return -EINVAL; + } + dbs_tuners_ins.up_threshold_multi_core = input; + return count; +} + +static ssize_t store_up_threshold_any_cpu_load(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > MAX_FREQUENCY_UP_THRESHOLD || + input < MIN_FREQUENCY_UP_THRESHOLD) { + return -EINVAL; + } + dbs_tuners_ins.up_threshold_any_cpu_load = input; + return count; +} + +static ssize_t store_down_differential(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input >= dbs_tuners_ins.up_threshold || + input < MIN_FREQUENCY_DOWN_DIFFERENTIAL) { + return -EINVAL; + } + + dbs_tuners_ins.down_differential = input; + + return count; +} + +static ssize_t store_sampling_down_factor(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + unsigned int input, j; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1) + return -EINVAL; + dbs_tuners_ins.sampling_down_factor = input; + + + for_each_online_cpu(j) { + struct cpu_dbs_info_s *dbs_info; + dbs_info = &per_cpu(imm_cpu_dbs_info, j); + dbs_info->rate_mult = 1; + } + return count; +} + +static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + unsigned int j; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + if (input > 1) + input = 1; + + if (input == dbs_tuners_ins.ignore_nice) { + return count; + } + dbs_tuners_ins.ignore_nice = input; + + + for_each_online_cpu(j) { + struct cpu_dbs_info_s *dbs_info; + dbs_info = &per_cpu(imm_cpu_dbs_info, j); + dbs_info->prev_cpu_idle = get_cpu_idle_time(j, + &dbs_info->prev_cpu_wall); + if (dbs_tuners_ins.ignore_nice) + dbs_info->prev_cpu_nice = + kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + + } + return count; +} + +static ssize_t store_powersave_bias(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + int input = 0; + int bypass = 0; + int ret, cpu, reenable_timer, j; + struct cpu_dbs_info_s *dbs_info; + + struct cpumask cpus_timer_done; + cpumask_clear(&cpus_timer_done); + + ret = sscanf(buf, "%d", &input); + + if (ret != 1) + return -EINVAL; + + if (input >= POWERSAVE_BIAS_MAXLEVEL) { + input = POWERSAVE_BIAS_MAXLEVEL; + bypass = 1; + } else if (input <= POWERSAVE_BIAS_MINLEVEL) { + input = POWERSAVE_BIAS_MINLEVEL; + bypass = 1; + } + + if (input == dbs_tuners_ins.powersave_bias) { + + return count; + } + + reenable_timer = ((dbs_tuners_ins.powersave_bias == + POWERSAVE_BIAS_MAXLEVEL) || + (dbs_tuners_ins.powersave_bias == + POWERSAVE_BIAS_MINLEVEL)); + + dbs_tuners_ins.powersave_bias = input; + + get_online_cpus(); + mutex_lock(&dbs_mutex); + + if (!bypass) { + if (reenable_timer) { + + for_each_online_cpu(cpu) { + if (lock_policy_rwsem_write(cpu) < 0) + continue; + + dbs_info = &per_cpu(imm_cpu_dbs_info, cpu); + + if (!dbs_info->cur_policy) { + pr_err("Dbs policy is NULL\n"); + goto skip_this_cpu; + } + + for_each_cpu(j, &cpus_timer_done) { + if (cpumask_test_cpu(j, dbs_info-> + cur_policy->cpus)) + goto skip_this_cpu; + } + + cpumask_set_cpu(cpu, &cpus_timer_done); + if (dbs_info->cur_policy) { + dbs_timer_exit(dbs_info); + + mutex_lock(&dbs_info->timer_mutex); + dbs_timer_init(dbs_info); + } +skip_this_cpu: + unlock_policy_rwsem_write(cpu); + } + } + intellimm_powersave_bias_init(); + } else { + for_each_online_cpu(cpu) { + if (lock_policy_rwsem_write(cpu) < 0) + continue; + + dbs_info = &per_cpu(imm_cpu_dbs_info, cpu); + + if (!dbs_info->cur_policy) { + pr_err("Dbs policy is NULL\n"); + goto skip_this_cpu_bypass; + } + + for_each_cpu(j, &cpus_timer_done) { + if (cpumask_test_cpu(j, dbs_info-> + cur_policy->cpus)) + goto skip_this_cpu_bypass; + } + + cpumask_set_cpu(cpu, &cpus_timer_done); + + if (dbs_info->cur_policy) { + + dbs_timer_exit(dbs_info); + + mutex_lock(&dbs_info->timer_mutex); + intellimm_powersave_bias_setspeed( + dbs_info->cur_policy, + NULL, + input); + mutex_unlock(&dbs_info->timer_mutex); + + } +skip_this_cpu_bypass: + unlock_policy_rwsem_write(cpu); + } + } + + mutex_unlock(&dbs_mutex); + put_online_cpus(); + + return count; +} + +static int input_event_min_freq_array[NR_CPUS] = + {[0 ... NR_CPUS-1] = + DBS_INPUT_EVENT_MIN_FREQ} ; + +static ssize_t show_input_event_min_freq(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + int i = 0; + int shift = 0; + char *buf_pos = buf; + for ( i = 0 ; i < NR_CPUS; i++) { + shift = sprintf(buf_pos,"%d,",input_event_min_freq_array[i]); + buf_pos += shift; + } + *(buf_pos-1) = '\n'; + *buf_pos = '\0'; + buf_pos++; + return strlen(buf); +} + +static ssize_t store_input_event_min_freq(struct kobject *a, + struct attribute *b, + const char *buf, size_t count) +{ + int ret = 0; + if (NR_CPUS == 1) + ret = sscanf(buf,"%u",&input_event_min_freq_array[0]); + else if (NR_CPUS == 2) + ret = sscanf(buf,"%u,%u",&input_event_min_freq_array[0], + &input_event_min_freq_array[1]); + else if (NR_CPUS == 4) + ret = sscanf(buf, "%u,%u,%u,%u", &input_event_min_freq_array[0], + &input_event_min_freq_array[1], + &input_event_min_freq_array[2], + &input_event_min_freq_array[3]); + if (ret < NR_CPUS) + return -EINVAL; + + return count; +} + +static int freq_cnt = 0; +static int low_index = 0; +static int nom_index = 0; + +static ssize_t show_multi_phase_freq_tbl(struct kobject *kobj, + struct attribute *attr, + char *buf) +{ + int i = 0,j = 0; + int shift = 0; + char *buf_pos = buf; + + for (i = 0; i < TABLE_SIZE; i++) { + shift = snprintf(buf_pos,PAGE_SIZE-(buf_pos - buf), + "%s %d %s","Table",i+1,"shows:\n"); + buf_pos += shift; + for (j = 0; j < freq_cnt; j++) { + shift = snprintf(buf_pos,PAGE_SIZE-(buf_pos - buf), + "%02d: %8u\n",j,tblmap[i][j]); + buf_pos += shift; + } + } + + *(buf_pos) = '\0'; + + return strlen(buf); +} + +static ssize_t store_multi_phase_freq_tbl(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + return 0; +} + +show_one(two_phase_freq, two_phase_freq); + +static ssize_t store_two_phase_freq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + struct cpu_dbs_info_s *dbs_info; + struct cpufreq_policy *policy; + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + dbs_info = &per_cpu(imm_cpu_dbs_info, 0); + policy = dbs_info->cur_policy; + if (policy) { + if (input < policy->cpuinfo.min_freq || + input > policy->cpuinfo.max_freq) + return -EINVAL; + if (dbs_tuners_ins.two_phase_freq != input) { + dbs_tuners_ins.two_phase_freq = input; + reset_freq_map_table(policy); + } + } + + return count; +} + +static ssize_t store_freq_down_step(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + dbs_tuners_ins.freq_down_step = input; + return count; +} + +static ssize_t store_freq_down_step_barrier(struct kobject *a, + struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + dbs_tuners_ins.freq_down_step_barrier = input; + return count; +} + +define_one_global_rw(sampling_rate); +define_one_global_rw(io_is_busy); +define_one_global_rw(shortcut); +define_one_global_rw(up_threshold); +define_one_global_rw(down_differential); +define_one_global_rw(sampling_down_factor); +define_one_global_rw(ignore_nice_load); +define_one_global_rw(powersave_bias); +define_one_global_rw(up_threshold_multi_core); +define_one_global_rw(down_differential_multi_core); +define_one_global_rw(optimal_freq); +define_one_global_rw(up_threshold_any_cpu_load); +define_one_global_rw(input_event_min_freq); +define_one_global_rw(multi_phase_freq_tbl); +define_one_global_rw(two_phase_freq); +define_one_global_rw(freq_down_step); +define_one_global_rw(freq_down_step_barrier); + +static struct attribute *dbs_attributes[] = { + &sampling_rate_min.attr, + &sampling_rate.attr, + &up_threshold.attr, + &down_differential.attr, + &sampling_down_factor.attr, + &ignore_nice_load.attr, + &powersave_bias.attr, + &io_is_busy.attr, + &shortcut.attr, + &up_threshold_multi_core.attr, + &down_differential_multi_core.attr, + &optimal_freq.attr, + &up_threshold_any_cpu_load.attr, + &input_event_min_freq.attr, + &multi_phase_freq_tbl.attr, + &two_phase_freq.attr, + &freq_down_step.attr, + &freq_down_step_barrier.attr, + NULL +}; + +static struct attribute_group dbs_attr_group = { + .attrs = dbs_attributes, + .name = "intellimm", +}; + +static int adjust_freq_map_table(int freq, int cnt, + struct cpufreq_policy *policy) +{ + int i, upper, lower; + + if (policy && tbl) { + upper = policy->cpuinfo.max_freq; + lower = policy->cpuinfo.min_freq; + } + else + return freq; + + for(i = 0; i < cnt; i++) + { + if(freq >= tbl[i].frequency) + { + lower = tbl[i].frequency; + } + else + { + upper = tbl[i].frequency; + break; + } + } + + return (freq - lower < upper - freq)?lower:upper; +} + +static void reset_freq_map_table(struct cpufreq_policy *policy) +{ + unsigned int real_freq, low_freq; + int index; + + if (!tbl) + return; + + for (index = 0; index < freq_cnt; index++) + tblmap[0][index] = tbl[index].frequency; + + low_freq = adjust_freq_map_table(dbs_tuners_ins.power_save_freq, + freq_cnt, policy); + + for (index = 0; index < 5; index++) { + tblmap[0][index] = low_freq; + low_index = index; + } + + real_freq = adjust_freq_map_table(dbs_tuners_ins.two_phase_freq, + freq_cnt, policy); + + for (index = 5; index < freq_cnt; index++) { + if (tbl[index].frequency <= real_freq) { + tblmap[0][index] = real_freq; + nom_index = index; + } + } +} + +static void dbs_init_freq_map_table(struct cpufreq_policy *policy) +{ + tbl = cpufreq_frequency_get_table(0); + + for (freq_cnt = 0; + (tbl[freq_cnt].frequency != CPUFREQ_TABLE_END); + freq_cnt++) + ; + + tblmap[0] = kmalloc(sizeof(unsigned int) * freq_cnt, GFP_KERNEL); + BUG_ON(!tblmap[0]); + + reset_freq_map_table(policy); +} + +static void dbs_deinit_freq_map_table(void) +{ + int i; + + if (!tbl) + return; + + tbl = NULL; + + for (i = 0; i < TABLE_SIZE; i++) + kfree(tblmap[i]); +} + +static inline int get_cpu_freq_next(unsigned int freq) +{ + static int saved_index = 0; + int index; + + if (!tbl) { + pr_warn("tbl is NULL, use previous value %d\n", saved_index); + return saved_index; + } + + for (index = 0; (tbl[index].frequency != CPUFREQ_TABLE_END); index++) { + if (tbl[index].frequency > freq) { + if (index >= freq_cnt) + index = freq_cnt - 1; + saved_index = index; + break; + } + } + return index; +} + +static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq) +{ + if (dbs_tuners_ins.powersave_bias) + freq = powersave_bias_target(p, freq, CPUFREQ_RELATION_H); + else if (p->cur == p->max){ + return; + } + + __cpufreq_driver_target(p, freq, (dbs_tuners_ins.powersave_bias || + freq < p->max) ? + CPUFREQ_RELATION_L : CPUFREQ_RELATION_H); +} + +int input_event_boosted(void) +{ + unsigned long flags; + + + spin_lock_irqsave(&input_boost_lock, flags); + if (input_event_boost) { + if (time_before(jiffies, input_event_boost_expired)) { + spin_unlock_irqrestore(&input_boost_lock, flags); + return 1; + } + input_event_boost = false; + } + spin_unlock_irqrestore(&input_boost_lock, flags); + + return 0; +} + +static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) +{ + struct cpufreq_policy *policy; + unsigned int j, prev_load = 0; + struct cpu_dbs_info_s *j_dbs_info; + + unsigned int up_threshold; + unsigned max_load = 0, avg_load = 0; + unsigned avg_load_freq = 0; + unsigned num_of_cpus = 0; + + this_dbs_info->freq_lo = 0; + policy = this_dbs_info->cur_policy; + +#ifdef CONFIG_ARCH_MSM_CORTEXMP + for (j = 1; j < NR_CPUS; j++) { + j_dbs_info = &per_cpu(imm_cpu_dbs_info, j); + if (j_dbs_info->prev_load && !cpu_online(j)) + j_dbs_info->prev_load = 0; + } +#endif + + /* per cpu load calculation */ + for_each_cpu(j, policy->cpus) { + cputime64_t cur_wall_time, cur_idle_time, cur_iowait_time; + unsigned int idle_time, wall_time, iowait_time; + unsigned int cur_load; + + j_dbs_info = &per_cpu(imm_cpu_dbs_info, j); + + cur_idle_time = get_cpu_idle_time(j, &cur_wall_time); + cur_iowait_time = get_cpu_iowait_time(j, &cur_wall_time); + + wall_time = (unsigned int) + (cur_wall_time - j_dbs_info->prev_cpu_wall); + j_dbs_info->prev_cpu_wall = cur_wall_time; + + idle_time = (unsigned int) + (cur_idle_time - j_dbs_info->prev_cpu_idle); + j_dbs_info->prev_cpu_idle = cur_idle_time; + + iowait_time = (unsigned int) + (cur_iowait_time - j_dbs_info->prev_cpu_iowait); + j_dbs_info->prev_cpu_iowait = cur_iowait_time; + + if (dbs_tuners_ins.ignore_nice) { + u64 cur_nice; + unsigned long cur_nice_jiffies; + + cur_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE] - + j_dbs_info->prev_cpu_nice; + cur_nice_jiffies = (unsigned long) + cputime64_to_jiffies64(cur_nice); + + j_dbs_info->prev_cpu_nice = + kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + idle_time += jiffies_to_usecs(cur_nice_jiffies); + } + + + if (dbs_tuners_ins.io_is_busy && idle_time >= iowait_time) + idle_time -= iowait_time; + + if (unlikely(!wall_time || wall_time < idle_time)) + continue; + + if (prev_load < j_dbs_info->prev_load) + prev_load = j_dbs_info->prev_load; + + cur_load = 100 * (wall_time - idle_time) / wall_time; + j_dbs_info->cur_load = cur_load; + j_dbs_info->max_load = max(cur_load, j_dbs_info->prev_load); + j_dbs_info->prev_load = cur_load; + } + + num_of_cpus = num_online_cpus(); + for_each_online_cpu(j) { + j_dbs_info = &per_cpu(imm_cpu_dbs_info, j); + avg_load += j_dbs_info->cur_load; + max_load += j_dbs_info->max_load; + } + + avg_load = avg_load / num_of_cpus; + max_load = max_load / num_of_cpus; + + if (max_load > avg_load) + avg_load_freq = (avg_load + max_load) / 2 * policy->cur; + else + avg_load_freq = avg_load * policy->cur; + + // normal path up + if (dbs_tuners_ins.shortcut) + up_threshold = dbs_tuners_ins.up_threshold; + else + up_threshold = up_threshold_level[1]; + + if (avg_load_freq > up_threshold * policy->cur) { + unsigned int freq_next; + int index; + + index = get_cpu_freq_next(policy->cur); + + if (dbs_tuners_ins.shortcut) { + freq_next = policy->max; + goto set_freq; + } + + if (FREQ_NEED_BURST(policy->cur) && + avg_load > up_threshold_level[0]) + freq_next = tblmap[tbl_select[0]][nom_index]; + else if (avg_load > up_threshold_level[1] && + avg_load < up_threshold_level[0]) + freq_next = + tblmap[tbl_select[0]][max(index, nom_index-1)]; + else if (avg_load >= up_threshold_level[0]) + freq_next = + tblmap[tbl_select[0]][index]; + else + freq_next = tblmap[tbl_select[0]] + [max(index, low_index-1)]; + +set_freq: + dbs_freq_increase(policy, freq_next); + + if (policy->cur == policy->max) + this_dbs_info->rate_mult = + dbs_tuners_ins.sampling_down_factor; + return; + } + + // shortcuts + if (input_event_boosted()) { + return; + } + + if (num_of_cpus > 1) { + if (avg_load_freq > dbs_tuners_ins.up_threshold_multi_core * + policy->cur) { + if (policy->cur < dbs_tuners_ins.optimal_freq) + dbs_freq_increase(policy, + dbs_tuners_ins.optimal_freq); + return; + } + } + + if (policy->cur == policy->min){ + return; + } + + // normal path down + if (avg_load_freq < + (dbs_tuners_ins.up_threshold - dbs_tuners_ins.down_differential) * + policy->cur) { + unsigned int freq_next; + freq_next = avg_load_freq / + (dbs_tuners_ins.up_threshold - + dbs_tuners_ins.down_differential); + + this_dbs_info->rate_mult = 1; + + if (freq_next < policy->min) + freq_next = policy->min; + + if (num_of_cpus > 1) { + if (dbs_tuners_ins.optimal_freq > + policy->min && avg_load_freq > + ((dbs_tuners_ins.up_threshold_multi_core - + dbs_tuners_ins.down_differential_multi_core) * + policy->cur) && + freq_next < dbs_tuners_ins.optimal_freq) + freq_next = dbs_tuners_ins.optimal_freq; + } + + if (dbs_tuners_ins.powersave_bias) { + freq_next = powersave_bias_target(policy, freq_next, + CPUFREQ_RELATION_L); + } + + if (dbs_tuners_ins.freq_down_step) { + unsigned int new_freq_next = freq_next; + if ((policy->cur - freq_next) > + dbs_tuners_ins.freq_down_step) { + new_freq_next = + policy->cur - + dbs_tuners_ins.freq_down_step; + } + + if (dbs_tuners_ins.freq_down_step_barrier) { + if (dbs_tuners_ins.freq_down_step_barrier < + new_freq_next) { + new_freq_next = + dbs_tuners_ins.freq_down_step_barrier; + } + + if (policy->cur <= + dbs_tuners_ins.freq_down_step_barrier) { + new_freq_next = freq_next; + } + } + + freq_next = new_freq_next; + } + + __cpufreq_driver_target(policy, freq_next, CPUFREQ_RELATION_L); + } +} + +static void do_dbs_timer(struct work_struct *work) +{ + struct cpu_dbs_info_s *dbs_info = + container_of(work, struct cpu_dbs_info_s, work.work); + unsigned int cpu = dbs_info->cpu; + int sample_type = dbs_info->sample_type; + + int delay; + + if (unlikely(!cpu_online(cpu) || !dbs_info->cur_policy)) + return; + + mutex_lock(&dbs_info->timer_mutex); + + + dbs_info->sample_type = DBS_NORMAL_SAMPLE; + if (!dbs_tuners_ins.powersave_bias || + sample_type == DBS_NORMAL_SAMPLE) { + dbs_check_cpu(dbs_info); + if (dbs_info->freq_lo) { + + dbs_info->sample_type = DBS_SUB_SAMPLE; + delay = dbs_info->freq_hi_jiffies; + } else { + delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate + * dbs_info->rate_mult); + + if (num_online_cpus() > 1) + delay -= jiffies % delay; + } + } else { + delay = dbs_info->freq_lo_jiffies; + if (input_event_boosted()) + goto sched_wait; + + __cpufreq_driver_target(dbs_info->cur_policy, + dbs_info->freq_lo, CPUFREQ_RELATION_H); + } + +sched_wait: + queue_delayed_work_on(cpu, dbs_wq, &dbs_info->work, delay); + mutex_unlock(&dbs_info->timer_mutex); +} + +static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) +{ + + int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + + if (num_online_cpus() > 1) + delay -= jiffies % delay; + + dbs_info->sample_type = DBS_NORMAL_SAMPLE; + INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer); + queue_delayed_work_on(dbs_info->cpu, dbs_wq, &dbs_info->work, delay); +} + +static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info) +{ + cancel_delayed_work_sync(&dbs_info->work); +} + +static int should_io_be_busy(void) +{ +#if defined(CONFIG_X86) + if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && + boot_cpu_data.x86 == 6 && + boot_cpu_data.x86_model >= 15) + return 1; +#endif + return 0; +} + +static void dbs_input_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + int i; + struct cpu_dbs_info_s *dbs_info; + unsigned long flags; + int input_event_min_freq = 0; + + if ((dbs_tuners_ins.powersave_bias == POWERSAVE_BIAS_MAXLEVEL) || + (dbs_tuners_ins.powersave_bias == POWERSAVE_BIAS_MINLEVEL)) { + + return; + } + + if (type == EV_SYN && code == SYN_REPORT) { + dbs_tuners_ins.powersave_bias = 0; + } + else if (type == EV_ABS && code == ABS_MT_TRACKING_ID) { + + if (value != -1) { + input_event_counter++; + input_event_min_freq = + input_event_min_freq_array[num_online_cpus() - 1]; + + } + else { + if (likely(input_event_counter > 0)) + input_event_counter--; + else + pr_warning("dbs_input_event: Touch isn't paired!\n"); + input_event_min_freq = 0; + } + } + else if (type == EV_KEY && value == 1 && + (code == KEY_POWER || code == KEY_VOLUMEUP || + code == KEY_VOLUMEDOWN)) + { + input_event_min_freq = + input_event_min_freq_array[num_online_cpus() - 1]; + } + + if (input_event_min_freq > 0) { + spin_lock_irqsave(&input_boost_lock, flags); + input_event_boost = true; + input_event_boost_expired = jiffies + + usecs_to_jiffies(dbs_tuners_ins.sampling_rate * + DEF_INPUT_BOOST_DURATION); + spin_unlock_irqrestore(&input_boost_lock, flags); + + for_each_online_cpu(i) + { +#ifdef CONFIG_ARCH_MSM_CORTEXMP + if (i != CPU0) + break; +#endif + dbs_info = &per_cpu(imm_cpu_dbs_info, i); + if (dbs_info->cur_policy && + dbs_info->cur_policy->cur < + input_event_min_freq) { + dbs_info->input_event_freq = + input_event_min_freq; + wake_up_process(per_cpu(up_task, i)); + } + } + } +} + +static int dbs_input_connect(struct input_handler *handler, + struct input_dev *dev, const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "cpufreq"; + + error = input_register_handle(handle); + if (error) + goto err2; + + error = input_open_device(handle); + if (error) + goto err1; + + return 0; +err1: + input_unregister_handle(handle); +err2: + kfree(handle); + return error; +} + +static void dbs_input_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id dbs_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) }, + .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] = + BIT_MASK(ABS_MT_POSITION_X) | + BIT_MASK(ABS_MT_POSITION_Y) }, + }, + + { + .flags = INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, + .absbit = { [BIT_WORD(ABS_X)] = + BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, + }, + + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { }, +}; + +static struct input_handler dbs_input_handler = { + .event = dbs_input_event, + .connect = dbs_input_connect, + .disconnect = dbs_input_disconnect, + .name = "cpufreq_imm", + .id_table = dbs_ids, +}; + +static int cpufreq_governor_dbs(struct cpufreq_policy *policy, + unsigned int event) +{ + unsigned int cpu = policy->cpu; + struct cpu_dbs_info_s *this_dbs_info; + unsigned int j; + int rc; + + this_dbs_info = &per_cpu(imm_cpu_dbs_info, cpu); + + switch (event) { + case CPUFREQ_GOV_START: + if ((!cpu_online(cpu)) || (!policy->cur)) + return -EINVAL; + + mutex_lock(&dbs_mutex); + + dbs_enable++; + for_each_cpu(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + j_dbs_info = &per_cpu(imm_cpu_dbs_info, j); + j_dbs_info->cur_policy = policy; + + j_dbs_info->prev_cpu_idle = get_cpu_idle_time(j, + &j_dbs_info->prev_cpu_wall); + if (dbs_tuners_ins.ignore_nice) + j_dbs_info->prev_cpu_nice = + kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + } + this_dbs_info->cpu = cpu; + this_dbs_info->rate_mult = 1; + intellimm_powersave_bias_init_cpu(cpu); + if (dbs_enable == 1) { + unsigned int latency; + + rc = sysfs_create_group(cpufreq_global_kobject, + &dbs_attr_group); + if (rc) { + mutex_unlock(&dbs_mutex); + return rc; + } + + + latency = policy->cpuinfo.transition_latency / 1000; + if (latency == 0) + latency = 1; + + min_sampling_rate = max(min_sampling_rate, + MIN_LATENCY_MULTIPLIER * latency); + dbs_tuners_ins.sampling_rate = + max(min_sampling_rate, + latency * LATENCY_MULTIPLIER); + + if (dbs_tuners_ins.sampling_rate < DEF_SAMPLING_RATE) + dbs_tuners_ins.sampling_rate = + DEF_SAMPLING_RATE; + + dbs_tuners_ins.io_is_busy = should_io_be_busy(); + + if (dbs_tuners_ins.optimal_freq == 0) + dbs_tuners_ins.optimal_freq = policy->min; + + dbs_init_freq_map_table(policy); + + } + if (!cpu) + rc = input_register_handler(&dbs_input_handler); + mutex_unlock(&dbs_mutex); + + if (!intellimm_powersave_bias_setspeed( + this_dbs_info->cur_policy, + NULL, + dbs_tuners_ins.powersave_bias)) + dbs_timer_init(this_dbs_info); + break; + + case CPUFREQ_GOV_STOP: + dbs_timer_exit(this_dbs_info); + this_dbs_info->prev_load = 0; + + mutex_lock(&dbs_mutex); + dbs_enable--; + + this_dbs_info->cur_policy = NULL; + if (!cpu) + input_unregister_handler(&dbs_input_handler); + if (!dbs_enable) { + dbs_deinit_freq_map_table(); + + sysfs_remove_group(cpufreq_global_kobject, + &dbs_attr_group); + + } + + mutex_unlock(&dbs_mutex); + break; + + case CPUFREQ_GOV_LIMITS: + mutex_lock(&this_dbs_info->timer_mutex); + if(this_dbs_info->cur_policy) { + if (policy->max < this_dbs_info->cur_policy->cur) + __cpufreq_driver_target(this_dbs_info-> + cur_policy, + policy->max, CPUFREQ_RELATION_H); + else if (policy->min > this_dbs_info->cur_policy->cur) + __cpufreq_driver_target(this_dbs_info-> + cur_policy, + policy->min, CPUFREQ_RELATION_L); + else if (dbs_tuners_ins.powersave_bias != 0) + intellimm_powersave_bias_setspeed( + this_dbs_info->cur_policy, + policy, + dbs_tuners_ins.powersave_bias); + } + mutex_unlock(&this_dbs_info->timer_mutex); + break; + } + return 0; +} + +static int cpufreq_gov_dbs_up_task(void *data) +{ + struct cpufreq_policy *policy; + struct cpu_dbs_info_s *this_dbs_info; + unsigned int cpu = smp_processor_id(); + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + + if (kthread_should_stop()) + break; + + set_current_state(TASK_RUNNING); + + get_online_cpus(); + + if (lock_policy_rwsem_write(cpu) < 0) + goto bail_acq_sema_failed; + + this_dbs_info = &per_cpu(imm_cpu_dbs_info, cpu); + policy = this_dbs_info->cur_policy; + if (!policy) { + + goto bail_incorrect_governor; + } + + mutex_lock(&this_dbs_info->timer_mutex); + + + dbs_tuners_ins.powersave_bias = 0; + dbs_freq_increase(policy, this_dbs_info->input_event_freq); + this_dbs_info->prev_cpu_idle = get_cpu_idle_time(cpu, + &this_dbs_info->prev_cpu_wall); + + mutex_unlock(&this_dbs_info->timer_mutex); + +bail_incorrect_governor: + unlock_policy_rwsem_write(cpu); + +bail_acq_sema_failed: + put_online_cpus(); + } + + return 0; +} + +static int __init cpufreq_gov_dbs_init(void) +{ + u64 idle_time; + unsigned int i; + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + struct task_struct *pthread; + int cpu = get_cpu(); + + idle_time = get_cpu_idle_time_us(cpu, NULL); + put_cpu(); + if (idle_time != -1ULL) { + + dbs_tuners_ins.up_threshold = MICRO_FREQUENCY_UP_THRESHOLD; + dbs_tuners_ins.down_differential = + MICRO_FREQUENCY_DOWN_DIFFERENTIAL; + min_sampling_rate = MICRO_FREQUENCY_MIN_SAMPLE_RATE; + } else { + + min_sampling_rate = + MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10); + } + + dbs_wq = alloc_workqueue("intellimm_dbs_wq", WQ_HIGHPRI, 0); + if (!dbs_wq) { + printk(KERN_ERR "Failed to create intellimm_dbs_wq workqueue\n"); + return -EFAULT; + } + + spin_lock_init(&input_boost_lock); + + for_each_possible_cpu(i) { + struct cpu_dbs_info_s *this_dbs_info = + &per_cpu(imm_cpu_dbs_info, i); + +#ifdef CONFIG_ARCH_MSM_CORTEXMP + if (i == CPU0) +#endif + { + pthread = kthread_create_on_node( + cpufreq_gov_dbs_up_task, NULL, + cpu_to_node(i), "kimm_up/%d", i); + if (likely(!IS_ERR(pthread))) { + kthread_bind(pthread, i); + sched_setscheduler_nocheck(pthread, + SCHED_FIFO, ¶m); + get_task_struct(pthread); + per_cpu(up_task, i) = pthread; + } + } + + mutex_init(&this_dbs_info->timer_mutex); + } + + return cpufreq_register_governor(&cpufreq_gov_intellimm); +} + +static void __exit cpufreq_gov_dbs_exit(void) +{ + unsigned int i; + + cpufreq_unregister_governor(&cpufreq_gov_intellimm); + for_each_possible_cpu(i) { + struct cpu_dbs_info_s *this_dbs_info = + &per_cpu(imm_cpu_dbs_info, i); +#ifdef CONFIG_ARCH_MSM_CORTEXMP + if (i == CPU0) +#endif + if (per_cpu(up_task, i)) { + kthread_stop(per_cpu(up_task, i)); + put_task_struct(per_cpu(up_task, i)); + } + + mutex_destroy(&this_dbs_info->timer_mutex); + } + destroy_workqueue(dbs_wq); +} + +MODULE_AUTHOR("Venkatesh Pallipadi "); +MODULE_AUTHOR("Alexey Starikovskiy "); +MODULE_AUTHOR("Paul Reioux "); +MODULE_DESCRIPTION("'cpufreq_intellimm' - A simple min/max cpufreq governor" + "for Low Latency Frequency Transition capable processors"); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIMM +fs_initcall(cpufreq_gov_dbs_init); +#else +module_init(cpufreq_gov_dbs_init); +#endif +module_exit(cpufreq_gov_dbs_exit); diff --git a/drivers/cpufreq/cpufreq_interactivex.c b/drivers/cpufreq/cpufreq_interactivex.c new file mode 100644 index 00000000..3b9f6a4d --- /dev/null +++ b/drivers/cpufreq/cpufreq_interactivex.c @@ -0,0 +1,405 @@ +/* + * drivers/cpufreq/cpufreq_interactivex.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) - modified for suspend/wake by imoseyon + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static void (*pm_idle_old)(void); +static atomic_t active_count = ATOMIC_INIT(0); + +static DEFINE_PER_CPU(struct timer_list, cpu_timer); + +static DEFINE_PER_CPU(u64, time_in_idle); +static DEFINE_PER_CPU(u64, idle_exit_time); + +static struct cpufreq_policy *policy; +static unsigned int target_freq; + +/* Workqueues handle frequency scaling */ +static struct workqueue_struct *up_wq; +static struct workqueue_struct *down_wq; +static struct work_struct freq_scale_work; + +static u64 freq_change_time; +static u64 freq_change_time_in_idle; + +static cpumask_t work_cpumask; + +static unsigned int suspended = 0; +static unsigned int enabled = 0; + +/* + * The minimum ammount of time to spend at a frequency before we can ramp down, + * default is 50ms. + */ +#define DEFAULT_MIN_SAMPLE_TIME 20000; +static unsigned long min_sample_time; + +#define FREQ_THRESHOLD 1209600; +static unsigned int freq_threshld; + +#define RESUME_SPEED 1209600; +static unsigned int resum_speed; + +static int cpufreq_governor_interactivex(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVEX +static +#endif +struct cpufreq_governor cpufreq_gov_interactivex = { + .name = "interactiveX", + .governor = cpufreq_governor_interactivex, +#if defined(CONFIG_ARCH_MSM_SCORPION) + .max_transition_latency = 8000000, +#else + .max_transition_latency = 10000000, +#endif + .owner = THIS_MODULE, +}; + +static void cpufreq_interactivex_timer(unsigned long data) +{ + u64 delta_idle; + u64 update_time; + u64 *cpu_time_in_idle; + u64 *cpu_idle_exit_time; + struct timer_list *t; + + u64 now_idle = get_cpu_idle_time_us(data, + &update_time); + + + cpu_time_in_idle = &per_cpu(time_in_idle, data); + cpu_idle_exit_time = &per_cpu(idle_exit_time, data); + + if (update_time == *cpu_idle_exit_time) + return; + + delta_idle = now_idle - *cpu_time_in_idle; + + /* Scale up if there were no idle cycles since coming out of idle */ + if (delta_idle == 0) { + if (policy->cur == policy->max) + return; + + if (nr_running() < 1) + return; + + target_freq = policy->max; + + cpumask_set_cpu(data, &work_cpumask); + queue_work(up_wq, &freq_scale_work); + return; + } + + /* + * There is a window where if the cpu utlization can go from low to high + * between the timer expiring, delta_idle will be > 0 and the cpu will + * be 100% busy, preventing idle from running, and this timer from + * firing. So setup another timer to fire to check cpu utlization. + * Do not setup the timer if there is no scheduled work. + */ + t = &per_cpu(cpu_timer, data); + if (!timer_pending(t) && nr_running() > 0) { + *cpu_time_in_idle = get_cpu_idle_time_us( + data, cpu_idle_exit_time); + mod_timer(t, jiffies + 2); + } + + if (policy->cur == policy->min) + return; + + /* + * Do not scale down unless we have been at this frequency for the + * minimum sample time. + */ + if ((update_time - freq_change_time) < min_sample_time) + return; + + target_freq = policy->min; + cpumask_set_cpu(data, &work_cpumask); + queue_work(down_wq, &freq_scale_work); +} + +static void cpufreq_idle(void) +{ + struct timer_list *t; + u64 *cpu_time_in_idle; + u64 *cpu_idle_exit_time; + + pm_idle_old(); + + if (!cpumask_test_cpu(smp_processor_id(), policy->cpus)) + return; + + /* Timer to fire in 1-2 ticks, jiffie aligned. */ + t = &per_cpu(cpu_timer, smp_processor_id()); + cpu_idle_exit_time = &per_cpu(idle_exit_time, smp_processor_id()); + cpu_time_in_idle = &per_cpu(time_in_idle, smp_processor_id()); + + if (timer_pending(t) == 0) { + *cpu_time_in_idle = get_cpu_idle_time_us( + smp_processor_id(), cpu_idle_exit_time); + mod_timer(t, jiffies + 2); + } +} + +/* + * Choose the cpu frequency based off the load. For now choose the minimum + * frequency that will satisfy the load, which is not always the lower power. + */ +static unsigned int cpufreq_interactivex_calc_freq(unsigned int cpu) +{ + unsigned int delta_time; + unsigned int idle_time; + unsigned int cpu_load; + unsigned int newfreq; + u64 current_wall_time; + u64 current_idle_time;; + + current_idle_time = get_cpu_idle_time_us(cpu, ¤t_wall_time); + + idle_time = (unsigned int) current_idle_time - freq_change_time_in_idle; + delta_time = (unsigned int) current_wall_time - freq_change_time; + + cpu_load = 100 * (delta_time - idle_time) / delta_time; + + if (cpu_load > 95) newfreq = policy->max; + else newfreq = policy->cur * cpu_load / 100; + + return newfreq; +} + + +/* We use the same work function to sale up and down */ +static void cpufreq_interactivex_freq_change_time_work(struct work_struct *work) +{ + unsigned int cpu; + unsigned int newtarget; + cpumask_t tmp_mask = work_cpumask; + newtarget = freq_threshld; + + for_each_cpu(cpu, &tmp_mask) { + if (!suspended) { + if (target_freq == policy->max) { + if (nr_running() == 1) { + cpumask_clear_cpu(cpu, &work_cpumask); + return; + } +// __cpufreq_driver_target(policy, target_freq, CPUFREQ_RELATION_H); + __cpufreq_driver_target(policy, newtarget, CPUFREQ_RELATION_H); + } else { + target_freq = cpufreq_interactivex_calc_freq(cpu); + __cpufreq_driver_target(policy, target_freq, + CPUFREQ_RELATION_L); + } + } + freq_change_time_in_idle = get_cpu_idle_time_us(cpu, &freq_change_time); + cpumask_clear_cpu(cpu, &work_cpumask); + } + + +} + +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) +{ + return strict_strtoul(buf, 0, &min_sample_time); +} + +static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, + show_min_sample_time, store_min_sample_time); + +static struct attribute *interactivex_attributes[] = { + &min_sample_time_attr.attr, + NULL, +}; + +static struct attribute_group interactivex_attr_group = { + .attrs = interactivex_attributes, + .name = "interactiveX", +}; + +static void interactivex_suspend(int suspend) +{ + unsigned int max_speed; + + max_speed = resum_speed; + + if (!enabled) return; + if (!suspend) { // resume at max speed: + suspended = 0; + __cpufreq_driver_target(policy, max_speed, CPUFREQ_RELATION_L); + pr_info("[imoseyon] interactiveX awake at %d\n", policy->cur); + } else { + suspended = 1; + __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); + pr_info("[imoseyon] interactiveX suspended at %d\n", policy->cur); + } +} + +static void interactivex_early_suspend(struct early_suspend *handler) { + interactivex_suspend(1); +} + +static void interactivex_late_resume(struct early_suspend *handler) { + interactivex_suspend(0); +} + +static struct early_suspend interactivex_power_suspend = { + .suspend = interactivex_early_suspend, + .resume = interactivex_late_resume, + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, +}; + +static int cpufreq_governor_interactivex(struct cpufreq_policy *new_policy, + unsigned int event) +{ + int rc; + unsigned int min_freq = ~0; + unsigned int max_freq = 0; + unsigned int i; + struct cpufreq_frequency_table *freq_table; + + switch (event) { + case CPUFREQ_GOV_START: + if (!cpu_online(new_policy->cpu)) + return -EINVAL; + + /* + * Do not register the idle hook and create sysfs + * entries if we have already done so. + */ + if (atomic_inc_return(&active_count) > 1) + return 0; + + rc = sysfs_create_group(cpufreq_global_kobject, + &interactivex_attr_group); + if (rc) + return rc; + + pm_idle_old = pm_idle; + pm_idle = cpufreq_idle; + policy = new_policy; + enabled = 1; + register_early_suspend(&interactivex_power_suspend); + pr_info("[imoseyon] interactiveX active\n"); + freq_table = cpufreq_frequency_get_table(new_policy->cpu); + for (i = 0; (freq_table[i].frequency != CPUFREQ_TABLE_END); i++) { + unsigned int freq = freq_table[i].frequency; + if (freq == CPUFREQ_ENTRY_INVALID) { + continue; + } + if (freq < min_freq) + min_freq = freq; + if (freq > max_freq) + max_freq = freq; + } + resum_speed = freq_table[(i-1)/2].frequency > min_freq ? freq_table[(i-1)/2].frequency : max_freq; //Value in midrange of available CPU frequencies if sufficient number of freq bins available + freq_threshld = max_freq; + break; + + case CPUFREQ_GOV_STOP: + if (atomic_dec_return(&active_count) > 1) + return 0; + + sysfs_remove_group(cpufreq_global_kobject, + &interactivex_attr_group); + + pm_idle = pm_idle_old; + del_timer(&per_cpu(cpu_timer, new_policy->cpu)); + enabled = 0; + unregister_early_suspend(&interactivex_power_suspend); + pr_info("[imoseyon] interactiveX inactive\n"); + break; + + case CPUFREQ_GOV_LIMITS: + if (new_policy->max < new_policy->cur) + __cpufreq_driver_target(new_policy, + new_policy->max, CPUFREQ_RELATION_H); + else if (new_policy->min > new_policy->cur) + __cpufreq_driver_target(new_policy, + new_policy->min, CPUFREQ_RELATION_L); + break; + } + return 0; +} + +static int __init cpufreq_interactivex_init(void) +{ + unsigned int i; + struct timer_list *t; + min_sample_time = DEFAULT_MIN_SAMPLE_TIME; + resum_speed = RESUME_SPEED; + freq_threshld = FREQ_THRESHOLD; + + /* Initalize per-cpu timers */ + for_each_possible_cpu(i) { + t = &per_cpu(cpu_timer, i); + init_timer_deferrable(t); + t->function = cpufreq_interactivex_timer; + t->data = i; + } + + /* Scale up is high priority */ + up_wq = create_workqueue("kinteractive_up"); + down_wq = create_workqueue("knteractive_down"); + + INIT_WORK(&freq_scale_work, cpufreq_interactivex_freq_change_time_work); + + pr_info("[imoseyon] interactiveX enter\n"); + return cpufreq_register_governor(&cpufreq_gov_interactivex); +} + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVEX +fs_initcall(cpufreq_interactivex_init); +#else +module_init(cpufreq_interactivex_init); +#endif + +static void __exit cpufreq_interactivex_exit(void) +{ + pr_info("[imoseyon] interactiveX exit\n"); + cpufreq_unregister_governor(&cpufreq_gov_interactivex); + destroy_workqueue(up_wq); + destroy_workqueue(down_wq); +} + +module_exit(cpufreq_interactivex_exit); + +MODULE_AUTHOR("Mike Chan "); +MODULE_DESCRIPTION("'cpufreq_interactiveX' - A cpufreq governor for " + "Latency sensitive workloads"); +MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/cpufreq_lionheart.c b/drivers/cpufreq/cpufreq_lionheart.c new file mode 100755 index 00000000..9e549007 --- /dev/null +++ b/drivers/cpufreq/cpufreq_lionheart.c @@ -0,0 +1,545 @@ +/* + * drivers/cpufreq/cpufreq_lionheart.c + * + * Patched & tweaked: knzo + * + * Based on the Conservative governor by: + * + * Copyright (C) 2001 Russell King + * (C) 2003 Venkatesh Pallipadi . + * Jun Nakajima + * (C) 2009 Alexander Clouter + * + * 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 DEF_FREQUENCY_UP_THRESHOLD (70) +#define DEF_FREQUENCY_DOWN_THRESHOLD (30) +#define MIN_SAMPLING_RATE_RATIO (2) + +static unsigned int min_sampling_rate; + +#define LATENCY_MULTIPLIER (1000) +#define MIN_LATENCY_MULTIPLIER (100) +#define DEF_SAMPLING_DOWN_FACTOR (1) +#define MAX_SAMPLING_DOWN_FACTOR (10) +#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) + +static void do_dbs_timer(struct work_struct *work); + +struct cpu_dbs_info_s { + cputime64_t prev_cpu_idle; + cputime64_t prev_cpu_wall; + cputime64_t prev_cpu_nice; + struct cpufreq_policy *cur_policy; + struct delayed_work work; + unsigned int down_skip; + unsigned int requested_freq; + int cpu; + unsigned int enable:1; + + struct mutex timer_mutex; +}; +static DEFINE_PER_CPU(struct cpu_dbs_info_s, cs_cpu_dbs_info); + +static unsigned int dbs_enable; + +static DEFINE_MUTEX(dbs_mutex); + +static struct dbs_tuners { + unsigned int sampling_rate; + unsigned int sampling_down_factor; + unsigned int up_threshold; + unsigned int down_threshold; + unsigned int ignore_nice; + unsigned int freq_step; +} dbs_tuners_ins = { + .up_threshold = DEF_FREQUENCY_UP_THRESHOLD, + .down_threshold = DEF_FREQUENCY_DOWN_THRESHOLD, + .sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR, + .ignore_nice = 0, + .freq_step = 5, +}; + +static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, + cputime64_t *wall) +{ + cputime64_t idle_time; + cputime64_t cur_wall_time; + cputime64_t busy_time; + + cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); + + busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; + + idle_time = cur_wall_time - busy_time; + if (wall) + *wall = (cputime64_t)jiffies_to_usecs(cur_wall_time); + + return (cputime64_t)jiffies_to_usecs(idle_time); +} + +static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall) +{ + u64 idle_time = get_cpu_idle_time_us(cpu, wall); + + if (idle_time == -1ULL) + return get_cpu_idle_time_jiffy(cpu, wall); + + return idle_time; +} + +static int +dbs_cpufreq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + struct cpufreq_freqs *freq = data; + struct cpu_dbs_info_s *this_dbs_info = &per_cpu(cs_cpu_dbs_info, + freq->cpu); + + struct cpufreq_policy *policy; + + if (!this_dbs_info->enable) + return 0; + + policy = this_dbs_info->cur_policy; + + if (this_dbs_info->requested_freq > policy->max + || this_dbs_info->requested_freq < policy->min) + this_dbs_info->requested_freq = freq->new; + + return 0; +} + +static struct notifier_block dbs_cpufreq_notifier_block = { + .notifier_call = dbs_cpufreq_notifier +}; + +static ssize_t show_sampling_rate_min(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", min_sampling_rate); +} + +define_one_global_ro(sampling_rate_min); + +#define show_one(file_name, object) \ +static ssize_t show_##file_name \ +(struct kobject *kobj, struct attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%u\n", dbs_tuners_ins.object); \ +} + +show_one(sampling_rate, sampling_rate); +show_one(sampling_down_factor, sampling_down_factor); +show_one(up_threshold, up_threshold); +show_one(down_threshold, down_threshold); +show_one(ignore_nice_load, ignore_nice); +show_one(freq_step, freq_step); + +static ssize_t store_sampling_down_factor(struct kobject *a, + struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1) + return -EINVAL; + + dbs_tuners_ins.sampling_down_factor = input; + return count; +} + +static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1) + return -EINVAL; + + dbs_tuners_ins.sampling_rate = max(input, min_sampling_rate); + return count; +} + +static ssize_t store_up_threshold(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > 100 || + input <= dbs_tuners_ins.down_threshold) + return -EINVAL; + + dbs_tuners_ins.up_threshold = input; + return count; +} + +static ssize_t store_down_threshold(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input < 11 || input > 100 || + input >= dbs_tuners_ins.up_threshold) + return -EINVAL; + + dbs_tuners_ins.down_threshold = input; + return count; +} + +static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + unsigned int j; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + if (input > 1) + input = 1; + + if (input == dbs_tuners_ins.ignore_nice) + return count; + + dbs_tuners_ins.ignore_nice = input; + + for_each_online_cpu(j) { + struct cpu_dbs_info_s *dbs_info; + dbs_info = &per_cpu(cs_cpu_dbs_info, j); + dbs_info->prev_cpu_idle = get_cpu_idle_time(j, + &dbs_info->prev_cpu_wall); + if (dbs_tuners_ins.ignore_nice) + dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + } + return count; +} + +static ssize_t store_freq_step(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1) + return -EINVAL; + + if (input > 100) + input = 100; + + dbs_tuners_ins.freq_step = input; + return count; +} + +define_one_global_rw(sampling_rate); +define_one_global_rw(sampling_down_factor); +define_one_global_rw(up_threshold); +define_one_global_rw(down_threshold); +define_one_global_rw(ignore_nice_load); +define_one_global_rw(freq_step); + +static struct attribute *dbs_attributes[] = { + &sampling_rate_min.attr, + &sampling_rate.attr, + &sampling_down_factor.attr, + &up_threshold.attr, + &down_threshold.attr, + &ignore_nice_load.attr, + &freq_step.attr, + NULL +}; + +static struct attribute_group dbs_attr_group = { + .attrs = dbs_attributes, + .name = "lionheart", +}; + +static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) +{ + unsigned int load = 0; + unsigned int max_load = 0; + unsigned int freq_target; + + struct cpufreq_policy *policy; + unsigned int j; + + policy = this_dbs_info->cur_policy; + + for_each_cpu(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + cputime64_t cur_wall_time, cur_idle_time; + unsigned int idle_time, wall_time; + + j_dbs_info = &per_cpu(cs_cpu_dbs_info, j); + + cur_idle_time = get_cpu_idle_time(j, &cur_wall_time); + + wall_time = (unsigned int) + (cur_wall_time - j_dbs_info->prev_cpu_wall); + j_dbs_info->prev_cpu_wall = cur_wall_time; + + idle_time = (unsigned int) + (cur_idle_time - j_dbs_info->prev_cpu_idle); + j_dbs_info->prev_cpu_idle = cur_idle_time; + + if (dbs_tuners_ins.ignore_nice) { + cputime64_t cur_nice; + unsigned long cur_nice_jiffies; + + cur_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE] - + j_dbs_info->prev_cpu_nice; + /* + * Assumption: nice time between sampling periods will + * be less than 2^32 jiffies for 32 bit sys + */ + cur_nice_jiffies = (unsigned long) + cputime64_to_jiffies64(cur_nice); + + j_dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + idle_time += jiffies_to_usecs(cur_nice_jiffies); + } + + if (unlikely(!wall_time || wall_time < idle_time)) + continue; + + load = 100 * (wall_time - idle_time) / wall_time; + + if (load > max_load) + max_load = load; + } + + if (dbs_tuners_ins.freq_step == 0) + return; + + if (max_load > dbs_tuners_ins.up_threshold) { + this_dbs_info->down_skip = 0; + + if (this_dbs_info->requested_freq == policy->max) + return; + + freq_target = (dbs_tuners_ins.freq_step * policy->max) / 100; + + if (unlikely(freq_target == 0)) + freq_target = 5; + + this_dbs_info->requested_freq += freq_target; + if (this_dbs_info->requested_freq > policy->max) + this_dbs_info->requested_freq = policy->max; + + __cpufreq_driver_target(policy, this_dbs_info->requested_freq, + CPUFREQ_RELATION_H); + return; + } + + if (max_load < (dbs_tuners_ins.down_threshold - 10)) { + freq_target = (dbs_tuners_ins.freq_step * policy->max) / 100; + + this_dbs_info->requested_freq -= freq_target; + if (this_dbs_info->requested_freq < policy->min) + this_dbs_info->requested_freq = policy->min; + + if (policy->cur == policy->min) + return; + + __cpufreq_driver_target(policy, this_dbs_info->requested_freq, + CPUFREQ_RELATION_H); + return; + } +} + +static void do_dbs_timer(struct work_struct *work) +{ + struct cpu_dbs_info_s *dbs_info = + container_of(work, struct cpu_dbs_info_s, work.work); + unsigned int cpu = dbs_info->cpu; + + int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + + // delay -= jiffies % delay; + + mutex_lock(&dbs_info->timer_mutex); + + dbs_check_cpu(dbs_info); + + schedule_delayed_work_on(cpu, &dbs_info->work, delay); + mutex_unlock(&dbs_info->timer_mutex); +} + +static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) +{ + int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + // delay -= jiffies % delay; + + dbs_info->enable = 1; + INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer); + schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, delay); +} + +static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info) +{ + dbs_info->enable = 0; + cancel_delayed_work_sync(&dbs_info->work); +} + +static int cpufreq_governor_dbs(struct cpufreq_policy *policy, + unsigned int event) +{ + unsigned int cpu = policy->cpu; + struct cpu_dbs_info_s *this_dbs_info; + unsigned int j; + int rc; + + this_dbs_info = &per_cpu(cs_cpu_dbs_info, cpu); + + switch (event) { + case CPUFREQ_GOV_START: + if ((!cpu_online(cpu)) || (!policy->cur)) + return -EINVAL; + + mutex_lock(&dbs_mutex); + + for_each_cpu(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + j_dbs_info = &per_cpu(cs_cpu_dbs_info, j); + j_dbs_info->cur_policy = policy; + + j_dbs_info->prev_cpu_idle = get_cpu_idle_time(j, + &j_dbs_info->prev_cpu_wall); + if (dbs_tuners_ins.ignore_nice) + j_dbs_info->prev_cpu_nice = + kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + } + this_dbs_info->down_skip = 0; + this_dbs_info->requested_freq = policy->cur; + + mutex_init(&this_dbs_info->timer_mutex); + dbs_enable++; + + if (dbs_enable == 1) { + unsigned int latency; + + latency = policy->cpuinfo.transition_latency / 1000; + if (latency == 0) + latency = 1; + + rc = sysfs_create_group(cpufreq_global_kobject, + &dbs_attr_group); + if (rc) { + mutex_unlock(&dbs_mutex); + return rc; + } + + min_sampling_rate = 10000; + dbs_tuners_ins.sampling_rate = 10000; + + cpufreq_register_notifier( + &dbs_cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + } + mutex_unlock(&dbs_mutex); + + dbs_timer_init(this_dbs_info); + + break; + + case CPUFREQ_GOV_STOP: + dbs_timer_exit(this_dbs_info); + + mutex_lock(&dbs_mutex); + dbs_enable--; + mutex_destroy(&this_dbs_info->timer_mutex); + + if (dbs_enable == 0) + cpufreq_unregister_notifier( + &dbs_cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + + mutex_unlock(&dbs_mutex); + if (!dbs_enable) + sysfs_remove_group(cpufreq_global_kobject, + &dbs_attr_group); + + break; + + case CPUFREQ_GOV_LIMITS: + mutex_lock(&this_dbs_info->timer_mutex); + if (policy->max < this_dbs_info->cur_policy->cur) + __cpufreq_driver_target( + this_dbs_info->cur_policy, + policy->max, CPUFREQ_RELATION_H); + else if (policy->min > this_dbs_info->cur_policy->cur) + __cpufreq_driver_target( + this_dbs_info->cur_policy, + policy->min, CPUFREQ_RELATION_L); + mutex_unlock(&this_dbs_info->timer_mutex); + + break; + } + return 0; +} + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_LIONHEART +static +#endif +struct cpufreq_governor cpufreq_gov_lionheart = { + .name = "lionheart", + .governor = cpufreq_governor_dbs, + .max_transition_latency = TRANSITION_LATENCY_LIMIT, + .owner = THIS_MODULE, +}; + +static int __init cpufreq_gov_dbs_init(void) +{ + return cpufreq_register_governor(&cpufreq_gov_lionheart); +} + +static void __exit cpufreq_gov_dbs_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_lionheart); +} + +MODULE_AUTHOR("knzo"); +MODULE_DESCRIPTION("'cpufreq_lionheart' - A brave and agile conservative-based governor."); +MODULE_LICENSE("GPL"); + +fs_initcall(cpufreq_gov_dbs_init); +module_exit(cpufreq_gov_dbs_exit); diff --git a/drivers/cpufreq/cpufreq_ondemandx.c b/drivers/cpufreq/cpufreq_ondemandx.c new file mode 100644 index 00000000..779d3962 --- /dev/null +++ b/drivers/cpufreq/cpufreq_ondemandx.c @@ -0,0 +1,830 @@ +/* + * drivers/cpufreq/cpufreq_ondemandX.c + * + * Copyright (C) 2001 Russell King + * (C) 2003 Venkatesh Pallipadi . + * Jun Nakajima + * + * 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 + +/* + * dbs is used in this file as a shortform for demandbased switching + * It helps to keep variable names smaller, simpler + */ + +#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) +#define DEF_FREQUENCY_UP_THRESHOLD (80) +#define DEF_SAMPLING_DOWN_FACTOR (170) +#define MAX_SAMPLING_DOWN_FACTOR (100000) +#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) +#define MICRO_FREQUENCY_UP_THRESHOLD (95) +#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) +#define MIN_FREQUENCY_UP_THRESHOLD (11) +#define MAX_FREQUENCY_UP_THRESHOLD (100) + +/* + * The polling frequency of this governor depends on the capability of + * the processor. Default polling frequency is 1000 times the transition + * latency of the processor. The governor will work on any processor with + * transition latency <= 10mS, using appropriate sampling + * rate. + * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL) + * this governor will not work. + * All times here are in uS. + */ +#define MIN_SAMPLING_RATE_RATIO (2) + +static unsigned int min_sampling_rate; + +static unsigned int suspendfreq = 384000; + +#define LATENCY_MULTIPLIER (1000) +#define MIN_LATENCY_MULTIPLIER (100) +#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) + +static void do_dbs_timer(struct work_struct *work); +static int cpufreq_governor_dbs(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMANDX +static +#endif +struct cpufreq_governor cpufreq_gov_ondemandX = { + .name = "ondemandX", + .governor = cpufreq_governor_dbs, + .max_transition_latency = TRANSITION_LATENCY_LIMIT, + .owner = THIS_MODULE, +}; + +/* Sampling types */ +enum {DBS_NORMAL_SAMPLE, DBS_SUB_SAMPLE}; + +struct cpu_dbs_info_s { + cputime64_t prev_cpu_idle; + cputime64_t prev_cpu_iowait; + cputime64_t prev_cpu_wall; + cputime64_t prev_cpu_nice; + struct cpufreq_policy *cur_policy; + struct delayed_work work; + struct cpufreq_frequency_table *freq_table; + unsigned int freq_lo; + unsigned int freq_lo_jiffies; + unsigned int freq_hi_jiffies; + unsigned int rate_mult; + int cpu; + unsigned int sample_type:1; + /* + * percpu mutex that serializes governor limit change with + * do_dbs_timer invocation. We do not want do_dbs_timer to run + * when user is changing the governor or limits. + */ + struct mutex timer_mutex; +}; +static DEFINE_PER_CPU(struct cpu_dbs_info_s, od_cpu_dbs_info); + +static unsigned int dbs_enable=0; /* number of CPUs using this policy */ + +// used for imoseyon's mods +static unsigned int suspended = 0; +static void ondemandX_suspend(int suspend) +{ + struct cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, smp_processor_id()); + if (dbs_enable==0) return; + if (!suspend) { // resume at max speed: + suspended = 0; + __cpufreq_driver_target(dbs_info->cur_policy, dbs_info->cur_policy->max, + CPUFREQ_RELATION_L); + pr_info("[imoseyon] ondemandX awake at %d\n", dbs_info->cur_policy->cur); + } else { + suspended = 1; + // let's give it a little breathing room + __cpufreq_driver_target(dbs_info->cur_policy, suspendfreq, CPUFREQ_RELATION_H); + pr_info("[imoseyon] ondemandX suspended at %d\n", dbs_info->cur_policy->cur); + } +} + +static void ondemandX_early_suspend(struct early_suspend *handler) { + ondemandX_suspend(1); +} + +static void ondemandX_late_resume(struct early_suspend *handler) { + ondemandX_suspend(0); +} + +static struct early_suspend ondemandX_power_suspend = { + .suspend = ondemandX_early_suspend, + .resume = ondemandX_late_resume, + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, +}; + +/* + * dbs_mutex protects dbs_enable in governor start/stop. + */ +static DEFINE_MUTEX(dbs_mutex); + +static struct dbs_tuners { + unsigned int sampling_rate; + unsigned int up_threshold; + unsigned int down_differential; + unsigned int ignore_nice; + unsigned int sampling_down_factor; + unsigned int powersave_bias; + unsigned int io_is_busy; +} dbs_tuners_ins = { + .up_threshold = DEF_FREQUENCY_UP_THRESHOLD, + .sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR, + .down_differential = DEF_FREQUENCY_DOWN_DIFFERENTIAL, + .ignore_nice = 0, + .powersave_bias = 100, +}; + +static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, + cputime64_t *wall) +{ + cputime64_t idle_time; + cputime64_t cur_wall_time; + cputime64_t busy_time; + + cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); + + busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; + + idle_time = cur_wall_time - busy_time; + if (wall) + *wall = (cputime64_t)jiffies_to_usecs(cur_wall_time); + + return (cputime64_t)jiffies_to_usecs(idle_time); +} + +static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall) +{ + u64 idle_time = get_cpu_idle_time_us(cpu, wall); + + if (idle_time == -1ULL) + return get_cpu_idle_time_jiffy(cpu, wall); + + return idle_time; +} + +static inline cputime64_t get_cpu_iowait_time(unsigned int cpu, cputime64_t *wall) +{ + u64 iowait_time = get_cpu_iowait_time_us(cpu, wall); + + if (iowait_time == -1ULL) + return 0; + + return iowait_time; +} + +/* + * Find right freq to be set now with powersave_bias on. + * Returns the freq_hi to be used right now and will set freq_hi_jiffies, + * freq_lo, and freq_lo_jiffies in percpu area for averaging freqs. + */ +static unsigned int powersave_bias_target(struct cpufreq_policy *policy, + unsigned int freq_next, + unsigned int relation) +{ + unsigned int freq_req, freq_reduc, freq_avg; + unsigned int freq_hi, freq_lo; + unsigned int index = 0; + unsigned int jiffies_total, jiffies_hi, jiffies_lo; + struct cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, + policy->cpu); + + if (!dbs_info->freq_table) { + dbs_info->freq_lo = 0; + dbs_info->freq_lo_jiffies = 0; + return freq_next; + } + + cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_next, + relation, &index); + freq_req = dbs_info->freq_table[index].frequency; + freq_reduc = freq_req * dbs_tuners_ins.powersave_bias / 1000; + freq_avg = freq_req - freq_reduc; + + /* Find freq bounds for freq_avg in freq_table */ + index = 0; + cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_avg, + CPUFREQ_RELATION_H, &index); + freq_lo = dbs_info->freq_table[index].frequency; + index = 0; + cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_avg, + CPUFREQ_RELATION_L, &index); + freq_hi = dbs_info->freq_table[index].frequency; + + /* Find out how long we have to be in hi and lo freqs */ + if (freq_hi == freq_lo) { + dbs_info->freq_lo = 0; + dbs_info->freq_lo_jiffies = 0; + return freq_lo; + } + jiffies_total = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + jiffies_hi = (freq_avg - freq_lo) * jiffies_total; + jiffies_hi += ((freq_hi - freq_lo) / 2); + jiffies_hi /= (freq_hi - freq_lo); + jiffies_lo = jiffies_total - jiffies_hi; + dbs_info->freq_lo = freq_lo; + dbs_info->freq_lo_jiffies = jiffies_lo; + dbs_info->freq_hi_jiffies = jiffies_hi; + return freq_hi; +} + +static void ondemandX_powersave_bias_init_cpu(int cpu) +{ + struct cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, cpu); + dbs_info->freq_table = cpufreq_frequency_get_table(cpu); + dbs_info->freq_lo = 0; +} + +static void ondemandX_powersave_bias_init(void) +{ + int i; + for_each_online_cpu(i) { + ondemandX_powersave_bias_init_cpu(i); + } +} + +/************************** sysfs interface ************************/ + +static ssize_t show_sampling_rate_min(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", min_sampling_rate); +} + +define_one_global_ro(sampling_rate_min); + +/* cpufreq_ondemandX Governor Tunables */ +#define show_one(file_name, object) \ +static ssize_t show_##file_name \ +(struct kobject *kobj, struct attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%u\n", dbs_tuners_ins.object); \ +} +show_one(sampling_rate, sampling_rate); +show_one(io_is_busy, io_is_busy); +show_one(up_threshold, up_threshold); +show_one(down_differential, down_differential); +show_one(sampling_down_factor, sampling_down_factor); +show_one(ignore_nice_load, ignore_nice); +show_one(powersave_bias, powersave_bias); + +static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + dbs_tuners_ins.sampling_rate = max(input, min_sampling_rate); + return count; +} + +static ssize_t store_io_is_busy(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + dbs_tuners_ins.io_is_busy = !!input; + return count; +} + +static ssize_t store_up_threshold(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > MAX_FREQUENCY_UP_THRESHOLD || + input < MIN_FREQUENCY_UP_THRESHOLD) { + return -EINVAL; + } + dbs_tuners_ins.up_threshold = input; + return count; +} + +static ssize_t store_sampling_down_factor(struct kobject *a, + struct attribute *b, const char *buf, size_t count) +{ + unsigned int input, j; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1) + return -EINVAL; + dbs_tuners_ins.sampling_down_factor = input; + + /* Reset down sampling multiplier in case it was active */ + for_each_online_cpu(j) { + struct cpu_dbs_info_s *dbs_info; + dbs_info = &per_cpu(od_cpu_dbs_info, j); + dbs_info->rate_mult = 1; + } + return count; +} + +static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + unsigned int j; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + if (input > 1) + input = 1; + + if (input == dbs_tuners_ins.ignore_nice) { /* nothing to do */ + return count; + } + dbs_tuners_ins.ignore_nice = input; + + /* we need to re-evaluate prev_cpu_idle */ + for_each_online_cpu(j) { + struct cpu_dbs_info_s *dbs_info; + dbs_info = &per_cpu(od_cpu_dbs_info, j); + dbs_info->prev_cpu_idle = get_cpu_idle_time(j, + &dbs_info->prev_cpu_wall); + if (dbs_tuners_ins.ignore_nice) + dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + + } + return count; +} + +static ssize_t store_powersave_bias(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1) + return -EINVAL; + + if (input > 1000) + input = 1000; + + dbs_tuners_ins.powersave_bias = input; + ondemandX_powersave_bias_init(); + return count; +} + +static ssize_t store_down_differential(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1) + return -EINVAL; + + if (input > 30) + input = 30; + + if (input < 0) + input = 0; + + mutex_lock(&dbs_mutex); + dbs_tuners_ins.down_differential = input; + mutex_unlock(&dbs_mutex); + + return count; +} + +define_one_global_rw(sampling_rate); +define_one_global_rw(io_is_busy); +define_one_global_rw(up_threshold); +define_one_global_rw(down_differential); +define_one_global_rw(sampling_down_factor); +define_one_global_rw(ignore_nice_load); +define_one_global_rw(powersave_bias); + +static struct attribute *dbs_attributes[] = { + &sampling_rate_min.attr, + &sampling_rate.attr, + &up_threshold.attr, + &down_differential.attr, + &sampling_down_factor.attr, + &ignore_nice_load.attr, + &powersave_bias.attr, + &io_is_busy.attr, + NULL +}; + +static struct attribute_group dbs_attr_group = { + .attrs = dbs_attributes, + .name = "ondemandX", +}; + +/************************** sysfs end ************************/ + +static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq) +{ + if (dbs_tuners_ins.powersave_bias) + freq = powersave_bias_target(p, freq, CPUFREQ_RELATION_H); + else if (p->cur == p->max) + return; + if (suspended && freq > suspendfreq) { + freq = suspendfreq; + __cpufreq_driver_target(p, freq, CPUFREQ_RELATION_H); + } else + __cpufreq_driver_target(p, freq, dbs_tuners_ins.powersave_bias ? + CPUFREQ_RELATION_L : CPUFREQ_RELATION_H); +} + +static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) +{ + unsigned int max_load_freq; + + struct cpufreq_policy *policy; + unsigned int j; + + this_dbs_info->freq_lo = 0; + policy = this_dbs_info->cur_policy; + + /* + * Every sampling_rate, we check, if current idle time is less + * than 20% (default), then we try to increase frequency + * Every sampling_rate, we look for a the lowest + * frequency which can sustain the load while keeping idle time over + * 30%. If such a frequency exist, we try to decrease to this frequency. + * + * Any frequency increase takes it to the maximum frequency. + * Frequency reduction happens at minimum steps of + * 5% (default) of current frequency + */ + + /* Get Absolute Load - in terms of freq */ + max_load_freq = 0; + + for_each_cpu(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + cputime64_t cur_wall_time, cur_idle_time, cur_iowait_time; + unsigned int idle_time, wall_time, iowait_time; + unsigned int load, load_freq; + int freq_avg; + + j_dbs_info = &per_cpu(od_cpu_dbs_info, j); + + cur_idle_time = get_cpu_idle_time(j, &cur_wall_time); + cur_iowait_time = get_cpu_iowait_time(j, &cur_wall_time); + + wall_time = (unsigned int) + (cur_wall_time - j_dbs_info->prev_cpu_wall); + j_dbs_info->prev_cpu_wall = cur_wall_time; + + idle_time = (unsigned int) + (cur_idle_time - j_dbs_info->prev_cpu_idle); + j_dbs_info->prev_cpu_idle = cur_idle_time; + + iowait_time = (unsigned int) + (cur_iowait_time - j_dbs_info->prev_cpu_iowait); + j_dbs_info->prev_cpu_iowait = cur_iowait_time; + + if (dbs_tuners_ins.ignore_nice) { + u64 cur_nice; + unsigned long cur_nice_jiffies; + + cur_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE] - + j_dbs_info->prev_cpu_nice; + /* + * Assumption: nice time between sampling periods will + * be less than 2^32 jiffies for 32 bit sys + */ + cur_nice_jiffies = (unsigned long) + cputime64_to_jiffies64(cur_nice); + + j_dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + idle_time += jiffies_to_usecs(cur_nice_jiffies); + } + + /* + * For the purpose of ondemandX, waiting for disk IO is an + * indication that you're performance critical, and not that + * the system is actually idle. So subtract the iowait time + * from the cpu idle time. + */ + + if (dbs_tuners_ins.io_is_busy && idle_time >= iowait_time) + idle_time -= iowait_time; + + if (unlikely(!wall_time || wall_time < idle_time)) + continue; + + load = 100 * (wall_time - idle_time) / wall_time; + + freq_avg = __cpufreq_driver_getavg(policy, j); + if (freq_avg <= 0) + freq_avg = policy->cur; + + load_freq = load * freq_avg; + if (load_freq > max_load_freq) + max_load_freq = load_freq; + } + + /* Check for frequency increase */ + if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) { + /* If switching to max speed, apply sampling_down_factor */ + if (policy->cur < policy->max) + this_dbs_info->rate_mult = + dbs_tuners_ins.sampling_down_factor; + dbs_freq_increase(policy, policy->max); + return; + } + + /* Check for frequency decrease */ + /* if we cannot reduce the frequency anymore, break out early */ + if (policy->cur == policy->min) + return; + + /* + * The optimal frequency is the frequency that is the lowest that + * can support the current CPU usage without triggering the up + * policy. To be safe, we focus 10 points under the threshold. + */ + if (max_load_freq < + (dbs_tuners_ins.up_threshold - dbs_tuners_ins.down_differential) * + policy->cur) { + unsigned int freq_next; + freq_next = max_load_freq / + (dbs_tuners_ins.up_threshold - + dbs_tuners_ins.down_differential); + + /* No longer fully busy, reset rate_mult */ + this_dbs_info->rate_mult = 1; + + if (freq_next < policy->min) + freq_next = policy->min; + + if (!dbs_tuners_ins.powersave_bias) { + __cpufreq_driver_target(policy, freq_next, + CPUFREQ_RELATION_L); + } else { + int freq = powersave_bias_target(policy, freq_next, + CPUFREQ_RELATION_L); + __cpufreq_driver_target(policy, freq, + CPUFREQ_RELATION_L); + } + } +} + +static void do_dbs_timer(struct work_struct *work) +{ + struct cpu_dbs_info_s *dbs_info = + container_of(work, struct cpu_dbs_info_s, work.work); + unsigned int cpu = dbs_info->cpu; + int sample_type = dbs_info->sample_type; + + int delay; + + mutex_lock(&dbs_info->timer_mutex); + + /* Common NORMAL_SAMPLE setup */ + dbs_info->sample_type = DBS_NORMAL_SAMPLE; + if (!dbs_tuners_ins.powersave_bias || + sample_type == DBS_NORMAL_SAMPLE) { + dbs_check_cpu(dbs_info); + if (dbs_info->freq_lo) { + /* Setup timer for SUB_SAMPLE */ + dbs_info->sample_type = DBS_SUB_SAMPLE; + delay = dbs_info->freq_hi_jiffies; + } else { + /* We want all CPUs to do sampling nearly on + * same jiffy + */ + delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate + * dbs_info->rate_mult); + + if (num_online_cpus() > 1) + delay -= jiffies % delay; + } + } else { + if (!suspended) + __cpufreq_driver_target(dbs_info->cur_policy, + dbs_info->freq_lo, CPUFREQ_RELATION_H); + delay = dbs_info->freq_lo_jiffies; + } + schedule_delayed_work_on(cpu, &dbs_info->work, delay); + mutex_unlock(&dbs_info->timer_mutex); +} + +static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) +{ + /* We want all CPUs to do sampling nearly on same jiffy */ + int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); + + if (num_online_cpus() > 1) + delay -= jiffies % delay; + + dbs_info->sample_type = DBS_NORMAL_SAMPLE; + INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer); + schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, delay); +} + +static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info) +{ + cancel_delayed_work_sync(&dbs_info->work); +} + +/* + * Not all CPUs want IO time to be accounted as busy; this dependson how + * efficient idling at a higher frequency/voltage is. + * Pavel Machek says this is not so for various generations of AMD and old + * Intel systems. + * Mike Chan (androidlcom) calis this is also not true for ARM. + * Because of this, whitelist specific known (series) of CPUs by default, and + * leave all others up to the user. + */ +static int should_io_be_busy(void) +{ +#if defined(CONFIG_X86) + /* + * For Intel, Core 2 (model 15) andl later have an efficient idle. + */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && + boot_cpu_data.x86 == 6 && + boot_cpu_data.x86_model >= 15) + return 1; +#endif + return 0; +} + +static int cpufreq_governor_dbs(struct cpufreq_policy *policy, + unsigned int event) +{ + unsigned int cpu = policy->cpu; + struct cpu_dbs_info_s *this_dbs_info; + unsigned int j; + int rc; + + this_dbs_info = &per_cpu(od_cpu_dbs_info, cpu); + + switch (event) { + case CPUFREQ_GOV_START: + if ((!cpu_online(cpu)) || (!policy->cur)) + return -EINVAL; + + mutex_lock(&dbs_mutex); + + dbs_enable++; + for_each_cpu(j, policy->cpus) { + struct cpu_dbs_info_s *j_dbs_info; + j_dbs_info = &per_cpu(od_cpu_dbs_info, j); + j_dbs_info->cur_policy = policy; + + j_dbs_info->prev_cpu_idle = get_cpu_idle_time(j, + &j_dbs_info->prev_cpu_wall); + if (dbs_tuners_ins.ignore_nice) + j_dbs_info->prev_cpu_nice = + kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + } + this_dbs_info->cpu = cpu; + this_dbs_info->rate_mult = 1; + ondemandX_powersave_bias_init_cpu(cpu); + /* + * Start the timerschedule work, when this governor + * is used for first time + */ + if (dbs_enable == 1) { + unsigned int latency; + + rc = sysfs_create_group(cpufreq_global_kobject, + &dbs_attr_group); + if (rc) { + mutex_unlock(&dbs_mutex); + return rc; + } + + /* policy latency is in nS. Convert it to uS first */ + latency = policy->cpuinfo.transition_latency / 1000; + if (latency == 0) + latency = 1; + /* Bring kernel and HW constraints together */ + min_sampling_rate = max(min_sampling_rate, + MIN_LATENCY_MULTIPLIER * latency); + dbs_tuners_ins.sampling_rate = + max(min_sampling_rate, + latency * LATENCY_MULTIPLIER); + dbs_tuners_ins.io_is_busy = should_io_be_busy(); + } + mutex_unlock(&dbs_mutex); + + mutex_init(&this_dbs_info->timer_mutex); + dbs_timer_init(this_dbs_info); + register_early_suspend(&ondemandX_power_suspend); + pr_info("[imoseyon] ondemandX active\n"); + break; + + case CPUFREQ_GOV_STOP: + dbs_timer_exit(this_dbs_info); + + mutex_lock(&dbs_mutex); + mutex_destroy(&this_dbs_info->timer_mutex); + dbs_enable--; + mutex_unlock(&dbs_mutex); + if (!dbs_enable) + sysfs_remove_group(cpufreq_global_kobject, + &dbs_attr_group); + unregister_early_suspend(&ondemandX_power_suspend); + pr_info("[imoseyon] ondemandX inactive\n"); + break; + + case CPUFREQ_GOV_LIMITS: + mutex_lock(&this_dbs_info->timer_mutex); + if (policy->max < this_dbs_info->cur_policy->cur) + __cpufreq_driver_target(this_dbs_info->cur_policy, + policy->max, CPUFREQ_RELATION_H); + else if (policy->min > this_dbs_info->cur_policy->cur) + __cpufreq_driver_target(this_dbs_info->cur_policy, + policy->min, CPUFREQ_RELATION_L); + mutex_unlock(&this_dbs_info->timer_mutex); + break; + } + return 0; +} + +static int __init cpufreq_gov_dbs_init(void) +{ + cputime64_t wall; + u64 idle_time; + int cpu = get_cpu(); + + idle_time = get_cpu_idle_time_us(cpu, &wall); + put_cpu(); + if (idle_time != -1ULL) { + /* Idle micro accounting is supported. Use finer thresholds */ + dbs_tuners_ins.up_threshold = MICRO_FREQUENCY_UP_THRESHOLD; + dbs_tuners_ins.down_differential = + MICRO_FREQUENCY_DOWN_DIFFERENTIAL; + /* + * In no_hz/micro accounting case we set the minimum frequency + * not depending on HZ, but fixed (very low). The deferred + * timer might skip some samples if idle/sleeping as needed. + */ + min_sampling_rate = MICRO_FREQUENCY_MIN_SAMPLE_RATE; + } else { + /* For correct statistics, we need 10 ticks for each measure */ + min_sampling_rate = + MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10); + } + + pr_info("[imoseyon] ondemandX enter\n"); + return cpufreq_register_governor(&cpufreq_gov_ondemandX); +} + +static void __exit cpufreq_gov_dbs_exit(void) +{ + pr_info("[imoseyon] ondemandX exit\n"); + cpufreq_unregister_governor(&cpufreq_gov_ondemandX); +} + + +MODULE_AUTHOR("Venkatesh Pallipadi "); +MODULE_AUTHOR("Alexey Starikovskiy "); +MODULE_DESCRIPTION("'cpufreq_ondemandX' - A dynamic cpufreq governor for " + "Low Latency Frequency Transition capable processors"); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMANDX +fs_initcall(cpufreq_gov_dbs_init); +#else +module_init(cpufreq_gov_dbs_init); +#endif +module_exit(cpufreq_gov_dbs_exit); + diff --git a/drivers/cpufreq/cpufreq_smartass2.c b/drivers/cpufreq/cpufreq_smartass2.c new file mode 100644 index 00000000..065bc124 --- /dev/null +++ b/drivers/cpufreq/cpufreq_smartass2.c @@ -0,0 +1,868 @@ +/* + * drivers/cpufreq/cpufreq_smartass2.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: Erasmux + * + * Based on the interactive governor By Mike Chan (mike@android.com) + * which was adaptated to 2.6.29 kernel by Nadlabak (pavel@doshaska.net) + * + * SMP support based on mod by faux123 + * + * For a general overview of smartassV2 see the relavent part in + * Documentation/cpu-freq/governors.txt + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define cputime64_sub(__a, __b) ((__a) - (__b)) + +/******************** Tunable parameters: ********************/ + +/* + * The "ideal" frequency to use when awake. The governor will ramp up faster + * towards the ideal frequency and slower after it has passed it. Similarly, + * lowering the frequency towards the ideal frequency is faster than below it. + */ +#define DEFAULT_AWAKE_IDEAL_FREQ 700000 +static unsigned int awake_ideal_freq; + +/* + * The "ideal" frequency to use when suspended. + * When set to 0, the governor will not track the suspended state (meaning + * that practically when sleep_ideal_freq==0 the awake_ideal_freq is used + * also when suspended). + */ +#define DEFAULT_SLEEP_IDEAL_FREQ 320000 +static unsigned int sleep_ideal_freq; + +/* + * Freqeuncy delta when ramping up above the ideal freqeuncy. + * Zero disables and causes to always jump straight to max frequency. + * When below the ideal freqeuncy we always ramp up to the ideal freq. + */ +#define DEFAULT_RAMP_UP_STEP 224000 +static unsigned int ramp_up_step; + +/* + * Freqeuncy delta when ramping down below the ideal freqeuncy. + * Zero disables and will calculate ramp down according to load heuristic. + * When above the ideal freqeuncy we always ramp down to the ideal freq. + */ +#define DEFAULT_RAMP_DOWN_STEP 192000 +static unsigned int ramp_down_step; + +/* + * CPU freq will be increased if measured load > max_cpu_load; + */ +#define DEFAULT_MAX_CPU_LOAD 60 +static unsigned long max_cpu_load; + +/* + * CPU freq will be decreased if measured load < min_cpu_load; + */ +#define DEFAULT_MIN_CPU_LOAD 30 +static unsigned long min_cpu_load; + +/* + * The minimum amount of time to spend at a frequency before we can ramp up. + * Notice we ignore this when we are below the ideal frequency. + */ +#define DEFAULT_UP_RATE_US 35000; +static unsigned long up_rate_us; + +/* + * The minimum amount of time to spend at a frequency before we can ramp down. + * Notice we ignore this when we are above the ideal frequency. + */ +#define DEFAULT_DOWN_RATE_US 75000; +static unsigned long down_rate_us; + +/* + * The frequency to set when waking up from sleep. + * When sleep_ideal_freq=0 this will have no effect. + */ +#define DEFAULT_SLEEP_WAKEUP_FREQ 99999999 +static unsigned int sleep_wakeup_freq; + +/* + * Sampling rate, I highly recommend to leave it at 2. + */ +#define DEFAULT_SAMPLE_RATE_JIFFIES 2 +static unsigned int sample_rate_jiffies; + + +/*************** End of tunables ***************/ + + +static void (*pm_idle_old)(void); +static atomic_t active_count = ATOMIC_INIT(0); + +struct smartass_info_s { + struct cpufreq_policy *cur_policy; + struct cpufreq_frequency_table *freq_table; + struct timer_list timer; + u64 time_in_idle; + u64 idle_exit_time; + u64 freq_change_time; + u64 freq_change_time_in_idle; + int cur_cpu_load; + int old_freq; + int ramp_dir; + unsigned int enable; + int ideal_speed; +}; +static DEFINE_PER_CPU(struct smartass_info_s, smartass_info); + +/* Workqueues handle frequency scaling */ +static struct workqueue_struct *up_wq; +static struct workqueue_struct *down_wq; +static struct work_struct freq_scale_work; + +static cpumask_t work_cpumask; +static spinlock_t cpumask_lock; + +static unsigned int suspended; + +#define dprintk(flag,msg...) do { \ + if (debug_mask & flag) printk(KERN_DEBUG msg); \ + } while (0) + +enum { + SMARTASS_DEBUG_JUMPS=1, + SMARTASS_DEBUG_LOAD=2, + SMARTASS_DEBUG_ALG=4 +}; + +/* + * Combination of the above debug flags. + */ +static unsigned long debug_mask; + +static int cpufreq_governor_smartass(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 +static +#endif +struct cpufreq_governor cpufreq_gov_smartass2 = { + .name = "smartassV2", + .governor = cpufreq_governor_smartass, + .max_transition_latency = 9000000, + .owner = THIS_MODULE, +}; + +inline static void smartass_update_min_max(struct smartass_info_s *this_smartass, struct cpufreq_policy *policy, int suspend) { + if (suspend) { + this_smartass->ideal_speed = // sleep_ideal_freq; but make sure it obeys the policy min/max + policy->max > sleep_ideal_freq ? + (sleep_ideal_freq > policy->min ? sleep_ideal_freq : policy->min) : policy->max; + } else { + this_smartass->ideal_speed = // awake_ideal_freq; but make sure it obeys the policy min/max + policy->min < awake_ideal_freq ? + (awake_ideal_freq < policy->max ? awake_ideal_freq : policy->max) : policy->min; + } +} + +inline static void smartass_update_min_max_allcpus(void) { + unsigned int i; + for_each_online_cpu(i) { + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, i); + if (this_smartass->enable) + smartass_update_min_max(this_smartass,this_smartass->cur_policy,suspended); + } +} + +inline static unsigned int validate_freq(struct cpufreq_policy *policy, int freq) { + if (freq > (int)policy->max) + return policy->max; + if (freq < (int)policy->min) + return policy->min; + return freq; +} + +inline static void reset_timer(unsigned long cpu, struct smartass_info_s *this_smartass) { + this_smartass->time_in_idle = get_cpu_idle_time_us(cpu, &this_smartass->idle_exit_time); + mod_timer(&this_smartass->timer, jiffies + sample_rate_jiffies); +} + +inline static void work_cpumask_set(unsigned long cpu) { + unsigned long flags; + spin_lock_irqsave(&cpumask_lock, flags); + cpumask_set_cpu(cpu, &work_cpumask); + spin_unlock_irqrestore(&cpumask_lock, flags); +} + +inline static int work_cpumask_test_and_clear(unsigned long cpu) { + unsigned long flags; + int res = 0; + spin_lock_irqsave(&cpumask_lock, flags); + res = cpumask_test_and_clear_cpu(cpu, &work_cpumask); + spin_unlock_irqrestore(&cpumask_lock, flags); + return res; +} + +inline static int target_freq(struct cpufreq_policy *policy, struct smartass_info_s *this_smartass, + int new_freq, int old_freq, int prefered_relation) { + int index, target; + struct cpufreq_frequency_table *table = this_smartass->freq_table; + + if (new_freq == old_freq) + return 0; + new_freq = validate_freq(policy,new_freq); + if (new_freq == old_freq) + return 0; + + if (table && + !cpufreq_frequency_table_target(policy,table,new_freq,prefered_relation,&index)) + { + target = table[index].frequency; + if (target == old_freq) { + // if for example we are ramping up to *at most* current + ramp_up_step + // but there is no such frequency higher than the current, try also + // to ramp up to *at least* current + ramp_up_step. + if (new_freq > old_freq && prefered_relation==CPUFREQ_RELATION_H + && !cpufreq_frequency_table_target(policy,table,new_freq, CPUFREQ_RELATION_L,&index)) + target = table[index].frequency; + // simlarly for ramping down: + else if (new_freq < old_freq && prefered_relation==CPUFREQ_RELATION_L + && !cpufreq_frequency_table_target(policy,table,new_freq, CPUFREQ_RELATION_H,&index)) + target = table[index].frequency; + } + + if (target == old_freq) { + // We should not get here: + // If we got here we tried to change to a validated new_freq which is different + // from old_freq, so there is no reason for us to remain at same frequency. + printk(KERN_WARNING "Smartass: frequency change failed: %d to %d => %d\n", + old_freq,new_freq,target); + return 0; + } + } + else target = new_freq; + + __cpufreq_driver_target(policy, target, prefered_relation); + + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassQ: jumping from %d to %d => %d (%d)\n", + old_freq,new_freq,target,policy->cur); + + return target; +} + +static void cpufreq_smartass_timer(unsigned long cpu) +{ + u64 delta_idle; + u64 delta_time; + int cpu_load; + int old_freq; + u64 update_time; + u64 now_idle; + int queued_work = 0; + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, cpu); + struct cpufreq_policy *policy = this_smartass->cur_policy; + + now_idle = get_cpu_idle_time_us(cpu, &update_time); + old_freq = policy->cur; + + if (this_smartass->idle_exit_time == 0 || update_time == this_smartass->idle_exit_time) + return; + + delta_idle = now_idle - this_smartass->time_in_idle; + delta_time = update_time - this_smartass->idle_exit_time; + + // If timer ran less than 1ms after short-term sample started, retry. + if (delta_time < 1000) { + if (!timer_pending(&this_smartass->timer)) + reset_timer(cpu,this_smartass); + return; + } + + if (delta_idle > delta_time) + cpu_load = 0; + else + cpu_load = 100 * (unsigned int)(delta_time - delta_idle) / (unsigned int)delta_time; + + dprintk(SMARTASS_DEBUG_LOAD,"smartassT @ %d: load %d (delta_time %llu)\n", + old_freq,cpu_load,delta_time); + + this_smartass->cur_cpu_load = cpu_load; + this_smartass->old_freq = old_freq; + + // Scale up if load is above max or if there where no idle cycles since coming out of idle, + // additionally, if we are at or above the ideal_speed, verify we have been at this frequency + // for at least up_rate_us: + if (cpu_load > max_cpu_load || delta_idle == 0) + { + if (old_freq < policy->max && + (old_freq < this_smartass->ideal_speed || delta_idle == 0 || + (update_time - this_smartass->freq_change_time) >= up_rate_us)) + { + dprintk(SMARTASS_DEBUG_ALG,"smartassT @ %d ramp up: load %d (delta_idle %llu)\n", + old_freq,cpu_load,delta_idle); + this_smartass->ramp_dir = 1; + work_cpumask_set(cpu); + queue_work(up_wq, &freq_scale_work); + queued_work = 1; + } + else this_smartass->ramp_dir = 0; + } + // Similarly for scale down: load should be below min and if we are at or below ideal + // frequency we require that we have been at this frequency for at least down_rate_us: + else if (cpu_load < min_cpu_load && old_freq > policy->min && + (old_freq > this_smartass->ideal_speed || + (update_time - this_smartass->freq_change_time) >= down_rate_us)) + { + dprintk(SMARTASS_DEBUG_ALG,"smartassT @ %d ramp down: load %d (delta_idle %llu)\n", + old_freq,cpu_load,delta_idle); + this_smartass->ramp_dir = -1; + work_cpumask_set(cpu); + queue_work(down_wq, &freq_scale_work); + queued_work = 1; + } + else this_smartass->ramp_dir = 0; + + // To avoid unnecessary load when the CPU is already at high load, we don't + // reset ourselves if we are at max speed. If and when there are idle cycles, + // the idle loop will activate the timer. + // Additionally, if we queued some work, the work task will reset the timer + // after it has done its adjustments. + if (!queued_work && old_freq < policy->max) + reset_timer(cpu,this_smartass); +} + +static void cpufreq_idle(void) +{ + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, smp_processor_id()); + struct cpufreq_policy *policy = this_smartass->cur_policy; + + if (!this_smartass->enable) { + pm_idle_old(); + return; + } + + if (policy->cur == policy->min && timer_pending(&this_smartass->timer)) + del_timer(&this_smartass->timer); + + pm_idle_old(); + + if (!timer_pending(&this_smartass->timer)) + reset_timer(smp_processor_id(), this_smartass); +} + +/* We use the same work function to sale up and down */ +static void cpufreq_smartass_freq_change_time_work(struct work_struct *work) +{ + unsigned int cpu; + int new_freq; + int old_freq; + int ramp_dir; + struct smartass_info_s *this_smartass; + struct cpufreq_policy *policy; + unsigned int relation = CPUFREQ_RELATION_L; + for_each_possible_cpu(cpu) { + this_smartass = &per_cpu(smartass_info, cpu); + if (!work_cpumask_test_and_clear(cpu)) + continue; + + ramp_dir = this_smartass->ramp_dir; + this_smartass->ramp_dir = 0; + + old_freq = this_smartass->old_freq; + policy = this_smartass->cur_policy; + + if (old_freq != policy->cur) { + // frequency was changed by someone else? + printk(KERN_WARNING "Smartass: frequency changed by 3rd party: %d to %d\n", + old_freq,policy->cur); + new_freq = old_freq; + } + else if (ramp_dir > 0 && nr_running() > 1) { + // ramp up logic: + if (old_freq < this_smartass->ideal_speed) + new_freq = this_smartass->ideal_speed; + else if (ramp_up_step) { + new_freq = old_freq + ramp_up_step; + relation = CPUFREQ_RELATION_H; + } + else { + new_freq = policy->max; + relation = CPUFREQ_RELATION_H; + } + dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d ramp up: ramp_dir=%d ideal=%d\n", + old_freq,ramp_dir,this_smartass->ideal_speed); + } + else if (ramp_dir < 0) { + // ramp down logic: + if (old_freq > this_smartass->ideal_speed) { + new_freq = this_smartass->ideal_speed; + relation = CPUFREQ_RELATION_H; + } + else if (ramp_down_step) + new_freq = old_freq - ramp_down_step; + else { + // Load heuristics: Adjust new_freq such that, assuming a linear + // scaling of load vs. frequency, the load in the new frequency + // will be max_cpu_load: + new_freq = old_freq * this_smartass->cur_cpu_load / max_cpu_load; + if (new_freq > old_freq) // min_cpu_load > max_cpu_load ?! + new_freq = old_freq -1; + } + dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d ramp down: ramp_dir=%d ideal=%d\n", + old_freq,ramp_dir,this_smartass->ideal_speed); + } + else { // ramp_dir==0 ?! Could the timer change its mind about a queued ramp up/down + // before the work task gets to run? + // This may also happen if we refused to ramp up because the nr_running()==1 + new_freq = old_freq; + dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d nothing: ramp_dir=%d nr_running=%lu\n", + old_freq,ramp_dir,nr_running()); + } + + // do actual ramp up (returns 0, if frequency change failed): + new_freq = target_freq(policy,this_smartass,new_freq,old_freq,relation); + if (new_freq) + this_smartass->freq_change_time_in_idle = + get_cpu_idle_time_us(cpu,&this_smartass->freq_change_time); + + // reset timer: + if (new_freq < policy->max) + reset_timer(cpu,this_smartass); + // if we are maxed out, it is pointless to use the timer + // (idle cycles wake up the timer when the timer comes) + else if (timer_pending(&this_smartass->timer)) + del_timer(&this_smartass->timer); + } +} + +static ssize_t show_debug_mask(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", debug_mask); +} + +static ssize_t store_debug_mask(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0) + debug_mask = input; + return res; +} + +static ssize_t show_up_rate_us(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", up_rate_us); +} + +static ssize_t store_up_rate_us(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0 && input <= 100000000) + up_rate_us = input; + return res; +} + +static ssize_t show_down_rate_us(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", down_rate_us); +} + +static ssize_t store_down_rate_us(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0 && input <= 100000000) + down_rate_us = input; + return res; +} + +static ssize_t show_sleep_ideal_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", sleep_ideal_freq); +} + +static ssize_t store_sleep_ideal_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) { + sleep_ideal_freq = input; + if (suspended) + smartass_update_min_max_allcpus(); + } + return res; +} + +static ssize_t show_sleep_wakeup_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", sleep_wakeup_freq); +} + +static ssize_t store_sleep_wakeup_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) + sleep_wakeup_freq = input; + return res; +} + +static ssize_t show_awake_ideal_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", awake_ideal_freq); +} + +static ssize_t store_awake_ideal_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) { + awake_ideal_freq = input; + if (!suspended) + smartass_update_min_max_allcpus(); + } + return res; +} + +static ssize_t show_sample_rate_jiffies(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", sample_rate_jiffies); +} + +static ssize_t store_sample_rate_jiffies(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input > 0 && input <= 1000) + sample_rate_jiffies = input; + return res; +} + +static ssize_t show_ramp_up_step(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", ramp_up_step); +} + +static ssize_t store_ramp_up_step(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) + ramp_up_step = input; + return res; +} + +static ssize_t show_ramp_down_step(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", ramp_down_step); +} + +static ssize_t store_ramp_down_step(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) + ramp_down_step = input; + return res; +} + +static ssize_t show_max_cpu_load(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", max_cpu_load); +} + +static ssize_t store_max_cpu_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input > 0 && input <= 100) + max_cpu_load = input; + return res; +} + +static ssize_t show_min_cpu_load(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", min_cpu_load); +} + +static ssize_t store_min_cpu_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input > 0 && input < 100) + min_cpu_load = input; + return res; +} + +#define define_global_rw_attr(_name) \ +static struct global_attr _name##_attr = \ + __ATTR(_name, 0644, show_##_name, store_##_name) + +define_global_rw_attr(debug_mask); +define_global_rw_attr(up_rate_us); +define_global_rw_attr(down_rate_us); +define_global_rw_attr(sleep_ideal_freq); +define_global_rw_attr(sleep_wakeup_freq); +define_global_rw_attr(awake_ideal_freq); +define_global_rw_attr(sample_rate_jiffies); +define_global_rw_attr(ramp_up_step); +define_global_rw_attr(ramp_down_step); +define_global_rw_attr(max_cpu_load); +define_global_rw_attr(min_cpu_load); + +static struct attribute * smartass_attributes[] = { + &debug_mask_attr.attr, + &up_rate_us_attr.attr, + &down_rate_us_attr.attr, + &sleep_ideal_freq_attr.attr, + &sleep_wakeup_freq_attr.attr, + &awake_ideal_freq_attr.attr, + &sample_rate_jiffies_attr.attr, + &ramp_up_step_attr.attr, + &ramp_down_step_attr.attr, + &max_cpu_load_attr.attr, + &min_cpu_load_attr.attr, + NULL, +}; + +static struct attribute_group smartass_attr_group = { + .attrs = smartass_attributes, + .name = "smartassV2", +}; + +static int cpufreq_governor_smartass(struct cpufreq_policy *new_policy, + unsigned int event) +{ + unsigned int cpu = new_policy->cpu; + int rc; + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, cpu); + + switch (event) { + case CPUFREQ_GOV_START: + if ((!cpu_online(cpu)) || (!new_policy->cur)) + return -EINVAL; + + this_smartass->cur_policy = new_policy; + + this_smartass->enable = 1; + + smartass_update_min_max(this_smartass,new_policy,suspended); + + this_smartass->freq_table = cpufreq_frequency_get_table(cpu); + if (!this_smartass->freq_table) + printk(KERN_WARNING "Smartass: no frequency table for cpu %d?!\n",cpu); + + smp_wmb(); + + // Do not register the idle hook and create sysfs + // entries if we have already done so. + if (atomic_inc_return(&active_count) <= 1) { + rc = sysfs_create_group(cpufreq_global_kobject, + &smartass_attr_group); + if (rc) + return rc; + + pm_idle_old = pm_idle; + pm_idle = cpufreq_idle; + } + + if (this_smartass->cur_policy->cur < new_policy->max && !timer_pending(&this_smartass->timer)) + reset_timer(cpu,this_smartass); + + break; + + case CPUFREQ_GOV_LIMITS: + smartass_update_min_max(this_smartass,new_policy,suspended); + + if (this_smartass->cur_policy->cur > new_policy->max) { + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassI: jumping to new max freq: %d\n",new_policy->max); + __cpufreq_driver_target(this_smartass->cur_policy, + new_policy->max, CPUFREQ_RELATION_H); + } + else if (this_smartass->cur_policy->cur < new_policy->min) { + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassI: jumping to new min freq: %d\n",new_policy->min); + __cpufreq_driver_target(this_smartass->cur_policy, + new_policy->min, CPUFREQ_RELATION_L); + } + + if (this_smartass->cur_policy->cur < new_policy->max && !timer_pending(&this_smartass->timer)) + reset_timer(cpu,this_smartass); + + break; + + case CPUFREQ_GOV_STOP: + this_smartass->enable = 0; + smp_wmb(); + del_timer(&this_smartass->timer); + flush_work(&freq_scale_work); + this_smartass->idle_exit_time = 0; + + if (atomic_dec_return(&active_count) <= 1) { + sysfs_remove_group(cpufreq_global_kobject, + &smartass_attr_group); + pm_idle = pm_idle_old; + } + break; + } + + return 0; +} + +static void smartass_suspend(int cpu, int suspend) +{ + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, smp_processor_id()); + struct cpufreq_policy *policy = this_smartass->cur_policy; + unsigned int new_freq; + + if (!this_smartass->enable) + return; + + smartass_update_min_max(this_smartass,policy,suspend); + if (!suspend) { // resume at max speed: + new_freq = validate_freq(policy,sleep_wakeup_freq); + + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassS: awaking at %d\n",new_freq); + + __cpufreq_driver_target(policy, new_freq, + CPUFREQ_RELATION_L); + } else { + // to avoid wakeup issues with quick sleep/wakeup don't change actual frequency when entering sleep + // to allow some time to settle down. Instead we just reset our statistics (and reset the timer). + // Eventually, the timer will adjust the frequency if necessary. + + this_smartass->freq_change_time_in_idle = + get_cpu_idle_time_us(cpu,&this_smartass->freq_change_time); + + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassS: suspending at %d\n",policy->cur); + } + + reset_timer(smp_processor_id(),this_smartass); +} + +static void smartass_early_suspend(struct early_suspend *handler) { + int i; + if (suspended || sleep_ideal_freq==0) // disable behavior for sleep_ideal_freq==0 + return; + suspended = 1; + for_each_online_cpu(i) + smartass_suspend(i,1); +} + +static void smartass_late_resume(struct early_suspend *handler) { + int i; + if (!suspended) // already not suspended so nothing to do + return; + suspended = 0; + for_each_online_cpu(i) + smartass_suspend(i,0); +} + +static struct early_suspend smartass_power_suspend = { + .suspend = smartass_early_suspend, + .resume = smartass_late_resume, +#ifdef CONFIG_MACH_HERO + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, +#endif +}; + +static int __init cpufreq_smartass_init(void) +{ + unsigned int i; + struct smartass_info_s *this_smartass; + debug_mask = 0; + up_rate_us = DEFAULT_UP_RATE_US; + down_rate_us = DEFAULT_DOWN_RATE_US; + sleep_ideal_freq = DEFAULT_SLEEP_IDEAL_FREQ; + sleep_wakeup_freq = DEFAULT_SLEEP_WAKEUP_FREQ; + awake_ideal_freq = DEFAULT_AWAKE_IDEAL_FREQ; + sample_rate_jiffies = DEFAULT_SAMPLE_RATE_JIFFIES; + ramp_up_step = DEFAULT_RAMP_UP_STEP; + ramp_down_step = DEFAULT_RAMP_DOWN_STEP; + max_cpu_load = DEFAULT_MAX_CPU_LOAD; + min_cpu_load = DEFAULT_MIN_CPU_LOAD; + + spin_lock_init(&cpumask_lock); + + suspended = 0; + + /* Initalize per-cpu data: */ + for_each_possible_cpu(i) { + this_smartass = &per_cpu(smartass_info, i); + this_smartass->enable = 0; + this_smartass->cur_policy = 0; + this_smartass->ramp_dir = 0; + this_smartass->time_in_idle = 0; + this_smartass->idle_exit_time = 0; + this_smartass->freq_change_time = 0; + this_smartass->freq_change_time_in_idle = 0; + this_smartass->cur_cpu_load = 0; + // intialize timer: + init_timer_deferrable(&this_smartass->timer); + this_smartass->timer.function = cpufreq_smartass_timer; + this_smartass->timer.data = i; + work_cpumask_test_and_clear(i); + } + + // Scale up is high priority + up_wq = alloc_workqueue("ksmartass_up", WQ_HIGHPRI, 1); + down_wq = alloc_workqueue("ksmartass_down", 0, 1); + if (!up_wq || !down_wq) + return -ENOMEM; + + INIT_WORK(&freq_scale_work, cpufreq_smartass_freq_change_time_work); + + register_early_suspend(&smartass_power_suspend); + + return cpufreq_register_governor(&cpufreq_gov_smartass2); +} + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 +fs_initcall(cpufreq_smartass_init); +#else +module_init(cpufreq_smartass_init); +#endif + +static void __exit cpufreq_smartass_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_smartass2); + destroy_workqueue(up_wq); + destroy_workqueue(down_wq); +} + +module_exit(cpufreq_smartass_exit); + +MODULE_AUTHOR ("Erasmux"); +MODULE_DESCRIPTION ("'cpufreq_smartass2' - A smart cpufreq governor"); +MODULE_LICENSE ("GPL"); diff --git a/drivers/cpufreq/cpufreq_smartassH3.c b/drivers/cpufreq/cpufreq_smartassH3.c new file mode 100644 index 00000000..3f02d597 --- /dev/null +++ b/drivers/cpufreq/cpufreq_smartassH3.c @@ -0,0 +1,981 @@ +/* +* drivers/cpufreq/cpufreq_smartassH3.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: Erasmux +* +* Based on the interactive governor By Mike Chan (mike@android.com) +* which was adaptated to 2.6.29 kernel by Nadlabak (pavel@doshaska.net) +* +* SMP support based on mod by faux123 +* +* ZTE Skate specific tweaks by H3ROS @ MoDaCo, integrated by C3C0 @ MoDaCo +* +* For a general overview of smartassV2 see the relavent part in +* Documentation/cpu-freq/governors.txt +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define cputime64_sub(__a, __b) ((__a) - (__b)) + +/******************** Tunable parameters: ********************/ + +/* +* The "ideal" frequency to use when awake. The governor will ramp up faster +* towards the ideal frequency and slower after it has passed it. Similarly, +* lowering the frequency towards the ideal frequency is faster than below it. +*/ +#define DEFAULT_AWAKE_IDEAL_FREQ 700000 +static unsigned int awake_ideal_freq; + +/* +* The "ideal" frequency to use when suspended. +* When set to 0, the governor will not track the suspended state (meaning +* that practically when sleep_ideal_freq==0 the awake_ideal_freq is used +* also when suspended). +*/ +#define DEFAULT_SLEEP_IDEAL_FREQ 122880 +static unsigned int sleep_ideal_freq; + +/* +* Freqeuncy delta when ramping up above the ideal freqeuncy. +* Zero disables and causes to always jump straight to max frequency. +* When below the ideal freqeuncy we always ramp up to the ideal freq. +*/ +#define DEFAULT_RAMP_UP_STEP 128000 +static unsigned int ramp_up_step; + +/* +* Freqeuncy delta when ramping down below the ideal freqeuncy. +* Zero disables and will calculate ramp down according to load heuristic. +* When above the ideal freqeuncy we always ramp down to the ideal freq. +*/ +#define DEFAULT_RAMP_DOWN_STEP 128000 +static unsigned int ramp_down_step; + +/* +* CPU freq will be increased if measured load > max_cpu_load; +*/ +#define DEFAULT_MAX_CPU_LOAD 50 +static unsigned long max_cpu_load; + +/* +* CPU freq will be decreased if measured load < min_cpu_load; +*/ +#define DEFAULT_MIN_CPU_LOAD 25 +static unsigned long min_cpu_load; + +/* +* The minimum amount of time to spend at a frequency before we can ramp up. +* Notice we ignore this when we are below the ideal frequency. +*/ +#define DEFAULT_UP_RATE_US 48000; +static unsigned long up_rate_us; + +/* +* The minimum amount of time to spend at a frequency before we can ramp down. +* Notice we ignore this when we are above the ideal frequency. +*/ +#define DEFAULT_DOWN_RATE_US 48000; +static unsigned long down_rate_us; + +/* +* The frequency to set when waking up from sleep. +* When sleep_ideal_freq=0 this will have no effect. +*/ +#define DEFAULT_SLEEP_WAKEUP_FREQ 700000 +static unsigned int sleep_wakeup_freq; + +/* +* Sampling rate, I highly recommend to leave it at 2. +*/ +#define DEFAULT_SAMPLE_RATE_JIFFIES 2 +static unsigned int sample_rate_jiffies; + +/* +* Boost enabled +*/ +#define DEFAULT_BOOST_ENABLED 0 +static unsigned int boost_enabled; + +/* +* Boost pulse +*/ +#define DEFAULT_BOOST_PULSE 500000 +#define MAX_BOOST_PULSE 5000000 +static unsigned long boost_pulse; +static u64 boost_pulse_time; + + +/*************** End of tunables ***************/ + + +static void (*pm_idle_old)(void); +static atomic_t active_count = ATOMIC_INIT(0); + +struct smartass_info_s { +struct cpufreq_policy *cur_policy; +struct cpufreq_frequency_table *freq_table; +struct timer_list timer; +u64 time_in_idle; +u64 idle_exit_time; +u64 freq_change_time; +u64 freq_change_time_in_idle; +int cur_cpu_load; +int old_freq; +int ramp_dir; +unsigned int enable; +int ideal_speed; +}; +static DEFINE_PER_CPU(struct smartass_info_s, smartass_info); + +/* Workqueues handle frequency scaling */ +static struct workqueue_struct *up_wq; +static struct workqueue_struct *down_wq; +static struct work_struct freq_scale_work; + +static cpumask_t work_cpumask; +static spinlock_t cpumask_lock; + +static unsigned int suspended; + +#define dprintk(flag,msg...) do { \ +if (debug_mask & flag) printk(KERN_DEBUG msg); \ +} while (0) + +enum { +SMARTASS_DEBUG_JUMPS=1, +SMARTASS_DEBUG_LOAD=2, +SMARTASS_DEBUG_ALG=4 +}; + +/* +* Combination of the above debug flags. +*/ +static unsigned long debug_mask; + +static int cpufreq_governor_smartass_h3(struct cpufreq_policy *policy, +unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASSH3 +static +#endif +struct cpufreq_governor cpufreq_gov_smartass_h3 = { +.name = "SmartassH3", +.governor = cpufreq_governor_smartass_h3, +.max_transition_latency = 9000000, +.owner = THIS_MODULE, +}; + +inline static void smartass_update_min_max(struct smartass_info_s *this_smartass, struct cpufreq_policy *policy, int suspend) { +if (suspend) { +this_smartass->ideal_speed = // sleep_ideal_freq; but make sure it obeys the policy min/max +policy->max > sleep_ideal_freq ? +(sleep_ideal_freq > policy->min ? sleep_ideal_freq : policy->min) : policy->max; +} else { +this_smartass->ideal_speed = // awake_ideal_freq; but make sure it obeys the policy min/max +policy->min < awake_ideal_freq ? +(awake_ideal_freq < policy->max ? awake_ideal_freq : policy->max) : policy->min; +} +} + +inline static void smartass_update_min_max_allcpus(void) { +unsigned int i; +for_each_online_cpu(i) { +struct smartass_info_s *this_smartass = &per_cpu(smartass_info, i); +if (this_smartass->enable) +smartass_update_min_max(this_smartass,this_smartass->cur_policy,suspended); +} +} + +inline static unsigned int validate_freq(struct cpufreq_policy *policy, int freq) { +if (freq > (int)policy->max) +return policy->max; +if (freq < (int)policy->min) +return policy->min; +return freq; +} + +inline static void reset_timer(unsigned long cpu, struct smartass_info_s *this_smartass) { +this_smartass->time_in_idle = get_cpu_idle_time_us(cpu, &this_smartass->idle_exit_time); +mod_timer(&this_smartass->timer, jiffies + sample_rate_jiffies); +} + +inline static void work_cpumask_set(unsigned long cpu) { +unsigned long flags; +spin_lock_irqsave(&cpumask_lock, flags); +cpumask_set_cpu(cpu, &work_cpumask); +spin_unlock_irqrestore(&cpumask_lock, flags); +} + +inline static int work_cpumask_test_and_clear(unsigned long cpu) { +unsigned long flags; +int res = 0; +spin_lock_irqsave(&cpumask_lock, flags); +res = cpumask_test_and_clear_cpu(cpu, &work_cpumask); +spin_unlock_irqrestore(&cpumask_lock, flags); +return res; +} + +inline static int target_freq(struct cpufreq_policy *policy, struct smartass_info_s *this_smartass, +int new_freq, int old_freq, int prefered_relation) { +int index, target; +struct cpufreq_frequency_table *table = this_smartass->freq_table; + +if (new_freq == old_freq) +return 0; +new_freq = validate_freq(policy,new_freq); +if (new_freq == old_freq) +return 0; + +if (table && +!cpufreq_frequency_table_target(policy,table,new_freq,prefered_relation,&index)) +{ +target = table[index].frequency; +if (target == old_freq) { +// if for example we are ramping up to *at most* current + ramp_up_step +// but there is no such frequency higher than the current, try also +// to ramp up to *at least* current + ramp_up_step. +if (new_freq > old_freq && prefered_relation==CPUFREQ_RELATION_H +&& !cpufreq_frequency_table_target(policy,table,new_freq, +CPUFREQ_RELATION_L,&index)) +target = table[index].frequency; +// simlarly for ramping down: +else if (new_freq < old_freq && prefered_relation==CPUFREQ_RELATION_L +&& !cpufreq_frequency_table_target(policy,table,new_freq, +CPUFREQ_RELATION_H,&index)) +target = table[index].frequency; +} + +if (target == old_freq) { +// We should not get here: +// If we got here we tried to change to a validated new_freq which is different +// from old_freq, so there is no reason for us to remain at same frequency. +printk(KERN_WARNING "Smartass: frequency change failed: %d to %d => %d\n", +old_freq,new_freq,target); +return 0; +} +} +else target = new_freq; + +__cpufreq_driver_target(policy, target, prefered_relation); + +dprintk(SMARTASS_DEBUG_JUMPS,"SmartassQ: jumping from %d to %d => %d (%d)\n", +old_freq,new_freq,target,policy->cur); + +return target; +} + +static void cpufreq_smartass_timer(unsigned long cpu) +{ +u64 delta_idle; +u64 delta_time; +int cpu_load; +int old_freq; +u64 update_time; +u64 now_idle; +int queued_work = 0; +struct smartass_info_s *this_smartass = &per_cpu(smartass_info, cpu); +struct cpufreq_policy *policy = this_smartass->cur_policy; + +now_idle = get_cpu_idle_time_us(cpu, &update_time); +old_freq = policy->cur; + +if (this_smartass->idle_exit_time == 0 || update_time == this_smartass->idle_exit_time) +return; + +delta_idle = cputime64_sub(now_idle, this_smartass->time_in_idle); +delta_time = cputime64_sub(update_time, this_smartass->idle_exit_time); + +// If timer ran less than 1ms after short-term sample started, retry. +if (delta_time < 1000) { +if (!timer_pending(&this_smartass->timer)) +reset_timer(cpu,this_smartass); +return; +} + +if (delta_idle > delta_time) +cpu_load = 0; +else +cpu_load = 100 * (unsigned int)(delta_time - delta_idle) / (unsigned int)delta_time; + +dprintk(SMARTASS_DEBUG_LOAD,"smartassT @ %d: load %d (delta_time %llu)\n", +old_freq,cpu_load,delta_time); + +this_smartass->cur_cpu_load = cpu_load; +this_smartass->old_freq = old_freq; + +// Scale up if load is above max or if there where no idle cycles since coming out of idle, +// additionally, if we are at or above the ideal_speed, verify we have been at this frequency +// for at least up_rate_us: +if (cpu_load > max_cpu_load || delta_idle == 0) +{ +if (old_freq < policy->max && +(old_freq < this_smartass->ideal_speed || delta_idle == 0 || +cputime64_sub(update_time, this_smartass->freq_change_time) >= up_rate_us)) +{ +dprintk(SMARTASS_DEBUG_ALG,"smartassT @ %d ramp up: load %d (delta_idle %llu)\n", +old_freq,cpu_load,delta_idle); +this_smartass->ramp_dir = 1; +work_cpumask_set(cpu); +queue_work(up_wq, &freq_scale_work); +queued_work = 1; +} +else this_smartass->ramp_dir = 0; +} +// Similarly for scale down: load should be below min and if we are at or below ideal +// frequency we require that we have been at this frequency for at least down_rate_us: +else if (cpu_load < min_cpu_load && old_freq > policy->min && +(old_freq > this_smartass->ideal_speed || +cputime64_sub(update_time, this_smartass->freq_change_time) >= down_rate_us)) +{ +dprintk(SMARTASS_DEBUG_ALG,"smartassT @ %d ramp down: load %d (delta_idle %llu)\n", +old_freq,cpu_load,delta_idle); +this_smartass->ramp_dir = -1; +work_cpumask_set(cpu); +queue_work(down_wq, &freq_scale_work); +queued_work = 1; +} +else this_smartass->ramp_dir = 0; + +// To avoid unnecessary load when the CPU is already at high load, we don't +// reset ourselves if we are at max speed. If and when there are idle cycles, +// the idle loop will activate the timer. +// Additionally, if we queued some work, the work task will reset the timer +// after it has done its adjustments. +if (!queued_work && old_freq < policy->max) +reset_timer(cpu,this_smartass); +} + +static void cpufreq_idle(void) +{ +struct smartass_info_s *this_smartass = &per_cpu(smartass_info, smp_processor_id()); +struct cpufreq_policy *policy = this_smartass->cur_policy; + +if (!this_smartass->enable) { +pm_idle_old(); +return; +} + +if (policy->cur == policy->min && timer_pending(&this_smartass->timer)) +del_timer(&this_smartass->timer); + +pm_idle_old(); + +if (!timer_pending(&this_smartass->timer)) +reset_timer(smp_processor_id(), this_smartass); +} + +/* We use the same work function to sale up and down */ +static void cpufreq_smartass_freq_change_time_work(struct work_struct *work) +{ +unsigned int cpu; +int new_freq; +int old_freq; +int ramp_dir; +struct smartass_info_s *this_smartass; +struct cpufreq_policy *policy; +unsigned int relation = CPUFREQ_RELATION_L; +unsigned int boosted; + +for_each_possible_cpu(cpu) { +this_smartass = &per_cpu(smartass_info, cpu); +if (!work_cpumask_test_and_clear(cpu)) +continue; + +ramp_dir = this_smartass->ramp_dir; +this_smartass->ramp_dir = 0; + +old_freq = this_smartass->old_freq; +policy = this_smartass->cur_policy; + +// boost pulse +boosted = 0; +if (boost_pulse > 0) { +u64 now = ktime_to_us(ktime_get()); +if (now <= boost_pulse_time + boost_pulse) { +// boost logic: +if (boost_enabled > 0 && +old_freq < this_smartass->ideal_speed) { +// Jump to ideal frequency +new_freq = this_smartass->ideal_speed; +relation = CPUFREQ_RELATION_H; +// Skip normal logic +boosted = 1; +dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d boost %d\n", +old_freq,new_freq); +} +} +else { +// Reset boost pulse +boost_pulse = 0; +} +} + +if (boosted == 0) { +if (old_freq != policy->cur) { +// frequency was changed by someone else? +printk(KERN_WARNING "Smartass: frequency changed by 3rd party: %d to %d\n", +old_freq,policy->cur); +new_freq = old_freq; +} +else if (ramp_dir > 0 && nr_running() > 1) { +// ramp up logic: +if (old_freq < this_smartass->ideal_speed) +new_freq = this_smartass->ideal_speed; +else if (ramp_up_step) { +new_freq = old_freq + ramp_up_step; +relation = CPUFREQ_RELATION_H; +} +else { +new_freq = policy->max; +relation = CPUFREQ_RELATION_H; +} +dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d ramp up: ramp_dir=%d ideal=%d\n", +old_freq,ramp_dir,this_smartass->ideal_speed); +} +else if (ramp_dir < 0) { +// ramp down logic: +if (old_freq > this_smartass->ideal_speed) { +new_freq = this_smartass->ideal_speed; +relation = CPUFREQ_RELATION_H; +} +else if (ramp_down_step) +new_freq = old_freq - ramp_down_step; +else { +// Load heuristics: Adjust new_freq such that, assuming a linear +// scaling of load vs. frequency, the load in the new frequency +// will be max_cpu_load: +new_freq = old_freq * this_smartass->cur_cpu_load / max_cpu_load; +if (new_freq > old_freq) // min_cpu_load > max_cpu_load ?! +new_freq = old_freq -1; +} +dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d ramp down: ramp_dir=%d ideal=%d\n", +old_freq,ramp_dir,this_smartass->ideal_speed); +} +else { // ramp_dir==0 ?! Could the timer change its mind about a queued ramp up/down +// before the work task gets to run? +// This may also happen if we refused to ramp up because the nr_running()==1 +new_freq = old_freq; +dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d nothing: ramp_dir=%d nr_running=%lu\n", +old_freq,ramp_dir,nr_running()); +} +} + +// do actual ramp up (returns 0, if frequency change failed): +new_freq = target_freq(policy,this_smartass,new_freq,old_freq,relation); +if (new_freq) +this_smartass->freq_change_time_in_idle = +get_cpu_idle_time_us(cpu,&this_smartass->freq_change_time); + +// reset timer: +if (new_freq < policy->max) +reset_timer(cpu,this_smartass); +// if we are maxed out, it is pointless to use the timer +// (idle cycles wake up the timer when the timer comes) +else if (timer_pending(&this_smartass->timer)) +del_timer(&this_smartass->timer); +} +} + +static ssize_t show_debug_mask(struct kobject *kobj, struct attribute *attr, char *buf) +{ +return sprintf(buf, "%lu\n", debug_mask); +} + +static ssize_t store_debug_mask(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ +ssize_t res; +unsigned long input; +res = strict_strtoul(buf, 0, &input); +if (res < 0) +return -EINVAL; +debug_mask = input; +return count; +} + +static ssize_t show_up_rate_us(struct kobject *kobj, struct attribute *attr, char *buf) +{ +return sprintf(buf, "%lu\n", up_rate_us); +} + +static ssize_t store_up_rate_us(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ +ssize_t res; +unsigned long input; +res = strict_strtoul(buf, 0, &input); +if (res < 0) +return -EINVAL; +if (input >= 0 && input <= 100000000) +up_rate_us = input; +return count; +} + +static ssize_t show_down_rate_us(struct kobject *kobj, struct attribute *attr, char *buf) +{ +return sprintf(buf, "%lu\n", down_rate_us); +} + +static ssize_t store_down_rate_us(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ +ssize_t res; +unsigned long input; +res = strict_strtoul(buf, 0, &input); +if (res < 0) +return -EINVAL; +if (input >= 0 && input <= 100000000) +down_rate_us = input; +return count; +} + +static ssize_t show_sleep_ideal_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ +return sprintf(buf, "%u\n", sleep_ideal_freq); +} + +static ssize_t store_sleep_ideal_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ +ssize_t res; +unsigned long input; +res = strict_strtoul(buf, 0, &input); +if (res < 0) +return -EINVAL; +if (input >= 0) { +sleep_ideal_freq = input; +if (suspended) +smartass_update_min_max_allcpus(); +} +return count; +} + +static ssize_t show_sleep_wakeup_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ +return sprintf(buf, "%u\n", sleep_wakeup_freq); +} + +static ssize_t store_sleep_wakeup_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ +ssize_t res; +unsigned long input; +res = strict_strtoul(buf, 0, &input); +if (res < 0) +return -EINVAL; +if (input >= 0) +sleep_wakeup_freq = input; +return count; +} + +static ssize_t show_awake_ideal_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ +return sprintf(buf, "%u\n", awake_ideal_freq); +} + +static ssize_t store_awake_ideal_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ +ssize_t res; +unsigned long input; +res = strict_strtoul(buf, 0, &input); +if (res < 0) +return -EINVAL; +if (input >= 0) { +awake_ideal_freq = input; +if (!suspended) +smartass_update_min_max_allcpus(); +} +return count; +} + +static ssize_t show_sample_rate_jiffies(struct kobject *kobj, struct attribute *attr, char *buf) +{ +return sprintf(buf, "%u\n", sample_rate_jiffies); +} + +static ssize_t store_sample_rate_jiffies(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ +ssize_t res; +unsigned long input; +res = strict_strtoul(buf, 0, &input); +if (res < 0) +return -EINVAL; +if (input > 0 && input <= 1000) +sample_rate_jiffies = input; +return count; +} + +static ssize_t show_ramp_up_step(struct kobject *kobj, struct attribute *attr, char *buf) +{ +return sprintf(buf, "%u\n", ramp_up_step); +} + +static ssize_t store_ramp_up_step(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ +ssize_t res; +unsigned long input; +res = strict_strtoul(buf, 0, &input); +if (res < 0) +return -EINVAL; +if (input >= 0) +ramp_up_step = input; +return count; +} + +static ssize_t show_ramp_down_step(struct kobject *kobj, struct attribute *attr, char *buf) +{ +return sprintf(buf, "%u\n", ramp_down_step); +} + +static ssize_t store_ramp_down_step(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ +ssize_t res; +unsigned long input; +res = strict_strtoul(buf, 0, &input); +if (res < 0) +return -EINVAL; +if (input >= 0) +ramp_down_step = input; +return count; +} + +static ssize_t show_max_cpu_load(struct kobject *kobj, struct attribute *attr, char *buf) +{ +return sprintf(buf, "%lu\n", max_cpu_load); +} + +static ssize_t store_max_cpu_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ +ssize_t res; +unsigned long input; +res = strict_strtoul(buf, 0, &input); +if (res < 0) +return -EINVAL; +if (input > 0 && input <= 100) +max_cpu_load = input; +return count; +} + +static ssize_t show_min_cpu_load(struct kobject *kobj, struct attribute *attr, char *buf) +{ +return sprintf(buf, "%lu\n", min_cpu_load); +} + +static ssize_t store_min_cpu_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ +ssize_t res; +unsigned long input; +res = strict_strtoul(buf, 0, &input); +if (res < 0) +return -EINVAL; +if (input > 0 && input < 100) +min_cpu_load = input; +return count; +} + +static ssize_t show_boost_enabled(struct kobject *kobj, struct attribute *attr, char *buf) +{ +return sprintf(buf, "%u\n", boost_enabled); +} + +static ssize_t store_boost_enabled(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ +ssize_t res; +unsigned long input; +res = strict_strtoul(buf, 0, &input); +if (res < 0) +return -EINVAL; +boost_enabled = (input == 0 ? 0 : 1); +return count; +} + +static ssize_t show_boost_pulse(struct kobject *kobj, struct attribute *attr, char *buf) +{ +return sprintf(buf, "%lu\n", boost_pulse); +} + +static ssize_t store_boost_pulse(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ +ssize_t res; +unsigned long input; +res = strict_strtoul(buf, 0, &input); +if (res < 0) +return -EINVAL; +boost_pulse_time = ktime_to_us(ktime_get()); +if (input > MAX_BOOST_PULSE) +boost_pulse = MAX_BOOST_PULSE; +else if (input <= 1) +boost_pulse = DEFAULT_BOOST_PULSE; +else +boost_pulse = input; +dprintk(SMARTASS_DEBUG_ALG,"Smartass: boost pulse %lu/%lu enabled %d\n", +input,boost_pulse,boost_enabled); +return count; +} + +#define define_global_rw_attr(_name) \ +static struct global_attr _name##_attr = \ +__ATTR(_name, 0644, show_##_name, store_##_name) + +define_global_rw_attr(debug_mask); +define_global_rw_attr(up_rate_us); +define_global_rw_attr(down_rate_us); +define_global_rw_attr(sleep_ideal_freq); +define_global_rw_attr(sleep_wakeup_freq); +define_global_rw_attr(awake_ideal_freq); +define_global_rw_attr(sample_rate_jiffies); +define_global_rw_attr(ramp_up_step); +define_global_rw_attr(ramp_down_step); +define_global_rw_attr(max_cpu_load); +define_global_rw_attr(min_cpu_load); +define_global_rw_attr(boost_enabled); +define_global_rw_attr(boost_pulse); + +static struct attribute * smartass_attributes[] = { +&debug_mask_attr.attr, +&up_rate_us_attr.attr, +&down_rate_us_attr.attr, +&sleep_ideal_freq_attr.attr, +&sleep_wakeup_freq_attr.attr, +&awake_ideal_freq_attr.attr, +&sample_rate_jiffies_attr.attr, +&ramp_up_step_attr.attr, +&ramp_down_step_attr.attr, +&max_cpu_load_attr.attr, +&min_cpu_load_attr.attr, +&boost_enabled_attr.attr, +&boost_pulse_attr.attr, +NULL, +}; + +static struct attribute_group smartass_attr_group = { +.attrs = smartass_attributes, +.name = "smartassH3", +}; + +static int cpufreq_governor_smartass_h3(struct cpufreq_policy *new_policy, +unsigned int event) +{ +unsigned int cpu = new_policy->cpu; +int rc; +struct smartass_info_s *this_smartass = &per_cpu(smartass_info, cpu); + +switch (event) { +case CPUFREQ_GOV_START: +if ((!cpu_online(cpu)) || (!new_policy->cur)) +return -EINVAL; + +this_smartass->cur_policy = new_policy; + +this_smartass->enable = 1; + +smartass_update_min_max(this_smartass,new_policy,suspended); + +this_smartass->freq_table = cpufreq_frequency_get_table(cpu); +if (!this_smartass->freq_table) +printk(KERN_WARNING "Smartass: no frequency table for cpu %d?!\n",cpu); + +smp_wmb(); + +// Do not register the idle hook and create sysfs +// entries if we have already done so. +if (atomic_inc_return(&active_count) <= 1) { +rc = sysfs_create_group(cpufreq_global_kobject, +&smartass_attr_group); +if (rc) +return rc; + +pm_idle_old = pm_idle; +pm_idle = cpufreq_idle; +} + +if (this_smartass->cur_policy->cur < new_policy->max && !timer_pending(&this_smartass->timer)) +reset_timer(cpu,this_smartass); + +break; + +case CPUFREQ_GOV_LIMITS: +smartass_update_min_max(this_smartass,new_policy,suspended); + +if (this_smartass->cur_policy->cur > new_policy->max) { +dprintk(SMARTASS_DEBUG_JUMPS,"SmartassI: jumping to new max freq: %d\n",new_policy->max); +__cpufreq_driver_target(this_smartass->cur_policy, +new_policy->max, CPUFREQ_RELATION_H); +} +else if (this_smartass->cur_policy->cur < new_policy->min) { +dprintk(SMARTASS_DEBUG_JUMPS,"SmartassI: jumping to new min freq: %d\n",new_policy->min); +__cpufreq_driver_target(this_smartass->cur_policy, +new_policy->min, CPUFREQ_RELATION_L); +} + +if (this_smartass->cur_policy->cur < new_policy->max && !timer_pending(&this_smartass->timer)) +reset_timer(cpu,this_smartass); + +break; + +case CPUFREQ_GOV_STOP: +this_smartass->enable = 0; +smp_wmb(); +del_timer(&this_smartass->timer); +flush_work(&freq_scale_work); +this_smartass->idle_exit_time = 0; + +if (atomic_dec_return(&active_count) <= 1) { +sysfs_remove_group(cpufreq_global_kobject, +&smartass_attr_group); +pm_idle = pm_idle_old; +} +break; +} + +return 0; +} + +static void smartass_suspend(int cpu, int suspend) +{ +struct smartass_info_s *this_smartass = &per_cpu(smartass_info, smp_processor_id()); +struct cpufreq_policy *policy = this_smartass->cur_policy; +unsigned int new_freq; + +if (!this_smartass->enable) +return; + +smartass_update_min_max(this_smartass,policy,suspend); +if (!suspend) { // resume at max speed: +new_freq = validate_freq(policy,sleep_wakeup_freq); + +dprintk(SMARTASS_DEBUG_JUMPS,"SmartassS: awaking at %d\n",new_freq); + +__cpufreq_driver_target(policy, new_freq, +CPUFREQ_RELATION_L); +} else { +// to avoid wakeup issues with quick sleep/wakeup don't change actual frequency when entering sleep +// to allow some time to settle down. Instead we just reset our statistics (and reset the timer). +// Eventually, the timer will adjust the frequency if necessary. + +this_smartass->freq_change_time_in_idle = +get_cpu_idle_time_us(cpu,&this_smartass->freq_change_time); + +dprintk(SMARTASS_DEBUG_JUMPS,"SmartassS: suspending at %d\n",policy->cur); +} + +reset_timer(smp_processor_id(),this_smartass); +} + +static void smartass_early_suspend(struct early_suspend *handler) { +int i; +if (suspended || sleep_ideal_freq==0) // disable behavior for sleep_ideal_freq==0 +return; +suspended = 1; +for_each_online_cpu(i) +smartass_suspend(i,1); +} + +static void smartass_late_resume(struct early_suspend *handler) { +int i; +if (!suspended) // already not suspended so nothing to do +return; +suspended = 0; +for_each_online_cpu(i) +smartass_suspend(i,0); +} + +static struct early_suspend smartass_power_suspend = { +.suspend = smartass_early_suspend, +.resume = smartass_late_resume, +#ifdef CONFIG_MACH_HERO +.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, +#endif +}; + +static int __init cpufreq_smartass_init(void) +{ +unsigned int i; +struct smartass_info_s *this_smartass; +debug_mask = 0; +up_rate_us = DEFAULT_UP_RATE_US; +down_rate_us = DEFAULT_DOWN_RATE_US; +sleep_ideal_freq = DEFAULT_SLEEP_IDEAL_FREQ; +sleep_wakeup_freq = DEFAULT_SLEEP_WAKEUP_FREQ; +awake_ideal_freq = DEFAULT_AWAKE_IDEAL_FREQ; +sample_rate_jiffies = DEFAULT_SAMPLE_RATE_JIFFIES; +ramp_up_step = DEFAULT_RAMP_UP_STEP; +ramp_down_step = DEFAULT_RAMP_DOWN_STEP; +max_cpu_load = DEFAULT_MAX_CPU_LOAD; +min_cpu_load = DEFAULT_MIN_CPU_LOAD; +boost_enabled = DEFAULT_BOOST_ENABLED; +boost_pulse = 0; + +spin_lock_init(&cpumask_lock); + +suspended = 0; + +/* Initalize per-cpu data: */ +for_each_possible_cpu(i) { +this_smartass = &per_cpu(smartass_info, i); +this_smartass->enable = 0; +this_smartass->cur_policy = 0; +this_smartass->ramp_dir = 0; +this_smartass->time_in_idle = 0; +this_smartass->idle_exit_time = 0; +this_smartass->freq_change_time = 0; +this_smartass->freq_change_time_in_idle = 0; +this_smartass->cur_cpu_load = 0; +// intialize timer: +init_timer_deferrable(&this_smartass->timer); +this_smartass->timer.function = cpufreq_smartass_timer; +this_smartass->timer.data = i; +work_cpumask_test_and_clear(i); +} + +// Scale up is high priority +up_wq = alloc_workqueue("ksmartass_up", WQ_HIGHPRI, 1); +down_wq = alloc_workqueue("ksmartass_down", 0, 1); +if (!up_wq || !down_wq) +return -ENOMEM; + +INIT_WORK(&freq_scale_work, cpufreq_smartass_freq_change_time_work); + +register_early_suspend(&smartass_power_suspend); + +return cpufreq_register_governor(&cpufreq_gov_smartass_h3); +} + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASSH3 +fs_initcall(cpufreq_smartass_init); +#else +module_init(cpufreq_smartass_init); +#endif + +static void __exit cpufreq_smartass_exit(void) +{ +cpufreq_unregister_governor(&cpufreq_gov_smartass_h3); +destroy_workqueue(up_wq); +destroy_workqueue(down_wq); +} + +module_exit(cpufreq_smartass_exit); + +MODULE_AUTHOR ("Erasmux, moded by H3ROS & C3C0"); +MODULE_DESCRIPTION ("'cpufreq_smartassH3' - A smart cpufreq governor"); +MODULE_LICENSE ("GPL"); + diff --git a/drivers/crypto/msm/qce.c b/drivers/crypto/msm/qce.c index ec4f7474..59caec29 100644 --- a/drivers/crypto/msm/qce.c +++ b/drivers/crypto/msm/qce.c @@ -479,19 +479,6 @@ static int count_sg(struct scatterlist *sg, int nbytes) return i; } -static int dma_map_pmem_sg(struct buf_info *pmem, unsigned entries, - struct scatterlist *sg) -{ - int i = 0; - for (i = 0; i < entries; i++) { - - sg->dma_address = (dma_addr_t)pmem->offset; - sg++; - pmem++; - } - return 0; -} - static int _probe_ce_engine(struct qce_device *pce_dev) { unsigned int val; @@ -1058,52 +1045,6 @@ static int _ablk_cipher_complete(struct qce_device *pce_dev) return 0; }; -static int _ablk_cipher_use_pmem_complete(struct qce_device *pce_dev) -{ - struct ablkcipher_request *areq; - uint32_t iv_out[4]; - unsigned char iv[4 * sizeof(uint32_t)]; - uint32_t status; - - areq = (struct ablkcipher_request *) pce_dev->areq; - - /* check ce error status */ - status = readl_relaxed(pce_dev->iobase + CRYPTO_STATUS_REG); - if (status & (1 << CRYPTO_SW_ERR)) { - pce_dev->err++; - dev_err(pce_dev->pdev, - "Qualcomm Crypto Error at 0x%x, status%x\n", - pce_dev->phy_iobase, status); - _init_ce_engine(pce_dev); - clk_disable(pce_dev->ce_clk); - pce_dev->qce_cb(areq, NULL, NULL, -ENXIO); - return 0; - }; - - /* get iv out */ - if (pce_dev->mode == QCE_MODE_ECB) { - clk_disable(pce_dev->ce_clk); - pce_dev->qce_cb(areq, NULL, NULL, pce_dev->chan_ce_in_status | - pce_dev->chan_ce_out_status); - } else { - iv_out[0] = readl_relaxed(pce_dev->iobase + - CRYPTO_CNTR0_IV0_REG); - iv_out[1] = readl_relaxed(pce_dev->iobase + - CRYPTO_CNTR1_IV1_REG); - iv_out[2] = readl_relaxed(pce_dev->iobase + - CRYPTO_CNTR2_IV2_REG); - iv_out[3] = readl_relaxed(pce_dev->iobase + - CRYPTO_CNTR3_IV3_REG); - - _net_words_to_byte_stream(iv_out, iv, sizeof(iv)); - clk_disable(pce_dev->ce_clk); - pce_dev->qce_cb(areq, NULL, iv, pce_dev->chan_ce_in_status | - pce_dev->chan_ce_out_status); - } - - return 0; -}; - static int qce_split_and_insert_dm_desc(struct dmov_desc *pdesc, unsigned int plen, unsigned int paddr, int *index) { @@ -1528,54 +1469,6 @@ static void _ablk_cipher_ce_out_call_back(struct msm_dmov_cmd *cmd_ptr, } }; - -static void _ablk_cipher_ce_in_call_back_pmem(struct msm_dmov_cmd *cmd_ptr, - unsigned int result, struct msm_dmov_errdata *err) -{ - struct qce_device *pce_dev; - - pce_dev = (struct qce_device *) cmd_ptr->user; - if (result != ADM_STATUS_OK) { - dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n", - result); - pce_dev->chan_ce_in_status = -1; - } else - pce_dev->chan_ce_in_status = 0; - - pce_dev->chan_ce_in_state = QCE_CHAN_STATE_COMP; - if (pce_dev->chan_ce_out_state == QCE_CHAN_STATE_COMP) { - pce_dev->chan_ce_in_state = QCE_CHAN_STATE_IDLE; - pce_dev->chan_ce_out_state = QCE_CHAN_STATE_IDLE; - - /* done */ - _ablk_cipher_use_pmem_complete(pce_dev); - } -}; - -static void _ablk_cipher_ce_out_call_back_pmem(struct msm_dmov_cmd *cmd_ptr, - unsigned int result, struct msm_dmov_errdata *err) -{ - struct qce_device *pce_dev; - - pce_dev = (struct qce_device *) cmd_ptr->user; - if (result != ADM_STATUS_OK) { - dev_err(pce_dev->pdev, "Qualcomm ADM status error %x\n", - result); - pce_dev->chan_ce_out_status = -1; - } else { - pce_dev->chan_ce_out_status = 0; - }; - - pce_dev->chan_ce_out_state = QCE_CHAN_STATE_COMP; - if (pce_dev->chan_ce_in_state == QCE_CHAN_STATE_COMP) { - pce_dev->chan_ce_in_state = QCE_CHAN_STATE_IDLE; - pce_dev->chan_ce_out_state = QCE_CHAN_STATE_IDLE; - - /* done */ - _ablk_cipher_use_pmem_complete(pce_dev); - } -}; - static int _setup_cmd_template(struct qce_device *pce_dev) { dmov_sg *pcmd; @@ -2157,13 +2050,9 @@ int qce_ablk_cipher_req(void *handle, struct qce_req *c_req) /* cipher input */ pce_dev->src_nents = count_sg(areq->src, areq->nbytes); - if (c_req->use_pmem != 1) - dma_map_sg(pce_dev->pdev, areq->src, pce_dev->src_nents, + qce_dma_map_sg(pce_dev->pdev, areq->src, pce_dev->src_nents, (areq->src == areq->dst) ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE); - else - dma_map_pmem_sg(&c_req->pmem->src[0], pce_dev->src_nents, - areq->src); if (_chain_sg_buffer_in(pce_dev, areq->src, areq->nbytes) < 0) { rc = -ENOMEM; @@ -2176,9 +2065,6 @@ int qce_ablk_cipher_req(void *handle, struct qce_req *c_req) if (c_req->use_pmem != 1) dma_map_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents, DMA_FROM_DEVICE); - else - dma_map_pmem_sg(&c_req->pmem->dst[0], - pce_dev->dst_nents, areq->dst); }; if (_chain_sg_buffer_out(pce_dev, areq->dst, areq->nbytes) < 0) { rc = -ENOMEM; @@ -2215,15 +2101,9 @@ int qce_ablk_cipher_req(void *handle, struct qce_req *c_req) /* setup for callback, and issue command to adm */ pce_dev->areq = areq; pce_dev->qce_cb = c_req->qce_cb; - if (c_req->use_pmem == 1) { - pce_dev->chan_ce_in_cmd->complete_func = - _ablk_cipher_ce_in_call_back_pmem; - pce_dev->chan_ce_out_cmd->complete_func = - _ablk_cipher_ce_out_call_back_pmem; - } else { - pce_dev->chan_ce_in_cmd->complete_func = + pce_dev->chan_ce_in_cmd->complete_func = _ablk_cipher_ce_in_call_back; - pce_dev->chan_ce_out_cmd->complete_func = + pce_dev->chan_ce_out_cmd->complete_func = _ablk_cipher_ce_out_call_back; } rc = _qce_start_dma(pce_dev, true, true); @@ -2231,18 +2111,16 @@ int qce_ablk_cipher_req(void *handle, struct qce_req *c_req) if (rc == 0) return 0; bad: - if (c_req->use_pmem != 1) { - if (pce_dev->dst_nents) { - dma_unmap_sg(pce_dev->pdev, areq->dst, - pce_dev->dst_nents, DMA_FROM_DEVICE); - } - if (pce_dev->src_nents) { - dma_unmap_sg(pce_dev->pdev, areq->src, - pce_dev->src_nents, - (areq->src == areq->dst) ? - DMA_BIDIRECTIONAL : - DMA_TO_DEVICE); - } + if (pce_dev->dst_nents) { + qce_dma_unmap_sg(pce_dev->pdev, areq->dst, + pce_dev->dst_nents, DMA_FROM_DEVICE); + } + if (pce_dev->src_nents) { + qce_dma_unmap_sg(pce_dev->pdev, areq->src, + pce_dev->src_nents, + (areq->src == areq->dst) ? + DMA_BIDIRECTIONAL : + DMA_TO_DEVICE); } return rc; } diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c index a105058b..906330d7 100644 --- a/drivers/crypto/msm/qcedev.c +++ b/drivers/crypto/msm/qcedev.c @@ -12,7 +12,6 @@ * GNU General Public License for more details. */ #include -#include #include #include #include @@ -456,11 +455,11 @@ static int start_cipher_req(struct qcedev_control *podev) qcedev_areq = podev->active_command; qcedev_areq->cipher_req.cookie = qcedev_areq->handle; - creq.use_pmem = qcedev_areq->cipher_op_req.use_pmem; - if (qcedev_areq->cipher_op_req.use_pmem == QCEDEV_USE_PMEM) - creq.pmem = &qcedev_areq->cipher_op_req.pmem; - else - creq.pmem = NULL; + if (qcedev_areq->cipher_op_req.use_pmem == QCEDEV_USE_PMEM) { + pr_err("%s: Use of PMEM is not supported\n", __func__); + goto unsupported; + } + creq.pmem = NULL; switch (qcedev_areq->cipher_op_req.alg) { case QCEDEV_ALG_DES: @@ -1263,224 +1262,6 @@ static int qcedev_hash_final(struct qcedev_async_req *areq, return qcedev_hmac_final(areq, handle); } -#ifdef CONFIG_ANDROID_PMEM -static int qcedev_pmem_ablk_cipher_max_xfer(struct qcedev_async_req *areq, - struct qcedev_handle *handle) -{ - int i = 0; - int err = 0; - struct scatterlist *sg_src = NULL; - struct scatterlist *sg_dst = NULL; - struct scatterlist *sg_ndex = NULL; - struct file *file_src = NULL; - struct file *file_dst = NULL; - unsigned long paddr; - unsigned long kvaddr; - unsigned long len; - - sg_src = kmalloc((sizeof(struct scatterlist) * - areq->cipher_op_req.entries), GFP_KERNEL); - if (sg_src == NULL) { - pr_err("%s: Can't Allocate memory:sg_src 0x%x\n", - __func__, (uint32_t)sg_src); - return -ENOMEM; - - } - memset(sg_src, 0, (sizeof(struct scatterlist) * - areq->cipher_op_req.entries)); - sg_ndex = sg_src; - areq->cipher_req.creq.src = sg_src; - - /* address src */ - get_pmem_file(areq->cipher_op_req.pmem.fd_src, &paddr, - &kvaddr, &len, &file_src); - - for (i = 0; i < areq->cipher_op_req.entries; i++) { - sg_set_buf(sg_ndex, - ((uint8_t *)(areq->cipher_op_req.pmem.src[i].offset) + kvaddr), - areq->cipher_op_req.pmem.src[i].len); - sg_ndex++; - } - sg_mark_end(--sg_ndex); - - for (i = 0; i < areq->cipher_op_req.entries; i++) - areq->cipher_op_req.pmem.src[i].offset += (uint32_t)paddr; - - /* address dst */ - /* If not place encryption/decryption */ - if (areq->cipher_op_req.in_place_op != 1) { - sg_dst = kmalloc((sizeof(struct scatterlist) * - areq->cipher_op_req.entries), GFP_KERNEL); - if (sg_dst == NULL) { - pr_err("%s: Can't Allocate memory: sg_dst 0x%x\n", - __func__, (uint32_t)sg_dst); - return -ENOMEM; - } - memset(sg_dst, 0, (sizeof(struct scatterlist) * - areq->cipher_op_req.entries)); - areq->cipher_req.creq.dst = sg_dst; - sg_ndex = sg_dst; - - get_pmem_file(areq->cipher_op_req.pmem.fd_dst, &paddr, - &kvaddr, &len, &file_dst); - for (i = 0; i < areq->cipher_op_req.entries; i++) - sg_set_buf(sg_ndex++, - ((uint8_t *)(areq->cipher_op_req.pmem.dst[i].offset) - + kvaddr), areq->cipher_op_req.pmem.dst[i].len); - sg_mark_end(--sg_ndex); - - for (i = 0; i < areq->cipher_op_req.entries; i++) - areq->cipher_op_req.pmem.dst[i].offset += - (uint32_t)paddr; - } else { - areq->cipher_req.creq.dst = sg_src; - for (i = 0; i < areq->cipher_op_req.entries; i++) { - areq->cipher_op_req.pmem.dst[i].offset = - areq->cipher_op_req.pmem.src[i].offset; - areq->cipher_op_req.pmem.dst[i].len = - areq->cipher_op_req.pmem.src[i].len; - } - } - - areq->cipher_req.creq.nbytes = areq->cipher_op_req.data_len; - areq->cipher_req.creq.info = areq->cipher_op_req.iv; - - err = submit_req(areq, handle); - - kfree(sg_src); - kfree(sg_dst); - - if (file_dst) - put_pmem_file(file_dst); - if (file_src) - put_pmem_file(file_src); - - return err; -}; - - -static int qcedev_pmem_ablk_cipher(struct qcedev_async_req *qcedev_areq, - struct qcedev_handle *handle) -{ - int err = 0; - int i = 0; - int j = 0; - int k = 0; - int num_entries = 0; - uint32_t total = 0; - struct qcedev_cipher_op_req *saved_req; - struct qcedev_cipher_op_req *creq = &qcedev_areq->cipher_op_req; - - saved_req = kmalloc(sizeof(struct qcedev_cipher_op_req), GFP_KERNEL); - if (saved_req == NULL) { - pr_err(KERN_ERR "%s:Can't Allocate mem:saved_req 0x%x\n", - __func__, (uint32_t)saved_req); - return -ENOMEM; - } - memcpy(saved_req, creq, sizeof(struct qcedev_cipher_op_req)); - - if (qcedev_areq->cipher_op_req.data_len > QCE_MAX_OPER_DATA) { - - struct qcedev_cipher_op_req req; - - /* save the original req structure */ - memcpy(&req, creq, sizeof(struct qcedev_cipher_op_req)); - - i = 0; - /* Address 32 KB at a time */ - while ((i < req.entries) && (err == 0)) { - if (creq->pmem.src[i].len > QCE_MAX_OPER_DATA) { - creq->pmem.src[0].len = QCE_MAX_OPER_DATA; - if (i > 0) { - creq->pmem.src[0].offset = - creq->pmem.src[i].offset; - } - - creq->data_len = QCE_MAX_OPER_DATA; - creq->entries = 1; - - err = - qcedev_pmem_ablk_cipher_max_xfer(qcedev_areq, - handle); - - creq->pmem.src[i].len = req.pmem.src[i].len - - QCE_MAX_OPER_DATA; - creq->pmem.src[i].offset = - req.pmem.src[i].offset + - QCE_MAX_OPER_DATA; - req.pmem.src[i].offset = - creq->pmem.src[i].offset; - req.pmem.src[i].len = creq->pmem.src[i].len; - } else { - total = 0; - for (j = i; j < req.entries; j++) { - num_entries++; - if ((total + creq->pmem.src[j].len) - >= QCE_MAX_OPER_DATA) { - creq->pmem.src[j].len = - QCE_MAX_OPER_DATA - total; - total = QCE_MAX_OPER_DATA; - break; - } - total += creq->pmem.src[j].len; - } - - creq->data_len = total; - if (i > 0) - for (k = 0; k < num_entries; k++) { - creq->pmem.src[k].len = - creq->pmem.src[i+k].len; - creq->pmem.src[k].offset = - creq->pmem.src[i+k].offset; - } - creq->entries = num_entries; - - i = j; - err = - qcedev_pmem_ablk_cipher_max_xfer(qcedev_areq, - handle); - num_entries = 0; - - creq->pmem.src[i].offset = - req.pmem.src[i].offset + - creq->pmem.src[i].len; - creq->pmem.src[i].len = - req.pmem.src[i].len - - creq->pmem.src[i].len; - req.pmem.src[i].offset = - creq->pmem.src[i].offset; - req.pmem.src[i].len = - creq->pmem.src[i].len; - - if (creq->pmem.src[i].len == 0) - i++; - } - - } /* end of while ((i < req.entries) && (err == 0)) */ - - } else - err = qcedev_pmem_ablk_cipher_max_xfer(qcedev_areq, handle); - - /* Restore the original req structure */ - for (i = 0; i < saved_req->entries; i++) { - creq->pmem.src[i].len = saved_req->pmem.src[i].len; - creq->pmem.src[i].offset = saved_req->pmem.src[i].offset; - } - creq->entries = saved_req->entries; - creq->data_len = saved_req->data_len; - kfree(saved_req); - - return err; - -} -#else -static int qcedev_pmem_ablk_cipher(struct qcedev_async_req *qcedev_areq, - struct qcedev_handle *handle) -{ - return -EPERM; -} -#endif/*CONFIG_ANDROID_PMEM*/ - static int qcedev_vbuf_ablk_cipher_max_xfer(struct qcedev_async_req *areq, int *di, struct qcedev_handle *handle, uint8_t *k_align_src) @@ -1742,6 +1523,10 @@ static int qcedev_vbuf_ablk_cipher(struct qcedev_async_req *areq, static int qcedev_check_cipher_params(struct qcedev_cipher_op_req *req, struct qcedev_control *podev) { + if (req->use_pmem) { + pr_err("%s: Use of PMEM is not supported\n", __func__); + goto error; + } if ((req->entries == 0) || (req->data_len == 0)) goto error; if ((req->alg >= QCEDEV_ALG_LAST) || @@ -1781,15 +1566,6 @@ static int qcedev_check_cipher_params(struct qcedev_cipher_op_req *req, if (req->byteoffset) { if (req->mode != QCEDEV_AES_MODE_CTR) goto error; - else { /* if using CTR mode make sure not using Pmem */ - if (req->use_pmem) - goto error; - } - } - /* if using PMEM with non-zero byteoffset, ensure it is in_place_op */ - if (req->use_pmem) { - if (!req->in_place_op) - goto error; } /* Ensure zer ivlen for ECB mode */ if (req->ivlen != 0) { @@ -1880,9 +1656,7 @@ static long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg) return -EINVAL; if (qcedev_areq.cipher_op_req.use_pmem) - err = qcedev_pmem_ablk_cipher(&qcedev_areq, handle); - else - err = qcedev_vbuf_ablk_cipher(&qcedev_areq, handle); + err = qcedev_vbuf_ablk_cipher(&qcedev_areq, handle); if (err) return err; if (__copy_to_user((void __user *)arg, diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c index f5fa1503..b8b00f0d 100644 --- a/drivers/gpu/ion/ion.c +++ b/drivers/gpu/ion/ion.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include #include "ion_priv.h" @@ -41,15 +43,17 @@ /** * struct ion_device - the metadata of the ion device node * @dev: the actual misc device - * @buffers: an rb tree of all the existing buffers - * @lock: lock protecting the buffers & heaps trees + * @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 lock; + struct mutex buffer_lock; + struct rw_semaphore lock; struct rb_root heaps; long (*custom_ioctl) (struct ion_client *client, unsigned int cmd, unsigned long arg); @@ -106,24 +110,6 @@ struct ion_handle { static void ion_iommu_release(struct kref *kref); -static int ion_validate_buffer_flags(struct ion_buffer *buffer, - unsigned long flags) -{ - if (buffer->kmap_cnt || buffer->dmap_cnt || buffer->umap_cnt || - buffer->iommu_map_cnt) { - if (buffer->flags != flags) { - pr_err("%s: buffer was already mapped with flags %lx," - " cannot map with flags %lx\n", __func__, - buffer->flags, flags); - return 1; - } - - } else { - buffer->flags = flags; - } - return 0; -} - /* this function should only be called while dev->lock is held */ static void ion_buffer_add(struct ion_device *dev, struct ion_buffer *buffer) @@ -231,6 +217,7 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, buffer->dev = dev; buffer->size = len; + buffer->flags = flags; table = buffer->heap->ops->map_dma(buffer->heap, buffer); if (IS_ERR_OR_NULL(table)) { @@ -241,7 +228,9 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, buffer->sg_table = table; mutex_init(&buffer->lock); + mutex_lock(&dev->buffer_lock); ion_buffer_add(dev, buffer); + mutex_unlock(&dev->buffer_lock); return buffer; } @@ -289,9 +278,9 @@ static void ion_buffer_destroy(struct kref *kref) ion_iommu_delayed_unmap(buffer); buffer->heap->ops->free(buffer); - mutex_lock(&dev->lock); + mutex_lock(&dev->buffer_lock); rb_erase(&buffer->node, &dev->buffers); - mutex_unlock(&dev->lock); + mutex_unlock(&dev->buffer_lock); kfree(buffer); } @@ -411,7 +400,8 @@ static void ion_handle_add(struct ion_client *client, struct ion_handle *handle) } struct ion_handle *ion_alloc(struct ion_client *client, size_t len, - size_t align, unsigned int flags) + size_t align, unsigned int heap_mask, + unsigned int flags) { struct rb_node *n; struct ion_handle *handle; @@ -435,17 +425,18 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, len = PAGE_ALIGN(len); - mutex_lock(&dev->lock); + down_read(&dev->lock); for (n = rb_first(&dev->heaps); n != NULL; n = rb_next(n)) { struct ion_heap *heap = rb_entry(n, struct ion_heap, node); /* if the client doesn't support this heap type */ if (!((1 << heap->type) & client->heap_mask)) continue; /* if the caller didn't specify this heap type */ - if (!((1 << heap->id) & flags)) + if (!((1 << heap->id) & heap_mask)) continue; /* Do not allow un-secure heap if secure is specified */ - if (secure_allocation && (heap->type != ION_HEAP_TYPE_CP)) + if (secure_allocation && + (heap->type != (enum ion_heap_type) ION_HEAP_TYPE_CP)) continue; buffer = ion_buffer_create(heap, dev, len, align, flags); if (!IS_ERR_OR_NULL(buffer)) @@ -466,7 +457,7 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, } } } - mutex_unlock(&dev->lock); + up_read(&dev->lock); if (buffer == NULL) return ERR_PTR(-ENODEV); @@ -770,8 +761,7 @@ void ion_unmap_iommu(struct ion_client *client, struct ion_handle *handle, } EXPORT_SYMBOL(ion_unmap_iommu); -void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle, - unsigned long flags) +void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle) { struct ion_buffer *buffer; void *vaddr; @@ -793,11 +783,6 @@ void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle, return ERR_PTR(-ENODEV); } - if (ion_validate_buffer_flags(buffer, flags)) { - mutex_unlock(&client->lock); - return ERR_PTR(-EEXIST); - } - mutex_lock(&buffer->lock); vaddr = ion_handle_kmap_get(handle); mutex_unlock(&buffer->lock); @@ -819,31 +804,6 @@ void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle) } EXPORT_SYMBOL(ion_unmap_kernel); -static int check_vaddr_bounds(unsigned long start, unsigned long end) -{ - struct mm_struct *mm = current->active_mm; - struct vm_area_struct *vma; - int ret = 1; - - if (end < start) - goto out; - - down_read(&mm->mmap_sem); - vma = find_vma(mm, start); - if (vma && vma->vm_start < end) { - if (start < vma->vm_start) - goto out_up; - if (end > vma->vm_end) - goto out_up; - ret = 0; - } - -out_up: - up_read(&mm->mmap_sem); -out: - return ret; -} - int ion_do_cache_op(struct ion_client *client, struct ion_handle *handle, void *uaddr, unsigned long offset, unsigned long len, unsigned int cmd) @@ -909,7 +869,7 @@ static int ion_debug_client_show(struct seq_file *s, void *unused) if (type == ION_HEAP_TYPE_SYSTEM_CONTIG || type == ION_HEAP_TYPE_CARVEOUT || - type == ION_HEAP_TYPE_CP) + type == (enum ion_heap_type) ION_HEAP_TYPE_CP) seq_printf(s, " : %12lx", handle->buffer->priv_phys); else seq_printf(s, " : %12s", "N/A"); @@ -997,7 +957,7 @@ struct ion_client *ion_client_create(struct ion_device *dev, client->task = task; client->pid = pid; - mutex_lock(&dev->lock); + down_write(&dev->lock); p = &dev->clients.rb_node; while (*p) { parent = *p; @@ -1015,7 +975,7 @@ struct ion_client *ion_client_create(struct ion_device *dev, client->debug_root = debugfs_create_file(name, 0664, dev->debug_root, client, &debug_client_fops); - mutex_unlock(&dev->lock); + up_write(&dev->lock); return client; } @@ -1031,12 +991,12 @@ void ion_client_destroy(struct ion_client *client) node); ion_handle_destroy(&handle->ref); } - mutex_lock(&dev->lock); + down_write(&dev->lock); if (client->task) put_task_struct(client->task); rb_erase(&client->node, &dev->clients); debugfs_remove_recursive(client->debug_root); - mutex_unlock(&dev->lock); + up_write(&dev->lock); kfree(client->name); kfree(client); @@ -1256,9 +1216,12 @@ static int ion_share_set_flags(struct ion_client *client, { struct ion_buffer *buffer; bool valid_handle; - unsigned long ion_flags = ION_SET_CACHE(CACHED); + unsigned long ion_flags = 0; if (flags & O_DSYNC) - ion_flags = ION_SET_CACHE(UNCACHED); + ion_flags = ION_SET_UNCACHED(ion_flags); + else + ion_flags = ION_SET_CACHED(ion_flags); + mutex_lock(&client->lock); valid_handle = ion_handle_validate(client, handle); @@ -1270,12 +1233,6 @@ static int ion_share_set_flags(struct ion_client *client, buffer = handle->buffer; - mutex_lock(&buffer->lock); - if (ion_validate_buffer_flags(buffer, ion_flags)) { - mutex_unlock(&buffer->lock); - return -EEXIST; - } - mutex_unlock(&buffer->lock); return 0; } @@ -1359,7 +1316,25 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (copy_from_user(&data, (void __user *)arg, sizeof(data))) return -EFAULT; data.handle = ion_alloc(client, data.len, data.align, - data.flags); + data.heap_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_ALLOC_COMPAT: + { + struct ion_allocation_data_old data; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) + return -EFAULT; + data.handle = ion_alloc(client, data.len, data.align, + data.flags, data.flags); if (IS_ERR(data.handle)) return PTR_ERR(data.handle); @@ -1413,8 +1388,10 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) sizeof(struct ion_fd_data))) return -EFAULT; data.handle = ion_import_dma_buf(client, data.fd); - if (IS_ERR(data.handle)) + 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; @@ -1435,65 +1412,21 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return dev->custom_ioctl(client, data.cmd, data.arg); } case ION_IOC_CLEAN_CACHES: + case ION_IOC_CLEAN_CACHES_COMPAT: + return client->dev->custom_ioctl(client, + ION_IOC_CLEAN_CACHES, arg); case ION_IOC_INV_CACHES: + case ION_IOC_INV_CACHES_COMPAT: + return client->dev->custom_ioctl(client, + ION_IOC_INV_CACHES, arg); case ION_IOC_CLEAN_INV_CACHES: - { - struct ion_flush_data data; - unsigned long start, end; - struct ion_handle *handle = NULL; - int ret; - - if (copy_from_user(&data, (void __user *)arg, - sizeof(struct ion_flush_data))) - return -EFAULT; - - start = (unsigned long) data.vaddr; - end = (unsigned long) data.vaddr + data.length; - - if (check_vaddr_bounds(start, end)) { - pr_err("%s: virtual address %p is out of bounds\n", - __func__, data.vaddr); - return -EINVAL; - } - - if (!data.handle) { - handle = ion_import_dma_buf(client, data.fd); - if (IS_ERR(handle)) { - pr_info("%s: Could not import handle: %d\n", - __func__, (int)handle); - return -EINVAL; - } - } - - ret = ion_do_cache_op(client, - data.handle ? data.handle : handle, - data.vaddr, data.offset, data.length, - cmd); - - if (!data.handle) - ion_free(client, handle); - - if (ret < 0) - return ret; - break; - - } + case ION_IOC_CLEAN_INV_CACHES_COMPAT: + return client->dev->custom_ioctl(client, + ION_IOC_CLEAN_INV_CACHES, arg); case ION_IOC_GET_FLAGS: - { - struct ion_flag_data data; - int ret; - if (copy_from_user(&data, (void __user *)arg, - sizeof(struct ion_flag_data))) - return -EFAULT; - - ret = ion_handle_get_flags(client, data.handle, &data.flags); - if (ret < 0) - return ret; - if (copy_to_user((void __user *)arg, &data, - sizeof(struct ion_flag_data))) - return -EFAULT; - break; - } + case ION_IOC_GET_FLAGS_COMPAT: + return client->dev->custom_ioctl(client, + ION_IOC_GET_FLAGS, arg); default: return -ENOTTY; } @@ -1551,80 +1484,6 @@ static size_t ion_debug_heap_total(struct ion_client *client, return size; } -/** - * Searches through a clients handles to find if the buffer is owned - * by this client. Used for debug output. - * @param client pointer to candidate owner of buffer - * @param buf pointer to buffer that we are trying to find the owner of - * @return 1 if found, 0 otherwise - */ -static int ion_debug_find_buffer_owner(const struct ion_client *client, - const struct ion_buffer *buf) -{ - struct rb_node *n; - - for (n = rb_first(&client->handles); n; n = rb_next(n)) { - const struct ion_handle *handle = rb_entry(n, - const struct ion_handle, - node); - if (handle->buffer == buf) - return 1; - } - return 0; -} - -/** - * Adds mem_map_data pointer to the tree of mem_map - * Used for debug output. - * @param mem_map The mem_map tree - * @param data The new data to add to the tree - */ -static void ion_debug_mem_map_add(struct rb_root *mem_map, - struct mem_map_data *data) -{ - struct rb_node **p = &mem_map->rb_node; - struct rb_node *parent = NULL; - struct mem_map_data *entry; - - while (*p) { - parent = *p; - entry = rb_entry(parent, struct mem_map_data, node); - - if (data->addr < entry->addr) { - p = &(*p)->rb_left; - } else if (data->addr > entry->addr) { - p = &(*p)->rb_right; - } else { - pr_err("%s: mem_map_data already found.", __func__); - BUG(); - } - } - rb_link_node(&data->node, parent, p); - rb_insert_color(&data->node, mem_map); -} - -/** - * Search for an owner of a buffer by iterating over all ION clients. - * @param dev ion device containing pointers to all the clients. - * @param buffer pointer to buffer we are trying to find the owner of. - * @return name of owner. - */ -const char *ion_debug_locate_owner(const struct ion_device *dev, - const struct ion_buffer *buffer) -{ - struct rb_node *j; - const char *client_name = NULL; - - for (j = rb_first(&dev->clients); j && !client_name; - j = rb_next(j)) { - struct ion_client *client = rb_entry(j, struct ion_client, - node); - if (ion_debug_find_buffer_owner(client, buffer)) - client_name = client->name; - } - return client_name; -} - /** * Create a mem_map of the heap. * @param s seq_file to log error message to. @@ -1632,54 +1491,83 @@ const char *ion_debug_locate_owner(const struct ion_device *dev, * @param mem_map The mem map to be created. */ void ion_debug_mem_map_create(struct seq_file *s, struct ion_heap *heap, - struct rb_root *mem_map) + struct list_head *mem_map) { struct ion_device *dev = heap->dev; - struct rb_node *n; + struct rb_node *cnode; size_t size; + struct ion_client *client; if (!heap->ops->phys) return; - 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) { - struct mem_map_data *data = + down_read(&dev->lock); + for (cnode = rb_first(&dev->clients); cnode; cnode = rb_next(cnode)) { + struct rb_node *hnode; + client = rb_entry(cnode, struct ion_client, node); + + mutex_lock(&client->lock); + for (hnode = rb_first(&client->handles); + hnode; + hnode = rb_next(hnode)) { + struct ion_handle *handle = rb_entry( + hnode, struct ion_handle, node); + if (handle->buffer->heap == heap) { + struct mem_map_data *data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - seq_printf(s, "ERROR: out of memory. " - "Part of memory map will not be logged\n"); - break; + if (!data) + goto inner_error; + heap->ops->phys(heap, handle->buffer, + &(data->addr), &size); + data->size = (unsigned long) size; + data->addr_end = data->addr + data->size - 1; + data->client_name = kstrdup(client->name, + GFP_KERNEL); + if (!data->client_name) { + kfree(data); + goto inner_error; + } + list_add(&data->node, mem_map); } - - buffer->heap->ops->phys(buffer->heap, buffer, - &(data->addr), &size); - data->size = (unsigned long) size; - data->addr_end = data->addr + data->size - 1; - data->client_name = ion_debug_locate_owner(dev, buffer); - ion_debug_mem_map_add(mem_map, data); } + mutex_unlock(&client->lock); } + up_read(&dev->lock); + return; + +inner_error: + seq_puts(s, + "ERROR: out of memory. Part of memory map will not be logged\n"); + mutex_unlock(&client->lock); + up_read(&dev->lock); } /** * Free the memory allocated by ion_debug_mem_map_create * @param mem_map The mem map to free. */ -static void ion_debug_mem_map_destroy(struct rb_root *mem_map) +static void ion_debug_mem_map_destroy(struct list_head *mem_map) { if (mem_map) { - struct rb_node *n; - while ((n = rb_first(mem_map)) != 0) { - struct mem_map_data *data = - rb_entry(n, struct mem_map_data, node); - rb_erase(&data->node, mem_map); + struct mem_map_data *data, *tmp; + list_for_each_entry_safe(data, tmp, mem_map, node) { + list_del(&data->node); + kfree(data->client_name); kfree(data); } } } +static int mem_map_cmp(void *priv, struct list_head *a, struct list_head *b) +{ + struct mem_map_data *d1, *d2; + d1 = list_entry(a, struct mem_map_data, node); + d2 = list_entry(b, struct mem_map_data, node); + if (d1->addr == d2->addr) + return d1->size - d2->size; + return d1->addr - d2->addr; +} + /** * Print heap debug information. * @param s seq_file to log message to. @@ -1688,8 +1576,9 @@ static void ion_debug_mem_map_destroy(struct rb_root *mem_map) static void ion_heap_print_debug(struct seq_file *s, struct ion_heap *heap) { if (heap->ops->print_debug) { - struct rb_root mem_map = RB_ROOT; + struct list_head mem_map = LIST_HEAD_INIT(mem_map); ion_debug_mem_map_create(s, heap, &mem_map); + list_sort(NULL, &mem_map, mem_map_cmp); heap->ops->print_debug(heap, s, &mem_map); ion_debug_mem_map_destroy(&mem_map); } @@ -1701,9 +1590,9 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused) struct ion_device *dev = heap->dev; struct rb_node *n; - mutex_lock(&dev->lock); seq_printf(s, "%16.s %16.s %16.s\n", "client", "pid", "size"); + down_read(&dev->lock); for (n = rb_first(&dev->clients); n; n = rb_next(n)) { struct ion_client *client = rb_entry(n, struct ion_client, node); @@ -1721,8 +1610,8 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused) client->pid, size); } } + up_read(&dev->lock); ion_heap_print_debug(s, heap); - mutex_unlock(&dev->lock); return 0; } @@ -1750,7 +1639,7 @@ void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap) __func__); heap->dev = dev; - mutex_lock(&dev->lock); + down_write(&dev->lock); while (*p) { parent = *p; entry = rb_entry(parent, struct ion_heap, node); @@ -1771,7 +1660,7 @@ void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap) debugfs_create_file(heap->name, 0664, dev->debug_root, heap, &debug_heap_fops); end: - mutex_unlock(&dev->lock); + up_write(&dev->lock); } int ion_secure_heap(struct ion_device *dev, int heap_id, int version, @@ -1784,10 +1673,10 @@ int ion_secure_heap(struct ion_device *dev, int heap_id, int version, * traverse the list of heaps available in this system * and find the heap that is specified. */ - mutex_lock(&dev->lock); + down_write(&dev->lock); for (n = rb_first(&dev->heaps); n != NULL; n = rb_next(n)) { struct ion_heap *heap = rb_entry(n, struct ion_heap, node); - if (heap->type != ION_HEAP_TYPE_CP) + if (heap->type != (enum ion_heap_type) ION_HEAP_TYPE_CP) continue; if (ION_HEAP(heap->id) != heap_id) continue; @@ -1797,7 +1686,7 @@ int ion_secure_heap(struct ion_device *dev, int heap_id, int version, ret_val = -EINVAL; break; } - mutex_unlock(&dev->lock); + up_write(&dev->lock); return ret_val; } EXPORT_SYMBOL(ion_secure_heap); @@ -1812,10 +1701,10 @@ int ion_unsecure_heap(struct ion_device *dev, int heap_id, int version, * traverse the list of heaps available in this system * and find the heap that is specified. */ - mutex_lock(&dev->lock); + down_write(&dev->lock); for (n = rb_first(&dev->heaps); n != NULL; n = rb_next(n)) { struct ion_heap *heap = rb_entry(n, struct ion_heap, node); - if (heap->type != ION_HEAP_TYPE_CP) + if (heap->type != (enum ion_heap_type) ION_HEAP_TYPE_CP) continue; if (ION_HEAP(heap->id) != heap_id) continue; @@ -1825,7 +1714,7 @@ int ion_unsecure_heap(struct ion_device *dev, int heap_id, int version, ret_val = -EINVAL; break; } - mutex_unlock(&dev->lock); + up_write(&dev->lock); return ret_val; } EXPORT_SYMBOL(ion_unsecure_heap); @@ -1839,7 +1728,7 @@ static int ion_debug_leak_show(struct seq_file *s, void *unused) /* mark all buffers as 1 */ seq_printf(s, "%16.s %16.s %16.s %16.s\n", "buffer", "heap", "size", "ref cnt"); - mutex_lock(&dev->lock); + down_write(&dev->lock); for (n = rb_first(&dev->buffers); n; n = rb_next(n)) { struct ion_buffer *buf = rb_entry(n, struct ion_buffer, node); @@ -1874,7 +1763,7 @@ static int ion_debug_leak_show(struct seq_file *s, void *unused) (int)buf, buf->heap->name, buf->size, atomic_read(&buf->ref.refcount)); } - mutex_unlock(&dev->lock); + up_write(&dev->lock); return 0; } @@ -1920,7 +1809,8 @@ struct ion_device *ion_device_create(long (*custom_ioctl) idev->custom_ioctl = custom_ioctl; idev->buffers = RB_ROOT; - mutex_init(&idev->lock); + mutex_init(&idev->buffer_lock); + init_rwsem(&idev->lock); idev->heaps = RB_ROOT; idev->clients = RB_ROOT; debugfs_create_file("check_leaked_fds", 0664, idev->debug_root, idev, diff --git a/drivers/gpu/ion/ion_carveout_heap.c b/drivers/gpu/ion/ion_carveout_heap.c index 3f9fe545..a6a5c2d9 100644 --- a/drivers/gpu/ion/ion_carveout_heap.c +++ b/drivers/gpu/ion/ion_carveout_heap.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include "ion_priv.h" @@ -31,6 +30,7 @@ #include #include #include +#include struct ion_carveout_heap { struct ion_heap heap; @@ -240,25 +240,78 @@ int ion_carveout_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer, void *vaddr, unsigned int offset, unsigned int length, unsigned int cmd) { - void (*outer_cache_op)(phys_addr_t, phys_addr_t); + void (*outer_cache_op)(phys_addr_t, phys_addr_t) = NULL; struct ion_carveout_heap *carveout_heap = container_of(heap, struct ion_carveout_heap, heap); - - switch (cmd) { - case ION_IOC_CLEAN_CACHES: - dmac_clean_range(vaddr, vaddr + length); - outer_cache_op = outer_clean_range; - break; - case ION_IOC_INV_CACHES: - dmac_inv_range(vaddr, vaddr + length); - outer_cache_op = outer_inv_range; - break; - case ION_IOC_CLEAN_INV_CACHES: - dmac_flush_range(vaddr, vaddr + length); - outer_cache_op = outer_flush_range; - break; - default: - return -EINVAL; + unsigned int size_to_vmap, total_size; + int i, j; + void *ptr = NULL; + ion_phys_addr_t buff_phys = buffer->priv_phys; + + if (!vaddr) { + /* + * Split the vmalloc space into smaller regions in + * order to clean and/or invalidate the cache. + */ + size_to_vmap = ((VMALLOC_END - VMALLOC_START)/8); + total_size = buffer->size; + + for (i = 0; i < total_size; i += size_to_vmap) { + size_to_vmap = min(size_to_vmap, total_size - i); + for (j = 0; j < 10 && size_to_vmap; ++j) { + ptr = ioremap(buff_phys, size_to_vmap); + if (ptr) { + switch (cmd) { + case ION_IOC_CLEAN_CACHES: + dmac_clean_range(ptr, + ptr + size_to_vmap); + outer_cache_op = + outer_clean_range; + break; + case ION_IOC_INV_CACHES: + dmac_inv_range(ptr, + ptr + size_to_vmap); + outer_cache_op = + outer_inv_range; + break; + case ION_IOC_CLEAN_INV_CACHES: + dmac_flush_range(ptr, + ptr + size_to_vmap); + outer_cache_op = + outer_flush_range; + break; + default: + return -EINVAL; + } + buff_phys += size_to_vmap; + break; + } else { + size_to_vmap >>= 1; + } + } + if (!ptr) { + pr_err("Couldn't io-remap the memory\n"); + return -EINVAL; + } + iounmap(ptr); + } + } else { + switch (cmd) { + case ION_IOC_CLEAN_CACHES: + dmac_clean_range(vaddr, vaddr + length); + outer_cache_op = outer_clean_range; + break; + case ION_IOC_INV_CACHES: + dmac_inv_range(vaddr, vaddr + length); + outer_cache_op = outer_inv_range; + break; + case ION_IOC_CLEAN_INV_CACHES: + dmac_flush_range(vaddr, vaddr + length); + outer_cache_op = outer_flush_range; + break; + default: + return -EINVAL; + } } if (carveout_heap->has_outer_cache) { @@ -269,7 +322,7 @@ int ion_carveout_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer, } static int ion_carveout_print_debug(struct ion_heap *heap, struct seq_file *s, - const struct rb_root *mem_map) + const struct list_head *mem_map) { struct ion_carveout_heap *carveout_heap = container_of(heap, struct ion_carveout_heap, heap); @@ -283,16 +336,14 @@ static int ion_carveout_print_debug(struct ion_heap *heap, struct seq_file *s, unsigned long size = carveout_heap->total_size; unsigned long end = base+size; unsigned long last_end = base; - struct rb_node *n; + struct mem_map_data *data; seq_printf(s, "\nMemory Map\n"); seq_printf(s, "%16.s %14.s %14.s %14.s\n", "client", "start address", "end address", "size (hex)"); - for (n = rb_first(mem_map); n; n = rb_next(n)) { - struct mem_map_data *data = - rb_entry(n, struct mem_map_data, node); + list_for_each_entry(data, mem_map, node) { const char *client_name = "(null)"; if (last_end < data->addr) { @@ -357,7 +408,7 @@ int ion_carveout_heap_map_iommu(struct ion_buffer *buffer, goto out1; } - sglist = vmalloc(sizeof(*sglist)); + sglist = kmalloc(sizeof(*sglist), GFP_KERNEL); if (!sglist) goto out1; @@ -381,13 +432,13 @@ int ion_carveout_heap_map_iommu(struct ion_buffer *buffer, if (ret) goto out2; } - vfree(sglist); + kfree(sglist); return ret; out2: iommu_unmap_range(domain, data->iova_addr, buffer->size); out1: - vfree(sglist); + kfree(sglist); msm_free_iova_address(data->iova_addr, domain_num, partition_num, data->mapped_size); diff --git a/drivers/gpu/ion/ion_cma_heap.c b/drivers/gpu/ion/ion_cma_heap.c index bef6b6f0..9c606b30 100644 --- a/drivers/gpu/ion/ion_cma_heap.c +++ b/drivers/gpu/ion/ion_cma_heap.c @@ -25,6 +25,7 @@ #include #include +#include /* for ion_heap_ops structure */ #include "ion_priv.h" @@ -115,6 +116,7 @@ static void ion_cma_free(struct ion_buffer *buffer) dev_dbg(dev, "Release buffer %p\n", buffer); /* release memory */ dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle); + sg_free_table(info->table); /* release sg table */ kfree(info->table); kfree(info); @@ -280,15 +282,30 @@ int ion_cma_cache_ops(struct ion_heap *heap, switch (cmd) { case ION_IOC_CLEAN_CACHES: - dmac_clean_range(vaddr, vaddr + length); + if (!vaddr) + dma_sync_sg_for_device(NULL, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_TO_DEVICE); + else + dmac_clean_range(vaddr, vaddr + length); outer_cache_op = outer_clean_range; break; case ION_IOC_INV_CACHES: - dmac_inv_range(vaddr, vaddr + length); + if (!vaddr) + dma_sync_sg_for_cpu(NULL, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_FROM_DEVICE); + else + dmac_inv_range(vaddr, vaddr + length); outer_cache_op = outer_inv_range; break; case ION_IOC_CLEAN_INV_CACHES: - dmac_flush_range(vaddr, vaddr + length); + if (!vaddr) { + dma_sync_sg_for_device(NULL, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_TO_DEVICE); + dma_sync_sg_for_cpu(NULL, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_FROM_DEVICE); + } else { + dmac_flush_range(vaddr, vaddr + length); + } outer_cache_op = outer_flush_range; break; default: diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c index 84ad9b0a..9ae79a86 100644 --- a/drivers/gpu/ion/ion_cp_heap.c +++ b/drivers/gpu/ion/ion_cp_heap.c @@ -2,7 +2,7 @@ * drivers/gpu/ion/ion_cp_heap.c * * Copyright (C) 2011 Google, Inc. - * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -15,20 +15,18 @@ * */ #include -#include + #include #include #include -#include +#include #include #include #include -#include #include #include #include #include -#include #include @@ -100,10 +98,7 @@ struct ion_cp_heap { int iommu_2x_map_domain; unsigned int has_outer_cache; atomic_t protect_cnt; - void *cpu_addr; - size_t heap_size; - dma_addr_t handle; - int cma; + int disallow_non_secure_allocation; }; enum { @@ -119,68 +114,6 @@ static int ion_cp_unprotect_mem(unsigned int phy_base, unsigned int size, unsigned int permission_type, int version, void *data); -static int allocate_heap_memory(struct ion_heap *heap) -{ - struct device *dev = heap->priv; - struct ion_cp_heap *cp_heap = - container_of(heap, struct ion_cp_heap, heap); - int ret; - int tries = 0; - - - if (cp_heap->cpu_addr) - return 0; - - while (!cp_heap->cpu_addr && (++tries < 500)) { - cp_heap->cpu_addr = dma_alloc_writecombine(dev, - cp_heap->heap_size, - &(cp_heap->handle), - 0); - if (!cp_heap->cpu_addr) - msleep(20); - } - - if (!cp_heap->cpu_addr) - goto out; - - cp_heap->base = cp_heap->handle; - - cp_heap->pool = gen_pool_create(12, -1); - if (!cp_heap->pool) - goto out_free; - - ret = gen_pool_add(cp_heap->pool, cp_heap->base, - cp_heap->heap_size, -1); - if (ret < 0) - goto out_pool; - - return 0; - -out_pool: - gen_pool_destroy(cp_heap->pool); -out_free: - dma_free_coherent(dev, cp_heap->heap_size, cp_heap->cpu_addr, - cp_heap->handle); -out: - return ION_CP_ALLOCATE_FAIL; -} - -static void free_heap_memory(struct ion_heap *heap) -{ - struct device *dev = heap->priv; - struct ion_cp_heap *cp_heap = - container_of(heap, struct ion_cp_heap, heap); - - /* release memory */ - dma_free_coherent(dev, cp_heap->heap_size, cp_heap->cpu_addr, - cp_heap->handle); - gen_pool_destroy(cp_heap->pool); - cp_heap->pool = NULL; - cp_heap->cpu_addr = 0; -} - - - /** * Get the total number of kernel mappings. * Must be called with heap->lock locked. @@ -202,12 +135,6 @@ static int ion_on_first_alloc(struct ion_heap *heap) if (ret_value) return 1; } - - if (cp_heap->cma) { - ret_value = allocate_heap_memory(heap); - if (ret_value) - return 1; - } return 0; } @@ -220,9 +147,6 @@ static void ion_on_last_free(struct ion_heap *heap) if (fmem_set_state(FMEM_T_STATE) != 0) pr_err("%s: unable to transition heap to T-state\n", __func__); - - if (cp_heap->cma) - free_heap_memory(heap); } /** @@ -238,9 +162,13 @@ static int ion_cp_protect(struct ion_heap *heap, int version, void *data) if (atomic_inc_return(&cp_heap->protect_cnt) == 1) { /* Make sure we are in C state when the heap is protected. */ - if (!cp_heap->allocated_bytes) - if (ion_on_first_alloc(heap)) + if (!cp_heap->allocated_bytes) { + ret_value = ion_on_first_alloc(heap); + if (ret_value) { + atomic_dec(&cp_heap->protect_cnt); goto out; + } + } ret_value = ion_cp_protect_mem(cp_heap->secure_base, cp_heap->secure_size, cp_heap->permission_type, @@ -262,7 +190,7 @@ static int ion_cp_protect(struct ion_heap *heap, int version, void *data) out: pr_debug("%s: protect count is %d\n", __func__, atomic_read(&cp_heap->protect_cnt)); - WARN_ON(atomic_read(&cp_heap->protect_cnt) < 0); + BUG_ON(atomic_read(&cp_heap->protect_cnt) < 0); return ret_value; } @@ -294,7 +222,7 @@ static void ion_cp_unprotect(struct ion_heap *heap, int version, void *data) } pr_debug("%s: protect count is %d\n", __func__, atomic_read(&cp_heap->protect_cnt)); - WARN_ON(atomic_read(&cp_heap->protect_cnt) < 0); + BUG_ON(atomic_read(&cp_heap->protect_cnt) < 0); } ion_phys_addr_t ion_cp_allocate(struct ion_heap *heap, @@ -304,6 +232,7 @@ ion_phys_addr_t ion_cp_allocate(struct ion_heap *heap, { unsigned long offset; unsigned long secure_allocation = flags & ION_SECURE; + unsigned long force_contig = flags & ION_FORCE_CONTIGUOUS; struct ion_cp_heap *cp_heap = container_of(heap, struct ion_cp_heap, heap); @@ -316,6 +245,14 @@ ion_phys_addr_t ion_cp_allocate(struct ion_heap *heap, return ION_CP_ALLOCATE_FAIL; } + if (!force_contig && !secure_allocation && + cp_heap->disallow_non_secure_allocation) { + mutex_unlock(&cp_heap->lock); + pr_debug("%s: non-secure allocation disallowed from this heap\n", + __func__); + return ION_CP_ALLOCATE_FAIL; + } + if (secure_allocation && (cp_heap->umap_count > 0 || cp_heap->kmap_cached_count > 0)) { mutex_unlock(&cp_heap->lock); @@ -500,7 +437,6 @@ static int ion_cp_request_region(struct ion_cp_heap *cp_heap) if (cp_heap->heap_request_region) ret_value = cp_heap->heap_request_region( cp_heap->bus_id); - return ret_value; } @@ -544,7 +480,6 @@ void *ion_cp_heap_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer) struct ion_cp_heap *cp_heap = container_of(heap, struct ion_cp_heap, heap); void *ret_value = NULL; - unsigned long start_phys = cp_heap->base; mutex_lock(&cp_heap->lock); if ((cp_heap->heap_protected == HEAP_NOT_PROTECTED) || @@ -559,9 +494,6 @@ void *ion_cp_heap_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer) if (cp_heap->reusable) { ret_value = ion_map_fmem_buffer(buffer, cp_heap->base, cp_heap->reserved_vrange, buffer->flags); - } else if (cp_heap->cma) { - ret_value = cp_heap->cpu_addr + - (buffer->priv_phys - start_phys); } else { if (ION_IS_CACHED(buffer->flags)) ret_value = ioremap_cached(buffer->priv_phys, @@ -580,7 +512,6 @@ void *ion_cp_heap_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer) ++cp_heap->kmap_uncached_count; } } - mutex_unlock(&cp_heap->lock); return ret_value; } @@ -593,7 +524,7 @@ void ion_cp_heap_unmap_kernel(struct ion_heap *heap, if (cp_heap->reusable) unmap_kernel_range((unsigned long)buffer->vaddr, buffer->size); - else if (!cp_heap->cma) + else __arm_iounmap(buffer->vaddr); buffer->vaddr = NULL; @@ -657,25 +588,77 @@ int ion_cp_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer, void *vaddr, unsigned int offset, unsigned int length, unsigned int cmd) { - void (*outer_cache_op)(phys_addr_t, phys_addr_t); + void (*outer_cache_op)(phys_addr_t, phys_addr_t) = NULL; struct ion_cp_heap *cp_heap = - container_of(heap, struct ion_cp_heap, heap); - - switch (cmd) { - case ION_IOC_CLEAN_CACHES: - dmac_clean_range(vaddr, vaddr + length); - outer_cache_op = outer_clean_range; - break; - case ION_IOC_INV_CACHES: - dmac_inv_range(vaddr, vaddr + length); - outer_cache_op = outer_inv_range; - break; - case ION_IOC_CLEAN_INV_CACHES: - dmac_flush_range(vaddr, vaddr + length); - outer_cache_op = outer_flush_range; - break; - default: - return -EINVAL; + container_of(heap, struct ion_cp_heap, heap); + unsigned int size_to_vmap, total_size; + int i, j; + void *ptr = NULL; + ion_phys_addr_t buff_phys = buffer->priv_phys; + + if (!vaddr) { + /* + * Split the vmalloc space into smaller regions in + * order to clean and/or invalidate the cache. + */ + size_to_vmap = (VMALLOC_END - VMALLOC_START)/8; + total_size = buffer->size; + for (i = 0; i < total_size; i += size_to_vmap) { + size_to_vmap = min(size_to_vmap, total_size - i); + for (j = 0; j < 10 && size_to_vmap; ++j) { + ptr = ioremap(buff_phys, size_to_vmap); + if (ptr) { + switch (cmd) { + case ION_IOC_CLEAN_CACHES: + dmac_clean_range(ptr, + ptr + size_to_vmap); + outer_cache_op = + outer_clean_range; + break; + case ION_IOC_INV_CACHES: + dmac_inv_range(ptr, + ptr + size_to_vmap); + outer_cache_op = + outer_inv_range; + break; + case ION_IOC_CLEAN_INV_CACHES: + dmac_flush_range(ptr, + ptr + size_to_vmap); + outer_cache_op = + outer_flush_range; + break; + default: + return -EINVAL; + } + buff_phys += size_to_vmap; + break; + } else { + size_to_vmap >>= 1; + } + } + if (!ptr) { + pr_err("Couldn't io-remap the memory\n"); + return -EINVAL; + } + iounmap(ptr); + } + } else { + switch (cmd) { + case ION_IOC_CLEAN_CACHES: + dmac_clean_range(vaddr, vaddr + length); + outer_cache_op = outer_clean_range; + break; + case ION_IOC_INV_CACHES: + dmac_inv_range(vaddr, vaddr + length); + outer_cache_op = outer_inv_range; + break; + case ION_IOC_CLEAN_INV_CACHES: + dmac_flush_range(vaddr, vaddr + length); + outer_cache_op = outer_flush_range; + break; + default: + return -EINVAL; + } } if (cp_heap->has_outer_cache) { @@ -686,7 +669,7 @@ int ion_cp_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer, } static int ion_cp_print_debug(struct ion_heap *heap, struct seq_file *s, - const struct rb_root *mem_map) + const struct list_head *mem_map) { unsigned long total_alloc; unsigned long total_size; @@ -716,16 +699,14 @@ static int ion_cp_print_debug(struct ion_heap *heap, struct seq_file *s, unsigned long size = cp_heap->total_size; unsigned long end = base+size; unsigned long last_end = base; - struct rb_node *n; + struct mem_map_data *data; seq_printf(s, "\nMemory Map\n"); seq_printf(s, "%16.s %14.s %14.s %14.s\n", "client", "start address", "end address", "size (hex)"); - for (n = rb_first(mem_map); n; n = rb_next(n)) { - struct mem_map_data *data = - rb_entry(n, struct mem_map_data, node); + list_for_each_entry(data, mem_map, node) { const char *client_name = "(null)"; if (last_end < data->addr) { @@ -1008,6 +989,14 @@ struct ion_heap *ion_cp_heap_create(struct ion_platform_heap *heap_data) mutex_init(&cp_heap->lock); + cp_heap->pool = gen_pool_create(12, -1); + if (!cp_heap->pool) + goto free_heap; + + cp_heap->base = heap_data->base; + ret = gen_pool_add(cp_heap->pool, cp_heap->base, heap_data->size, -1); + if (ret < 0) + goto destroy_pool; cp_heap->allocated_bytes = 0; cp_heap->umap_count = 0; @@ -1015,13 +1004,11 @@ struct ion_heap *ion_cp_heap_create(struct ion_platform_heap *heap_data) cp_heap->kmap_uncached_count = 0; cp_heap->total_size = heap_data->size; cp_heap->heap.ops = &cp_heap_ops; - cp_heap->heap.type = ION_HEAP_TYPE_CP; + cp_heap->heap.type = (enum ion_heap_type) ION_HEAP_TYPE_CP; cp_heap->heap_protected = HEAP_NOT_PROTECTED; - cp_heap->secure_base = heap_data->base; + cp_heap->secure_base = cp_heap->base; cp_heap->secure_size = heap_data->size; cp_heap->has_outer_cache = heap_data->has_outer_cache; - cp_heap->heap_size = heap_data->size; - atomic_set(&cp_heap->protect_cnt, 0); if (heap_data->extra_data) { struct ion_cp_heap_pdata *extra_data = @@ -1045,25 +1032,11 @@ struct ion_heap *ion_cp_heap_create(struct ion_platform_heap *heap_data) extra_data->iommu_map_all; cp_heap->iommu_2x_map_domain = extra_data->iommu_2x_map_domain; - cp_heap->cma = extra_data->is_cma; - } - - if (cp_heap->cma) { - cp_heap->pool = NULL; - cp_heap->cpu_addr = 0; - cp_heap->heap.priv = heap_data->priv; - } else { - cp_heap->pool = gen_pool_create(12, -1); - if (!cp_heap->pool) - goto free_heap; - - cp_heap->base = heap_data->base; - ret = gen_pool_add(cp_heap->pool, cp_heap->base, - heap_data->size, -1); - if (ret < 0) - goto destroy_pool; + cp_heap->disallow_non_secure_allocation = + extra_data->no_nonsecure_alloc; } + return &cp_heap->heap; destroy_pool: diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c index 2b5d24c6..98c1a8c3 100644 --- a/drivers/gpu/ion/ion_heap.c +++ b/drivers/gpu/ion/ion_heap.c @@ -18,12 +18,13 @@ #include #include #include "ion_priv.h" +#include struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) { struct ion_heap *heap = NULL; - switch (heap_data->type) { + switch ((int) heap_data->type) { case ION_HEAP_TYPE_SYSTEM_CONTIG: heap = ion_system_contig_heap_create(heap_data); break; @@ -68,7 +69,7 @@ void ion_heap_destroy(struct ion_heap *heap) if (!heap) return; - switch (heap->type) { + switch ((int) heap->type) { case ION_HEAP_TYPE_SYSTEM_CONTIG: ion_system_contig_heap_destroy(heap); break; diff --git a/drivers/gpu/ion/ion_iommu_heap.c b/drivers/gpu/ion/ion_iommu_heap.c index b94f1f53..49721fb6 100644 --- a/drivers/gpu/ion/ion_iommu_heap.c +++ b/drivers/gpu/ion/ion_iommu_heap.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2013, The Linux Foundation. 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 and @@ -12,56 +12,152 @@ */ #include #include -#include +#include #include +#include #include #include #include #include #include +#include #include "ion_priv.h" #include #include #include #include -#include struct ion_iommu_heap { struct ion_heap heap; unsigned int has_outer_cache; }; +/* + * We will attempt to allocate high-order pages and store those in an + * sg_list. However, some APIs expect an array of struct page * where + * each page is of size PAGE_SIZE. We use this extra structure to + * carry around an array of such pages (derived from the high-order + * pages with nth_page). + */ struct ion_iommu_priv_data { struct page **pages; + unsigned int pages_uses_vmalloc; int nrpages; unsigned long size; }; +#define MAX_VMAP_RETRIES 10 + +static const unsigned int orders[] = {8, 4, 0}; +static const int num_orders = ARRAY_SIZE(orders); + +struct page_info { + struct page *page; + unsigned int order; + struct list_head list; +}; + +static unsigned int order_to_size(int order) +{ + return PAGE_SIZE << order; +} + +static struct page_info *alloc_largest_available(unsigned long size, + unsigned int max_order) +{ + struct page *page; + struct page_info *info; + int i; + + for (i = 0; i < num_orders; i++) { + gfp_t gfp; + if (size < order_to_size(orders[i])) + continue; + if (max_order < orders[i]) + continue; + + gfp = __GFP_HIGHMEM; + + if (orders[i]) { + gfp |= __GFP_COMP | __GFP_NORETRY | + __GFP_NO_KSWAPD | __GFP_NOWARN; + } else { + gfp |= GFP_KERNEL; + } + page = alloc_pages(gfp, 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_iommu_heap_allocate(struct ion_heap *heap, struct ion_buffer *buffer, unsigned long size, unsigned long align, unsigned long flags) { int ret, i; + struct list_head pages_list; + struct page_info *info, *tmp_info; struct ion_iommu_priv_data *data = NULL; if (msm_use_iommu()) { struct scatterlist *sg; struct sg_table *table; - unsigned int i; + int j; + void *ptr = NULL; + unsigned int npages_to_vmap, total_pages, num_large_pages = 0; + unsigned long size_remaining = PAGE_ALIGN(size); + unsigned int max_order = orders[0]; + unsigned int page_tbl_size; data = kmalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; + INIT_LIST_HEAD(&pages_list); + while (size_remaining > 0) { + info = alloc_largest_available(size_remaining, + max_order); + if (!info) { + ret = -ENOMEM; + goto err_free_data; + } + list_add_tail(&info->list, &pages_list); + size_remaining -= order_to_size(info->order); + max_order = info->order; + num_large_pages++; + } + data->size = PFN_ALIGN(size); data->nrpages = data->size >> PAGE_SHIFT; - data->pages = kzalloc(sizeof(struct page *)*data->nrpages, - GFP_KERNEL); + data->pages_uses_vmalloc = 0; + page_tbl_size = sizeof(struct page *) * data->nrpages; + + if (page_tbl_size > SZ_8K) { + /* + * Do fallback to ensure we have a balance between + * performance and availability. + */ + data->pages = kmalloc(page_tbl_size, + __GFP_COMP | __GFP_NORETRY | + __GFP_NO_KSWAPD | __GFP_NOWARN); + if (!data->pages) { + data->pages = vmalloc(page_tbl_size); + data->pages_uses_vmalloc = 1; + } + } else { + data->pages = kmalloc(page_tbl_size, GFP_KERNEL); + } if (!data->pages) { ret = -ENOMEM; - goto err1; + goto err_free_data; } table = buffer->sg_table = @@ -71,17 +167,55 @@ static int ion_iommu_heap_allocate(struct ion_heap *heap, ret = -ENOMEM; goto err1; } - ret = sg_alloc_table(table, data->nrpages, GFP_KERNEL); + ret = sg_alloc_table(table, num_large_pages, GFP_KERNEL); if (ret) goto err2; - for_each_sg(table->sgl, sg, table->nents, i) { - data->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO); - if (!data->pages[i]) - goto err3; - - sg_set_page(sg, data->pages[i], PAGE_SIZE, 0); + i = 0; + sg = table->sgl; + list_for_each_entry_safe(info, tmp_info, &pages_list, list) { + struct page *page = info->page; + sg_set_page(sg, page, order_to_size(info->order), 0); sg_dma_address(sg) = sg_phys(sg); + sg = sg_next(sg); + for (j = 0; j < (1 << info->order); ++j) + data->pages[i++] = nth_page(page, j); + list_del(&info->list); + kfree(info); + } + + /* + * As an optimization, we omit __GFP_ZERO from + * alloc_page above and manually zero out all of the + * pages in one fell swoop here. To safeguard against + * insufficient vmalloc space, we only vmap + * `npages_to_vmap' at a time, starting with a + * conservative estimate of 1/8 of the total number of + * vmalloc pages available. Note that the `pages' + * array is composed of all 4K pages, irrespective of + * the size of the pages on the sg list. + */ + npages_to_vmap = ((VMALLOC_END - VMALLOC_START)/8) + >> PAGE_SHIFT; + total_pages = data->nrpages; + for (i = 0; i < total_pages; i += npages_to_vmap) { + npages_to_vmap = min(npages_to_vmap, total_pages - i); + for (j = 0; j < MAX_VMAP_RETRIES && npages_to_vmap; + ++j) { + ptr = vmap(&data->pages[i], npages_to_vmap, + VM_IOREMAP, pgprot_kernel); + if (ptr) + break; + else + npages_to_vmap >>= 1; + } + if (!ptr) { + pr_err("Couldn't vmap the pages for zeroing\n"); + ret = -ENOMEM; + goto err3; + } + memset(ptr, 0, npages_to_vmap * PAGE_SIZE); + vunmap(ptr); } if (!ION_IS_CACHED(flags)) @@ -101,29 +235,45 @@ static int ion_iommu_heap_allocate(struct ion_heap *heap, err2: kfree(buffer->sg_table); buffer->sg_table = 0; - - for (i = 0; i < data->nrpages; i++) { - if (data->pages[i]) - __free_page(data->pages[i]); - } - kfree(data->pages); err1: + if (data->pages_uses_vmalloc) + vfree(data->pages); + else + kfree(data->pages); +err_free_data: kfree(data); + + list_for_each_entry_safe(info, tmp_info, &pages_list, list) { + if (info->page) + __free_pages(info->page, info->order); + list_del(&info->list); + kfree(info); + } return ret; } static void ion_iommu_heap_free(struct ion_buffer *buffer) { - struct ion_iommu_priv_data *data = buffer->priv_virt; int i; + struct scatterlist *sg; + struct sg_table *table = buffer->sg_table; + struct ion_iommu_priv_data *data = buffer->priv_virt; + if (!table) + return; if (!data) return; - for (i = 0; i < data->nrpages; i++) - __free_page(data->pages[i]); + for_each_sg(table->sgl, sg, table->nents, i) + __free_pages(sg_page(sg), get_order(sg_dma_len(sg))); - kfree(data->pages); + sg_free_table(table); + kfree(table); + table = 0; + if (data->pages_uses_vmalloc) + vfree(data->pages); + else + kfree(data->pages); kfree(data); } @@ -137,7 +287,7 @@ void *ion_iommu_heap_map_kernel(struct ion_heap *heap, return NULL; if (!ION_IS_CACHED(buffer->flags)) - page_prot = pgprot_noncached(page_prot); + page_prot = pgprot_writecombine(page_prot); buffer->vaddr = vmap(data->pages, data->nrpages, VM_IOREMAP, page_prot); @@ -157,25 +307,34 @@ void ion_iommu_heap_unmap_kernel(struct ion_heap *heap, int ion_iommu_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, struct vm_area_struct *vma) { - struct ion_iommu_priv_data *data = buffer->priv_virt; + 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; - unsigned long curr_addr; - if (!data) - return -EINVAL; if (!ION_IS_CACHED(buffer->flags)) vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); - curr_addr = vma->vm_start; - for (i = 0; i < data->nrpages && curr_addr < vma->vm_end; i++) { - if (vm_insert_page(vma, curr_addr, data->pages[i])) { - /* - * This will fail the mmap which will - * clean up the vma space properly. - */ - return -EINVAL; + 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; } - curr_addr += PAGE_SIZE; + 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; } @@ -199,6 +358,14 @@ int ion_iommu_heap_map_iommu(struct ion_buffer *buffer, data->mapped_size = iova_length; extra = iova_length - buffer->size; + /* Use the biggest alignment to allow bigger IOMMU mappings. + * Use the first entry since the first entry will always be the + * biggest entry. To take advantage of bigger mapping sizes both the + * VA and PA addresses have to be aligned to the biggest size. + */ + if (buffer->sg_table->sgl->length > align) + align = buffer->sg_table->sgl->length; + ret = msm_allocate_iova_address(domain_num, partition_num, data->mapped_size, align, &data->iova_addr); @@ -277,15 +444,30 @@ static int ion_iommu_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer, switch (cmd) { case ION_IOC_CLEAN_CACHES: - dmac_clean_range(vaddr, vaddr + length); + if (!vaddr) + dma_sync_sg_for_device(NULL, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_TO_DEVICE); + else + dmac_clean_range(vaddr, vaddr + length); outer_cache_op = outer_clean_range; break; case ION_IOC_INV_CACHES: - dmac_inv_range(vaddr, vaddr + length); + if (!vaddr) + dma_sync_sg_for_cpu(NULL, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_FROM_DEVICE); + else + dmac_inv_range(vaddr, vaddr + length); outer_cache_op = outer_inv_range; break; case ION_IOC_CLEAN_INV_CACHES: - dmac_flush_range(vaddr, vaddr + length); + if (!vaddr) { + dma_sync_sg_for_device(NULL, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_TO_DEVICE); + dma_sync_sg_for_cpu(NULL, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_FROM_DEVICE); + } else { + dmac_flush_range(vaddr, vaddr + length); + } outer_cache_op = outer_flush_range; break; default: @@ -316,10 +498,6 @@ static struct sg_table *ion_iommu_heap_map_dma(struct ion_heap *heap, static void ion_iommu_heap_unmap_dma(struct ion_heap *heap, struct ion_buffer *buffer) { - if (buffer->sg_table) - sg_free_table(buffer->sg_table); - kfree(buffer->sg_table); - buffer->sg_table = 0; } static struct ion_heap_ops iommu_heap_ops = { diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h index d494f7a7..37ababc3 100644 --- a/drivers/gpu/ion/ion_priv.h +++ b/drivers/gpu/ion/ion_priv.h @@ -144,7 +144,7 @@ struct ion_heap_ops { unsigned long flags); void (*unmap_iommu)(struct ion_iommu_map *data); int (*print_debug)(struct ion_heap *heap, struct seq_file *s, - const struct rb_root *mem_map); + const struct list_head *mem_map); int (*secure_heap)(struct ion_heap *heap, int version, void *data); int (*unsecure_heap)(struct ion_heap *heap, int version, void *data); }; @@ -178,7 +178,7 @@ struct ion_heap { /** * struct mem_map_data - represents information about the memory map for a heap - * @node: rb node used to store in the tree of mem_map_data + * @node: list node used to store in the list of mem_map_data * @addr: start address of memory region. * @addr: end address of memory region. * @size: size of memory region @@ -186,7 +186,7 @@ struct ion_heap { * */ struct mem_map_data { - struct rb_node node; + struct list_head node; unsigned long addr; unsigned long addr_end; unsigned long size; diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c index 6cac7c59..50ec2ecf 100644 --- a/drivers/gpu/ion/ion_system_heap.c +++ b/drivers/gpu/ion/ion_system_heap.c @@ -27,6 +27,8 @@ #include "ion_priv.h" #include #include +#include +#include static atomic_t system_heap_allocated; static atomic_t system_contig_heap_allocated; @@ -183,15 +185,30 @@ int ion_system_heap_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer, switch (cmd) { case ION_IOC_CLEAN_CACHES: - dmac_clean_range(vaddr, vaddr + length); + if (!vaddr) + dma_sync_sg_for_device(NULL, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_TO_DEVICE); + else + dmac_clean_range(vaddr, vaddr + length); outer_cache_op = outer_clean_range; break; case ION_IOC_INV_CACHES: - dmac_inv_range(vaddr, vaddr + length); + if (!vaddr) + dma_sync_sg_for_cpu(NULL, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_FROM_DEVICE); + else + dmac_inv_range(vaddr, vaddr + length); outer_cache_op = outer_inv_range; break; case ION_IOC_CLEAN_INV_CACHES: - dmac_flush_range(vaddr, vaddr + length); + if (!vaddr) { + dma_sync_sg_for_device(NULL, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_TO_DEVICE); + dma_sync_sg_for_cpu(NULL, buffer->sg_table->sgl, + buffer->sg_table->nents, DMA_FROM_DEVICE); + } else { + dmac_flush_range(vaddr, vaddr + length); + } outer_cache_op = outer_flush_range; break; default: @@ -221,7 +238,7 @@ int ion_system_heap_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer, } static int ion_system_print_debug(struct ion_heap *heap, struct seq_file *s, - const struct rb_root *unused) + const struct list_head *unused) { seq_printf(s, "total bytes currently allocated: %lx\n", (unsigned long) atomic_read(&system_heap_allocated)); @@ -254,6 +271,14 @@ int ion_system_heap_map_iommu(struct ion_buffer *buffer, data->mapped_size = iova_length; extra = iova_length - buffer->size; + /* Use the biggest alignment to allow bigger IOMMU mappings. + * Use the first entry since the first entry will always be the + * biggest entry. To take advantage of bigger mapping sizes both the + * VA and PA addresses have to be aligned to the biggest size. + */ + if (table->sgl->length > align) + align = table->sgl->length; + ret = msm_allocate_iova_address(domain_num, partition_num, data->mapped_size, align, &data->iova_addr); @@ -432,7 +457,7 @@ int ion_system_contig_heap_cache_ops(struct ion_heap *heap, static int ion_system_contig_print_debug(struct ion_heap *heap, struct seq_file *s, - const struct rb_root *unused) + const struct list_head *unused) { seq_printf(s, "total bytes currently allocated: %lx\n", (unsigned long) atomic_read(&system_contig_heap_allocated)); @@ -482,7 +507,7 @@ int ion_system_contig_heap_map_iommu(struct ion_buffer *buffer, } page = virt_to_page(buffer->vaddr); - sglist = vmalloc(sizeof(*sglist)); + sglist = kmalloc(sizeof(*sglist), GFP_KERNEL); if (!sglist) goto out1; @@ -504,13 +529,13 @@ int ion_system_contig_heap_map_iommu(struct ion_buffer *buffer, if (ret) goto out2; } - vfree(sglist); + kfree(sglist); return ret; out2: iommu_unmap_range(domain, data->iova_addr, buffer->size); out1: - vfree(sglist); + kfree(sglist); msm_free_iova_address(data->iova_addr, domain_num, partition_num, data->mapped_size); out: diff --git a/drivers/gpu/ion/msm/ion_cp_common.h b/drivers/gpu/ion/msm/ion_cp_common.h index 0c08d86b..eec66e6f 100644 --- a/drivers/gpu/ion/msm/ion_cp_common.h +++ b/drivers/gpu/ion/msm/ion_cp_common.h @@ -15,7 +15,7 @@ #define ION_CP_COMMON_H #include -#include +#include #define ION_CP_V1 1 #define ION_CP_V2 2 diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c index 0507dcd5..d896391c 100644 --- a/drivers/gpu/ion/msm/msm_ion.c +++ b/drivers/gpu/ion/msm/msm_ion.c @@ -13,20 +13,96 @@ #include #include -#include +#include #include #include #include -#include +#include +#include +#include +#include +#include +#include #include #include #include "../ion_priv.h" #include "ion_cp_common.h" +#define ION_COMPAT_STR "qcom,msm-ion" +#define ION_COMPAT_MEM_RESERVE_STR "qcom,msm-ion-reserve" + static struct ion_device *idev; static int num_heaps; static struct ion_heap **heaps; +struct ion_heap_desc { + unsigned int id; + enum ion_heap_type type; + const char *name; + unsigned int permission_type; +}; + + +static struct ion_heap_desc ion_heap_meta[] = { + { + .id = ION_SYSTEM_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM, + .name = ION_VMALLOC_HEAP_NAME, + }, + { + .id = ION_SYSTEM_CONTIG_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM_CONTIG, + .name = ION_KMALLOC_HEAP_NAME, + }, + { + .id = ION_CP_MM_HEAP_ID, + .type = ION_HEAP_TYPE_CP, + .name = ION_MM_HEAP_NAME, + .permission_type = IPT_TYPE_MM_CARVEOUT, + }, + { + .id = ION_MM_FIRMWARE_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_MM_FIRMWARE_HEAP_NAME, + }, + { + .id = ION_CP_MFC_HEAP_ID, + .type = ION_HEAP_TYPE_CP, + .name = ION_MFC_HEAP_NAME, + .permission_type = IPT_TYPE_MFC_SHAREDMEM, + }, + { + .id = ION_SF_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_SF_HEAP_NAME, + }, + { + .id = ION_IOMMU_HEAP_ID, + .type = ION_HEAP_TYPE_IOMMU, + .name = ION_IOMMU_HEAP_NAME, + }, + { + .id = ION_QSECOM_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_QSECOM_HEAP_NAME, + }, + { + .id = ION_AUDIO_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_AUDIO_HEAP_NAME, + }, + { + .id = ION_CP_WB_HEAP_ID, + .type = ION_HEAP_TYPE_CP, + .name = ION_WB_HEAP_NAME, + }, + { + .id = ION_CAMERA_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_CAMERA_HEAP_NAME, + }, +}; + struct ion_client *msm_ion_client_create(unsigned int heap_mask, const char *name) { @@ -101,22 +177,9 @@ static void ion_set_base_address(struct ion_platform_heap *heap, struct ion_co_heap_pdata *co_heap_data, struct ion_cp_heap_pdata *cp_data) { - if (cp_data->reusable) { - const struct fmem_data *fmem_info = fmem_get_info(); - - if (!fmem_info) { - pr_err("fmem info pointer NULL!\n"); - BUG(); - } - - heap->base = fmem_info->phys - fmem_info->reserved_size_low; - cp_data->virt_addr = fmem_info->virt; - pr_info("ION heap %s using FMEM\n", shared_heap->name); - } else { - heap->base = msm_ion_get_base(heap->size + shared_heap->size, - shared_heap->memory_type, - co_heap_data->align); - } + heap->base = msm_ion_get_base(heap->size + shared_heap->size, + shared_heap->memory_type, + co_heap_data->align); if (heap->base) { shared_heap->base = heap->base + heap->size; cp_data->secure_base = heap->base; @@ -142,15 +205,6 @@ static void allocate_co_memory(struct ion_platform_heap *heap, struct ion_cp_heap_pdata *cp_data = (struct ion_cp_heap_pdata *) shared_heap->extra_data; if (cp_data->fixed_position == FIXED_MIDDLE) { - const struct fmem_data *fmem_info = - fmem_get_info(); - - if (!fmem_info) { - pr_err("fmem info pointer NULL!\n"); - BUG(); - } - - cp_data->virt_addr = fmem_info->virt; if (!cp_data->secure_base) { cp_data->secure_base = heap->base; cp_data->secure_size = @@ -192,7 +246,7 @@ static void msm_ion_allocate(struct ion_platform_heap *heap) if (!heap->base && heap->extra_data) { unsigned int align = 0; - switch (heap->type) { + switch ((int) heap->type) { case ION_HEAP_TYPE_CARVEOUT: align = ((struct ion_co_heap_pdata *) heap->extra_data)->align; @@ -202,17 +256,6 @@ static void msm_ion_allocate(struct ion_platform_heap *heap) struct ion_cp_heap_pdata *data = (struct ion_cp_heap_pdata *) heap->extra_data; - if (data->reusable) { - const struct fmem_data *fmem_info = - fmem_get_info(); - heap->base = fmem_info->phys; - data->virt_addr = fmem_info->virt; - pr_info("ION heap %s using FMEM\n", heap->name); - } else if (data->mem_is_fmem) { - const struct fmem_data *fmem_info = - fmem_get_info(); - heap->base = fmem_info->phys + fmem_info->size; - } align = data->align; break; } @@ -269,11 +312,339 @@ static void check_for_heap_overlap(const struct ion_platform_heap heap_list[], } } +static int msm_init_extra_data(struct ion_platform_heap *heap, + const struct ion_heap_desc *heap_desc) +{ + int ret = 0; + + switch ((int) heap->type) { + case ION_HEAP_TYPE_CP: + { + heap->extra_data = kzalloc(sizeof(struct ion_cp_heap_pdata), + GFP_KERNEL); + if (!heap->extra_data) { + ret = -ENOMEM; + } else { + struct ion_cp_heap_pdata *extra = heap->extra_data; + extra->permission_type = heap_desc->permission_type; + } + break; + } + case ION_HEAP_TYPE_CARVEOUT: + { + heap->extra_data = kzalloc(sizeof(struct ion_co_heap_pdata), + GFP_KERNEL); + if (!heap->extra_data) + ret = -ENOMEM; + break; + } + default: + heap->extra_data = 0; + break; + } + return ret; +} + +static int msm_ion_populate_heap(struct ion_platform_heap *heap) +{ + unsigned int i; + int ret = -EINVAL; + unsigned int len = ARRAY_SIZE(ion_heap_meta); + for (i = 0; i < len; ++i) { + if (ion_heap_meta[i].id == heap->id) { + heap->name = ion_heap_meta[i].name; + heap->type = ion_heap_meta[i].type; + ret = msm_init_extra_data(heap, &ion_heap_meta[i]); + break; + } + } + if (ret) + pr_err("%s: Unable to populate heap, error: %d", __func__, ret); + return ret; +} + +static void free_pdata(const struct ion_platform_data *pdata) +{ + unsigned int i; + for (i = 0; i < pdata->nr; ++i) + kfree(pdata->heaps[i].extra_data); + kfree(pdata); +} + +static int memtype_to_ion_memtype[] = { + [MEMTYPE_SMI_KERNEL] = ION_SMI_TYPE, + [MEMTYPE_SMI] = ION_SMI_TYPE, + [MEMTYPE_EBI0] = ION_EBI_TYPE, + [MEMTYPE_EBI1] = ION_EBI_TYPE, +}; + +static void msm_ion_get_heap_align(struct device_node *node, + struct ion_platform_heap *heap) +{ + unsigned int val; + + int ret = of_property_read_u32(node, "qcom,heap-align", &val); + if (!ret) { + switch ((int) heap->type) { + case ION_HEAP_TYPE_CP: + { + struct ion_cp_heap_pdata *extra = + heap->extra_data; + extra->align = val; + break; + } + case ION_HEAP_TYPE_CARVEOUT: + { + struct ion_co_heap_pdata *extra = + heap->extra_data; + extra->align = val; + break; + } + default: + pr_err("ION-heap %s: Cannot specify alignment for this type of heap\n", + heap->name); + break; + } + } +} + +static int msm_ion_get_heap_size(struct device_node *node, + struct ion_platform_heap *heap) +{ + unsigned int val; + int ret = 0; + const char *memory_name_prop; + + ret = of_property_read_u32(node, "qcom,memory-reservation-size", &val); + if (!ret) { + heap->size = val; + ret = of_property_read_string(node, + "qcom,memory-reservation-type", + &memory_name_prop); + + if (!ret && memory_name_prop) { + val = msm_get_memory_type_from_name(memory_name_prop); + if (val < 0) { + ret = -EINVAL; + goto out; + } + heap->memory_type = memtype_to_ion_memtype[val]; + } + if (heap->size && (ret || !memory_name_prop)) { + pr_err("%s: Need to specify reservation type\n", + __func__); + ret = -EINVAL; + } + } else { + ret = 0; + } +out: + return ret; +} + + +static void msm_ion_get_heap_adjacent(struct device_node *node, + struct ion_platform_heap *heap) +{ + unsigned int val; + int ret = of_property_read_u32(node, "qcom,heap-adjacent", &val); + if (!ret) { + switch (heap->type) { + case ION_HEAP_TYPE_CARVEOUT: + { + struct ion_co_heap_pdata *extra = heap->extra_data; + extra->adjacent_mem_id = val; + break; + } + default: + pr_err("ION-heap %s: Cannot specify adjcent mem id for this type of heap\n", + heap->name); + break; + } + } else { + switch (heap->type) { + case ION_HEAP_TYPE_CARVEOUT: + { + struct ion_co_heap_pdata *extra = heap->extra_data; + extra->adjacent_mem_id = INVALID_HEAP_ID; + break; + } + default: + break; + } + } +} + +static struct ion_platform_data *msm_ion_parse_dt( + const struct device_node *dt_node) +{ + struct ion_platform_data *pdata = 0; + struct device_node *node; + uint32_t val = 0; + int ret = 0; + uint32_t num_heaps = 0; + int idx = 0; + + for_each_child_of_node(dt_node, node) + num_heaps++; + + if (!num_heaps) + return ERR_PTR(-EINVAL); + + pdata = kzalloc(sizeof(struct ion_platform_data) + + num_heaps*sizeof(struct ion_platform_heap), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->nr = num_heaps; + + for_each_child_of_node(dt_node, node) { + /** + * TODO: Replace this with of_get_address() when this patch + * gets merged: http:// + * permalink.gmane.org/gmane.linux.drivers.devicetree/18614 + */ + ret = of_property_read_u32(node, "reg", &val); + if (ret) { + pr_err("%s: Unable to find reg key", __func__); + goto free_heaps; + } + pdata->heaps[idx].id = val; + + ret = msm_ion_populate_heap(&pdata->heaps[idx]); + if (ret) + goto free_heaps; + + msm_ion_get_heap_align(node, &pdata->heaps[idx]); + + ret = msm_ion_get_heap_size(node, &pdata->heaps[idx]); + if (ret) + goto free_heaps; + + msm_ion_get_heap_adjacent(node, &pdata->heaps[idx]); + + ++idx; + } + return pdata; + +free_heaps: + free_pdata(pdata); + return ERR_PTR(ret); +} + +static int check_vaddr_bounds(unsigned long start, unsigned long end) +{ + struct mm_struct *mm = current->active_mm; + struct vm_area_struct *vma; + int ret = 1; + + if (end < start) + goto out; + + down_read(&mm->mmap_sem); + vma = find_vma(mm, start); + if (vma && vma->vm_start < end) { + if (start < vma->vm_start) + goto out_up; + if (end > vma->vm_end) + goto out_up; + ret = 0; + } + +out_up: + up_read(&mm->mmap_sem); +out: + return ret; +} + +static long msm_ion_custom_ioctl(struct ion_client *client, + unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case ION_IOC_CLEAN_CACHES: + case ION_IOC_INV_CACHES: + case ION_IOC_CLEAN_INV_CACHES: + { + struct ion_flush_data data; + unsigned long start, end; + struct ion_handle *handle = NULL; + int ret; + + if (copy_from_user(&data, (void __user *)arg, + sizeof(struct ion_flush_data))) + return -EFAULT; + + start = (unsigned long) data.vaddr; + end = (unsigned long) data.vaddr + data.length; + + if (start && check_vaddr_bounds(start, end)) { + pr_err("%s: virtual address %p is out of bounds\n", + __func__, data.vaddr); + return -EINVAL; + } + + if (!data.handle) { + handle = ion_import_dma_buf(client, data.fd); + if (IS_ERR(handle)) { + pr_info("%s: Could not import handle: %d\n", + __func__, (int)handle); + return -EINVAL; + } + } + + ret = ion_do_cache_op(client, + data.handle ? data.handle : handle, + data.vaddr, data.offset, data.length, + cmd); + + if (!data.handle) + ion_free(client, handle); + + if (ret < 0) + return ret; + break; + + } + case ION_IOC_GET_FLAGS: + { + struct ion_flag_data data; + int ret; + if (copy_from_user(&data, (void __user *)arg, + sizeof(struct ion_flag_data))) + return -EFAULT; + + ret = ion_handle_get_flags(client, data.handle, &data.flags); + if (ret < 0) + return ret; + if (copy_to_user((void __user *)arg, &data, + sizeof(struct ion_flag_data))) + return -EFAULT; + break; + } + default: + return -ENOTTY; + } + return 0; +} + static int msm_ion_probe(struct platform_device *pdev) { - struct ion_platform_data *pdata = pdev->dev.platform_data; - int err; + struct ion_platform_data *pdata; + unsigned int pdata_needs_to_be_freed; + int err = -1; int i; + if (pdev->dev.of_node) { + pdata = msm_ion_parse_dt(pdev->dev.of_node); + if (IS_ERR(pdata)) { + err = PTR_ERR(pdata); + goto out; + } + pdata_needs_to_be_freed = 1; + } else { + pdata = pdev->dev.platform_data; + pdata_needs_to_be_freed = 0; + } num_heaps = pdata->nr; @@ -284,7 +655,7 @@ static int msm_ion_probe(struct platform_device *pdev) goto out; } - idev = ion_device_create(NULL); + idev = ion_device_create(msm_ion_custom_ioctl); if (IS_ERR_OR_NULL(idev)) { err = PTR_ERR(idev); goto freeheaps; @@ -315,6 +686,8 @@ static int msm_ion_probe(struct platform_device *pdev) ion_device_add_heap(idev, heaps[i]); } + if (pdata_needs_to_be_freed) + free_pdata(pdata); check_for_heap_overlap(pdata->heaps, num_heaps); platform_set_drvdata(pdev, idev); @@ -322,6 +695,8 @@ static int msm_ion_probe(struct platform_device *pdev) freeheaps: kfree(heaps); + if (pdata_needs_to_be_freed) + free_pdata(pdata); out: return err; } @@ -339,10 +714,19 @@ static int msm_ion_remove(struct platform_device *pdev) return 0; } +static struct of_device_id msm_ion_match_table[] = { + {.compatible = ION_COMPAT_STR}, + {}, +}; +EXPORT_COMPAT(ION_COMPAT_MEM_RESERVE_STR); + static struct platform_driver msm_ion_driver = { .probe = msm_ion_probe, .remove = msm_ion_remove, - .driver = { .name = "ion-msm" } + .driver = { + .name = "ion-msm", + .of_match_table = msm_ion_match_table, + }, }; static int __init msm_ion_init(void) diff --git a/drivers/gpu/msm/Kconfig b/drivers/gpu/msm/Kconfig new file mode 100644 index 00000000..ba63fbcb --- /dev/null +++ b/drivers/gpu/msm/Kconfig @@ -0,0 +1,98 @@ +config MSM_KGSL + tristate "MSM 3D Graphics driver" + default n + depends on ARCH_MSM && !ARCH_MSM7X00A && !ARCH_MSM7X25 + select GENERIC_ALLOCATOR + select FW_LOADER + ---help--- + 3D graphics driver. Required to use hardware accelerated + OpenGL ES 2.0 and 1.1. + +config MSM_KGSL_CFF_DUMP + bool "Enable KGSL Common File Format (CFF) Dump Feature [Use with caution]" + default n + depends on MSM_KGSL + select RELAY + ---help--- + This is an analysis and diagnostic feature only, and should only be + turned on during KGSL GPU diagnostics and will slow down the KGSL + performance sigificantly, hence *do not use in production builds*. + When enabled, CFF Dump is on at boot. It can be turned off at runtime + via 'echo 0 > /d/kgsl/cff_dump'. The log can be captured via + /d/kgsl-cff/cpu[0|1]. + +config MSM_KGSL_CFF_DUMP_NO_CONTEXT_MEM_DUMP + bool "When selected will disable KGSL CFF Dump for context switches" + default n + depends on MSM_KGSL_CFF_DUMP + ---help--- + Dumping all the memory for every context switch can produce quite + huge log files, to reduce this, turn this feature on. + +config MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL + bool "Disable human readable CP_STAT fields in post-mortem dump" + default n + depends on MSM_KGSL + ---help--- + For a more compact kernel log the human readable output of + CP_STAT can be turned off with this option. + +config MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP + bool "Disable dumping current IB1 and IB2 in post-mortem dump" + default n + depends on MSM_KGSL + ---help--- + For a more compact kernel log the IB1 and IB2 embedded dump + can be turned off with this option. Some IB dumps take up + so much space that vital other information gets cut from the + post-mortem dump. + +config MSM_KGSL_PSTMRTMDMP_RB_HEX + bool "Use hex version for ring-buffer in post-mortem dump" + default n + depends on MSM_KGSL + ---help--- + Use hex version for the ring-buffer in the post-mortem dump, instead + of the human readable version. + +config MSM_KGSL_2D + tristate "MSM 2D graphics driver. Required for OpenVG" + default y + depends on MSM_KGSL && !ARCH_MSM7X27 && !ARCH_MSM7X27A && !(ARCH_QSD8X50 && !MSM_SOC_REV_A) + +config MSM_KGSL_DRM + bool "Build a DRM interface for the MSM_KGSL driver" + depends on MSM_KGSL && DRM + +config KGSL_PER_PROCESS_PAGE_TABLE + bool "Enable Per Process page tables for the KGSL driver" + default n + depends on !MSM_KGSL_DRM + ---help--- + The MMU will use per process pagetables when enabled. + +config MSM_KGSL_PAGE_TABLE_SIZE + hex "Size of pagetables" + default 0xFFF0000 + ---help--- + Sets the pagetable size used by the MMU. The max value + is 0xFFF0000 or (256M - 64K). + +config MSM_KGSL_PAGE_TABLE_COUNT + int "Minimum of concurrent pagetables to support" + default 8 + depends on KGSL_PER_PROCESS_PAGE_TABLE + ---help--- + Specify the number of pagetables to allocate at init time + This is the number of concurrent processes that are guaranteed to + to run at any time. Additional processes can be created dynamically + assuming there is enough contiguous memory to allocate the pagetable. + +config MSM_KGSL_MMU_PAGE_FAULT + bool "Force the GPU MMU to page fault for unmapped regions" + default y + +config MSM_KGSL_DISABLE_SHADOW_WRITES + bool "Disable register shadow writes for context switches" + default n + depends on MSM_KGSL diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile index dfc4b8da..fec53633 100644 --- a/drivers/gpu/msm/Makefile +++ b/drivers/gpu/msm/Makefile @@ -47,5 +47,3 @@ msm_z180-objs = $(msm_z180-y) obj-$(CONFIG_MSM_KGSL) += msm_kgsl_core.o obj-$(CONFIG_MSM_KGSL) += msm_adreno.o obj-$(CONFIG_MSM_KGSL_2D) += msm_z180.o - -CFLAGS_adreno.o += -Wframe-larger-than=2048 diff --git a/drivers/gpu/msm/a2xx_reg.h b/drivers/gpu/msm/a2xx_reg.h index c70c4eb8..e1e2c153 100644 --- a/drivers/gpu/msm/a2xx_reg.h +++ b/drivers/gpu/msm/a2xx_reg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2012,2014 The Linux Foundation. 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 and @@ -183,6 +183,7 @@ union reg_cp_rb_cntl { #define SQ_INT_CNTL__VS_WATCHDOG_MASK 0x00000002L #define RBBM_INT_CNTL__RDERR_INT_MASK 0x00000001L +#define RBBM_INT_CNTL__PROTECT_INT_MASK 0x00100000L #define RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK 0x00000002L #define RBBM_INT_CNTL__GUI_IDLE_INT_MASK 0x00080000L @@ -291,6 +292,10 @@ union reg_cp_rb_cntl { #define REG_RBBM_PERFCOUNTER1_HI 0x0398 #define REG_RBBM_PERFCOUNTER1_LO 0x0397 +#define REG_SQ_PERFCOUNTER3_SELECT 0x0DCB +#define REG_SQ_PERFCOUNTER3_LO 0x0DD2 +#define REG_SQ_PERFCOUNTER3_HI 0x0DD3 + #define REG_MASTER_INT_SIGNAL 0x03B7 #define REG_PA_CL_VPORT_XSCALE 0x210F @@ -332,6 +337,24 @@ union reg_cp_rb_cntl { #define REG_RBBM_SOFT_RESET 0x003C #define REG_RBBM_STATUS 0x05D0 +/*A2XX Protection */ +#define REG_RBBM_PROTECT_0 0x0140 +#define REG_RBBM_PROTECT_1 0x0141 +#define REG_RBBM_PROTECT_2 0x0142 +#define REG_RBBM_PROTECT_3 0x0143 +#define REG_RBBM_PROTECT_4 0x0144 +#define REG_RBBM_PROTECT_5 0x0145 +#define REG_RBBM_PROTECT_6 0x0146 +#define REG_RBBM_PROTECT_7 0x0147 +#define REG_RBBM_PROTECT_8 0x0148 +#define REG_RBBM_PROTECT_9 0x0149 +#define REG_RBBM_PROTECT_A 0x014A +#define REG_RBBM_PROTECT_B 0x014B +#define REG_RBBM_PROTECT_C 0x014C +#define REG_RBBM_PROTECT_D 0x014D +#define REG_RBBM_PROTECT_E 0x014E +#define REG_RBBM_PROTECT_F 0x014F + #define REG_RB_COLORCONTROL 0x2202 #define REG_RB_COLOR_DEST_MASK 0x2326 #define REG_RB_COLOR_MASK 0x2104 @@ -401,6 +424,7 @@ union reg_cp_rb_cntl { #define REG_A225_GRAS_UCP0X 0x2340 #define REG_A225_GRAS_UCP5W 0x2357 #define REG_A225_GRAS_UCP_ENABLED 0x2360 +#define REG_VSC_BINNING_ENABLE 0x0C00 /* Debug registers used by snapshot */ #define REG_PA_SU_DEBUG_CNTL 0x0C80 diff --git a/drivers/gpu/msm/a3xx_reg.h b/drivers/gpu/msm/a3xx_reg.h index be9f3ace..21d47595 100644 --- a/drivers/gpu/msm/a3xx_reg.h +++ b/drivers/gpu/msm/a3xx_reg.h @@ -66,15 +66,103 @@ #define A3XX_RBBM_INT_0_MASK 0x063 #define A3XX_RBBM_INT_0_STATUS 0x064 #define A3XX_RBBM_PERFCTR_CTL 0x80 +#define A3XX_RBBM_PERFCTR_LOAD_CMD0 0x81 +#define A3XX_RBBM_PERFCTR_LOAD_CMD1 0x82 +#define A3XX_RBBM_PERFCTR_LOAD_VALUE_LO 0x84 +#define A3XX_RBBM_PERFCTR_LOAD_VALUE_HI 0x85 +#define A3XX_RBBM_PERFCOUNTER0_SELECT 0x86 +#define A3XX_RBBM_PERFCOUNTER1_SELECT 0x87 #define A3XX_RBBM_GPU_BUSY_MASKED 0x88 +#define A3XX_RBBM_PERFCTR_CP_0_LO 0x90 +#define A3XX_RBBM_PERFCTR_CP_0_HI 0x91 +#define A3XX_RBBM_PERFCTR_RBBM_0_LO 0x92 +#define A3XX_RBBM_PERFCTR_RBBM_0_HI 0x93 +#define A3XX_RBBM_PERFCTR_RBBM_1_LO 0x94 +#define A3XX_RBBM_PERFCTR_RBBM_1_HI 0x95 +#define A3XX_RBBM_PERFCTR_PC_0_LO 0x96 +#define A3XX_RBBM_PERFCTR_PC_0_HI 0x97 +#define A3XX_RBBM_PERFCTR_PC_1_LO 0x98 +#define A3XX_RBBM_PERFCTR_PC_1_HI 0x99 +#define A3XX_RBBM_PERFCTR_PC_2_LO 0x9A +#define A3XX_RBBM_PERFCTR_PC_2_HI 0x9B +#define A3XX_RBBM_PERFCTR_PC_3_LO 0x9C +#define A3XX_RBBM_PERFCTR_PC_3_HI 0x9D +#define A3XX_RBBM_PERFCTR_VFD_0_LO 0x9E +#define A3XX_RBBM_PERFCTR_VFD_0_HI 0x9F +#define A3XX_RBBM_PERFCTR_VFD_1_LO 0xA0 +#define A3XX_RBBM_PERFCTR_VFD_1_HI 0xA1 +#define A3XX_RBBM_PERFCTR_HLSQ_0_LO 0xA2 +#define A3XX_RBBM_PERFCTR_HLSQ_0_HI 0xA3 +#define A3XX_RBBM_PERFCTR_HLSQ_1_LO 0xA4 +#define A3XX_RBBM_PERFCTR_HLSQ_1_HI 0xA5 +#define A3XX_RBBM_PERFCTR_HLSQ_2_LO 0xA6 +#define A3XX_RBBM_PERFCTR_HLSQ_2_HI 0xA7 +#define A3XX_RBBM_PERFCTR_HLSQ_3_LO 0xA8 +#define A3XX_RBBM_PERFCTR_HLSQ_3_HI 0xA9 +#define A3XX_RBBM_PERFCTR_HLSQ_4_LO 0xAA +#define A3XX_RBBM_PERFCTR_HLSQ_4_HI 0xAB +#define A3XX_RBBM_PERFCTR_HLSQ_5_LO 0xAC +#define A3XX_RBBM_PERFCTR_HLSQ_5_HI 0xAD +#define A3XX_RBBM_PERFCTR_VPC_0_LO 0xAE +#define A3XX_RBBM_PERFCTR_VPC_0_HI 0xAF +#define A3XX_RBBM_PERFCTR_VPC_1_LO 0xB0 +#define A3XX_RBBM_PERFCTR_VPC_1_HI 0xB1 +#define A3XX_RBBM_PERFCTR_TSE_0_LO 0xB2 +#define A3XX_RBBM_PERFCTR_TSE_0_HI 0xB3 +#define A3XX_RBBM_PERFCTR_TSE_1_LO 0xB4 +#define A3XX_RBBM_PERFCTR_TSE_1_HI 0xB5 +#define A3XX_RBBM_PERFCTR_RAS_0_LO 0xB6 +#define A3XX_RBBM_PERFCTR_RAS_0_HI 0xB7 +#define A3XX_RBBM_PERFCTR_RAS_1_LO 0xB8 +#define A3XX_RBBM_PERFCTR_RAS_1_HI 0xB9 +#define A3XX_RBBM_PERFCTR_UCHE_0_LO 0xBA +#define A3XX_RBBM_PERFCTR_UCHE_0_HI 0xBB +#define A3XX_RBBM_PERFCTR_UCHE_1_LO 0xBC +#define A3XX_RBBM_PERFCTR_UCHE_1_HI 0xBD +#define A3XX_RBBM_PERFCTR_UCHE_2_LO 0xBE +#define A3XX_RBBM_PERFCTR_UCHE_2_HI 0xBF +#define A3XX_RBBM_PERFCTR_UCHE_3_LO 0xC0 +#define A3XX_RBBM_PERFCTR_UCHE_3_HI 0xC1 +#define A3XX_RBBM_PERFCTR_UCHE_4_LO 0xC2 +#define A3XX_RBBM_PERFCTR_UCHE_4_HI 0xC3 +#define A3XX_RBBM_PERFCTR_UCHE_5_LO 0xC4 +#define A3XX_RBBM_PERFCTR_UCHE_5_HI 0xC5 +#define A3XX_RBBM_PERFCTR_TP_0_LO 0xC6 +#define A3XX_RBBM_PERFCTR_TP_0_HI 0xC7 +#define A3XX_RBBM_PERFCTR_TP_1_LO 0xC8 +#define A3XX_RBBM_PERFCTR_TP_1_HI 0xC9 +#define A3XX_RBBM_PERFCTR_TP_2_LO 0xCA +#define A3XX_RBBM_PERFCTR_TP_2_HI 0xCB +#define A3XX_RBBM_PERFCTR_TP_3_LO 0xCC +#define A3XX_RBBM_PERFCTR_TP_3_HI 0xCD +#define A3XX_RBBM_PERFCTR_TP_4_LO 0xCE +#define A3XX_RBBM_PERFCTR_TP_4_HI 0xCF +#define A3XX_RBBM_PERFCTR_TP_5_LO 0xD0 +#define A3XX_RBBM_PERFCTR_TP_5_HI 0xD1 +#define A3XX_RBBM_PERFCTR_SP_0_LO 0xD2 +#define A3XX_RBBM_PERFCTR_SP_0_HI 0xD3 +#define A3XX_RBBM_PERFCTR_SP_1_LO 0xD4 +#define A3XX_RBBM_PERFCTR_SP_1_HI 0xD5 +#define A3XX_RBBM_PERFCTR_SP_2_LO 0xD6 +#define A3XX_RBBM_PERFCTR_SP_2_HI 0xD7 +#define A3XX_RBBM_PERFCTR_SP_3_LO 0xD8 +#define A3XX_RBBM_PERFCTR_SP_3_HI 0xD9 +#define A3XX_RBBM_PERFCTR_SP_4_LO 0xDA +#define A3XX_RBBM_PERFCTR_SP_4_HI 0xDB #define A3XX_RBBM_PERFCTR_SP_5_LO 0xDC #define A3XX_RBBM_PERFCTR_SP_5_HI 0xDD #define A3XX_RBBM_PERFCTR_SP_6_LO 0xDE #define A3XX_RBBM_PERFCTR_SP_6_HI 0xDF #define A3XX_RBBM_PERFCTR_SP_7_LO 0xE0 #define A3XX_RBBM_PERFCTR_SP_7_HI 0xE1 +#define A3XX_RBBM_PERFCTR_RB_0_LO 0xE2 +#define A3XX_RBBM_PERFCTR_RB_0_HI 0xE3 +#define A3XX_RBBM_PERFCTR_RB_1_LO 0xE4 +#define A3XX_RBBM_PERFCTR_RB_1_HI 0xE5 + #define A3XX_RBBM_RBBM_CTL 0x100 -#define A3XX_RBBM_RBBM_CTL 0x100 +#define A3XX_RBBM_PERFCTR_PWR_0_LO 0x0EA +#define A3XX_RBBM_PERFCTR_PWR_0_HI 0x0EB #define A3XX_RBBM_PERFCTR_PWR_1_LO 0x0EC #define A3XX_RBBM_PERFCTR_PWR_1_HI 0x0ED #define A3XX_RBBM_DEBUG_BUS_CTL 0x111 @@ -90,6 +178,7 @@ #define A3XX_CP_MERCIU_DATA2 0x1D3 #define A3XX_CP_MEQ_ADDR 0x1DA #define A3XX_CP_MEQ_DATA 0x1DB +#define A3XX_CP_PERFCOUNTER_SELECT 0x445 #define A3XX_CP_HW_FAULT 0x45C #define A3XX_CP_AHB_FAULT 0x54D #define A3XX_CP_PROTECT_CTRL 0x45E @@ -138,6 +227,14 @@ #define A3XX_VSC_PIPE_CONFIG_7 0xC1B #define A3XX_VSC_PIPE_DATA_ADDRESS_7 0xC1C #define A3XX_VSC_PIPE_DATA_LENGTH_7 0xC1D +#define A3XX_PC_PERFCOUNTER0_SELECT 0xC48 +#define A3XX_PC_PERFCOUNTER1_SELECT 0xC49 +#define A3XX_PC_PERFCOUNTER2_SELECT 0xC4A +#define A3XX_PC_PERFCOUNTER3_SELECT 0xC4B +#define A3XX_GRAS_PERFCOUNTER0_SELECT 0xC88 +#define A3XX_GRAS_PERFCOUNTER1_SELECT 0xC89 +#define A3XX_GRAS_PERFCOUNTER2_SELECT 0xC8A +#define A3XX_GRAS_PERFCOUNTER3_SELECT 0xC8B #define A3XX_GRAS_CL_USER_PLANE_X0 0xCA0 #define A3XX_GRAS_CL_USER_PLANE_Y0 0xCA1 #define A3XX_GRAS_CL_USER_PLANE_Z0 0xCA2 @@ -163,14 +260,42 @@ #define A3XX_GRAS_CL_USER_PLANE_Z5 0xCB6 #define A3XX_GRAS_CL_USER_PLANE_W5 0xCB7 #define A3XX_RB_GMEM_BASE_ADDR 0xCC0 +#define A3XX_RB_PERFCOUNTER0_SELECT 0xCC6 +#define A3XX_RB_PERFCOUNTER1_SELECT 0xCC7 +#define A3XX_HLSQ_PERFCOUNTER0_SELECT 0xE00 +#define A3XX_HLSQ_PERFCOUNTER1_SELECT 0xE01 +#define A3XX_HLSQ_PERFCOUNTER2_SELECT 0xE02 +#define A3XX_HLSQ_PERFCOUNTER3_SELECT 0xE03 +#define A3XX_HLSQ_PERFCOUNTER4_SELECT 0xE04 +#define A3XX_HLSQ_PERFCOUNTER5_SELECT 0xE05 #define A3XX_VFD_PERFCOUNTER0_SELECT 0xE44 +#define A3XX_VFD_PERFCOUNTER1_SELECT 0xE45 #define A3XX_VPC_VPC_DEBUG_RAM_SEL 0xE61 #define A3XX_VPC_VPC_DEBUG_RAM_READ 0xE62 +#define A3XX_VPC_PERFCOUNTER0_SELECT 0xE64 +#define A3XX_VPC_PERFCOUNTER1_SELECT 0xE65 #define A3XX_UCHE_CACHE_MODE_CONTROL_REG 0xE82 +#define A3XX_UCHE_PERFCOUNTER0_SELECT 0xE84 +#define A3XX_UCHE_PERFCOUNTER1_SELECT 0xE85 +#define A3XX_UCHE_PERFCOUNTER2_SELECT 0xE86 +#define A3XX_UCHE_PERFCOUNTER3_SELECT 0xE87 +#define A3XX_UCHE_PERFCOUNTER4_SELECT 0xE88 +#define A3XX_UCHE_PERFCOUNTER5_SELECT 0xE89 #define A3XX_UCHE_CACHE_INVALIDATE0_REG 0xEA0 +#define A3XX_SP_PERFCOUNTER0_SELECT 0xEC4 +#define A3XX_SP_PERFCOUNTER1_SELECT 0xEC5 +#define A3XX_SP_PERFCOUNTER2_SELECT 0xEC6 +#define A3XX_SP_PERFCOUNTER3_SELECT 0xEC7 +#define A3XX_SP_PERFCOUNTER4_SELECT 0xEC8 #define A3XX_SP_PERFCOUNTER5_SELECT 0xEC9 #define A3XX_SP_PERFCOUNTER6_SELECT 0xECA #define A3XX_SP_PERFCOUNTER7_SELECT 0xECB +#define A3XX_TP_PERFCOUNTER0_SELECT 0xF04 +#define A3XX_TP_PERFCOUNTER1_SELECT 0xF05 +#define A3XX_TP_PERFCOUNTER2_SELECT 0xF06 +#define A3XX_TP_PERFCOUNTER3_SELECT 0xF07 +#define A3XX_TP_PERFCOUNTER4_SELECT 0xF08 +#define A3XX_TP_PERFCOUNTER5_SELECT 0xF09 #define A3XX_GRAS_CL_CLIP_CNTL 0x2040 #define A3XX_GRAS_CL_GB_CLIP_ADJ 0x2044 #define A3XX_GRAS_CL_VPORT_XOFFSET 0x2048 @@ -232,12 +357,14 @@ #define A3XX_SP_VS_OUT_REG_7 0x22CE #define A3XX_SP_VS_VPC_DST_REG_0 0x22D0 #define A3XX_SP_VS_OBJ_OFFSET_REG 0x22D4 +#define A3XX_SP_VS_OBJ_START_REG 0x22D5 #define A3XX_SP_VS_PVT_MEM_ADDR_REG 0x22D7 #define A3XX_SP_VS_PVT_MEM_SIZE_REG 0x22D8 #define A3XX_SP_VS_LENGTH_REG 0x22DF #define A3XX_SP_FS_CTRL_REG0 0x22E0 #define A3XX_SP_FS_CTRL_REG1 0x22E1 #define A3XX_SP_FS_OBJ_OFFSET_REG 0x22E2 +#define A3XX_SP_FS_OBJ_START_REG 0x22E3 #define A3XX_SP_FS_PVT_MEM_ADDR_REG 0x22E5 #define A3XX_SP_FS_PVT_MEM_SIZE_REG 0x22E6 #define A3XX_SP_FS_FLAT_SHAD_MODE_REG_0 0x22E8 @@ -269,10 +396,25 @@ #define A3XX_VBIF_OUT_AXI_AMEMTYPE_CONF0 0x3058 #define A3XX_VBIF_OUT_AXI_AOOO_EN 0x305E #define A3XX_VBIF_OUT_AXI_AOOO 0x305F +#define A3XX_VBIF_PERF_CNT_EN 0x3070 +#define A3XX_VBIF_PERF_CNT_CLR 0x3071 +#define A3XX_VBIF_PERF_CNT_SEL 0x3072 +#define A3XX_VBIF_PERF_CNT0_LO 0x3073 +#define A3XX_VBIF_PERF_CNT0_HI 0x3074 +#define A3XX_VBIF_PERF_CNT1_LO 0x3075 +#define A3XX_VBIF_PERF_CNT1_HI 0x3076 +#define A3XX_VBIF_PERF_PWR_CNT0_LO 0x3077 +#define A3XX_VBIF_PERF_PWR_CNT0_HI 0x3078 +#define A3XX_VBIF_PERF_PWR_CNT1_LO 0x3079 +#define A3XX_VBIF_PERF_PWR_CNT1_HI 0x307a +#define A3XX_VBIF_PERF_PWR_CNT2_LO 0x307b +#define A3XX_VBIF_PERF_PWR_CNT2_HI 0x307c /* Bit flags for RBBM_CTL */ -#define RBBM_RBBM_CTL_RESET_PWR_CTR1 (1 << 1) -#define RBBM_RBBM_CTL_ENABLE_PWR_CTR1 (1 << 17) +#define RBBM_RBBM_CTL_RESET_PWR_CTR0 BIT(0) +#define RBBM_RBBM_CTL_RESET_PWR_CTR1 BIT(1) +#define RBBM_RBBM_CTL_ENABLE_PWR_CTR0 BIT(16) +#define RBBM_RBBM_CTL_ENABLE_PWR_CTR1 BIT(17) /* Various flags used by the context switch code */ @@ -537,7 +679,13 @@ #define RBBM_BLOCK_ID_MARB_3 0x2b /* RBBM_CLOCK_CTL default value */ -#define A3XX_RBBM_CLOCK_CTL_DEFAULT 0xBFFFFFFF +#define A305_RBBM_CLOCK_CTL_DEFAULT 0xAAAAAAAA +#define A320_RBBM_CLOCK_CTL_DEFAULT 0xBFFFFFFF +#define A330_RBBM_CLOCK_CTL_DEFAULT 0xAAAAAAAE +#define A330v2_RBBM_CLOCK_CTL_DEFAULT 0xAAAAAAAA + +#define A330_RBBM_GPR0_CTL_DEFAULT 0x0AE2B8AE +#define A330v2_RBBM_GPR0_CTL_DEFAULT 0x0AA2A8AA /* COUNTABLE FOR SP PERFCOUNTER */ #define SP_FS_FULL_ALU_INSTRUCTIONS 0x0E @@ -545,4 +693,20 @@ #define SP0_ICL1_MISSES 0x1A #define SP_FS_CFLOW_INSTRUCTIONS 0x0C +/* VBIF PERFCOUNTER ENA/CLR values */ +#define VBIF_PERF_CNT_0 BIT(0) +#define VBIF_PERF_CNT_1 BIT(1) +#define VBIF_PERF_PWR_CNT_0 BIT(2) +#define VBIF_PERF_PWR_CNT_1 BIT(3) +#define VBIF_PERF_PWR_CNT_2 BIT(4) + +/* VBIF PERFCOUNTER SEL values */ +#define VBIF_PERF_CNT_0_SEL 0 +#define VBIF_PERF_CNT_0_SEL_MASK 0x7f +#define VBIF_PERF_CNT_1_SEL 8 +#define VBIF_PERF_CNT_1_SEL_MASK 0x7f00 + +/* VBIF countables */ +#define VBIF_DDR_TOTAL_CYCLES 110 + #endif diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index b49261ce..8a1894e7 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2013, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2014, The Linux Foundation. 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 and @@ -73,6 +73,13 @@ | (MMU_CONFIG << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT) \ | (MMU_CONFIG << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT)) +/*default log levels is error for everything*/ +#define KGSL_LOG_LEVEL_DEFAULT 3 + +#ifndef CONFIG_DEBUG_FS +unsigned int kgsl_cff_dump_enable; +#endif + static const struct kgsl_functable adreno_functable; static struct adreno_device device_3d0 = { @@ -101,6 +108,7 @@ static struct adreno_device device_3d0 = { }, .iomemname = KGSL_3D0_REG_MEMORY, .ftbl = &adreno_functable, +#if 0 #ifdef CONFIG_HAS_EARLYSUSPEND .display_off = { .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING, @@ -108,6 +116,14 @@ static struct adreno_device device_3d0 = { .resume = kgsl_late_resume_driver, }, #endif +#endif + .cmd_log = KGSL_LOG_LEVEL_DEFAULT, + .ctxt_log = KGSL_LOG_LEVEL_DEFAULT, + .drv_log = KGSL_LOG_LEVEL_DEFAULT, + .mem_log = KGSL_LOG_LEVEL_DEFAULT, + .pwr_log = KGSL_LOG_LEVEL_DEFAULT, + .ft_log = KGSL_LOG_LEVEL_DEFAULT, + .pm_dump_enable = 0, }, .gmem_base = 0, .gmem_size = SZ_256K, @@ -115,11 +131,15 @@ static struct adreno_device device_3d0 = { .pm4_fw = NULL, .wait_timeout = 0, /* in milliseconds, 0 means disabled */ .ib_check_level = 0, + .ft_policy = KGSL_FT_DEFAULT_POLICY, + .ft_pf_policy = KGSL_FT_PAGEFAULT_DEFAULT_POLICY, + .fast_hang_detect = 1, + .long_ib_detect = 1, }; /* This set of registers are used for Hang detection * If the values of these registers are same after - * KGSL_TIMEOUT_PART time, GPU hang is reported in + * KGSL_TIMEOUT_HANG_DETECT time, GPU hang is reported in * kernel log. * *****ALERT******ALERT********ALERT************* * Order of registers below is important, registers @@ -129,7 +149,7 @@ static struct adreno_device device_3d0 = { #define LONG_IB_DETECT_REG_INDEX_START 1 #define LONG_IB_DETECT_REG_INDEX_END 5 -unsigned int ft_detect_regs[] = { +unsigned int ft_detect_regs[FT_DETECT_REGS_COUNT] = { A3XX_RBBM_STATUS, REG_CP_RB_RPTR, /* LONG_IB_DETECT_REG_INDEX_START */ REG_CP_IB1_BASE, @@ -144,8 +164,6 @@ unsigned int ft_detect_regs[] = { 0 }; -const unsigned int ft_detect_regs_count = ARRAY_SIZE(ft_detect_regs); - /* * This is the master list of all GPU cores that are supported by this * driver. @@ -206,11 +224,321 @@ static const struct { { ADRENO_REV_A320, 3, 2, ANY_ID, ANY_ID, "a300_pm4.fw", "a300_pfp.fw", &adreno_a3xx_gpudev, 512, 0, 2, SZ_512K, 0x3FF037, 0x3FF016 }, - { ADRENO_REV_A330, 3, 3, 0, 0, + { ADRENO_REV_A330, 3, 3, 0, ANY_ID, "a330_pm4.fw", "a330_pfp.fw", &adreno_a3xx_gpudev, 512, 0, 2, SZ_1M, NO_VER, NO_VER }, }; +static unsigned int adreno_isidle(struct kgsl_device *device); + +/** + * adreno_perfcounter_init: Reserve kernel performance counters + * @device: device to configure + * + * The kernel needs/wants a certain group of performance counters for + * its own activities. Reserve these performance counters at init time + * to ensure that they are always reserved for the kernel. The performance + * counters used by the kernel can be obtained by the user, but these + * performance counters will remain active as long as the device is alive. + */ + +static void adreno_perfcounter_init(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + if (adreno_dev->gpudev->perfcounter_init) + adreno_dev->gpudev->perfcounter_init(adreno_dev); +}; + +/** + * adreno_perfcounter_start: Enable performance counters + * @adreno_dev: Adreno device to configure + * + * Ensure all performance counters are enabled that are allocated. Since + * the device was most likely stopped, we can't trust that the counters + * are still valid so make it so. + */ + +static void adreno_perfcounter_start(struct adreno_device *adreno_dev) +{ + struct adreno_perfcounters *counters = adreno_dev->gpudev->perfcounters; + struct adreno_perfcount_group *group; + unsigned int i, j; + + /* group id iter */ + for (i = 0; i < counters->group_count; i++) { + group = &(counters->groups[i]); + + /* countable iter */ + for (j = 0; j < group->reg_count; j++) { + if (group->regs[j].countable == + KGSL_PERFCOUNTER_NOT_USED) + continue; + + if (adreno_dev->gpudev->perfcounter_enable) + adreno_dev->gpudev->perfcounter_enable( + adreno_dev, i, j, + group->regs[j].countable); + } + } +} + +/** + * adreno_perfcounter_read_group: Determine which countables are in counters + * @adreno_dev: Adreno device to configure + * @reads: List of kgsl_perfcounter_read_groups + * @count: Length of list + * + * Read the performance counters for the groupid/countable pairs and return + * the 64 bit result for each pair + */ + +int adreno_perfcounter_read_group(struct adreno_device *adreno_dev, + struct kgsl_perfcounter_read_group __user *reads, unsigned int count) +{ + struct adreno_perfcounters *counters = adreno_dev->gpudev->perfcounters; + struct adreno_perfcount_group *group; + struct kgsl_perfcounter_read_group *list = NULL; + unsigned int i, j; + int ret = 0; + + /* perfcounter get/put/query/read not allowed on a2xx */ + if (adreno_is_a2xx(adreno_dev)) + return -EINVAL; + + /* sanity check for later */ + if (!adreno_dev->gpudev->perfcounter_read) + return -EINVAL; + + /* sanity check params passed in */ + if (reads == NULL || count == 0 || count > 100) + return -EINVAL; + + list = kmalloc(sizeof(struct kgsl_perfcounter_read_group) * count, + GFP_KERNEL); + if (!list) + return -ENOMEM; + + if (copy_from_user(list, reads, + sizeof(struct kgsl_perfcounter_read_group) * count)) { + ret = -EFAULT; + goto done; + } + + /* list iterator */ + for (j = 0; j < count; j++) { + + list[j].value = 0; + + /* Verify that the group ID is within range */ + if (list[j].groupid >= counters->group_count) { + ret = -EINVAL; + goto done; + } + + group = &(counters->groups[list[j].groupid]); + + /* group/counter iterator */ + for (i = 0; i < group->reg_count; i++) { + if (group->regs[i].countable == list[j].countable) { + list[j].value = + adreno_dev->gpudev->perfcounter_read( + adreno_dev, list[j].groupid, + i, group->regs[i].offset); + break; + } + } + } + + /* write the data */ + if (copy_to_user(reads, list, + sizeof(struct kgsl_perfcounter_read_group) * + count) != 0) + ret = -EFAULT; + +done: + kfree(list); + return ret; +} + +/** + * adreno_perfcounter_query_group: Determine which countables are in counters + * @adreno_dev: Adreno device to configure + * @groupid: Desired performance counter group + * @countables: Return list of all countables in the groups counters + * @count: Max length of the array + * @max_counters: max counters for the groupid + * + * Query the current state of counters for the group. + */ + +int adreno_perfcounter_query_group(struct adreno_device *adreno_dev, + unsigned int groupid, unsigned int *countables, unsigned int count, + unsigned int *max_counters) +{ + struct adreno_perfcounters *counters = adreno_dev->gpudev->perfcounters; + struct adreno_perfcount_group *group; + unsigned int i; + + *max_counters = 0; + + /* perfcounter get/put/query not allowed on a2xx */ + if (adreno_is_a2xx(adreno_dev)) + return -EINVAL; + + if (groupid >= counters->group_count) + return -EINVAL; + + group = &(counters->groups[groupid]); + *max_counters = group->reg_count; + + /* + * if NULL countable or *count of zero, return max reg_count in + * *max_counters and return success + */ + if (countables == NULL || count == 0) + return 0; + + /* + * Go through all available counters. Write upto *count * countable + * values. + */ + for (i = 0; i < group->reg_count && i < count; i++) { + if (copy_to_user(&countables[i], &(group->regs[i].countable), + sizeof(unsigned int)) != 0) + return -EFAULT; + } + + return 0; +} + +/** + * adreno_perfcounter_get: Try to put a countable in an available counter + * @adreno_dev: Adreno device to configure + * @groupid: Desired performance counter group + * @countable: Countable desired to be in a counter + * @offset: Return offset of the countable + * @flags: Used to setup kernel perf counters + * + * Try to place a countable in an available counter. If the countable is + * already in a counter, reference count the counter/countable pair resource + * and return success + */ + +int adreno_perfcounter_get(struct adreno_device *adreno_dev, + unsigned int groupid, unsigned int countable, unsigned int *offset, + unsigned int flags) +{ + struct adreno_perfcounters *counters = adreno_dev->gpudev->perfcounters; + struct adreno_perfcount_group *group; + unsigned int i, empty = -1; + + /* always clear return variables */ + if (offset) + *offset = 0; + + /* perfcounter get/put/query not allowed on a2xx */ + if (adreno_is_a2xx(adreno_dev)) + return -EINVAL; + + if (groupid >= counters->group_count) + return -EINVAL; + + group = &(counters->groups[groupid]); + + /* + * Check if the countable is already associated with a counter. + * Refcount and return the offset, otherwise, try and find an empty + * counter and assign the countable to it. + */ + for (i = 0; i < group->reg_count; i++) { + if (group->regs[i].countable == countable) { + /* Countable already associated with counter */ + group->regs[i].refcount++; + group->regs[i].flags |= flags; + if (offset) + *offset = group->regs[i].offset; + return 0; + } else if (group->regs[i].countable == + KGSL_PERFCOUNTER_NOT_USED) { + /* keep track of unused counter */ + empty = i; + } + } + + /* no available counters, so do nothing else */ + if (empty == -1) + return -EBUSY; + + /* initialize the new counter */ + group->regs[empty].countable = countable; + group->regs[empty].refcount = 1; + + /* enable the new counter */ + adreno_dev->gpudev->perfcounter_enable(adreno_dev, groupid, empty, + countable); + + group->regs[empty].flags = flags; + + if (offset) + *offset = group->regs[empty].offset; + + return 0; +} + + +/** + * adreno_perfcounter_put: Release a countable from counter resource + * @adreno_dev: Adreno device to configure + * @groupid: Desired performance counter group + * @countable: Countable desired to be freed from a counter + * + * Put a performance counter/countable pair that was previously received. If + * noone else is using the countable, free up the counter for others. + */ +int adreno_perfcounter_put(struct adreno_device *adreno_dev, + unsigned int groupid, unsigned int countable) +{ + struct adreno_perfcounters *counters = adreno_dev->gpudev->perfcounters; + struct adreno_perfcount_group *group; + + unsigned int i; + + /* perfcounter get/put/query not allowed on a2xx */ + if (adreno_is_a2xx(adreno_dev)) + return -EINVAL; + + if (groupid >= counters->group_count) + return -EINVAL; + + group = &(counters->groups[groupid]); + + for (i = 0; i < group->reg_count; i++) { + if (group->regs[i].countable == countable) { + if (group->regs[i].refcount > 0) { + group->regs[i].refcount--; + + /* + * book keeping to ensure we never free a + * perf counter used by kernel + */ + if (group->regs[i].flags && + group->regs[i].refcount == 0) + group->regs[i].refcount++; + + /* make available if not used */ + if (group->regs[i].refcount == 0) + group->regs[i].countable = + KGSL_PERFCOUNTER_NOT_USED; + } + + return 0; + } + } + + return -EINVAL; +} + static irqreturn_t adreno_irq_handler(struct kgsl_device *device) { irqreturn_t result; @@ -230,6 +558,8 @@ static irqreturn_t adreno_irq_handler(struct kgsl_device *device) /* Reset the time-out in our idle timer */ mod_timer_pending(&device->idle_timer, jiffies + device->pwrctrl.interval_timeout); + mod_timer_pending(&device->hang_timer, + (jiffies + msecs_to_jiffies(KGSL_TIMEOUT_HANG_DETECT))); return result; } @@ -255,26 +585,29 @@ static int adreno_setup_pt(struct kgsl_device *device, struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; - result = kgsl_mmu_map_global(pagetable, &rb->buffer_desc, - GSL_PT_PAGE_RV); + result = kgsl_mmu_map_global(pagetable, &rb->buffer_desc); if (result) goto error; - result = kgsl_mmu_map_global(pagetable, &rb->memptrs_desc, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + result = kgsl_mmu_map_global(pagetable, &rb->memptrs_desc); if (result) goto unmap_buffer_desc; - result = kgsl_mmu_map_global(pagetable, &device->memstore, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + result = kgsl_mmu_map_global(pagetable, &device->memstore); if (result) goto unmap_memptrs_desc; - result = kgsl_mmu_map_global(pagetable, &device->mmu.setstate_memory, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + result = kgsl_mmu_map_global(pagetable, &device->mmu.setstate_memory); if (result) goto unmap_memstore_desc; + /* + * Set the mpu end to the last "normal" global memory we use. + * For the IOMMU, this will be used to restrict access to the + * mapped registers. + */ + device->mh.mpu_range = device->mmu.setstate_memory.gpuaddr + + device->mmu.setstate_memory.size; return result; unmap_memstore_desc: @@ -290,31 +623,62 @@ static int adreno_setup_pt(struct kgsl_device *device, return result; } +/* + * adreno_use_default_setstate() - Use CPU instead of the GPU to manage the mmu? + * @adreno_dev: the device + * + * In many cases it is preferable to poke the iommu or gpummu directly rather + * than using the GPU command stream. If we are idle or trying to go to a low + * power state, using the command stream will be slower and asynchronous, which + * needlessly complicates the power state transitions. Additionally, + * the hardware simulators do not support command stream MMU operations so + * the command stream can never be used if we are capturing CFF data. + * + */ +static bool adreno_use_default_setstate(struct adreno_device *adreno_dev) +{ + return (adreno_isidle(&adreno_dev->dev) || + KGSL_STATE_ACTIVE != adreno_dev->dev.state || + adreno_dev->dev.active_cnt == 0 || + kgsl_cff_dump_enable); +} + static void adreno_iommu_setstate(struct kgsl_device *device, unsigned int context_id, uint32_t flags) { unsigned int pt_val, reg_pt_val; - unsigned int link[250]; - unsigned int *cmds = &link[0]; - int sizedwords = 0; + unsigned int *link = NULL, *cmds; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); int num_iommu_units, i; struct kgsl_context *context; struct adreno_context *adreno_ctx = NULL; - if (!adreno_dev->drawctxt_active) + /* + * If we're idle and we don't need to use the GPU to save context + * state, use the CPU instead of the GPU to reprogram the + * iommu for simplicity's sake. + */ + if (adreno_use_default_setstate(adreno_dev)) return kgsl_mmu_device_setstate(&device->mmu, flags); + num_iommu_units = kgsl_mmu_get_num_iommu_units(&device->mmu); - context = idr_find(&device->context_idr, context_id); + context = kgsl_context_get(device, context_id); + if (context == NULL) return; adreno_ctx = context->devctxt; + link = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (link == NULL) + goto done; + + cmds = link; + if (kgsl_mmu_enable_clk(&device->mmu, KGSL_IOMMU_CONTEXT_USER)) - return; + goto done; cmds += __adreno_add_idle_indirect_cmds(cmds, device->mmu.setstate_memory.gpuaddr + @@ -413,25 +777,26 @@ static void adreno_iommu_setstate(struct kgsl_device *device, cmds += adreno_add_idle_cmds(adreno_dev, cmds); - sizedwords += (cmds - &link[0]); - if (sizedwords) { + if ((unsigned int) (cmds - link)) { /* invalidate all base pointers */ *cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1); *cmds++ = 0x7fff; - sizedwords += 2; /* This returns the per context timestamp but we need to * use the global timestamp for iommu clock disablement */ adreno_ringbuffer_issuecmds(device, adreno_ctx, KGSL_CMD_FLAGS_PMODE, - &link[0], sizedwords); + link, (unsigned int)(cmds - link)); kgsl_mmu_disable_clk_on_ts(&device->mmu, - adreno_dev->ringbuffer.timestamp[KGSL_MEMSTORE_GLOBAL], true); + adreno_dev->ringbuffer.global_ts, true); } - if (sizedwords > (sizeof(link)/sizeof(unsigned int))) { + if ((unsigned int) (cmds - link) > (PAGE_SIZE / sizeof(unsigned int))) { KGSL_DRV_ERR(device, "Temp command buffer overflow\n"); BUG(); } +done: + kfree(link); + kgsl_context_put(context); } static void adreno_gpummu_setstate(struct kgsl_device *device, @@ -458,8 +823,8 @@ static void adreno_gpummu_setstate(struct kgsl_device *device, * writes For CFF dump we must idle and use the registers so that it is * easier to filter out the mmu accesses from the dump */ - if (!kgsl_cff_dump_enable && adreno_dev->drawctxt_active) { - context = idr_find(&device->context_idr, context_id); + if (!adreno_use_default_setstate(adreno_dev)) { + context = kgsl_context_get(device, context_id); if (context == NULL) return; adreno_ctx = context->devctxt; @@ -539,6 +904,8 @@ static void adreno_gpummu_setstate(struct kgsl_device *device, adreno_ringbuffer_issuecmds(device, adreno_ctx, KGSL_CMD_FLAGS_PMODE, &link[0], sizedwords); + + kgsl_context_put(context); } else { kgsl_mmu_device_setstate(&device->mmu, flags); } @@ -606,7 +973,7 @@ a2xx_getchipid(struct kgsl_device *device) /* 8x25 returns 0 for minor id, but it should be 1 */ if (cpu_is_qsd8x50()) patchid = 1; - else if (cpu_is_msm8625() && minorid == 0) + else if ((cpu_is_msm8625() || cpu_is_msm8625q()) && minorid == 0) minorid = 1; chipid |= (minorid << 8) | patchid; @@ -1172,6 +1539,8 @@ adreno_probe(struct platform_device *pdev) adreno_debugfs_init(device); + adreno_ft_init_sysfs(device); + kgsl_pwrscale_init(device); kgsl_pwrscale_attach_policy(device, ADRENO_DEFAULT_PWRSCALE_POLICY); @@ -1203,10 +1572,10 @@ static int __devexit adreno_remove(struct platform_device *pdev) return 0; } -static int adreno_start(struct kgsl_device *device, unsigned int init_ram) +static int adreno_init(struct kgsl_device *device) { - int status = -EINVAL; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; if (KGSL_STATE_DUMP_AND_FT != device->state) kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT); @@ -1232,10 +1601,9 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) if (adreno_dev->gpurev == ADRENO_REV_UNKNOWN) { KGSL_DRV_ERR(device, "Unknown chip ID %x\n", adreno_dev->chip_id); - goto error_clk_off; + BUG_ON(1); } - /* * Check if firmware supports the sync lock PM4 packets needed * for IOMMUv1 @@ -1247,7 +1615,36 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) adreno_gpulist[adreno_dev->gpulist_index].sync_lock_pfp_ver)) device->mmu.flags |= KGSL_MMU_FLAGS_IOMMU_SYNC; - /* Set up the MMU */ + rb->global_ts = 0; + + /* Assign correct RBBM status register to hang detect regs + */ + ft_detect_regs[0] = adreno_dev->gpudev->reg_rbbm_status; + + if (!adreno_is_a2xx(adreno_dev)) + adreno_perfcounter_init(device); + + /* Power down the device */ + kgsl_pwrctrl_disable(device); + + return 0; +} + +static int adreno_start(struct kgsl_device *device) +{ + int status = -EINVAL; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + unsigned int state = device->state; + + kgsl_cffdump_open(device); + + if (KGSL_STATE_DUMP_AND_FT != device->state) + kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT); + + /* Power up the device */ + kgsl_pwrctrl_enable(device); + + /* Set up a2xx special case */ if (adreno_is_a2xx(adreno_dev)) { /* * the MH_CLNT_INTF_CTRL_CONFIG registers aren't present @@ -1291,22 +1688,58 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); device->ftbl->irqctrl(device, 1); - status = adreno_ringbuffer_start(&adreno_dev->ringbuffer, init_ram); - if (status == 0) { - /* While fault tolerance is on we do not want timer to - * fire and attempt to change any device state */ - if (KGSL_STATE_DUMP_AND_FT != device->state) - mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT); - return 0; + status = adreno_ringbuffer_start(&adreno_dev->ringbuffer); + if (status) + goto error_irq_off; + + /* + * While recovery is on we do not want timer to + * fire and attempt to change any device state + */ + + if (KGSL_STATE_DUMP_AND_FT != device->state) + mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT); + + if (!adreno_is_a2xx(adreno_dev)) + adreno_perfcounter_start(adreno_dev); + else { + unsigned int reg; + + kgsl_regread(device, REG_RBBM_PM_OVERRIDE2, ®); + kgsl_regwrite(device, REG_RBBM_PM_OVERRIDE2, (reg | 0x40)); + + /* + * Select SP_ALU_INSTR_EXEC (0x85) to get number of + * ALU instructions executed. + */ + kgsl_regwrite(device, REG_SQ_PERFCOUNTER3_SELECT, 0x85); + + kgsl_regwrite(device, REG_CP_PERFMON_CNTL, + REG_PERF_MODE_CNT | REG_PERF_STATE_ENABLE); + + ft_detect_regs[6] = REG_SQ_PERFCOUNTER3_LO; + ft_detect_regs[7] = REG_SQ_PERFCOUNTER3_HI; } + mod_timer(&device->hang_timer, + (jiffies + msecs_to_jiffies(KGSL_TIMEOUT_HANG_DETECT))); + + device->reset_counter++; + + return 0; + +error_irq_off: kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); error_mmu_off: kgsl_mmu_stop(&device->mmu); error_clk_off: - kgsl_pwrctrl_disable(device); + if (KGSL_STATE_DUMP_AND_FT != device->state) { + kgsl_pwrctrl_disable(device); + /* set the state back to original state */ + kgsl_pwrctrl_set_state(device, state); + } return status; } @@ -1324,68 +1757,89 @@ static int adreno_stop(struct kgsl_device *device) device->ftbl->irqctrl(device, 0); kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); del_timer_sync(&device->idle_timer); + del_timer_sync(&device->hang_timer); adreno_ocmem_gmem_free(adreno_dev); /* Power down the device */ kgsl_pwrctrl_disable(device); + kgsl_cffdump_close(device->id); + return 0; } -static void adreno_mark_context_status(struct kgsl_device *device, - int ft_status) +/* + * Set the reset status of all contexts to + * INNOCENT_CONTEXT_RESET_EXT except for the bad context + * since thats the guilty party, if fault tolerance failed then + * mark all as guilty + */ + +static int _mark_context_status(int id, void *ptr, void *data) { - struct kgsl_context *context; - int next = 0; - /* - * Set the reset status of all contexts to - * INNOCENT_CONTEXT_RESET_EXT except for the bad context - * since thats the guilty party, if fault tolerance failed then - * mark all as guilty - */ - while ((context = idr_get_next(&device->context_idr, &next))) { - struct adreno_context *adreno_context = context->devctxt; - if (ft_status) { - context->reset_status = - KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT; - adreno_context->flags |= CTXT_FLAGS_GPU_HANG; - } else if (KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT != - context->reset_status) { - if (adreno_context->flags & (CTXT_FLAGS_GPU_HANG | - CTXT_FLAGS_GPU_HANG_FT)) - context->reset_status = + unsigned int ft_status = *((unsigned int *) data); + struct kgsl_context *context = ptr; + struct adreno_context *adreno_context = context->devctxt; + + if (ft_status) { + context->reset_status = KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT; - else - context->reset_status = - KGSL_CTX_STAT_INNOCENT_CONTEXT_RESET_EXT; - } - next = next + 1; + adreno_context->flags |= CTXT_FLAGS_GPU_HANG; + } else if (KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT != + context->reset_status) { + if (adreno_context->flags & (CTXT_FLAGS_GPU_HANG | + CTXT_FLAGS_GPU_HANG_FT)) + context->reset_status = + KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT; + else + context->reset_status = + KGSL_CTX_STAT_INNOCENT_CONTEXT_RESET_EXT; } + + return 0; } -static void adreno_set_max_ts_for_bad_ctxs(struct kgsl_device *device) +static void adreno_mark_context_status(struct kgsl_device *device, + int ft_status) { - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; - struct kgsl_context *context; - struct adreno_context *temp_adreno_context; - int next = 0; + /* Mark the status for all the contexts in the device */ - while ((context = idr_get_next(&device->context_idr, &next))) { - temp_adreno_context = context->devctxt; - if (temp_adreno_context->flags & CTXT_FLAGS_GPU_HANG) { - kgsl_sharedmem_writel(&device->memstore, - KGSL_MEMSTORE_OFFSET(context->id, - soptimestamp), - rb->timestamp[context->id]); - kgsl_sharedmem_writel(&device->memstore, - KGSL_MEMSTORE_OFFSET(context->id, - eoptimestamp), - rb->timestamp[context->id]); - } - next = next + 1; + read_lock(&device->context_lock); + idr_for_each(&device->context_idr, _mark_context_status, &ft_status); + read_unlock(&device->context_lock); +} + +/* + * For hung contexts set the current memstore value to the most recent issued + * timestamp - this resets the status and lets the system continue on + */ + +static int _set_max_ts(int id, void *ptr, void *data) +{ + struct kgsl_device *device = data; + struct kgsl_context *context = ptr; + struct adreno_context *drawctxt = context->devctxt; + + if (drawctxt->flags & CTXT_FLAGS_GPU_HANG) { + kgsl_sharedmem_writel(&device->memstore, + KGSL_MEMSTORE_OFFSET(context->id, + soptimestamp), + drawctxt->timestamp); + kgsl_sharedmem_writel(&device->memstore, + KGSL_MEMSTORE_OFFSET(context->id, + eoptimestamp), + drawctxt->timestamp); } + + return 0; +} + +static void adreno_set_max_ts_for_bad_ctxs(struct kgsl_device *device) +{ + read_lock(&device->context_lock); + idr_for_each(&device->context_idr, _set_max_ts, device); + read_unlock(&device->context_lock); } static void adreno_destroy_ft_data(struct adreno_ft_data *ft_data) @@ -1499,7 +1953,8 @@ static int _find_cmd_seq_after_eop_ts(struct adreno_ringbuffer *rb, } if (status) KGSL_FT_ERR(rb->device, - "Failed to find the command sequence after eop timestamp\n"); + "Failed to find the command sequence after eop timestamp %x\n", + global_eop); return status; } @@ -1612,46 +2067,43 @@ static void adreno_setup_ft_data(struct kgsl_device *device, ft_data->status = 0; /* find the start of bad command sequence in rb */ - context = idr_find(&device->context_idr, ft_data->context_id); - /* Look for the command stream that is right after the global eop */ - - if (!context) { - /* - * If there is no context then fault tolerance does not need to - * replay anything, just reset GPU and thats it - */ - return; - } + context = kgsl_context_get(device, ft_data->context_id); ft_data->ft_policy = adreno_dev->ft_policy; if (!adreno_dev->ft_policy) adreno_dev->ft_policy = KGSL_FT_DEFAULT_POLICY; + /* Look for the command stream that is right after the global eop */ ret = _find_cmd_seq_after_eop_ts(rb, &rb_rptr, ft_data->global_eop + 1, false); if (ret) { ft_data->ft_policy |= KGSL_FT_TEMP_DISABLE; - return; - } else + goto done; + } else { + ft_data->start_of_replay_cmds = rb_rptr; ft_data->ft_policy &= ~KGSL_FT_TEMP_DISABLE; + } - ft_data->start_of_replay_cmds = rb_rptr; - - adreno_context = context->devctxt; - if (adreno_context->flags & CTXT_FLAGS_PREAMBLE) { - if (ft_data->ib1) { - ret = _find_hanging_ib_sequence(rb, - &rb_rptr, ft_data->ib1); - if (ret) { - KGSL_FT_ERR(device, - "Start not found for replay IB sequence\n"); - return; + if (context) { + adreno_context = context->devctxt; + if (adreno_context->flags & CTXT_FLAGS_PREAMBLE) { + if (ft_data->ib1) { + ret = _find_hanging_ib_sequence(rb, + &rb_rptr, ft_data->ib1); + if (ret) { + KGSL_FT_ERR(device, + "Start not found for replay IB seq\n"); + goto done; + } + ft_data->start_of_replay_cmds = rb_rptr; + ft_data->replay_for_snapshot = rb_rptr; } - ft_data->start_of_replay_cmds = rb_rptr; - ft_data->replay_for_snapshot = rb_rptr; } } + +done: + kgsl_context_put(context); } static int @@ -1686,23 +2138,29 @@ static int _adreno_ft_restart_device(struct kgsl_device *device, struct kgsl_context *context) { - - struct adreno_context *adreno_context = context->devctxt; + struct adreno_context *adreno_context = NULL; /* restart device */ if (adreno_stop(device)) { KGSL_FT_ERR(device, "Device stop failed\n"); return 1; } + + if (adreno_init(device)) { + KGSL_FT_ERR(device, "Device start failed\n"); + return 1; + } - if (adreno_start(device, true)) { + if (adreno_start(device)) { KGSL_FT_ERR(device, "Device start failed\n"); return 1; } - if (context) + if ((context != NULL) && (context->devctxt != NULL)) { + adreno_context = context->devctxt; kgsl_mmu_setstate(&device->mmu, adreno_context->pagetable, KGSL_MEMSTORE_GLOBAL); + } /* If iommu is used then we need to make sure that the iommu clocks * are on since there could be commands in pipeline that touch iommu */ @@ -1802,13 +2260,23 @@ _adreno_ft(struct kgsl_device *device, struct adreno_context *adreno_context = NULL; struct adreno_context *last_active_ctx = adreno_dev->drawctxt_active; unsigned int long_ib = 0; + static int no_context_ft; + + context = kgsl_context_get(device, ft_data->context_id); - context = idr_find(&device->context_idr, ft_data->context_id); if (context == NULL) { KGSL_FT_ERR(device, "Last context unknown id:%d\n", ft_data->context_id); - goto play_good_cmds; + if (no_context_ft) { + /* + * If 2 consecutive no context ft occurred then + * just reset GPU + */ + no_context_ft = 0; + goto play_good_cmds; + } } else { + no_context_ft = 0; adreno_context = context->devctxt; adreno_context->flags |= CTXT_FLAGS_GPU_HANG; /* @@ -1838,7 +2306,7 @@ _adreno_ft(struct kgsl_device *device, /* Check if we detected a long running IB, * if true do not attempt replay of bad cmds */ - if (adreno_dev->long_ib) { + if ((adreno_context) && (adreno_dev->long_ib)) { long_ib = _adreno_check_long_ib(device); if (!long_ib) { adreno_context->flags &= ~CTXT_FLAGS_GPU_HANG; @@ -1867,7 +2335,7 @@ _adreno_ft(struct kgsl_device *device, } /* Do not try the reply if hang is due to a pagefault */ - if (adreno_context->pagefault) { + if (adreno_context && adreno_context->pagefault) { if ((ft_data->context_id == adreno_context->id) && (ft_data->global_eop == adreno_context->pagefault_ts)) { ft_data->ft_policy &= ~KGSL_FT_REPLAY; @@ -1932,7 +2400,7 @@ _adreno_ft(struct kgsl_device *device, /* EOF not found in RB, discard till EOF in next IB submission */ - if (i == ft_data->bad_rb_size) { + if (adreno_context && (i == ft_data->bad_rb_size)) { adreno_context->flags |= CTXT_FLAGS_SKIP_EOF; KGSL_FT_INFO(device, "EOF not found in RB, skip next issueib till EOF\n"); @@ -1969,8 +2437,14 @@ _adreno_ft(struct kgsl_device *device, ft_data->good_rb_buffer, ft_data->good_rb_size); if (ret) { - /* If we fail here we can try to invalidate another - * context and try fault tolerance again */ + /* + * If we fail here we can try to invalidate another + * context and try fault tolerance again, although + * we will only try ft with no context once to avoid + * going into continuous loop of trying ft with no context + */ + if (!context) + no_context_ft = 1; ret = -EAGAIN; KGSL_FT_ERR(device, "Playing good commands unsuccessful\n"); goto done; @@ -1980,17 +2454,21 @@ _adreno_ft(struct kgsl_device *device, /* ringbuffer now has data from the last valid context id, * so restore the active_ctx to the last valid context */ if (ft_data->last_valid_ctx_id) { - struct kgsl_context *last_ctx = - idr_find(&device->context_idr, - ft_data->last_valid_ctx_id); + struct kgsl_context *last_ctx = kgsl_context_get(device, + ft_data->last_valid_ctx_id); + if (last_ctx) adreno_dev->drawctxt_active = last_ctx->devctxt; + + kgsl_context_put(last_ctx); } done: /* Turn off iommu clocks */ if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) kgsl_mmu_disable_clk_on_ts(&device->mmu, 0, false); + + kgsl_context_put(context); return ret; } @@ -2001,15 +2479,14 @@ adreno_ft(struct kgsl_device *device, int ret = 0; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; - unsigned int timestamp; KGSL_FT_INFO(device, "Start Parameters: IB1: 0x%X, " "Bad context_id: %u, global_eop: 0x%x\n", ft_data->ib1, ft_data->context_id, ft_data->global_eop); - timestamp = rb->timestamp[KGSL_MEMSTORE_GLOBAL]; - KGSL_FT_INFO(device, "Last issued global timestamp: %x\n", timestamp); + KGSL_FT_INFO(device, "Last issued global timestamp: %x\n", + rb->global_ts); /* We may need to replay commands multiple times based on whether * multiple contexts hang the GPU */ @@ -2037,15 +2514,15 @@ adreno_ft(struct kgsl_device *device, /* Restore correct states after fault tolerance */ if (adreno_dev->drawctxt_active) - device->mmu.hwpagetable = - adreno_dev->drawctxt_active->pagetable; + kgsl_mmu_setstate(&device->mmu, + adreno_dev->drawctxt_active->pagetable, + adreno_dev->drawctxt_active->id); else - device->mmu.hwpagetable = device->mmu.defaultpagetable; - rb->timestamp[KGSL_MEMSTORE_GLOBAL] = timestamp; + kgsl_mmu_setstate(&device->mmu, + device->mmu.defaultpagetable, KGSL_MEMSTORE_GLOBAL); kgsl_sharedmem_writel(&device->memstore, KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, - eoptimestamp), - rb->timestamp[KGSL_MEMSTORE_GLOBAL]); + eoptimestamp), rb->global_ts); /* switch to NULL ctxt */ if (adreno_dev->drawctxt_active != NULL) @@ -2119,6 +2596,9 @@ adreno_dump_and_exec_ft(struct kgsl_device *device) } else { kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE); mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT); + mod_timer(&device->hang_timer, + (jiffies + + msecs_to_jiffies(KGSL_TIMEOUT_HANG_DETECT))); } complete_all(&device->ft_gate); } @@ -2127,6 +2607,264 @@ adreno_dump_and_exec_ft(struct kgsl_device *device) } EXPORT_SYMBOL(adreno_dump_and_exec_ft); +/** + * _ft_sysfs_store() - Common routine to write to FT sysfs files + * @buf: value to write + * @count: size of the value to write + * @sysfs_cfg: KGSL FT sysfs config to write + * + * This is a common routine to write to FT sysfs files. + */ +static int _ft_sysfs_store(const char *buf, size_t count, unsigned int *ptr) +{ + char temp[20]; + unsigned long val; + int rc; + + snprintf(temp, sizeof(temp), "%.*s", + (int)min(count, sizeof(temp) - 1), buf); + rc = kstrtoul(temp, 0, &val); + if (rc) + return rc; + + *ptr = val; + + return count; +} + +/** + * _get_adreno_dev() - Routine to get a pointer to adreno dev + * @dev: device ptr + * @attr: Device attribute + * @buf: value to write + * @count: size of the value to write + */ +struct adreno_device *_get_adreno_dev(struct device *dev) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + return device ? ADRENO_DEVICE(device) : NULL; +} + +/** + * _ft_policy_store() - Routine to configure FT policy + * @dev: device ptr + * @attr: Device attribute + * @buf: value to write + * @count: size of the value to write + * + * FT policy can be set to any of the options below. + * KGSL_FT_DISABLE -> BIT(0) Set to disable FT + * KGSL_FT_REPLAY -> BIT(1) Set to enable replay + * KGSL_FT_SKIPIB -> BIT(2) Set to skip IB + * KGSL_FT_SKIPFRAME -> BIT(3) Set to skip frame + * by default set FT policy to KGSL_FT_DEFAULT_POLICY + */ +static int _ft_policy_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adreno_device *adreno_dev = _get_adreno_dev(dev); + int ret; + if (adreno_dev == NULL) + return 0; + + mutex_lock(&adreno_dev->dev.mutex); + ret = _ft_sysfs_store(buf, count, &adreno_dev->ft_policy); + mutex_unlock(&adreno_dev->dev.mutex); + + return ret; +} + +/** + * _ft_policy_show() - Routine to read FT policy + * @dev: device ptr + * @attr: Device attribute + * @buf: value read + * + * This is a routine to read current FT policy + */ +static int _ft_policy_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct adreno_device *adreno_dev = _get_adreno_dev(dev); + if (adreno_dev == NULL) + return 0; + return snprintf(buf, PAGE_SIZE, "0x%X\n", adreno_dev->ft_policy); +} + +/** + * _ft_pagefault_policy_store() - Routine to configure FT + * pagefault policy + * @dev: device ptr + * @attr: Device attribute + * @buf: value to write + * @count: size of the value to write + * + * FT pagefault policy can be set to any of the options below. + * KGSL_FT_PAGEFAULT_INT_ENABLE -> BIT(0) set to enable pagefault INT + * KGSL_FT_PAGEFAULT_GPUHALT_ENABLE -> BIT(1) Set to enable GPU HALT on + * pagefaults. This stalls the GPU on a pagefault on IOMMU v1 HW. + * KGSL_FT_PAGEFAULT_LOG_ONE_PER_PAGE -> BIT(2) Set to log only one + * pagefault per page. + * KGSL_FT_PAGEFAULT_LOG_ONE_PER_INT -> BIT(3) Set to log only one + * pagefault per INT. + */ +static int _ft_pagefault_policy_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adreno_device *adreno_dev = _get_adreno_dev(dev); + int ret; + if (adreno_dev == NULL) + return 0; + + mutex_lock(&adreno_dev->dev.mutex); + ret = _ft_sysfs_store(buf, count, &adreno_dev->ft_pf_policy); + mutex_unlock(&adreno_dev->dev.mutex); + + return ret; +} + +/** + * _ft_pagefault_policy_show() - Routine to read FT pagefault + * policy + * @dev: device ptr + * @attr: Device attribute + * @buf: value read + * + * This is a routine to read current FT pagefault policy + */ +static int _ft_pagefault_policy_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct adreno_device *adreno_dev = _get_adreno_dev(dev); + if (adreno_dev == NULL) + return 0; + return snprintf(buf, PAGE_SIZE, "0x%X\n", adreno_dev->ft_pf_policy); +} + +/** + * _ft_fast_hang_detect_store() - Routine to configure FT fast + * hang detect policy + * @dev: device ptr + * @attr: Device attribute + * @buf: value to write + * @count: size of the value to write + * + * 0x1 - Enable fast hang detection + * 0x0 - Disable fast hang detection + */ +static int _ft_fast_hang_detect_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adreno_device *adreno_dev = _get_adreno_dev(dev); + int ret; + if (adreno_dev == NULL) + return 0; + + mutex_lock(&adreno_dev->dev.mutex); + ret = _ft_sysfs_store(buf, count, &adreno_dev->fast_hang_detect); + mutex_unlock(&adreno_dev->dev.mutex); + + return ret; + +} + +/** + * _ft_fast_hang_detect_show() - Routine to read FT fast + * hang detect policy + * @dev: device ptr + * @attr: Device attribute + * @buf: value read + */ +static int _ft_fast_hang_detect_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct adreno_device *adreno_dev = _get_adreno_dev(dev); + if (adreno_dev == NULL) + return 0; + return snprintf(buf, PAGE_SIZE, "%d\n", + (adreno_dev->fast_hang_detect ? 1 : 0)); +} + +/** + * _ft_long_ib_detect_store() - Routine to configure FT long IB + * detect policy + * @dev: device ptr + * @attr: Device attribute + * @buf: value to write + * @count: size of the value to write + * + * 0x0 - Enable long IB detection + * 0x1 - Disable long IB detection + */ +static int _ft_long_ib_detect_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adreno_device *adreno_dev = _get_adreno_dev(dev); + int ret; + if (adreno_dev == NULL) + return 0; + + mutex_lock(&adreno_dev->dev.mutex); + ret = _ft_sysfs_store(buf, count, &adreno_dev->long_ib_detect); + mutex_unlock(&adreno_dev->dev.mutex); + + return ret; + +} + +/** + * _ft_long_ib_detect_show() - Routine to read FT long IB + * detect policy + * @dev: device ptr + * @attr: Device attribute + * @buf: value read + */ +static int _ft_long_ib_detect_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct adreno_device *adreno_dev = _get_adreno_dev(dev); + if (adreno_dev == NULL) + return 0; + return snprintf(buf, PAGE_SIZE, "%d\n", + (adreno_dev->long_ib_detect ? 1 : 0)); +} + + +#define FT_DEVICE_ATTR(name) \ + DEVICE_ATTR(name, 0644, _ ## name ## _show, _ ## name ## _store); + +FT_DEVICE_ATTR(ft_policy); +FT_DEVICE_ATTR(ft_pagefault_policy); +FT_DEVICE_ATTR(ft_fast_hang_detect); +FT_DEVICE_ATTR(ft_long_ib_detect); + + +const struct device_attribute *ft_attr_list[] = { + &dev_attr_ft_policy, + &dev_attr_ft_pagefault_policy, + &dev_attr_ft_fast_hang_detect, + &dev_attr_ft_long_ib_detect, + NULL, +}; + +int adreno_ft_init_sysfs(struct kgsl_device *device) +{ + return kgsl_create_device_sysfs_files(device->dev, ft_attr_list); +} + +void adreno_ft_uninit_sysfs(struct kgsl_device *device) +{ + kgsl_remove_device_sysfs_files(device->dev, ft_attr_list); +} + static int adreno_getproperty(struct kgsl_device *device, enum kgsl_property_type type, void *value, @@ -2277,13 +3015,10 @@ static int adreno_ringbuffer_drain(struct kgsl_device *device, unsigned long wait; unsigned long timeout = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT); - if (!(rb->flags & KGSL_FLAGS_STARTED)) - return 0; - /* * The first time into the loop, wait for 100 msecs and kick wptr again * to ensure that the hardware has updated correctly. After that, kick - * it periodically every KGSL_TIMEOUT_PART msecs until the timeout + * it periodically every KGSL_TIMEOUT_HANG_DETECT msecs until the timeout * expires */ @@ -2295,7 +3030,7 @@ static int adreno_ringbuffer_drain(struct kgsl_device *device, if (adreno_ft_detect(device, regs)) return -ETIMEDOUT; - wait = jiffies + msecs_to_jiffies(KGSL_TIMEOUT_PART); + wait = jiffies + msecs_to_jiffies(KGSL_TIMEOUT_HANG_DETECT); } GSL_RB_GET_READPTR(rb, &rb->rptr); @@ -2312,11 +3047,10 @@ static int adreno_ringbuffer_drain(struct kgsl_device *device, /* Caller must hold the device mutex. */ int adreno_idle(struct kgsl_device *device) { - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - unsigned int rbbm_status; unsigned long wait_time; unsigned long wait_time_part; - unsigned int prev_reg_val[ft_detect_regs_count]; + unsigned int prev_reg_val[FT_DETECT_REGS_COUNT]; + memset(prev_reg_val, 0, sizeof(prev_reg_val)); @@ -2331,26 +3065,18 @@ int adreno_idle(struct kgsl_device *device) /* now, wait for the GPU to finish its operations */ wait_time = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT); - wait_time_part = jiffies + msecs_to_jiffies(KGSL_TIMEOUT_PART); + wait_time_part = jiffies + msecs_to_jiffies(KGSL_TIMEOUT_HANG_DETECT); while (time_before(jiffies, wait_time)) { - adreno_regread(device, adreno_dev->gpudev->reg_rbbm_status, - &rbbm_status); - if (adreno_is_a2xx(adreno_dev)) { - if (rbbm_status == 0x110) - return 0; - } else { - if (!(rbbm_status & 0x80000000)) - return 0; - } + if (adreno_isidle(device)) + return 0; - /* Dont wait for timeout, detect hang faster. - */ + /* Dont wait for timeout, detect hang faster. */ if (time_after(jiffies, wait_time_part)) { - wait_time_part = jiffies + - msecs_to_jiffies(KGSL_TIMEOUT_PART); - if ((adreno_ft_detect(device, prev_reg_val))) - goto err; + wait_time_part = jiffies + + msecs_to_jiffies(KGSL_TIMEOUT_HANG_DETECT); + if ((adreno_ft_detect(device, prev_reg_val))) + goto err; } } @@ -2359,7 +3085,7 @@ int adreno_idle(struct kgsl_device *device) KGSL_DRV_ERR(device, "spun too long waiting for RB to idle\n"); if (KGSL_STATE_DUMP_AND_FT != device->state && !adreno_dump_and_exec_ft(device)) { - wait_time = jiffies + ADRENO_IDLE_TIMEOUT; + wait_time = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT); goto retry; } return -ETIMEDOUT; @@ -2400,12 +3126,11 @@ static unsigned int adreno_isidle(struct kgsl_device *device) struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; - WARN_ON(device->state == KGSL_STATE_INIT); /* If the device isn't active, don't force it on. */ - if (device->state == KGSL_STATE_ACTIVE) { + if (kgsl_pwrctrl_isenabled(device)) { /* Is the ring buffer is empty? */ GSL_RB_GET_READPTR(rb, &rb->rptr); - if (!device->active_cnt && (rb->rptr == rb->wptr)) { + if (rb->rptr == rb->wptr) { /* * Are there interrupts pending? If so then pretend we * are not idle - this avoids the possiblity that we go @@ -2428,6 +3153,7 @@ static unsigned int adreno_isidle(struct kgsl_device *device) static int adreno_suspend_context(struct kgsl_device *device) { int status = 0; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); /* switch to NULL ctxt */ @@ -2447,7 +3173,10 @@ struct kgsl_memdesc *adreno_find_ctxtmem(struct kgsl_device *device, struct kgsl_context *context; struct adreno_context *adreno_context = NULL; int next = 0; + struct kgsl_memdesc *desc = NULL; + + read_lock(&device->context_lock); while (1) { context = idr_get_next(&device->context_idr, &next); if (context == NULL) @@ -2457,20 +3186,19 @@ struct kgsl_memdesc *adreno_find_ctxtmem(struct kgsl_device *device, if (kgsl_mmu_pt_equal(&device->mmu, adreno_context->pagetable, pt_base)) { - struct kgsl_memdesc *desc; - desc = &adreno_context->gpustate; if (kgsl_gpuaddr_in_memdesc(desc, gpuaddr, size)) - return desc; + break; desc = &adreno_context->context_gmem_shadow.gmemshadow; if (kgsl_gpuaddr_in_memdesc(desc, gpuaddr, size)) - return desc; + break; } next = next + 1; + desc = NULL; } - - return NULL; + read_unlock(&device->context_lock); + return desc; } struct kgsl_memdesc *adreno_find_region(struct kgsl_device *device, @@ -2539,6 +3267,8 @@ void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords, if (!in_interrupt()) kgsl_pre_hwaccess(device); + kgsl_trace_regwrite(device, offsetwords, value); + kgsl_cffdump_regwrite(device->id, offsetwords << 2, value); reg = (unsigned int *)(device->reg_virt + (offsetwords << 2)); @@ -2624,7 +3354,7 @@ static unsigned int adreno_check_hw_ts(struct kgsl_device *device, if (context && device->state != KGSL_STATE_SLUMBER) adreno_ringbuffer_issuecmds(device, context->devctxt, - KGSL_CMD_FLAGS_NONE, NULL, 0); + KGSL_CMD_FLAGS_GET_INT, NULL, 0); } return 0; @@ -2671,7 +3401,8 @@ unsigned int adreno_ft_detect(struct kgsl_device *device, unsigned int *prev_reg_val) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - unsigned int curr_reg_val[ft_detect_regs_count]; + struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; + unsigned int curr_reg_val[FT_DETECT_REGS_COUNT]; unsigned int fast_hang_detected = 1; unsigned int long_ib_detected = 1; unsigned int i; @@ -2688,7 +3419,12 @@ unsigned int adreno_ft_detect(struct kgsl_device *device, if (!adreno_dev->long_ib_detect) long_ib_detected = 0; - if (is_adreno_rbbm_status_idle(device)) { + if (!(adreno_dev->ringbuffer.flags & KGSL_FLAGS_STARTED)) + return 0; + + if (is_adreno_rbbm_status_idle(device) && + (kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED) + == rb->global_ts)) { /* * On A20X if the RPTR != WPTR and the device is idle, then @@ -2708,8 +3444,8 @@ unsigned int adreno_ft_detect(struct kgsl_device *device, } /* - * Time interval between hang detection should be KGSL_TIMEOUT_PART - * or more, if next hang detection is requested < KGSL_TIMEOUT_PART + * Time interval between hang detection should be KGSL_TIMEOUT_HANG_DETECT + * or more, if next hang detection is requested < KGSL_TIMEOUT_HANG_DETECT * from the last time do nothing. */ if ((next_hang_detect_time) && @@ -2717,10 +3453,10 @@ unsigned int adreno_ft_detect(struct kgsl_device *device, return 0; else next_hang_detect_time = (jiffies + - msecs_to_jiffies(KGSL_TIMEOUT_PART-1)); + msecs_to_jiffies(KGSL_TIMEOUT_HANG_DETECT)); /* Read the current Hang detect reg values here */ - for (i = 0; i < ft_detect_regs_count; i++) { + for (i = 0; i < FT_DETECT_REGS_COUNT; i++) { if (ft_detect_regs[i] == 0) continue; adreno_regread(device, ft_detect_regs[i], @@ -2743,8 +3479,10 @@ unsigned int adreno_ft_detect(struct kgsl_device *device, &curr_context_id, KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context)); - context = idr_find(&device->context_idr, - curr_context_id); + /* Make sure the memstore read has posted */ + mb(); + + context = kgsl_context_get(device, curr_context_id); if (context != NULL) { curr_context = context->devctxt; curr_context->ib_gpu_time_used = 0; @@ -2756,44 +3494,46 @@ unsigned int adreno_ft_detect(struct kgsl_device *device, mb(); + for (i = 0; i < FT_DETECT_REGS_COUNT; i++) { + if (curr_reg_val[i] != prev_reg_val[i]) { + fast_hang_detected = 0; + + /* Check for long IB here */ + if ((i >= + LONG_IB_DETECT_REG_INDEX_START) + && + (i <= + LONG_IB_DETECT_REG_INDEX_END)) + long_ib_detected = 0; + } + } + + if (fast_hang_detected) { + KGSL_FT_ERR(device, + "Proc %s, ctxt_id %d ts %d triggered fault tolerance" + " on global ts %d\n", + curr_context ? curr_context->pid_name : "", + curr_context ? curr_context->id : 0, + (kgsl_readtimestamp(device, context, + KGSL_TIMESTAMP_RETIRED) + 1), + curr_global_ts + 1); + kgsl_context_put(context); + context = NULL; + curr_context = NULL; + return 1; + } + if (curr_context != NULL) { - curr_context->ib_gpu_time_used += KGSL_TIMEOUT_PART; + curr_context->ib_gpu_time_used += KGSL_TIMEOUT_HANG_DETECT; KGSL_FT_INFO(device, "Proc %s used GPU Time %d ms on timestamp 0x%X\n", curr_context->pid_name, curr_context->ib_gpu_time_used, curr_global_ts+1); - for (i = 0; i < ft_detect_regs_count; i++) { - if (curr_reg_val[i] != prev_reg_val[i]) { - fast_hang_detected = 0; - - /* Check for long IB here */ - if ((i >= - LONG_IB_DETECT_REG_INDEX_START) - && - (i <= - LONG_IB_DETECT_REG_INDEX_END)) - long_ib_detected = 0; - } - } - - if (fast_hang_detected) { - KGSL_FT_ERR(device, - "Proc %s, ctxt_id %d ts %d triggered fault tolerance" - " on global ts %d\n", - curr_context->pid_name, curr_context->id - , (kgsl_readtimestamp(device, context, - KGSL_TIMESTAMP_RETIRED)+1), - curr_global_ts+1); - return 1; - } - if ((long_ib_detected) && (!(curr_context->flags & CTXT_FLAGS_NO_FAULT_TOLERANCE))) { - curr_context->ib_gpu_time_used += - KGSL_TIMEOUT_PART; if (curr_context->ib_gpu_time_used > KGSL_TIMEOUT_LONG_IB_DETECTION) { if (adreno_dev->long_ib_ts != @@ -2814,18 +3554,18 @@ unsigned int adreno_ft_detect(struct kgsl_device *device, curr_global_ts; curr_context->ib_gpu_time_used = 0; + kgsl_context_put(context); + context = NULL; + curr_context = NULL; return 1; } } } - } else { - KGSL_FT_ERR(device, - "Last context unknown id:%d\n", - curr_context_id); } } else { /* GPU is moving forward */ prev_global_ts = curr_global_ts; + kgsl_context_put(context); context = NULL; curr_context = NULL; adreno_dev->long_ib = 0; @@ -2835,53 +3575,11 @@ unsigned int adreno_ft_detect(struct kgsl_device *device, /* If hangs are not detected copy the current reg values * to previous values and return no hang */ - for (i = 0; i < ft_detect_regs_count; i++) + for (i = 0; i < FT_DETECT_REGS_COUNT; i++) prev_reg_val[i] = curr_reg_val[i]; return 0; } -/** - * adreno_handle_hang - Process a hang detected in adreno_waittimestamp - * @device - pointer to a KGSL device structure - * @context - pointer to the active KGSL context - * @timestamp - the timestamp that the process was waiting for - * - * Process a possible GPU hang and try fault tolerance from it - * cleanly - */ -static int adreno_handle_hang(struct kgsl_device *device, - struct kgsl_context *context, unsigned int timestamp) -{ - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - unsigned int context_id = _get_context_id(context); - unsigned int ts_issued; - unsigned int rptr; - - /* Do one last check to see if we somehow made it through */ - if (kgsl_check_timestamp(device, context, timestamp)) - return 0; - - ts_issued = adreno_dev->ringbuffer.timestamp[context_id]; - - adreno_regread(device, REG_CP_RB_RPTR, &rptr); - mb(); - - KGSL_DRV_WARN(device, - "Device hang detected while waiting for timestamp: " - "<%d:0x%x>, last submitted timestamp: <%d:0x%x>, " - "retired timestamp: <%d:0x%x>, wptr: 0x%x, rptr: 0x%x\n", - context_id, timestamp, context_id, ts_issued, context_id, - kgsl_readtimestamp(device, context, - KGSL_TIMESTAMP_RETIRED), - adreno_dev->ringbuffer.wptr, rptr); - - /* Return 0 after a successful fault tolerance */ - if (!adreno_dump_and_exec_ft(device)) - return 0; - - return -ETIMEDOUT; -} - static int _check_pending_timestamp(struct kgsl_device *device, struct kgsl_context *context, unsigned int timestamp) { @@ -2892,7 +3590,7 @@ static int _check_pending_timestamp(struct kgsl_device *device, if (context_id == KGSL_CONTEXT_INVALID) return -EINVAL; - ts_issued = adreno_dev->ringbuffer.timestamp[context_id]; + ts_issued = adreno_context_timestamp(context, &adreno_dev->ringbuffer); if (timestamp_cmp(timestamp, ts_issued) <= 0) return 0; @@ -2930,14 +3628,11 @@ static int adreno_waittimestamp(struct kgsl_device *device, struct adreno_context *adreno_ctx = context ? context->devctxt : NULL; struct kgsl_pwrctrl *pwr = &device->pwrctrl; unsigned int context_id = _get_context_id(context); - unsigned int prev_reg_val[ft_detect_regs_count]; unsigned int time_elapsed = 0; unsigned int wait; int ts_compare = 1; int io, ret = -ETIMEDOUT; - /* Get out early if the context has already been destroyed */ - if (context_id == KGSL_CONTEXT_INVALID) { KGSL_DRV_WARN(device, "context was detached"); return -EINVAL; @@ -2958,10 +3653,6 @@ static int adreno_waittimestamp(struct kgsl_device *device, context->wait_on_invalid_ts = false; } - - /* Clear the registers used for hang detection */ - memset(prev_reg_val, 0, sizeof(prev_reg_val)); - /* * On the first time through the loop only wait 100ms. * this gives enough time for the engine to start moving and oddly @@ -2988,12 +3679,6 @@ static int adreno_waittimestamp(struct kgsl_device *device, break; } - /* Check to see if the GPU is hung */ - if (adreno_ft_detect(device, prev_reg_val)) { - ret = adreno_handle_hang(device, context, timestamp); - break; - } - /* * For proper power accounting sometimes we need to call * io_wait_interruptible_timeout and sometimes we need to call @@ -3028,6 +3713,7 @@ static int adreno_waittimestamp(struct kgsl_device *device, } time_elapsed += wait; + /* If user specified timestamps are being used, wait at least * KGSL_SYNCOBJ_SERVER_TIMEOUT msecs for the user driver to * issue a IB for a timestamp before checking to see if the @@ -3049,6 +3735,7 @@ static int adreno_waittimestamp(struct kgsl_device *device, * Reset the invalid timestamp flag on a valid * wait */ + context->wait_on_invalid_ts = false; } } @@ -3085,13 +3772,14 @@ static unsigned int adreno_readtimestamp(struct kgsl_device *device, switch (type) { case KGSL_TIMESTAMP_QUEUED: { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; - timestamp = rb->timestamp[context_id]; + timestamp = adreno_context_timestamp(context, + &adreno_dev->ringbuffer); break; } case KGSL_TIMESTAMP_CONSUMED: - adreno_regread(device, REG_CP_TIMESTAMP, ×tamp); + kgsl_sharedmem_readl(&device->memstore, ×tamp, + KGSL_MEMSTORE_OFFSET(context_id, soptimestamp)); break; case KGSL_TIMESTAMP_RETIRED: kgsl_sharedmem_readl(&device->memstore, ×tamp, @@ -3107,27 +3795,58 @@ static unsigned int adreno_readtimestamp(struct kgsl_device *device, static long adreno_ioctl(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) { + struct kgsl_device *device = dev_priv->device; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); int result = 0; - struct kgsl_drawctxt_set_bin_base_offset *binbase; - struct kgsl_context *context; switch (cmd) { - case IOCTL_KGSL_DRAWCTXT_SET_BIN_BASE_OFFSET: + case IOCTL_KGSL_DRAWCTXT_SET_BIN_BASE_OFFSET: { + struct kgsl_drawctxt_set_bin_base_offset *binbase = data; + struct kgsl_context *context; + binbase = data; - context = kgsl_find_context(dev_priv, binbase->drawctxt_id); + context = kgsl_context_get_owner(dev_priv, + binbase->drawctxt_id); if (context) { adreno_drawctxt_set_bin_base_offset( - dev_priv->device, context, binbase->offset); + device, context, binbase->offset); } else { result = -EINVAL; - KGSL_DRV_ERR(dev_priv->device, + KGSL_DRV_ERR(device, "invalid drawctxt drawctxt_id %d " "device_id=%d\n", - binbase->drawctxt_id, dev_priv->device->id); + binbase->drawctxt_id, device->id); } - break; + kgsl_context_put(context); + break; + } + case IOCTL_KGSL_PERFCOUNTER_GET: { + struct kgsl_perfcounter_get *get = data; + result = adreno_perfcounter_get(adreno_dev, get->groupid, + get->countable, &get->offset, PERFCOUNTER_FLAG_NONE); + break; + } + case IOCTL_KGSL_PERFCOUNTER_PUT: { + struct kgsl_perfcounter_put *put = data; + result = adreno_perfcounter_put(adreno_dev, put->groupid, + put->countable); + break; + } + case IOCTL_KGSL_PERFCOUNTER_QUERY: { + struct kgsl_perfcounter_query *query = data; + result = adreno_perfcounter_query_group(adreno_dev, + query->groupid, query->countables, + query->count, &query->max_counters); + break; + } + case IOCTL_KGSL_PERFCOUNTER_READ: { + struct kgsl_perfcounter_read *read = data; + result = adreno_perfcounter_read_group(adreno_dev, + read->reads, read->count); + break; + } default: KGSL_DRV_INFO(dev_priv->device, "invalid ioctl code %08x\n", cmd); @@ -3149,15 +3868,20 @@ static void adreno_power_stats(struct kgsl_device *device, { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct kgsl_pwrctrl *pwr = &device->pwrctrl; - unsigned int cycles; + unsigned int cycles = 0; - /* Get the busy cycles counted since the counter was last reset */ - /* Calling this function also resets and restarts the counter */ - - cycles = adreno_dev->gpudev->busy_cycles(adreno_dev); + /* + * Get the busy cycles counted since the counter was last reset. + * If we're not currently active, there shouldn't have been + * any cycles since the last time this function was called. + */ + if (device->state == KGSL_STATE_ACTIVE) + cycles = adreno_dev->gpudev->busy_cycles(adreno_dev); - /* In order to calculate idle you have to have run the algorithm * - * at least once to get a start time. */ + /* + * In order to calculate idle you have to have run the algorithm + * at least once to get a start time. + */ if (pwr->time != 0) { s64 tmp = ktime_to_us(ktime_get()); stats->total_time = tmp - pwr->time; @@ -3204,6 +3928,7 @@ static const struct kgsl_functable adreno_functable = { .idle = adreno_idle, .isidle = adreno_isidle, .suspend_context = adreno_suspend_context, + .init = adreno_init, .start = adreno_start, .stop = adreno_stop, .getproperty = adreno_getproperty, diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 8d16bfa0..a6d14ef0 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2012,2014 The Linux Foundation. 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 and @@ -19,6 +19,8 @@ #include "kgsl_iommu.h" #include +#include "a3xx_reg.h" + #define DEVICE_3D_NAME "kgsl-3d" #define DEVICE_3D0_NAME "kgsl-3d0" @@ -34,15 +36,18 @@ #define KGSL_CMD_FLAGS_NONE 0x00000000 #define KGSL_CMD_FLAGS_PMODE 0x00000001 #define KGSL_CMD_FLAGS_INTERNAL_ISSUE 0x00000002 +#define KGSL_CMD_FLAGS_GET_INT 0x00000004 #define KGSL_CMD_FLAGS_EOF 0x00000100 /* Command identifiers */ #define KGSL_CONTEXT_TO_MEM_IDENTIFIER 0x2EADBEEF #define KGSL_CMD_IDENTIFIER 0x2EEDFACE +#define KGSL_CMD_INTERNAL_IDENTIFIER 0x2EEDD00D #define KGSL_START_OF_IB_IDENTIFIER 0x2EADEABE #define KGSL_END_OF_IB_IDENTIFIER 0x2ABEDEAD #define KGSL_END_OF_FRAME_IDENTIFIER 0x2E0F2E0F #define KGSL_NOP_IB_IDENTIFIER 0x20F20F20 +#define KGSL_NOP_DATA_FILLER 0xFEEDFACE #ifdef CONFIG_MSM_SCM #define ADRENO_DEFAULT_PWRSCALE_POLICY (&kgsl_pwrscale_policy_tz) @@ -52,8 +57,6 @@ #define ADRENO_DEFAULT_PWRSCALE_POLICY NULL #endif -void adreno_debugfs_init(struct kgsl_device *device); - #define ADRENO_ISTORE_START 0x5000 /* Istore offset */ #define ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW 50 @@ -111,6 +114,45 @@ struct adreno_device { unsigned int gpulist_index; struct ocmem_buf *ocmem_hdl; unsigned int ocmem_base; + unsigned int gpu_cycles; +}; + +#define PERFCOUNTER_FLAG_NONE 0x0 +#define PERFCOUNTER_FLAG_KERNEL 0x1 + +/* Structs to maintain the list of active performance counters */ + +/** + * struct adreno_perfcount_register: register state + * @countable: countable the register holds + * @refcount: number of users of the register + * @offset: register hardware offset + */ +struct adreno_perfcount_register { + unsigned int countable; + unsigned int refcount; + unsigned int offset; + unsigned int flags; +}; + +/** + * struct adreno_perfcount_group: registers for a hardware group + * @regs: available registers for this group + * @reg_count: total registers for this group + */ +struct adreno_perfcount_group { + struct adreno_perfcount_register *regs; + unsigned int reg_count; +}; + +/** + * adreno_perfcounts: all available perfcounter groups + * @groups: available groups for this device + * @group_count: total groups for this device + */ +struct adreno_perfcounters { + struct adreno_perfcount_group *groups; + unsigned int group_count; }; struct adreno_gpudev { @@ -124,6 +166,8 @@ struct adreno_gpudev { /* keeps track of when we need to execute the draw workaround code */ int ctx_switches_since_last_draw; + struct adreno_perfcounters *perfcounters; + /* GPU specific function hooks */ int (*ctxt_create)(struct adreno_device *, struct adreno_context *); void (*ctxt_save)(struct adreno_device *, struct adreno_context *); @@ -134,9 +178,15 @@ struct adreno_gpudev { void (*irq_control)(struct adreno_device *, int); unsigned int (*irq_pending)(struct adreno_device *); void * (*snapshot)(struct adreno_device *, void *, int *, int); - void (*rb_init)(struct adreno_device *, struct adreno_ringbuffer *); + int (*rb_init)(struct adreno_device *, struct adreno_ringbuffer *); + void (*perfcounter_init)(struct adreno_device *); void (*start)(struct adreno_device *); unsigned int (*busy_cycles)(struct adreno_device *); + void (*perfcounter_enable)(struct adreno_device *, unsigned int group, + unsigned int counter, unsigned int countable); + uint64_t (*perfcounter_read)(struct adreno_device *adreno_dev, + unsigned int group, unsigned int counter, + unsigned int offset); }; /* @@ -179,6 +229,8 @@ struct adreno_ft_data { unsigned int replay_for_snapshot; }; +#define FT_DETECT_REGS_COUNT 12 + /* Fault Tolerance policy flags */ #define KGSL_FT_DISABLE BIT(0) #define KGSL_FT_REPLAY BIT(1) @@ -188,10 +240,10 @@ struct adreno_ft_data { #define KGSL_FT_DEFAULT_POLICY (KGSL_FT_REPLAY + KGSL_FT_SKIPIB) /* Pagefault policy flags */ -#define KGSL_FT_PAGEFAULT_INT_ENABLE 0x00000001 -#define KGSL_FT_PAGEFAULT_GPUHALT_ENABLE 0x00000002 -#define KGSL_FT_PAGEFAULT_LOG_ONE_PER_PAGE 0x00000004 -#define KGSL_FT_PAGEFAULT_LOG_ONE_PER_INT 0x00000008 +#define KGSL_FT_PAGEFAULT_INT_ENABLE BIT(0) +#define KGSL_FT_PAGEFAULT_GPUHALT_ENABLE BIT(1) +#define KGSL_FT_PAGEFAULT_LOG_ONE_PER_PAGE BIT(2) +#define KGSL_FT_PAGEFAULT_LOG_ONE_PER_INT BIT(3) #define KGSL_FT_PAGEFAULT_DEFAULT_POLICY (KGSL_FT_PAGEFAULT_INT_ENABLE + \ KGSL_FT_PAGEFAULT_GPUHALT_ENABLE) @@ -217,7 +269,6 @@ extern const unsigned int a330_registers[]; extern const unsigned int a330_registers_count; extern unsigned int ft_detect_regs[]; -extern const unsigned int ft_detect_regs_count; int adreno_idle(struct kgsl_device *device); @@ -227,6 +278,8 @@ void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords, unsigned int value); int adreno_dump(struct kgsl_device *device, int manual); +unsigned int adreno_a3xx_rbbm_clock_ctl_default(struct adreno_device + *adreno_dev); struct kgsl_memdesc *adreno_find_region(struct kgsl_device *device, unsigned int pt_base, @@ -250,6 +303,16 @@ void adreno_dump_rb(struct kgsl_device *device, const void *buf, unsigned int adreno_ft_detect(struct kgsl_device *device, unsigned int *prev_reg_val); +int adreno_perfcounter_get(struct adreno_device *adreno_dev, + unsigned int groupid, unsigned int countable, unsigned int *offset, + unsigned int flags); + +int adreno_perfcounter_put(struct adreno_device *adreno_dev, + unsigned int groupid, unsigned int countable); + +int adreno_ft_init_sysfs(struct kgsl_device *device); +void adreno_ft_uninit_sysfs(struct kgsl_device *device); + static inline int adreno_is_a200(struct adreno_device *adreno_dev) { return (adreno_dev->gpurev == ADRENO_REV_A200); @@ -311,12 +374,32 @@ static inline int adreno_is_a330(struct adreno_device *adreno_dev) return (adreno_dev->gpurev == ADRENO_REV_A330); } +static inline int adreno_is_a330v2(struct adreno_device *adreno_dev) +{ + return ((adreno_dev->gpurev == ADRENO_REV_A330) && + (ADRENO_CHIPID_PATCH(adreno_dev->chip_id) > 0)); +} + static inline int adreno_rb_ctxtswitch(unsigned int *cmd) { return (cmd[0] == cp_nop_packet(1) && cmd[1] == KGSL_CONTEXT_TO_MEM_IDENTIFIER); } +static inline int adreno_context_timestamp(struct kgsl_context *k_ctxt, + struct adreno_ringbuffer *rb) +{ + struct adreno_context *a_ctxt = NULL; + + if (k_ctxt) + a_ctxt = k_ctxt->devctxt; + + if (a_ctxt && a_ctxt->flags & CTXT_FLAGS_PER_CONTEXT_TS) + return a_ctxt->timestamp; + + return rb->global_ts; +} + /** * adreno_encode_istore_size - encode istore size in CP format * @adreno_dev - The 3D device. @@ -401,6 +484,11 @@ static inline int adreno_add_read_cmds(struct kgsl_device *device, *cmds++ = val; *cmds++ = 0xFFFFFFFF; *cmds++ = 0xFFFFFFFF; + + /* WAIT_REG_MEM turns back on protected mode - push it off */ + *cmds++ = cp_type3_packet(CP_SET_PROTECTED_MODE, 1); + *cmds++ = 0; + cmds += __adreno_add_idle_indirect_cmds(cmds, nop_gpuaddr); return cmds - start; } @@ -427,4 +515,55 @@ static inline int adreno_add_idle_cmds(struct adreno_device *adreno_dev, return cmds - start; } +#ifdef CONFIG_DEBUG_FS +void adreno_debugfs_init(struct kgsl_device *device); +#else +static inline void adreno_debugfs_init(struct kgsl_device *device) { } +#endif + +/** + * adreno_set_protected_registers() - Protect the specified range of registers + * from being accessed by the GPU + * @device: pointer to the KGSL device + * @index: Pointer to the index of the protect mode register to write to + * @reg: Starting dword register to write + * @mask: Size of the mask to protect (A3xx# of registers = 2 ** mask, + * A2xx# Contains the address mask used to mask the protect base address) + * + * Add the range of registers to the list of protected mode registers that will + * cause an exception if the GPU accesses them. There are 16 available + * protected mode registers. Index is used to specify which register to write + * to - the intent is to call this function multiple times with the same index + * pointer for each range and the registers will be magically programmed in + * incremental fashion + */ +static inline void adreno_set_protected_registers(struct kgsl_device *device, + unsigned int *index, unsigned int reg, int mask) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + unsigned int val; + unsigned int protect_reg_offset; + + /* There are only 16 registers available */ + BUG_ON(*index >= 16); + + if (adreno_is_a3xx(adreno_dev)) { + val = 0x60000000 | ((mask & 0x1F) << 24) | + ((reg << 2) & 0x1FFFF); + protect_reg_offset = A3XX_CP_PROTECT_REG_0; + } else if (adreno_is_a2xx(adreno_dev)) { + val = 0xc0000000 | ((reg << 2) << 16) | (mask & 0xffff); + protect_reg_offset = REG_RBBM_PROTECT_0; + } else { + return; + } + + /* + * Write the protection range to the next available protection + * register + */ + + kgsl_regwrite(device, protect_reg_offset + *index, val); + *index = *index + 1; +} #endif /*__ADRENO_H */ diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c index d88494f2..05aa5056 100644 --- a/drivers/gpu/msm/adreno_a2xx.c +++ b/drivers/gpu/msm/adreno_a2xx.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2013, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2014, The Linux Foundation. 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 and @@ -1515,18 +1515,26 @@ static void a2xx_drawctxt_save(struct adreno_device *adreno_dev, "Current active context has caused gpu hang\n"); if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { - + kgsl_cffdump_syncmem(NULL, &context->gpustate, + context->reg_save[1], + context->reg_save[2] << 2, true); /* save registers and constants. */ adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_NONE, context->reg_save, 3); if (context->flags & CTXT_FLAGS_SHADER_SAVE) { + kgsl_cffdump_syncmem(NULL, &context->gpustate, + context->shader_save[1], + context->shader_save[2] << 2, true); /* save shader partitioning and instructions. */ adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_PMODE, context->shader_save, 3); + kgsl_cffdump_syncmem(NULL, &context->gpustate, + context->shader_fixup[1], + context->shader_fixup[2] << 2, true); /* * fixup shader partitioning parameter for * SET_SHADER_BASES. @@ -1541,6 +1549,9 @@ static void a2xx_drawctxt_save(struct adreno_device *adreno_dev, if ((context->flags & CTXT_FLAGS_GMEM_SAVE) && (context->flags & CTXT_FLAGS_GMEM_SHADOW)) { + kgsl_cffdump_syncmem(NULL, &context->gpustate, + context->context_gmem_shadow.gmem_save[1], + context->context_gmem_shadow.gmem_save[2] << 2, true); /* save gmem. * (note: changes shader. shader must already be saved.) */ @@ -1548,6 +1559,10 @@ static void a2xx_drawctxt_save(struct adreno_device *adreno_dev, KGSL_CMD_FLAGS_PMODE, context->context_gmem_shadow.gmem_save, 3); + kgsl_cffdump_syncmem(NULL, &context->gpustate, + context->chicken_restore[1], + context->chicken_restore[2] << 2, true); + /* Restore TP0_CHICKEN */ if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { adreno_ringbuffer_issuecmds(device, context, @@ -1565,7 +1580,9 @@ static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev, struct adreno_context *context) { struct kgsl_device *device = &adreno_dev->dev; - unsigned int cmds[5]; + unsigned int link[10]; + unsigned int *cmds = &link[0]; + unsigned int sizedwords = 0; if (context == NULL) { /* No context - set the default apgetable and thats it */ @@ -1576,31 +1593,42 @@ static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev, KGSL_CTXT_INFO(device, "context flags %08x\n", context->flags); - cmds[0] = cp_nop_packet(1); - cmds[1] = KGSL_CONTEXT_TO_MEM_IDENTIFIER; - cmds[2] = cp_type3_packet(CP_MEM_WRITE, 2); - cmds[3] = device->memstore.gpuaddr + + /* Reset VSC Binning cntrol Regiseter */ + if (adreno_is_a225(adreno_dev)) { + *cmds++ = cp_type0_packet(REG_VSC_BINNING_ENABLE, 1); + *cmds++ = 0; + } + + *cmds++ = cp_nop_packet(1); + *cmds++ = KGSL_CONTEXT_TO_MEM_IDENTIFIER; + *cmds++ = cp_type3_packet(CP_MEM_WRITE, 2); + *cmds++ = device->memstore.gpuaddr + KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context); - cmds[4] = context->id; + *cmds++ = context->id; + sizedwords = (cmds - &link[0]); + adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_NONE, - cmds, 5); + &link[0], sizedwords); kgsl_mmu_setstate(&device->mmu, context->pagetable, context->id); -#ifndef CONFIG_MSM_KGSL_CFF_DUMP_NO_CONTEXT_MEM_DUMP - kgsl_cffdump_syncmem(NULL, &context->gpustate, - context->gpustate.gpuaddr, LCC_SHADOW_SIZE + - REG_SHADOW_SIZE + CMD_BUFFER_SIZE + TEX_SHADOW_SIZE, false); -#endif - /* restore gmem. * (note: changes shader. shader must not already be restored.) */ if (context->flags & CTXT_FLAGS_GMEM_RESTORE) { + kgsl_cffdump_syncmem(NULL, &context->gpustate, + context->context_gmem_shadow.gmem_restore[1], + context->context_gmem_shadow.gmem_restore[2] << 2, + true); + adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_PMODE, context->context_gmem_shadow.gmem_restore, 3); if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { + kgsl_cffdump_syncmem(NULL, &context->gpustate, + context->chicken_restore[1], + context->chicken_restore[2] << 2, true); + /* Restore TP0_CHICKEN */ adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_NONE, @@ -1611,6 +1639,9 @@ static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev, } if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { + kgsl_cffdump_syncmem(NULL, &context->gpustate, + context->reg_restore[1], + context->reg_restore[2] << 2, true); /* restore registers and constants. */ adreno_ringbuffer_issuecmds(device, context, @@ -1618,6 +1649,10 @@ static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev, /* restore shader instructions & partitioning. */ if (context->flags & CTXT_FLAGS_SHADER_RESTORE) { + kgsl_cffdump_syncmem(NULL, &context->gpustate, + context->shader_restore[1], + context->shader_restore[2] << 2, true); + adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_NONE, context->shader_restore, 3); @@ -1643,7 +1678,8 @@ static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev, * managing the interrupts */ -#define RBBM_INT_MASK RBBM_INT_CNTL__RDERR_INT_MASK +#define RBBM_INT_MASK (RBBM_INT_CNTL__RDERR_INT_MASK | \ + RBBM_INT_CNTL__PROTECT_INT_MASK) #define CP_INT_MASK \ (CP_INT_CNTL__T0_PACKET_IN_IB_MASK | \ @@ -1764,6 +1800,16 @@ static void a2xx_rbbm_intrcallback(struct kgsl_device *device) KGSL_DRV_CRIT(device, "rbbm read error interrupt: %s reg: %04X\n", source, addr); + } else if (status & RBBM_INT_CNTL__PROTECT_INT_MASK) { + adreno_regread(device, REG_RBBM_READ_ERROR, &rderr); + source = (rderr & RBBM_READ_ERROR_REQUESTER) + ? "host" : "cp"; + /* convert to dword address */ + addr = (rderr & RBBM_READ_ERROR_ADDRESS_MASK) >> 2; + KGSL_DRV_CRIT(device, + "RBBM | Protected mode error |%s|%s| addr=%x\n", + rderr & (1 << 31) ? "WRITE" : "READ", source, + addr); } status &= RBBM_INT_MASK; @@ -1828,13 +1874,16 @@ static unsigned int a2xx_irq_pending(struct adreno_device *adreno_dev) MASTER_INT_SIGNAL__RBBM_INT_STAT)) ? 1 : 0; } -static void a2xx_rb_init(struct adreno_device *adreno_dev, +static int a2xx_rb_init(struct adreno_device *adreno_dev, struct adreno_ringbuffer *rb) { unsigned int *cmds, cmds_gpu; /* ME_INIT */ cmds = adreno_ringbuffer_allocspace(rb, NULL, 19); + if (cmds == NULL) + return -ENOMEM; + cmds_gpu = rb->buffer_desc.gpuaddr + sizeof(uint)*(rb->wptr-19); GSL_RB_WRITE(cmds, cmds_gpu, cp_type3_packet(CP_ME_INIT, 18)); @@ -1874,19 +1923,18 @@ static void a2xx_rb_init(struct adreno_device *adreno_dev, /* NQ and External Memory Swap */ GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); - /* Protected mode error checking - * If iommu is used then protection needs to be turned off - * to enable context bank switching */ - if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) - GSL_RB_WRITE(cmds, cmds_gpu, 0); - else - GSL_RB_WRITE(cmds, cmds_gpu, GSL_RB_PROTECTED_MODE_CONTROL); + + /* Enable Protected mode registers for A2xx */ + GSL_RB_WRITE(cmds, cmds_gpu, GSL_RB_PROTECTED_MODE_CONTROL); + /* Disable header dumping and Header dump address */ GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); /* Header dump size */ GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); adreno_ringbuffer_submit(rb); + + return 0; } static unsigned int a2xx_busy_cycles(struct adreno_device *adreno_dev) @@ -1938,6 +1986,38 @@ static void a2xx_gmeminit(struct adreno_device *adreno_dev) adreno_regwrite(device, REG_RB_EDRAM_INFO, rb_edram_info.val); } +/** + * a2xx_protect_init() - Initializes register protection on a3xx + * @device: Pointer to the device structure + * Performs register writes to enable protected access to sensitive + * registers + */ +static void a2xx_protect_init(struct kgsl_device *device) +{ + int index = 0; + + /* Enable access protection to privileged registers */ + kgsl_regwrite(device, REG_RBBM_INT_CNTL, + RBBM_INT_CNTL__PROTECT_INT_MASK); + + /* RBBM_SOFT_RESET register */ + adreno_set_protected_registers(device, &index, 0x03C, 0x0); + /* RBBM_INT_CNTL & RBBM_INT_STATUS */ + adreno_set_protected_registers(device, &index, 0x3B4, 0x1); + /* RBBM_PROTECT_ registers */ + adreno_set_protected_registers(device, &index, 0x140, 0xF); + + /* CP registers */ + adreno_set_protected_registers(device, &index, 0x1C0, 0x20); + /* CP_STATE_DEBUG_INDEX & CP_STATE_DEBUG_DATA */ + adreno_set_protected_registers(device, &index, 0x1EC, 0x1); + /* CP_ME_CNTL,CP_ME_STATUS, CP_ME_RAM_ and CP_DEBUG registers */ + adreno_set_protected_registers(device, &index, 0x1F6, 0x7); + + /* MH_MMU_PT_BASE register */ + adreno_set_protected_registers(device, &index, 0x042, 0x0); +} + static void a2xx_start(struct adreno_device *adreno_dev) { struct kgsl_device *device = &adreno_dev->dev; @@ -2000,6 +2080,9 @@ static void a2xx_start(struct adreno_device *adreno_dev) adreno_regwrite(device, REG_RBBM_DEBUG, 0x00080000); + /* Turn on protection */ + a2xx_protect_init(device); + /* Make sure interrupts are disabled */ adreno_regwrite(device, REG_RBBM_INT_CNTL, 0); adreno_regwrite(device, REG_CP_INT_CNTL, 0); diff --git a/drivers/gpu/msm/adreno_a2xx_snapshot.c b/drivers/gpu/msm/adreno_a2xx_snapshot.c index 75795b13..2c86f82d 100644 --- a/drivers/gpu/msm/adreno_a2xx_snapshot.c +++ b/drivers/gpu/msm/adreno_a2xx_snapshot.c @@ -224,6 +224,31 @@ static int a2xx_snapshot_miudebug(struct kgsl_device *device, void *snapshot, return DEBUG_SECTION_SZ(MIUDEBUG_COUNT); } +/* Snapshot the istore memory */ +static int a2xx_snapshot_istore(struct kgsl_device *device, void *snapshot, + int remain, void *priv) +{ + struct kgsl_snapshot_istore *header = snapshot; + unsigned int *data = snapshot + sizeof(*header); + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + int count, i; + + count = adreno_dev->istore_size * adreno_dev->instruction_size; + + if (remain < (count * 4) + sizeof(*header)) { + KGSL_DRV_ERR(device, + "snapshot: Not enough memory for the istore section"); + return 0; + } + + header->count = adreno_dev->istore_size; + + for (i = 0; i < count; i++) + kgsl_regread(device, ADRENO_ISTORE_START + i, &data[i]); + + return (count * 4) + sizeof(*header); +} + /* A2XX GPU snapshot function - this is where all of the A2XX specific * bits and pieces are grabbed into the snapshot memory */ @@ -338,6 +363,18 @@ void *a2xx_snapshot(struct adreno_device *adreno_dev, void *snapshot, } } + /* + * Only dump the istore on a hang - reading it on a running system + * has a non zero chance of hanging the GPU. + */ + + if (adreno_is_a2xx(adreno_dev) && hang) { + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_ISTORE, snapshot, remain, + a2xx_snapshot_istore, NULL); + } + + /* Reset the clock gating */ adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, pmoverride); diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c index 019a6c78..9c1341d7 100644 --- a/drivers/gpu/msm/adreno_a3xx.c +++ b/drivers/gpu/msm/adreno_a3xx.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2014, The Linux Foundation. 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 and @@ -445,6 +445,21 @@ static void build_regconstantsave_cmds(struct adreno_device *adreno_dev, tmp_ctx.cmd = cmd; } +unsigned int adreno_a3xx_rbbm_clock_ctl_default(struct adreno_device + *adreno_dev) +{ + if (adreno_is_a305(adreno_dev)) + return A305_RBBM_CLOCK_CTL_DEFAULT; + else if (adreno_is_a320(adreno_dev)) + return A320_RBBM_CLOCK_CTL_DEFAULT; + else if (adreno_is_a330v2(adreno_dev)) + return A330v2_RBBM_CLOCK_CTL_DEFAULT; + else if (adreno_is_a330(adreno_dev)) + return A330_RBBM_CLOCK_CTL_DEFAULT; + + BUG_ON(1); +} + /* Copy GMEM contents to system memory shadow. */ static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev, struct adreno_context *drawctxt, @@ -454,7 +469,7 @@ static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev, unsigned int *start = cmds; *cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1); - *cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT; + *cmds++ = adreno_a3xx_rbbm_clock_ctl_default(adreno_dev); *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); *cmds++ = CP_REG(A3XX_RB_MODE_CONTROL); @@ -1250,7 +1265,7 @@ static unsigned int *build_sys2gmem_cmds(struct adreno_device *adreno_dev, unsigned int *start = cmds; *cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1); - *cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT; + *cmds++ = adreno_a3xx_rbbm_clock_ctl_default(adreno_dev); *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); *cmds++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG); @@ -2400,6 +2415,11 @@ static void a3xx_drawctxt_save(struct adreno_device *adreno_dev, * already be saved.) */ + kgsl_cffdump_syncmem(NULL, + &context->gpustate, + context->context_gmem_shadow.gmem_save[1], + context->context_gmem_shadow.gmem_save[2] << 2, true); + adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_PMODE, context->context_gmem_shadow. @@ -2439,6 +2459,12 @@ static void a3xx_drawctxt_restore(struct adreno_device *adreno_dev, */ if (context->flags & CTXT_FLAGS_GMEM_RESTORE) { + kgsl_cffdump_syncmem(NULL, + &context->gpustate, + context->context_gmem_shadow.gmem_restore[1], + context->context_gmem_shadow.gmem_restore[2] << 2, + true); + adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_PMODE, context->context_gmem_shadow. @@ -2471,11 +2497,14 @@ static void a3xx_drawctxt_restore(struct adreno_device *adreno_dev, } } -static void a3xx_rb_init(struct adreno_device *adreno_dev, +static int a3xx_rb_init(struct adreno_device *adreno_dev, struct adreno_ringbuffer *rb) { unsigned int *cmds, cmds_gpu; cmds = adreno_ringbuffer_allocspace(rb, NULL, 18); + if (cmds == NULL) + return -ENOMEM; + cmds_gpu = rb->buffer_desc.gpuaddr + sizeof(uint) * (rb->wptr - 18); GSL_RB_WRITE(cmds, cmds_gpu, cp_type3_packet(CP_ME_INIT, 17)); @@ -2493,12 +2522,14 @@ static void a3xx_rb_init(struct adreno_device *adreno_dev, GSL_RB_WRITE(cmds, cmds_gpu, 0x00000001); GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); - /* Protected mode control - turned off for A3XX */ - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + /* Enable protected mode registers for A3XX */ + GSL_RB_WRITE(cmds, cmds_gpu, 0x20000000); GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); adreno_ringbuffer_submit(rb); + + return 0; } static void a3xx_err_callback(struct adreno_device *adreno_dev, int bit) @@ -2554,9 +2585,16 @@ static void a3xx_err_callback(struct adreno_device *adreno_dev, int bit) case A3XX_INT_CP_HW_FAULT: err = "ringbuffer hardware fault"; break; - case A3XX_INT_CP_REG_PROTECT_FAULT: - err = "ringbuffer protected mode error interrupt"; - break; + case A3XX_INT_CP_REG_PROTECT_FAULT: { + unsigned int reg; + kgsl_regread(device, A3XX_CP_PROTECT_STATUS, ®); + + KGSL_DRV_CRIT(device, + "CP | Protected mode error| %s | addr=%x\n", + reg & (1 << 24) ? "WRITE" : "READ", + (reg & 0x1FFFF) >> 2); + return; + } case A3XX_INT_CP_AHB_ERROR_HALT: err = "ringbuffer AHB error interrupt"; break; @@ -2583,6 +2621,272 @@ static void a3xx_cp_callback(struct adreno_device *adreno_dev, int irq) queue_work(device->work_queue, &device->ts_expired_ws); } +/** + * struct a3xx_perfcounter_register - Define a performance counter register + * @load_bit: the bit to set in RBBM_LOAD_CMD0/RBBM_LOAD_CMD1 to force the RBBM + * to load the reset value into the appropriate counter + * @select: The dword offset of the register to write the selected + * countable into + */ + +struct a3xx_perfcounter_register { + unsigned int load_bit; + unsigned int select; +}; + +static struct a3xx_perfcounter_register a3xx_perfcounter_reg_cp[] = { + { 0, A3XX_CP_PERFCOUNTER_SELECT }, +}; + +static struct a3xx_perfcounter_register a3xx_perfcounter_reg_rbbm[] = { + { 1, A3XX_RBBM_PERFCOUNTER0_SELECT }, + { 2, A3XX_RBBM_PERFCOUNTER1_SELECT }, +}; + +static struct a3xx_perfcounter_register a3xx_perfcounter_reg_pc[] = { + { 3, A3XX_PC_PERFCOUNTER0_SELECT }, + { 4, A3XX_PC_PERFCOUNTER1_SELECT }, + { 5, A3XX_PC_PERFCOUNTER2_SELECT }, + { 6, A3XX_PC_PERFCOUNTER3_SELECT }, +}; + +static struct a3xx_perfcounter_register a3xx_perfcounter_reg_vfd[] = { + { 7, A3XX_VFD_PERFCOUNTER0_SELECT }, + { 8, A3XX_VFD_PERFCOUNTER1_SELECT }, +}; + +static struct a3xx_perfcounter_register a3xx_perfcounter_reg_hlsq[] = { + { 9, A3XX_HLSQ_PERFCOUNTER0_SELECT }, + { 10, A3XX_HLSQ_PERFCOUNTER1_SELECT }, + { 11, A3XX_HLSQ_PERFCOUNTER2_SELECT }, + { 12, A3XX_HLSQ_PERFCOUNTER3_SELECT }, + { 13, A3XX_HLSQ_PERFCOUNTER4_SELECT }, + { 14, A3XX_HLSQ_PERFCOUNTER5_SELECT }, +}; + +static struct a3xx_perfcounter_register a3xx_perfcounter_reg_vpc[] = { + { 15, A3XX_VPC_PERFCOUNTER0_SELECT }, + { 16, A3XX_VPC_PERFCOUNTER1_SELECT }, +}; + +static struct a3xx_perfcounter_register a3xx_perfcounter_reg_tse[] = { + { 17, A3XX_GRAS_PERFCOUNTER0_SELECT }, + { 18, A3XX_GRAS_PERFCOUNTER1_SELECT }, +}; + +static struct a3xx_perfcounter_register a3xx_perfcounter_reg_ras[] = { + { 19, A3XX_GRAS_PERFCOUNTER2_SELECT }, + { 20, A3XX_GRAS_PERFCOUNTER3_SELECT }, +}; + +static struct a3xx_perfcounter_register a3xx_perfcounter_reg_uche[] = { + { 21, A3XX_UCHE_PERFCOUNTER0_SELECT }, + { 22, A3XX_UCHE_PERFCOUNTER1_SELECT }, + { 23, A3XX_UCHE_PERFCOUNTER2_SELECT }, + { 24, A3XX_UCHE_PERFCOUNTER3_SELECT }, + { 25, A3XX_UCHE_PERFCOUNTER4_SELECT }, + { 26, A3XX_UCHE_PERFCOUNTER5_SELECT }, +}; + +static struct a3xx_perfcounter_register a3xx_perfcounter_reg_tp[] = { + { 27, A3XX_TP_PERFCOUNTER0_SELECT }, + { 28, A3XX_TP_PERFCOUNTER1_SELECT }, + { 29, A3XX_TP_PERFCOUNTER2_SELECT }, + { 30, A3XX_TP_PERFCOUNTER3_SELECT }, + { 31, A3XX_TP_PERFCOUNTER4_SELECT }, + { 32, A3XX_TP_PERFCOUNTER5_SELECT }, +}; + +static struct a3xx_perfcounter_register a3xx_perfcounter_reg_sp[] = { + { 33, A3XX_SP_PERFCOUNTER0_SELECT }, + { 34, A3XX_SP_PERFCOUNTER1_SELECT }, + { 35, A3XX_SP_PERFCOUNTER2_SELECT }, + { 36, A3XX_SP_PERFCOUNTER3_SELECT }, + { 37, A3XX_SP_PERFCOUNTER4_SELECT }, + { 38, A3XX_SP_PERFCOUNTER5_SELECT }, + { 39, A3XX_SP_PERFCOUNTER6_SELECT }, + { 40, A3XX_SP_PERFCOUNTER7_SELECT }, +}; + +static struct a3xx_perfcounter_register a3xx_perfcounter_reg_rb[] = { + { 41, A3XX_RB_PERFCOUNTER0_SELECT }, + { 42, A3XX_RB_PERFCOUNTER1_SELECT }, +}; + +#define REGCOUNTER_GROUP(_x) { (_x), ARRAY_SIZE((_x)) } + +static struct { + struct a3xx_perfcounter_register *regs; + int count; +} a3xx_perfcounter_reglist[] = { + REGCOUNTER_GROUP(a3xx_perfcounter_reg_cp), + REGCOUNTER_GROUP(a3xx_perfcounter_reg_rbbm), + REGCOUNTER_GROUP(a3xx_perfcounter_reg_pc), + REGCOUNTER_GROUP(a3xx_perfcounter_reg_vfd), + REGCOUNTER_GROUP(a3xx_perfcounter_reg_hlsq), + REGCOUNTER_GROUP(a3xx_perfcounter_reg_vpc), + REGCOUNTER_GROUP(a3xx_perfcounter_reg_tse), + REGCOUNTER_GROUP(a3xx_perfcounter_reg_ras), + REGCOUNTER_GROUP(a3xx_perfcounter_reg_uche), + REGCOUNTER_GROUP(a3xx_perfcounter_reg_tp), + REGCOUNTER_GROUP(a3xx_perfcounter_reg_sp), + REGCOUNTER_GROUP(a3xx_perfcounter_reg_rb), +}; + +static void a3xx_perfcounter_enable_pwr(struct kgsl_device *device, + unsigned int countable) +{ + unsigned int in, out; + + adreno_regread(device, A3XX_RBBM_RBBM_CTL, &in); + + if (countable == 0) + out = in | RBBM_RBBM_CTL_RESET_PWR_CTR0; + else + out = in | RBBM_RBBM_CTL_RESET_PWR_CTR1; + + adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, out); + + if (countable == 0) + out = in | RBBM_RBBM_CTL_ENABLE_PWR_CTR0; + else + out = in | RBBM_RBBM_CTL_ENABLE_PWR_CTR1; + + adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, out); + + return; +} + +static void a3xx_perfcounter_enable_vbif(struct kgsl_device *device, + unsigned int counter, + unsigned int countable) +{ + unsigned int in, out, bit, sel; + + if (counter > 1 || countable > 0x7f) + return; + + adreno_regread(device, A3XX_VBIF_PERF_CNT_EN, &in); + adreno_regread(device, A3XX_VBIF_PERF_CNT_SEL, &sel); + + if (counter == 0) { + bit = VBIF_PERF_CNT_0; + sel = (sel & ~VBIF_PERF_CNT_0_SEL_MASK) | countable; + } else { + bit = VBIF_PERF_CNT_1; + sel = (sel & ~VBIF_PERF_CNT_1_SEL_MASK) + | (countable << VBIF_PERF_CNT_1_SEL); + } + + out = in | bit; + + adreno_regwrite(device, A3XX_VBIF_PERF_CNT_SEL, sel); + + adreno_regwrite(device, A3XX_VBIF_PERF_CNT_CLR, bit); + adreno_regwrite(device, A3XX_VBIF_PERF_CNT_CLR, 0); + + adreno_regwrite(device, A3XX_VBIF_PERF_CNT_EN, out); +} + +static void a3xx_perfcounter_enable_vbif_pwr(struct kgsl_device *device, + unsigned int countable) +{ + unsigned int in, out, bit; + + adreno_regread(device, A3XX_VBIF_PERF_CNT_EN, &in); + if (countable == 0) + bit = VBIF_PERF_PWR_CNT_0; + else if (countable == 1) + bit = VBIF_PERF_PWR_CNT_1; + else + bit = VBIF_PERF_PWR_CNT_2; + + out = in | bit; + + adreno_regwrite(device, A3XX_VBIF_PERF_CNT_CLR, bit); + adreno_regwrite(device, A3XX_VBIF_PERF_CNT_CLR, 0); + + adreno_regwrite(device, A3XX_VBIF_PERF_CNT_EN, out); +} + +/* + * a3xx_perfcounter_enable - Configure a performance counter for a countable + * @adreno_dev - Adreno device to configure + * @group - Desired performance counter group + * @counter - Desired performance counter in the group + * @countable - Desired countable + * + * Physically set up a counter within a group with the desired countable + */ + +static void a3xx_perfcounter_enable(struct adreno_device *adreno_dev, + unsigned int group, unsigned int counter, unsigned int countable) +{ + struct kgsl_device *device = &adreno_dev->dev; + unsigned int val = 0; + struct a3xx_perfcounter_register *reg; + + if (group >= ARRAY_SIZE(a3xx_perfcounter_reglist)) + return; + + if (counter >= a3xx_perfcounter_reglist[group].count) + return; + + /* Special cases */ + if (group == KGSL_PERFCOUNTER_GROUP_PWR) + return a3xx_perfcounter_enable_pwr(device, countable); + else if (group == KGSL_PERFCOUNTER_GROUP_VBIF) + return a3xx_perfcounter_enable_vbif(device, counter, countable); + else if (group == KGSL_PERFCOUNTER_GROUP_VBIF_PWR) + return a3xx_perfcounter_enable_vbif_pwr(device, countable); + + reg = &(a3xx_perfcounter_reglist[group].regs[counter]); + + /* Select the desired perfcounter */ + adreno_regwrite(device, reg->select, countable); + + if (reg->load_bit < 32) { + val = 1 << reg->load_bit; + adreno_regwrite(device, A3XX_RBBM_PERFCTR_LOAD_CMD0, val); + } else { + val = 1 << (reg->load_bit - 32); + adreno_regwrite(device, A3XX_RBBM_PERFCTR_LOAD_CMD1, val); + } +} + +static uint64_t a3xx_perfcounter_read(struct adreno_device *adreno_dev, + unsigned int group, unsigned int counter, + unsigned int offset) +{ + struct kgsl_device *device = &adreno_dev->dev; + struct a3xx_perfcounter_register *reg = NULL; + unsigned int lo = 0, hi = 0; + unsigned int val; + + if (group >= ARRAY_SIZE(a3xx_perfcounter_reglist)) + return 0; + + if (counter >= a3xx_perfcounter_reglist[group].count) + return 0; + + reg = &(a3xx_perfcounter_reglist[group].regs[counter]); + + /* Freeze the counter */ + adreno_regread(device, A3XX_RBBM_PERFCTR_CTL, &val); + val &= ~reg->load_bit; + adreno_regwrite(device, A3XX_RBBM_PERFCTR_CTL, val); + + /* Read the values */ + adreno_regread(device, offset, &lo); + adreno_regread(device, offset + 1, &hi); + + /* Re-Enable the counter */ + val |= reg->load_bit; + adreno_regwrite(device, A3XX_RBBM_PERFCTR_CTL, val); + + return (((uint64_t) hi) << 32) | lo; +} + #define A3XX_IRQ_CALLBACK(_c) { .func = _c } #define A3XX_INT_MASK \ @@ -2684,26 +2988,22 @@ static unsigned int a3xx_irq_pending(struct adreno_device *adreno_dev) static unsigned int a3xx_busy_cycles(struct adreno_device *adreno_dev) { struct kgsl_device *device = &adreno_dev->dev; - unsigned int reg, val; - - /* Freeze the counter */ - adreno_regread(device, A3XX_RBBM_RBBM_CTL, ®); - reg &= ~RBBM_RBBM_CTL_ENABLE_PWR_CTR1; - adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, reg); + unsigned int val; + unsigned int ret = 0; /* Read the value */ adreno_regread(device, A3XX_RBBM_PERFCTR_PWR_1_LO, &val); - /* Reset the counter */ - reg |= RBBM_RBBM_CTL_RESET_PWR_CTR1; - adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, reg); - - /* Re-enable the counter */ - reg &= ~RBBM_RBBM_CTL_RESET_PWR_CTR1; - reg |= RBBM_RBBM_CTL_ENABLE_PWR_CTR1; - adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, reg); + /* Return 0 for the first read */ + if (adreno_dev->gpu_cycles != 0) { + if (val < adreno_dev->gpu_cycles) + ret = (0xFFFFFFFF - adreno_dev->gpu_cycles) + val; + else + ret = val - adreno_dev->gpu_cycles; + } - return val; + adreno_dev->gpu_cycles = val; + return ret; } struct a3xx_vbif_data { @@ -2781,17 +3081,123 @@ static struct a3xx_vbif_data a330_vbif[] = { {0, 0}, }; +/* + * Most of the VBIF registers on 8974v2 have the correct values at power on, so + * we won't modify those if we don't need to + */ +static struct a3xx_vbif_data a330v2_vbif[] = { + /* Enable 1k sort */ + { A3XX_VBIF_ABIT_SORT, 0x0001003F }, + { A3XX_VBIF_ABIT_SORT_CONF, 0x000000A4 }, + /* Enable WR-REQ */ + { A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x00003F }, + { A3XX_VBIF_DDR_OUT_MAX_BURST, 0x0000303 }, + /* Set up VBIF_ROUND_ROBIN_QOS_ARB */ + { A3XX_VBIF_ROUND_ROBIN_QOS_ARB, 0x0003 }, + /* Disable VBIF clock gating. This is to enable AXI running + * higher frequency than GPU. + */ + { A3XX_VBIF_CLKON, 1 }, + {0, 0}, +}; + +static struct { + int(*devfunc)(struct adreno_device *); + struct a3xx_vbif_data *vbif; +} a3xx_vbif_platforms[] = { + { adreno_is_a305, a305_vbif }, + { adreno_is_a320, a320_vbif }, + /* A330v2 needs to be ahead of A330 so the right device matches */ + { adreno_is_a330v2, a330v2_vbif }, + { adreno_is_a330, a330_vbif }, +}; + +static void a3xx_perfcounter_init(struct adreno_device *adreno_dev) +{ + /* + * Set SP to count SP_ALU_ACTIVE_CYCLES, it includes + * all ALU instruction execution regardless precision or shader ID. + * Set SP to count SP0_ICL1_MISSES, It counts + * USP L1 instruction miss request. + * Set SP to count SP_FS_FULL_ALU_INSTRUCTIONS, it + * counts USP flow control instruction execution. + * we will use this to augment our hang detection + */ + if (adreno_dev->fast_hang_detect) { + adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_SP, + SP_ALU_ACTIVE_CYCLES, &ft_detect_regs[6], + PERFCOUNTER_FLAG_KERNEL); + ft_detect_regs[7] = ft_detect_regs[6] + 1; + adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_SP, + SP0_ICL1_MISSES, &ft_detect_regs[8], + PERFCOUNTER_FLAG_KERNEL); + ft_detect_regs[9] = ft_detect_regs[8] + 1; + adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_SP, + SP_FS_CFLOW_INSTRUCTIONS, &ft_detect_regs[10], + PERFCOUNTER_FLAG_KERNEL); + ft_detect_regs[11] = ft_detect_regs[10] + 1; + } + + adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_SP, + SP_FS_FULL_ALU_INSTRUCTIONS, NULL, PERFCOUNTER_FLAG_KERNEL); + + /* Reserve and start countable 1 in the PWR perfcounter group */ + adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_PWR, 1, + NULL, PERFCOUNTER_FLAG_KERNEL); +} + +/** + * a3xx_protect_init() - Initializes register protection on a3xx + * @device: Pointer to the device structure + * Performs register writes to enable protected access to sensitive + * registers + */ +static void a3xx_protect_init(struct kgsl_device *device) +{ + int index = 0; + + /* enable access protection to privileged registers */ + kgsl_regwrite(device, A3XX_CP_PROTECT_CTRL, 0x00000007); + + /* RBBM registers */ + adreno_set_protected_registers(device, &index, 0x18, 0); + adreno_set_protected_registers(device, &index, 0x20, 2); + adreno_set_protected_registers(device, &index, 0x33, 0); + adreno_set_protected_registers(device, &index, 0x42, 0); + adreno_set_protected_registers(device, &index, 0x50, 4); + adreno_set_protected_registers(device, &index, 0x63, 0); + adreno_set_protected_registers(device, &index, 0x100, 4); + + /* CP registers */ + adreno_set_protected_registers(device, &index, 0x1C0, 5); + adreno_set_protected_registers(device, &index, 0x1EC, 1); + adreno_set_protected_registers(device, &index, 0x1F6, 1); + adreno_set_protected_registers(device, &index, 0x1F8, 2); + adreno_set_protected_registers(device, &index, 0x45E, 2); + adreno_set_protected_registers(device, &index, 0x460, 4); + + /* RB registers */ + adreno_set_protected_registers(device, &index, 0xCC0, 0); + + /* VBIF registers */ + adreno_set_protected_registers(device, &index, 0x3000, 6); + + /* SMMU registers */ + adreno_set_protected_registers(device, &index, 0x4000, 14); +} + static void a3xx_start(struct adreno_device *adreno_dev) { struct kgsl_device *device = &adreno_dev->dev; struct a3xx_vbif_data *vbif = NULL; + int i; - if (adreno_is_a305(adreno_dev)) - vbif = a305_vbif; - else if (adreno_is_a320(adreno_dev)) - vbif = a320_vbif; - else if (adreno_is_a330(adreno_dev)) - vbif = a330_vbif; + for (i = 0; i < ARRAY_SIZE(a3xx_vbif_platforms); i++) { + if (a3xx_vbif_platforms[i].devfunc(adreno_dev)) { + vbif = a3xx_vbif_platforms[i].vbif; + break; + } + } BUG_ON(vbif == NULL); @@ -2829,36 +3235,153 @@ static void a3xx_start(struct adreno_device *adreno_dev) /* Enable Clock gating */ adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL, - A3XX_RBBM_CLOCK_CTL_DEFAULT); + adreno_a3xx_rbbm_clock_ctl_default(adreno_dev)); + + if (adreno_is_a330v2(adreno_dev)) + adreno_regwrite(device, A3XX_RBBM_GPR0_CTL, + A330v2_RBBM_GPR0_CTL_DEFAULT); + else if (adreno_is_a330(adreno_dev)) + adreno_regwrite(device, A3XX_RBBM_GPR0_CTL, + A330_RBBM_GPR0_CTL_DEFAULT); /* Set the OCMEM base address for A330 */ if (adreno_is_a330(adreno_dev)) { adreno_regwrite(device, A3XX_RB_GMEM_BASE_ADDR, (unsigned int)(adreno_dev->ocmem_base >> 14)); } + /* Turn on protection */ + a3xx_protect_init(device); /* Turn on performance counters */ adreno_regwrite(device, A3XX_RBBM_PERFCTR_CTL, 0x01); - /* - * Set SP perfcounter 5 to count SP_ALU_ACTIVE_CYCLES, it includes - * all ALU instruction execution regardless precision or shader ID. - * Set SP perfcounter 6 to count SP0_ICL1_MISSES, It counts - * USP L1 instruction miss request. - * Set SP perfcounter 7 to count SP_FS_FULL_ALU_INSTRUCTIONS, it - * counts USP flow control instruction execution. - * we will use this to augment our hang detection - */ - if (adreno_dev->fast_hang_detect) { - adreno_regwrite(device, A3XX_SP_PERFCOUNTER5_SELECT, - SP_ALU_ACTIVE_CYCLES); - adreno_regwrite(device, A3XX_SP_PERFCOUNTER6_SELECT, - SP0_ICL1_MISSES); - adreno_regwrite(device, A3XX_SP_PERFCOUNTER7_SELECT, - SP_FS_CFLOW_INSTRUCTIONS); - } + /* Turn on the GPU busy counter and let it run free */ + + adreno_dev->gpu_cycles = 0; } +/* + * Define the available perfcounter groups - these get used by + * adreno_perfcounter_get and adreno_perfcounter_put + */ + +static struct adreno_perfcount_register a3xx_perfcounters_cp[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_CP_0_LO, 0 }, +}; + +static struct adreno_perfcount_register a3xx_perfcounters_rbbm[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_RBBM_0_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_RBBM_1_LO, 0 }, +}; + +static struct adreno_perfcount_register a3xx_perfcounters_pc[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_PC_0_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_PC_1_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_PC_2_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_PC_3_LO, 0 }, +}; + +static struct adreno_perfcount_register a3xx_perfcounters_vfd[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_VFD_0_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_VFD_1_LO, 0 }, +}; + +static struct adreno_perfcount_register a3xx_perfcounters_hlsq[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_HLSQ_0_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_HLSQ_1_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_HLSQ_2_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_HLSQ_3_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_HLSQ_4_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_HLSQ_5_LO, 0 }, +}; + +static struct adreno_perfcount_register a3xx_perfcounters_vpc[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_VPC_0_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_VPC_1_LO, 0 }, +}; + +static struct adreno_perfcount_register a3xx_perfcounters_tse[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_TSE_0_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_TSE_1_LO, 0 }, +}; + +static struct adreno_perfcount_register a3xx_perfcounters_ras[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_RAS_0_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_RAS_1_LO, 0 }, +}; + +static struct adreno_perfcount_register a3xx_perfcounters_uche[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_UCHE_0_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_UCHE_1_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_UCHE_2_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_UCHE_3_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_UCHE_4_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_UCHE_5_LO, 0 }, +}; + +static struct adreno_perfcount_register a3xx_perfcounters_tp[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_TP_0_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_TP_1_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_TP_2_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_TP_3_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_TP_4_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_TP_5_LO, 0 }, +}; + +static struct adreno_perfcount_register a3xx_perfcounters_sp[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_SP_0_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_SP_1_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_SP_2_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_SP_3_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_SP_4_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_SP_5_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_SP_6_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_SP_7_LO, 0 }, +}; + +static struct adreno_perfcount_register a3xx_perfcounters_rb[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_RB_0_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_RB_1_LO, 0 }, +}; + +static struct adreno_perfcount_register a3xx_perfcounters_pwr[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_PWR_0_LO, 0 }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_RBBM_PERFCTR_PWR_1_LO, 0 }, +}; + +static struct adreno_perfcount_register a3xx_perfcounters_vbif[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_VBIF_PERF_CNT0_LO }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_VBIF_PERF_CNT1_LO }, +}; +static struct adreno_perfcount_register a3xx_perfcounters_vbif_pwr[] = { + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_VBIF_PERF_PWR_CNT0_LO }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_VBIF_PERF_PWR_CNT1_LO }, + { KGSL_PERFCOUNTER_NOT_USED, 0, A3XX_VBIF_PERF_PWR_CNT2_LO }, +}; + +static struct adreno_perfcount_group a3xx_perfcounter_groups[] = { + { a3xx_perfcounters_cp, ARRAY_SIZE(a3xx_perfcounters_cp) }, + { a3xx_perfcounters_rbbm, ARRAY_SIZE(a3xx_perfcounters_rbbm) }, + { a3xx_perfcounters_pc, ARRAY_SIZE(a3xx_perfcounters_pc) }, + { a3xx_perfcounters_vfd, ARRAY_SIZE(a3xx_perfcounters_vfd) }, + { a3xx_perfcounters_hlsq, ARRAY_SIZE(a3xx_perfcounters_hlsq) }, + { a3xx_perfcounters_vpc, ARRAY_SIZE(a3xx_perfcounters_vpc) }, + { a3xx_perfcounters_tse, ARRAY_SIZE(a3xx_perfcounters_tse) }, + { a3xx_perfcounters_ras, ARRAY_SIZE(a3xx_perfcounters_ras) }, + { a3xx_perfcounters_uche, ARRAY_SIZE(a3xx_perfcounters_uche) }, + { a3xx_perfcounters_tp, ARRAY_SIZE(a3xx_perfcounters_tp) }, + { a3xx_perfcounters_sp, ARRAY_SIZE(a3xx_perfcounters_sp) }, + { a3xx_perfcounters_rb, ARRAY_SIZE(a3xx_perfcounters_rb) }, + { a3xx_perfcounters_pwr, ARRAY_SIZE(a3xx_perfcounters_pwr) }, + { a3xx_perfcounters_vbif, ARRAY_SIZE(a3xx_perfcounters_vbif) }, + { a3xx_perfcounters_vbif_pwr, ARRAY_SIZE(a3xx_perfcounters_vbif_pwr) }, +}; + +static struct adreno_perfcounters a3xx_perfcounters = { + a3xx_perfcounter_groups, + ARRAY_SIZE(a3xx_perfcounter_groups), +}; + /* Defined in adreno_a3xx_snapshot.c */ void *a3xx_snapshot(struct adreno_device *adreno_dev, void *snapshot, int *remain, int hang); @@ -2867,16 +3390,20 @@ struct adreno_gpudev adreno_a3xx_gpudev = { .reg_rbbm_status = A3XX_RBBM_STATUS, .reg_cp_pfp_ucode_addr = A3XX_CP_PFP_UCODE_ADDR, .reg_cp_pfp_ucode_data = A3XX_CP_PFP_UCODE_DATA, + .perfcounters = &a3xx_perfcounters, .ctxt_create = a3xx_drawctxt_create, .ctxt_save = a3xx_drawctxt_save, .ctxt_restore = a3xx_drawctxt_restore, .ctxt_draw_workaround = NULL, .rb_init = a3xx_rb_init, + .perfcounter_init = a3xx_perfcounter_init, .irq_control = a3xx_irq_control, .irq_handler = a3xx_irq_handler, .irq_pending = a3xx_irq_pending, .busy_cycles = a3xx_busy_cycles, .start = a3xx_start, .snapshot = a3xx_snapshot, + .perfcounter_enable = a3xx_perfcounter_enable, + .perfcounter_read = a3xx_perfcounter_read, }; diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c index d9d5ec82..b939fa00 100644 --- a/drivers/gpu/msm/adreno_a3xx_snapshot.c +++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012,2014 The Linux Foundation. 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 and @@ -21,6 +21,22 @@ #define SHADER_MEMORY_SIZE 0x4000 +/** + * _rbbm_debug_bus_read - Helper function to read data from the RBBM + * debug bus. + * @device - GPU device to read/write registers + * @block_id - Debug bus block to read from + * @index - Index in the debug bus block to read + * @ret - Value of the register read + */ +static void _rbbm_debug_bus_read(struct kgsl_device *device, + unsigned int block_id, unsigned int index, unsigned int *val) +{ + unsigned int block = (block_id << 8) | 1 << 16; + adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, block | index); + adreno_regread(device, A3XX_RBBM_DEBUG_BUS_DATA_STATUS, val); +} + static int a3xx_snapshot_shader_memory(struct kgsl_device *device, void *snapshot, int remain, void *priv) { @@ -243,11 +259,8 @@ static int a3xx_snapshot_debugbus_block(struct kgsl_device *device, header->id = id; header->count = DEBUGFS_BLOCK_SIZE; - for (i = 0; i < DEBUGFS_BLOCK_SIZE; i++) { - adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, val | i); - adreno_regread(device, A3XX_RBBM_DEBUG_BUS_DATA_STATUS, - &data[i]); - } + for (i = 0; i < DEBUGFS_BLOCK_SIZE; i++) + _rbbm_debug_bus_read(device, id, i, &data[i]); return size; } @@ -309,18 +322,58 @@ static void _snapshot_hlsq_regs(struct kgsl_snapshot_registers *regs, struct kgsl_snapshot_registers_list *list, struct adreno_device *adreno_dev) { - /* HLSQ specific registers */ + struct kgsl_device *device = &adreno_dev->dev; + /* - * Don't dump any a3xx HLSQ registers just yet. Reading the HLSQ - * registers can cause the device to hang if the HLSQ block is - * busy. Add specific checks for each a3xx core as the requirements - * are discovered. Disable by default for now. + * Trying to read HLSQ registers when the HLSQ block is busy + * will cause the device to hang. The RBBM_DEBUG_BUS has information + * that will tell us if the HLSQ block is busy or not. Read values + * from the debug bus to ensure the HLSQ block is not busy (this + * is hardware dependent). If the HLSQ block is busy do not + * dump the registers, otherwise dump the HLSQ registers. */ - if (!adreno_is_a3xx(adreno_dev)) { - regs[list->count].regs = (unsigned int *) a3xx_hlsq_registers; - regs[list->count].count = a3xx_hlsq_registers_count; - list->count++; + + if (adreno_is_a330(adreno_dev)) { + /* + * stall_ctxt_full status bit: RBBM_BLOCK_ID_HLSQ index 49 [27] + * + * if (!stall_context_full) + * then dump HLSQ registers + */ + unsigned int stall_context_full = 0; + + _rbbm_debug_bus_read(device, RBBM_BLOCK_ID_HLSQ, 49, + &stall_context_full); + stall_context_full &= 0x08000000; + + if (stall_context_full) + return; + } else { + /* + * tpif status bits: RBBM_BLOCK_ID_HLSQ index 4 [4:0] + * spif status bits: RBBM_BLOCK_ID_HLSQ index 7 [5:0] + * + * if ((tpif == 0, 1, 28) && (spif == 0, 1, 10)) + * then dump HLSQ registers + */ + unsigned int next_pif = 0; + + /* check tpif */ + _rbbm_debug_bus_read(device, RBBM_BLOCK_ID_HLSQ, 4, &next_pif); + next_pif &= 0x1f; + if (next_pif != 0 && next_pif != 1 && next_pif != 28) + return; + + /* check spif */ + _rbbm_debug_bus_read(device, RBBM_BLOCK_ID_HLSQ, 7, &next_pif); + next_pif &= 0x3f; + if (next_pif != 0 && next_pif != 1 && next_pif != 10) + return; } + + regs[list->count].regs = (unsigned int *) a3xx_hlsq_registers; + regs[list->count].count = a3xx_hlsq_registers_count; + list->count++; } static void _snapshot_a330_regs(struct kgsl_snapshot_registers *regs, @@ -390,6 +443,22 @@ void *a3xx_snapshot(struct adreno_device *adreno_dev, void *snapshot, /* Reading these will hang the GPU if it isn't already hung */ if (hang) { + unsigned int reg; + + /* + * Reading the microcode while the CP will is running will + * basically basically move the CP instruction pointer to + * whatever address we read. Big badaboom ensues. Stop the CP + * (if it isn't already stopped) to ensure that we are safe. + * We do this here and not earlier to avoid corrupting the RBBM + * status and CP registers - by the time we get here we don't + * care about the contents of the CP anymore. + */ + + adreno_regread(device, REG_CP_ME_CNTL, ®); + reg |= (1 << 27) | (1 << 28); + adreno_regwrite(device, REG_CP_ME_CNTL, reg); + snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, a3xx_snapshot_cp_pfp_ram, NULL); @@ -414,7 +483,7 @@ void *a3xx_snapshot(struct adreno_device *adreno_dev, void *snapshot, /* Enable Clock gating */ adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL, - A3XX_RBBM_CLOCK_CTL_DEFAULT); + adreno_a3xx_rbbm_clock_ctl_default(adreno_dev)); return snapshot; } diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c index 890c8a18..86dcc430 100644 --- a/drivers/gpu/msm/adreno_debugfs.c +++ b/drivers/gpu/msm/adreno_debugfs.c @@ -60,43 +60,6 @@ void adreno_debugfs_init(struct kgsl_device *device) &adreno_dev->wait_timeout); debugfs_create_u32("ib_check", 0644, device->d_debugfs, &adreno_dev->ib_check_level); - /* By Default enable fast hang detection */ - adreno_dev->fast_hang_detect = 1; - debugfs_create_u32("fast_hang_detect", 0644, device->d_debugfs, - &adreno_dev->fast_hang_detect); - - /* Top level switch to enable/disable userspace FT control */ - adreno_dev->ft_user_control = 0; - debugfs_create_u32("ft_user_control", 0644, device->d_debugfs, - &adreno_dev->ft_user_control); - /* - * FT policy can be set to any of the options below. - * KGSL_FT_DISABLE -> BIT(0) Set to disable FT - * KGSL_FT_REPLAY -> BIT(1) Set to enable replay - * KGSL_FT_SKIPIB -> BIT(2) Set to skip IB - * KGSL_FT_SKIPFRAME -> BIT(3) Set to skip frame - * by default set FT policy to KGSL_FT_DEFAULT_POLICY - */ - adreno_dev->ft_policy = KGSL_FT_DEFAULT_POLICY; - debugfs_create_u32("ft_policy", 0644, device->d_debugfs, - &adreno_dev->ft_policy); - - /* By default enable long IB detection */ - adreno_dev->long_ib_detect = 1; - debugfs_create_u32("long_ib_detect", 0644, device->d_debugfs, - &adreno_dev->long_ib_detect); - - /* - * FT pagefault policy can be set to any of the options below. - * KGSL_FT_PAGEFAULT_INT_ENABLE -> BIT(0) set to enable pagefault INT - * KGSL_FT_PAGEFAULT_GPUHALT_ENABLE -> BIT(1) Set to enable GPU HALT on - * pagefaults. This stalls the GPU on a pagefault on IOMMU v1 HW. - * KGSL_FT_PAGEFAULT_LOG_ONE_PER_PAGE -> BIT(2) Set to log only one - * pagefault per page. - * KGSL_FT_PAGEFAULT_LOG_ONE_PER_INT -> BIT(3) Set to log only one - * pagefault per INT. - */ - adreno_dev->ft_pf_policy = KGSL_FT_PAGEFAULT_DEFAULT_POLICY; - debugfs_create_u32("ft_pagefault_policy", 0644, device->d_debugfs, - &adreno_dev->ft_pf_policy); + debugfs_create_u32("active_cnt", 0444, device->d_debugfs, + &device->active_cnt); } diff --git a/drivers/gpu/msm/adreno_debugfs.h b/drivers/gpu/msm/adreno_debugfs.h new file mode 100644 index 00000000..af7e797b --- /dev/null +++ b/drivers/gpu/msm/adreno_debugfs.h @@ -0,0 +1,46 @@ +/* Copyright (c) 2002,2008-2012, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __ADRENO_DEBUGFS_H +#define __ADRENO_DEBUGFS_H + +#ifdef CONFIG_DEBUG_FS + +int adreno_debugfs_init(struct kgsl_device *device); + +extern int adreno_pm_regs_enabled; +extern int adreno_pm_ib_enabled; + +static inline int is_adreno_pm_regs_enabled(void) +{ + return adreno_pm_regs_enabled; +} + +static inline int is_adreno_pm_ib_enabled(void) +{ + return adreno_pm_ib_enabled; +} + +#else +static inline int adreno_debugfs_init(struct kgsl_device *device) +{ + return 0; +} + +static inline int kgsl_pmregs_enabled(void) +{ + /* If debugfs is turned off, then always print registers */ + return 1; +} +#endif + +#endif /* __ADRENO_DEBUGFS_H */ diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c new file mode 100644 index 00000000..72b73b6f --- /dev/null +++ b/drivers/gpu/msm/adreno_dispatch.c @@ -0,0 +1,1415 @@ +/* Copyright (c) 2013, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include "kgsl.h" +#include "adreno.h" +#include "adreno_ringbuffer.h" +#include "adreno_trace.h" + +#define ADRENO_DISPATCHER_ACTIVE 0 +#define ADRENO_DISPATCHER_PAUSE 1 + +#define ADRENO_DISPATCHER_SOFT_FAULT 1 +#define ADRENO_DISPATCHER_HARD_FAULT 2 +#define ADRENO_DISPATCHER_TIMEOUT_FAULT 3 + +#define CMDQUEUE_NEXT(_i, _s) (((_i) + 1) % (_s)) + +/* Number of commands that can be queued in a context before it sleeps */ +static unsigned int _context_cmdqueue_size = 50; + +/* Number of milliseconds to wait for the context queue to clear */ +static unsigned int _context_queue_wait = 10000; + +/* Number of command batches sent at a time from a single context */ +static unsigned int _context_cmdbatch_burst = 5; + +/* Number of command batches inflight in the ringbuffer at any time */ +static unsigned int _dispatcher_inflight = 15; + +/* Command batch timeout (in milliseconds) */ +static unsigned int _cmdbatch_timeout = 2000; + +/* Interval for reading and comparing fault detection registers */ +static unsigned int _fault_timer_interval = 100; + +/* Local array for the current set of fault detect registers */ +static unsigned int *fault_detect_regs; + +/** + * fault_detect_read() - Read the set of fault detect registers + * @device: Pointer to the KGSL device struct + * + * Read the set of fault detect registers and store them in the local array. + * This is for the initial values that are compared later with + * fault_detect_read_compare + */ +static void fault_detect_read(struct kgsl_device *device) +{ + int i; + + for (i = 0; i < FT_DETECT_REGS_COUNT; i++) { + if (ft_detect_regs[i] == 0) + continue; + adreno_regread(device, ft_detect_regs[i], + &fault_detect_regs[i]); + } +} + +/** + * fault_detect_read_compare() - Read the fault detect registers and compare + * them to the current value + * @device: Pointer to the KGSL device struct + * + * Read the set of fault detect registers and compare them to the current set + * of registers. Return 1 if any of the register values changed + */ +static int fault_detect_read_compare(struct kgsl_device *device) +{ + int i, ret = 0; + + for (i = 0; i < FT_DETECT_REGS_COUNT; i++) { + unsigned int val; + + if (ft_detect_regs[i] == 0) + continue; + adreno_regread(device, ft_detect_regs[i], &val); + if (val != fault_detect_regs[i]) + ret = 1; + fault_detect_regs[i] = val; + } + + return ret; +} + +/** + * adreno_context_get_cmdbatch() - Get a new command from a context queue + * @drawctxt: Pointer to the adreno draw context + * + * Dequeue a new command batch from the context list + */ +static inline struct kgsl_cmdbatch *adreno_context_get_cmdbatch( + struct adreno_context *drawctxt) +{ + struct kgsl_cmdbatch *cmdbatch = NULL; + + mutex_lock(&drawctxt->mutex); + if (drawctxt->cmdqueue_head != drawctxt->cmdqueue_tail) { + cmdbatch = drawctxt->cmdqueue[drawctxt->cmdqueue_head]; + + /* + * Don't dequeue a cmdbatch that is still waiting for other + * events + */ + if (kgsl_cmdbatch_sync_pending(cmdbatch)) { + cmdbatch = ERR_PTR(-EAGAIN); + goto done; + } + + drawctxt->cmdqueue_head = + CMDQUEUE_NEXT(drawctxt->cmdqueue_head, + ADRENO_CONTEXT_CMDQUEUE_SIZE); + drawctxt->queued--; + } + +done: + mutex_unlock(&drawctxt->mutex); + + return cmdbatch; +} + +/** + * adreno_context_requeue_cmdbatch() - Put a command back on the context queue + * @drawctxt: Pointer to the adreno draw context + * @cmdbatch: Pointer to the KGSL cmdbatch to requeue + * + * Failure to submit a command to the ringbuffer isn't the fault of the command + * being submitted so if a failure happens, push it back on the head of the the + * context queue to be reconsidered again + */ +static inline void adreno_context_requeue_cmdbatch( + struct adreno_context *drawctxt, struct kgsl_cmdbatch *cmdbatch) +{ + unsigned int prev; + mutex_lock(&drawctxt->mutex); + + prev = drawctxt->cmdqueue_head - 1; + + if (prev < 0) + prev = ADRENO_CONTEXT_CMDQUEUE_SIZE - 1; + + /* + * The maximum queue size always needs to be one less then the size of + * the ringbuffer queue so there is "room" to put the cmdbatch back in + */ + + BUG_ON(prev == drawctxt->cmdqueue_tail); + + drawctxt->cmdqueue[prev] = cmdbatch; + drawctxt->queued++; + + /* Reset the command queue head to reflect the newly requeued change */ + drawctxt->cmdqueue_head = prev; + mutex_unlock(&drawctxt->mutex); +} + +/** + * dispatcher_queue_context() - Queue a context in the dispatcher pending list + * @dispatcher: Pointer to the adreno dispatcher struct + * @drawctxt: Pointer to the adreno draw context + * + * Add a context to the dispatcher pending list. + */ +static void dispatcher_queue_context(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + + spin_lock(&dispatcher->plist_lock); + + + if (plist_node_empty(&drawctxt->pending)) { + /* Get a reference to the context while it sits on the list */ + _kgsl_context_get(&drawctxt->base); + trace_dispatch_queue_context(drawctxt); + plist_add(&drawctxt->pending, &dispatcher->pending); + } + + spin_unlock(&dispatcher->plist_lock); +} + +/** + * sendcmd() - Send a command batch to the GPU hardware + * @dispatcher: Pointer to the adreno dispatcher struct + * @cmdbatch: Pointer to the KGSL cmdbatch being sent + * + * Send a KGSL command batch to the GPU hardware + */ +static int sendcmd(struct adreno_device *adreno_dev, + struct kgsl_cmdbatch *cmdbatch) +{ + struct kgsl_device *device = &adreno_dev->dev; + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + int ret; + + dispatcher->inflight++; + + mutex_lock(&device->mutex); + + if (dispatcher->inflight == 1) { + /* Time to make the donuts. Turn on the GPU */ + ret = kgsl_active_count_get(device); + if (ret) { + dispatcher->inflight--; + mutex_unlock(&device->mutex); + return ret; + } + } + + ret = adreno_ringbuffer_submitcmd(adreno_dev, cmdbatch); + + /* + * On the first command, if the submission was successful, then read the + * fault registers. If it failed then turn off the GPU. Sad face. + */ + + if (dispatcher->inflight == 1) { + if (ret == 0) + fault_detect_read(device); + else + kgsl_active_count_put(device); + } + + mutex_unlock(&device->mutex); + + if (ret) { + dispatcher->inflight--; + KGSL_DRV_ERR(device, + "Unable to submit command to the ringbuffer\n"); + return ret; + } + + trace_adreno_cmdbatch_submitted(cmdbatch, dispatcher->inflight); + + dispatcher->cmdqueue[dispatcher->tail] = cmdbatch; + dispatcher->tail = (dispatcher->tail + 1) % + ADRENO_DISPATCH_CMDQUEUE_SIZE; + + /* + * If this is the first command in the pipe then the GPU will + * immediately start executing it so we can start the expiry timeout on + * the command batch here. Subsequent command batches will have their + * timer started when the previous command batch is retired + */ + if (dispatcher->inflight == 1) { + cmdbatch->expires = jiffies + + msecs_to_jiffies(_cmdbatch_timeout); + mod_timer(&dispatcher->timer, cmdbatch->expires); + + /* Start the fault detection timer */ + if (adreno_dev->fast_hang_detect) + mod_timer(&dispatcher->fault_timer, + jiffies + + msecs_to_jiffies(_fault_timer_interval)); + } + + return 0; +} + +/** + * dispatcher_context_sendcmds() - Send commands from a context to the GPU + * @adreno_dev: Pointer to the adreno device struct + * @drawctxt: Pointer to the adreno context to dispatch commands from + * + * Dequeue and send a burst of commands from the specified context to the GPU + */ +static int dispatcher_context_sendcmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + int count = 0; + + /* + * Each context can send a specific number of command batches per cycle + */ + for ( ; count < _context_cmdbatch_burst && + dispatcher->inflight < _dispatcher_inflight; count++) { + int ret; + struct kgsl_cmdbatch *cmdbatch = + adreno_context_get_cmdbatch(drawctxt); + + if (cmdbatch == NULL) + break; + + /* + * adreno_context_get_cmdbatch returns -EAGAIN if the current + * cmdbatch has pending sync points so no more to do here. + * When the sync points are satisfied then the context will get + * reqeueued + */ + + if (IS_ERR(cmdbatch)) + return count; + + /* + * If this is a synchronization submission then there are no + * commands to submit. Discard it and get the next item from + * the queue. Decrement count so this packet doesn't count + * against the burst for the context + */ + + if (cmdbatch->flags & KGSL_CONTEXT_SYNC) { + count--; + kgsl_cmdbatch_destroy(cmdbatch); + continue; + } + + ret = sendcmd(adreno_dev, cmdbatch); + + /* + * There are various reasons why we can't submit a command (no + * memory for the commands, full ringbuffer, etc) but none of + * these are actually the current command's fault. Requeue it + * back on the context and let it come back around again if + * conditions improve + */ + if (ret) { + adreno_context_requeue_cmdbatch(drawctxt, cmdbatch); + break; + } + } + + /* + * If the context successfully submitted commands, then + * unconditionally put it back on the queue to be considered the + * next time around. This might seem a little wasteful but it is + * reasonable to think that a busy context will stay busy. + */ + + if (count) { + dispatcher_queue_context(adreno_dev, drawctxt); + + /* + * If we submitted something there will be room in the + * context queue so ping the context wait queue on the + * chance that the context is snoozing + */ + + wake_up_interruptible_all(&drawctxt->wq); + } + + return count; +} + +/** + * _adreno_dispatcher_issuecmds() - Issue commmands from pending contexts + * @adreno_dev: Pointer to the adreno device struct + * + * Issue as many commands as possible (up to inflight) from the pending contexts + * This function assumes the dispatcher mutex has been locked. + */ +static int _adreno_dispatcher_issuecmds(struct adreno_device *adreno_dev) +{ + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + + /* Don't do anything if the dispatcher is paused */ + if (dispatcher->state != ADRENO_DISPATCHER_ACTIVE) + return 0; + + while (dispatcher->inflight < _dispatcher_inflight) { + struct adreno_context *drawctxt = NULL; + + spin_lock(&dispatcher->plist_lock); + + if (!plist_head_empty(&dispatcher->pending)) { + drawctxt = plist_first_entry(&dispatcher->pending, + struct adreno_context, pending); + + plist_del(&drawctxt->pending, &dispatcher->pending); + } + + spin_unlock(&dispatcher->plist_lock); + + if (drawctxt == NULL) + break; + + if (kgsl_context_detached(&drawctxt->base) || + drawctxt->state == ADRENO_CONTEXT_STATE_INVALID) { + kgsl_context_put(&drawctxt->base); + continue; + } + + dispatcher_context_sendcmds(adreno_dev, drawctxt); + kgsl_context_put(&drawctxt->base); + } + + return 0; +} + +/** + * adreno_dispatcher_issuecmds() - Issue commmands from pending contexts + * @adreno_dev: Pointer to the adreno device struct + * + * Lock the dispatcher and call _adreno_dispatcher_issueibcmds + */ +int adreno_dispatcher_issuecmds(struct adreno_device *adreno_dev) +{ + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + int ret; + + mutex_lock(&dispatcher->mutex); + ret = _adreno_dispatcher_issuecmds(adreno_dev); + mutex_unlock(&dispatcher->mutex); + + return ret; +} + +static int _check_context_queue(struct adreno_context *drawctxt) +{ + int ret; + + mutex_lock(&drawctxt->mutex); + + /* + * Wake up if there is room in the context or if the whole thing got + * invalidated while we were asleep + */ + + if (drawctxt->state == ADRENO_CONTEXT_STATE_INVALID) + ret = 1; + else + ret = drawctxt->queued < _context_cmdqueue_size ? 1 : 0; + + mutex_unlock(&drawctxt->mutex); + + return ret; +} + +/** + * get_timestamp() - Return the next timestamp for the context + * @drawctxt - Pointer to an adreno draw context struct + * @cmdbatch - Pointer to a command batch + * @timestamp - Pointer to a timestamp value possibly passed from the user + * + * Assign a timestamp based on the settings of the draw context and the command + * batch. + */ +static int get_timestamp(struct adreno_context *drawctxt, + struct kgsl_cmdbatch *cmdbatch, unsigned int *timestamp) +{ + /* Synchronization commands don't get a timestamp */ + if (cmdbatch->flags & KGSL_CONTEXT_SYNC) { + *timestamp = 0; + return 0; + } + + if (drawctxt->flags & CTXT_FLAGS_USER_GENERATED_TS) { + /* + * User specified timestamps need to be greater than the last + * issued timestamp in the context + */ + if (timestamp_cmp(drawctxt->timestamp, *timestamp) >= 0) + return -ERANGE; + + drawctxt->timestamp = *timestamp; + } else + drawctxt->timestamp++; + + *timestamp = drawctxt->timestamp; + return 0; +} + +/** + * adreno_context_queue_cmd() - Queue a new command in the context + * @adreno_dev: Pointer to the adreno device struct + * @drawctxt: Pointer to the adreno draw context + * @cmdbatch: Pointer to the command batch being submitted + * @timestamp: Pointer to the requested timestamp + * + * Queue a command in the context - if there isn't any room in the queue, then + * block until there is + */ +int adreno_context_queue_cmd(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt, struct kgsl_cmdbatch *cmdbatch, + uint32_t *timestamp) +{ + int ret; + + mutex_lock(&drawctxt->mutex); + + if (drawctxt->flags & CTXT_FLAGS_BEING_DESTROYED) { + mutex_unlock(&drawctxt->mutex); + return -EINVAL; + } + + /* + * After skipping to the end of the frame we need to force the preamble + * to run (if it exists) regardless of the context state. + */ + + if (drawctxt->flags & CTXT_FLAGS_FORCE_PREAMBLE) { + cmdbatch->priv |= CMDBATCH_FLAG_FORCE_PREAMBLE; + drawctxt->flags &= ~CTXT_FLAGS_FORCE_PREAMBLE; + } + + /* + * If we are waiting for the end of frame and it hasn't appeared yet, + * then mark the command batch as skipped. It will still progress + * through the pipeline but it won't actually send any commands + */ + + if (drawctxt->flags & CTXT_FLAGS_SKIP_EOF) { + cmdbatch->priv |= CMDBATCH_FLAG_SKIP; + + /* + * If this command batch represents the EOF then clear the way + * for the dispatcher to continue submitting + */ + + if (cmdbatch->flags & KGSL_CONTEXT_END_OF_FRAME) { + drawctxt->flags &= ~CTXT_FLAGS_SKIP_EOF; + + /* + * Force the preamble on the next command to ensure that + * the state is correct + */ + + drawctxt->flags |= CTXT_FLAGS_FORCE_PREAMBLE; + } + } + + /* Wait for room in the context queue */ + + while (drawctxt->queued >= _context_cmdqueue_size) { + trace_adreno_context_sleep(drawctxt); + mutex_unlock(&drawctxt->mutex); + + ret = wait_event_interruptible_timeout(drawctxt->wq, + _check_context_queue(drawctxt), + msecs_to_jiffies(_context_queue_wait)); + + mutex_lock(&drawctxt->mutex); + trace_adreno_context_wake(drawctxt); + + if (ret <= 0) { + mutex_unlock(&drawctxt->mutex); + return (ret == 0) ? -ETIMEDOUT : (int) ret; + } + + /* + * Account for the possiblity that the context got invalidated + * while we were sleeping + */ + + if (drawctxt->state == ADRENO_CONTEXT_STATE_INVALID) { + mutex_unlock(&drawctxt->mutex); + return -EDEADLK; + } + } + + ret = get_timestamp(drawctxt, cmdbatch, timestamp); + if (ret) { + mutex_unlock(&drawctxt->mutex); + return ret; + } + + cmdbatch->timestamp = *timestamp; + + /* The batch fault policy is the current system fault policy */ + cmdbatch->fault_policy = adreno_dev->ft_policy; + + /* Put the command into the queue */ + drawctxt->cmdqueue[drawctxt->cmdqueue_tail] = cmdbatch; + drawctxt->cmdqueue_tail = (drawctxt->cmdqueue_tail + 1) % + ADRENO_CONTEXT_CMDQUEUE_SIZE; + + drawctxt->queued++; + trace_adreno_cmdbatch_queued(cmdbatch, drawctxt->queued); + + + mutex_unlock(&drawctxt->mutex); + + /* Add the context to the dispatcher pending list */ + dispatcher_queue_context(adreno_dev, drawctxt); + + /* + * Only issue commands if inflight is less than burst -this prevents us + * from sitting around waiting for the mutex on a busy system - the work + * loop will schedule it for us. Inflight is mutex protected but the + * worse that can happen is that it will go to 0 after we check and if + * it goes to 0 it is because the work loop decremented it and the work + * queue will try to schedule new commands anyway. + */ + + if (adreno_dev->dispatcher.inflight < _context_cmdbatch_burst) + adreno_dispatcher_issuecmds(adreno_dev); + + return 0; +} + +/* + * If an IB inside of the command batch has a gpuaddr that matches the base + * passed in then zero the size which effectively skips it when it is submitted + * in the ringbuffer. + */ +static void cmdbatch_skip_ib(struct kgsl_cmdbatch *cmdbatch, unsigned int base) +{ + int i; + + for (i = 0; i < cmdbatch->ibcount; i++) { + if (cmdbatch->ibdesc[i].gpuaddr == base) { + cmdbatch->ibdesc[i].sizedwords = 0; + return; + } + } +} + +static void cmdbatch_skip_frame(struct kgsl_cmdbatch *cmdbatch, + struct kgsl_cmdbatch **replay, int count) +{ + struct adreno_context *drawctxt = ADRENO_CONTEXT(cmdbatch->context); + int skip = 1; + int i; + + for (i = 0; i < count; i++) { + + /* + * Only operate on command batches that belong to the + * faulting context + */ + + if (replay[i]->context->id != cmdbatch->context->id) + continue; + + /* + * Skip all the command batches in this context until + * the EOF flag is seen. If the EOF flag is seen then + * force the preamble for the next command. + */ + + if (skip) { + replay[i]->priv |= CMDBATCH_FLAG_SKIP; + + if (replay[i]->flags & KGSL_CONTEXT_END_OF_FRAME) + skip = 0; + } else { + replay[i]->priv |= CMDBATCH_FLAG_FORCE_PREAMBLE; + return; + } + } + + /* + * If the EOF flag hasn't been seen yet then set the flag in the + * drawctxt to keep looking for it + */ + + if (skip && drawctxt) + drawctxt->flags |= CTXT_FLAGS_SKIP_EOF; + + /* + * If we did see the EOF flag then force the preamble on for the + * next command issued on this context + */ + + if (!skip && drawctxt) + drawctxt->flags |= CTXT_FLAGS_FORCE_PREAMBLE; +} + +static void remove_invalidated_cmdbatches(struct kgsl_device *device, + struct kgsl_cmdbatch **replay, int count) +{ + int i; + + for (i = 0; i < count; i++) { + struct kgsl_cmdbatch *cmd = replay[i]; + struct adreno_context *drawctxt; + + if (cmd == NULL) + continue; + + drawctxt = ADRENO_CONTEXT(cmd->context); + + if (kgsl_context_detached(cmd->context) || + drawctxt->state == ADRENO_CONTEXT_STATE_INVALID) { + replay[i] = NULL; + + mutex_lock(&device->mutex); + kgsl_cancel_events_timestamp(device, cmd->context, + cmd->timestamp); + mutex_unlock(&device->mutex); + + kgsl_cmdbatch_destroy(cmd); + } + } +} + +static void dispatcher_do_fault(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + unsigned int ptr; + unsigned int reg, base; + struct kgsl_cmdbatch **replay = NULL; + struct kgsl_cmdbatch *cmdbatch; + int ret, i, count = 0; + + BUG_ON(dispatcher->inflight == 0); + + /* Turn off all the timers */ + del_timer_sync(&dispatcher->timer); + del_timer_sync(&dispatcher->fault_timer); + + mutex_lock(&device->mutex); + + cmdbatch = dispatcher->cmdqueue[dispatcher->head]; + + /* + * If the fault was due to a timeout then stop the CP to ensure we don't + * get activity while we are trying to dump the state of the system + */ + + if (dispatcher->fault == ADRENO_DISPATCHER_TIMEOUT_FAULT) { + kgsl_regread(device, REG_CP_ME_CNTL, ®); + reg |= (1 << 27) | (1 << 28); + kgsl_regwrite(device, REG_CP_ME_CNTL, reg); + + /* Skip the PM dump for a timeout because it confuses people */ + cmdbatch->fault_policy |= KGSL_FT_SKIP_PMDUMP; + } + + kgsl_regread(device, REG_CP_IB1_BASE, &base); + + /* + * Dump the postmortem and snapshot information if this is the first + * detected fault for the oldest active command batch + */ + + if (!(cmdbatch->fault_policy & KGSL_FT_SKIP_PMDUMP)) { + kgsl_postmortem_dump(device, 0); + kgsl_device_snapshot(device, 1); + } + + mutex_unlock(&device->mutex); + + /* Allocate memory to store the inflight commands */ + replay = kzalloc(sizeof(*replay) * dispatcher->inflight, GFP_KERNEL); + + if (replay == NULL) { + unsigned int ptr = dispatcher->head; + + while (ptr != dispatcher->tail) { + struct kgsl_context *context = + dispatcher->cmdqueue[ptr]->context; + + adreno_drawctxt_invalidate(device, context); + kgsl_cmdbatch_destroy(dispatcher->cmdqueue[ptr]); + + ptr = CMDQUEUE_NEXT(ptr, ADRENO_DISPATCH_CMDQUEUE_SIZE); + } + + /* + * Set the replay count to zero - this will ensure that the + * hardware gets reset but nothing else goes played + */ + + count = 0; + goto replay; + } + + /* Copy the inflight command batches into the temporary storage */ + ptr = dispatcher->head; + + while (ptr != dispatcher->tail) { + replay[count++] = dispatcher->cmdqueue[ptr]; + ptr = CMDQUEUE_NEXT(ptr, ADRENO_DISPATCH_CMDQUEUE_SIZE); + } + + /* + * For the purposes of replay, we assume that the oldest command batch + * that hasn't retired a timestamp is "hung". + */ + + cmdbatch = replay[0]; + + /* + * Set a flag so we don't print another PM dump if the cmdbatch fails + * again on replay + */ + + cmdbatch->fault_policy |= KGSL_FT_SKIP_PMDUMP; + + /* + * A hardware fault generally means something was deterministically + * wrong with the command batch - no point in trying to replay it + * Clear the replay bit and move on to the next policy level + */ + + if (dispatcher->fault == ADRENO_DISPATCHER_HARD_FAULT) + cmdbatch->fault_policy &= ~KGSL_FT_REPLAY; + + /* + * A timeout fault means the IB timed out - don't be silly and replay + * it, because it will probably timeout again + */ + + if (dispatcher->fault == ADRENO_DISPATCHER_TIMEOUT_FAULT) + cmdbatch->fault_policy &= ~KGSL_FT_REPLAY; + + /* + * Execute the fault tolerance policy. Each command batch stores the + * current fault policy that was set when it was queued. + * As the options are tried in descending priority + * (REPLAY -> SKIPIBS -> SKIPFRAME -> NOTHING) the bits are cleared + * from the cmdbatch policy so the next thing can be tried if the + * change comes around again + */ + + /* Replay the hanging command batch again */ + if (cmdbatch->fault_policy & KGSL_FT_REPLAY) { + cmdbatch->fault_policy &= ~KGSL_FT_REPLAY; + goto replay; + } + + /* + * Skip the last IB1 that was played but replay everything else. + * Note that the last IB1 might not be in the "hung" command batch + * because the CP may have caused a page-fault while it was prefetching + * the next IB1/IB2. walk all outstanding commands and zap the + * supposedly bad IB1 where ever it lurks. + */ + + if (cmdbatch->fault_policy & KGSL_FT_SKIPIB) { + cmdbatch->fault_policy &= ~KGSL_FT_SKIPIB; + + for (i = 0; i < count; i++) { + if (replay[i] != NULL) + cmdbatch_skip_ib(replay[i], base); + } + + goto replay; + } + + if (cmdbatch->fault_policy & KGSL_FT_SKIPFRAME) { + + cmdbatch->fault_policy &= ~KGSL_FT_SKIPFRAME; + + /* + * Skip all the pending command batches for this context until + * the EOF frame is seen + */ + cmdbatch_skip_frame(cmdbatch, replay, count); + goto replay; + } + + /* If we get here then all the policies failed or FT is disabled */ + + /* Invalidate the context */ + adreno_drawctxt_invalidate(device, cmdbatch->context); + + /* Remove any pending command batches that have been invalidated */ + remove_invalidated_cmdbatches(device, replay, count); + +replay: + /* Reset the dispatcher queue */ + dispatcher->inflight = 0; + dispatcher->head = dispatcher->tail = 0; + + /* Reset the GPU */ + mutex_lock(&device->mutex); + ret = adreno_reset(device); + mutex_unlock(&device->mutex); + + /* If adreno_reset() fails then what hope do we have for the future? */ + BUG_ON(ret); + + /* + * Force the preamble on the first command (if applicable) to avoid any + * strange stage issues + */ + + if (replay[0]) + replay[0]->priv |= CMDBATCH_FLAG_FORCE_PREAMBLE; + + /* Replay the pending command buffers */ + for (i = 0; i < count; i++) { + + int ret; + + if (replay[i] == NULL) + continue; + + /* + * Force each command batch to wait for idle - this avoids wierd + * CP parse issues + */ + + replay[i]->flags |= KGSL_CMD_FLAGS_WFI; + + ret = sendcmd(adreno_dev, replay[i]); + + /* + * If sending the command fails, then try to recover by + * invalidating the context + */ + + if (ret) { + adreno_drawctxt_invalidate(device, cmdbatch->context); + + remove_invalidated_cmdbatches(device, &replay[i], + count - i); + } + } + + mutex_lock(&device->mutex); + kgsl_active_count_put(device); + mutex_unlock(&device->mutex); + + kfree(replay); +} + +static inline int cmdbatch_consumed(struct kgsl_cmdbatch *cmdbatch, + unsigned int consumed, unsigned int retired) +{ + return ((timestamp_cmp(cmdbatch->timestamp, consumed) >= 0) && + (timestamp_cmp(retired, cmdbatch->timestamp) < 0)); +} + +/** + * adreno_dispatcher_work() - Master work handler for the dispatcher + * @work: Pointer to the work struct for the current work queue + * + * Process expired commands and send new ones. + */ +static void adreno_dispatcher_work(struct work_struct *work) +{ + struct adreno_dispatcher *dispatcher = + container_of(work, struct adreno_dispatcher, work); + struct adreno_device *adreno_dev = + container_of(dispatcher, struct adreno_device, dispatcher); + struct kgsl_device *device = &adreno_dev->dev; + int count = 0; + + mutex_lock(&dispatcher->mutex); + + while (dispatcher->head != dispatcher->tail) { + uint32_t consumed, retired = 0; + struct kgsl_cmdbatch *cmdbatch = + dispatcher->cmdqueue[dispatcher->head]; + struct adreno_context *drawctxt; + BUG_ON(cmdbatch == NULL); + + drawctxt = ADRENO_CONTEXT(cmdbatch->context); + + /* + * First try to expire the timestamp. This happens if the + * context is valid and the timestamp expired normally or if the + * context was destroyed before the command batch was finished + * in the GPU. Either way retire the command batch advance the + * pointers and continue processing the queue + */ + + if (!kgsl_context_detached(cmdbatch->context)) + retired = kgsl_readtimestamp(device, cmdbatch->context, + KGSL_TIMESTAMP_RETIRED); + + if (kgsl_context_detached(cmdbatch->context) || + (timestamp_cmp(cmdbatch->timestamp, retired) <= 0)) { + + trace_adreno_cmdbatch_retired(cmdbatch, + dispatcher->inflight - 1); + + /* Reduce the number of inflight command batches */ + dispatcher->inflight--; + + /* Zero the old entry*/ + dispatcher->cmdqueue[dispatcher->head] = NULL; + + /* Advance the buffer head */ + dispatcher->head = CMDQUEUE_NEXT(dispatcher->head, + ADRENO_DISPATCH_CMDQUEUE_SIZE); + + /* Destroy the retired command batch */ + kgsl_cmdbatch_destroy(cmdbatch); + + /* Update the expire time for the next command batch */ + + if (dispatcher->inflight > 0) { + cmdbatch = + dispatcher->cmdqueue[dispatcher->head]; + cmdbatch->expires = jiffies + + msecs_to_jiffies(_cmdbatch_timeout); + } + + count++; + continue; + } + + /* + * If we got a fault from the interrupt handler, this command + * is to blame. Invalidate it, reset and replay + */ + + if (dispatcher->fault) { + dispatcher_do_fault(device); + dispatcher->fault = 0; + goto done; + } + + /* Get the last consumed timestamp */ + consumed = kgsl_readtimestamp(device, cmdbatch->context, + KGSL_TIMESTAMP_CONSUMED); + + /* + * Break here if fault detection is dsiabled for the context or + * if the long running IB detection is disabled device wide + * Long running command buffers will be allowed to run to + * completion - but badly behaving command buffers (infinite + * shaders etc) can end up running forever. + */ + + if (!adreno_dev->long_ib_detect || + drawctxt->flags & CTXT_FLAGS_NO_FAULT_TOLERANCE) + break; + + /* + * The last line of defense is to check if the command batch has + * timed out. If we get this far but the timeout hasn't expired + * yet then the GPU is still ticking away + */ + + if (time_is_after_jiffies(cmdbatch->expires)) + break; + + /* Boom goes the dynamite */ + + KGSL_DRV_ERR(device, + "Context %d, timestamp %d ran too long\n", + drawctxt->base.id, drawctxt->timestamp); + + dispatcher->fault = ADRENO_DISPATCHER_TIMEOUT_FAULT; + + dispatcher_do_fault(device); + break; + } + + /* + * Decrement the active count to 0 - this will allow the system to go + * into suspend even if there are queued command batches + */ + + if (count && dispatcher->inflight == 0) { + mutex_lock(&device->mutex); + kgsl_active_count_put(device); + mutex_unlock(&device->mutex); + } + + /* Dispatch new commands if we have the room */ + if (dispatcher->inflight < _dispatcher_inflight) + _adreno_dispatcher_issuecmds(adreno_dev); + +done: + /* Either update the timer for the next command batch or disable it */ + if (dispatcher->inflight) { + struct kgsl_cmdbatch *cmdbatch + = dispatcher->cmdqueue[dispatcher->head]; + + /* Update the timeout timer for the next command batch */ + mod_timer(&dispatcher->timer, cmdbatch->expires); + } else { + del_timer_sync(&dispatcher->timer); + del_timer_sync(&dispatcher->fault_timer); + } + + /* Before leaving update the pwrscale information */ + mutex_lock(&device->mutex); + kgsl_pwrscale_idle(device); + mutex_unlock(&device->mutex); + + mutex_unlock(&dispatcher->mutex); +} + +void adreno_dispatcher_schedule(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + + queue_work(device->work_queue, &dispatcher->work); +} + +/** + * adreno_dispatcher_queue_context() - schedule a drawctxt in the dispatcher + * device: pointer to the KGSL device + * drawctxt: pointer to the drawctxt to schedule + * + * Put a draw context on the dispatcher pending queue and schedule the + * dispatcher. This is used to reschedule changes that might have been blocked + * for sync points or other concerns + */ +void adreno_dispatcher_queue_context(struct kgsl_device *device, + struct adreno_context *drawctxt) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + dispatcher_queue_context(adreno_dev, drawctxt); + adreno_dispatcher_schedule(device); +} + +/* + * This is called on a regular basis while command batches are inflight. Fault + * detection registers are read and compared to the existing values - if they + * changed then the GPU is still running. If they are the same between + * subsequent calls then the GPU may have faulted + */ + +void adreno_dispatcher_fault_timer(unsigned long data) +{ + struct adreno_device *adreno_dev = (struct adreno_device *) data; + struct kgsl_device *device = &adreno_dev->dev; + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + + /* Leave if the user decided to turn off fast hang detection */ + if (adreno_dev->fast_hang_detect == 0) + return; + + /* Don't do anything if the dispatcher is idle or faulted */ + if (dispatcher->inflight == 0 || dispatcher->fault) + return; + + /* Make sure the device is active before trying a read */ + if (device->state != KGSL_STATE_ACTIVE) + return; + + /* + * Read the fault registers - if it returns 0 then they haven't changed + * so mark the dispatcher as faulted and schedule the work loop. + */ + + if (!fault_detect_read_compare(device)) { + dispatcher->fault = ADRENO_DISPATCHER_SOFT_FAULT; + adreno_dispatcher_schedule(device); + } else { + mod_timer(&dispatcher->fault_timer, + jiffies + msecs_to_jiffies(_fault_timer_interval)); + } +} + +/* + * This is called when the timer expires - it either means the GPU is hung or + * the IB is taking too long to execute + */ +void adreno_dispatcher_timer(unsigned long data) +{ + struct adreno_device *adreno_dev = (struct adreno_device *) data; + struct kgsl_device *device = &adreno_dev->dev; + + adreno_dispatcher_schedule(device); +} +/** + * adreno_dispatcher_irq_fault() - Trigger a fault in the dispatcher + * @device: Pointer to the KGSL device + * + * Called from an interrupt context this will trigger a fault in the + * dispatcher for the oldest pending command batch + */ +void adreno_dispatcher_irq_fault(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + + dispatcher->fault = ADRENO_DISPATCHER_HARD_FAULT; + adreno_dispatcher_schedule(device); +} + +/** + * adreno_dispatcher_pause() - stop the dispatcher + * @adreno_dev: pointer to the adreno device structure + * + * Pause the dispather so it doesn't accept any new commands + */ +void adreno_dispatcher_pause(struct adreno_device *adreno_dev) +{ + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + + /* + * This will probably get called while holding other mutexes so don't + * take the dispatcher mutex. The biggest penalty is that another + * command might be submitted while we are in here but thats okay + * because whoever is waiting for the drain will just have another + * command batch to wait for + */ + + dispatcher->state = ADRENO_DISPATCHER_PAUSE; +} + +/** + * adreno_dispatcher_start() - activate the dispatcher + * @adreno_dev: pointer to the adreno device structure + * + * Set the disaptcher active and start the loop once to get things going + */ +void adreno_dispatcher_start(struct adreno_device *adreno_dev) +{ + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + + dispatcher->state = ADRENO_DISPATCHER_ACTIVE; + + /* Schedule the work loop to get things going */ + adreno_dispatcher_schedule(&adreno_dev->dev); +} + +/** + * adreno_dispatcher_stop() - stop the dispatcher + * @adreno_dev: pointer to the adreno device structure + * + * Stop the dispatcher and close all the timers + */ +void adreno_dispatcher_stop(struct adreno_device *adreno_dev) +{ + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + + del_timer_sync(&dispatcher->timer); + del_timer_sync(&dispatcher->fault_timer); +} + +/** + * adreno_dispatcher_close() - close the dispatcher + * @adreno_dev: pointer to the adreno device structure + * + * Close the dispatcher and free all the oustanding commands and memory + */ +void adreno_dispatcher_close(struct adreno_device *adreno_dev) +{ + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + + mutex_lock(&dispatcher->mutex); + del_timer_sync(&dispatcher->timer); + del_timer_sync(&dispatcher->fault_timer); + + while (dispatcher->head != dispatcher->tail) { + kgsl_cmdbatch_destroy(dispatcher->cmdqueue[dispatcher->head]); + dispatcher->head = (dispatcher->head + 1) + % ADRENO_DISPATCH_CMDQUEUE_SIZE; + } + + kfree(fault_detect_regs); + fault_detect_regs = NULL; + + mutex_unlock(&dispatcher->mutex); + + kobject_put(&dispatcher->kobj); +} + +struct dispatcher_attribute { + struct attribute attr; + ssize_t (*show)(struct adreno_dispatcher *, + struct dispatcher_attribute *, char *); + ssize_t (*store)(struct adreno_dispatcher *, + struct dispatcher_attribute *, const char *buf, + size_t count); + unsigned int max; + unsigned int *value; +}; + +#define DISPATCHER_UINT_ATTR(_name, _mode, _max, _value) \ + struct dispatcher_attribute dispatcher_attr_##_name = { \ + .attr = { .name = __stringify(_name), .mode = _mode }, \ + .show = _show_uint, \ + .store = _store_uint, \ + .max = _max, \ + .value = &(_value), \ + } + +#define to_dispatcher_attr(_a) \ + container_of((_a), struct dispatcher_attribute, attr) +#define to_dispatcher(k) container_of(k, struct adreno_dispatcher, kobj) + +static ssize_t _store_uint(struct adreno_dispatcher *dispatcher, + struct dispatcher_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + int ret = kstrtoul(buf, 0, &val); + + if (ret) + return ret; + + if (!val || (attr->max && (val > attr->max))) + return -EINVAL; + + *((unsigned int *) attr->value) = val; + return size; +} + +static ssize_t _show_uint(struct adreno_dispatcher *dispatcher, + struct dispatcher_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", + *((unsigned int *) attr->value)); +} + +static DISPATCHER_UINT_ATTR(inflight, 0644, ADRENO_DISPATCH_CMDQUEUE_SIZE, + _dispatcher_inflight); +/* + * Our code that "puts back" a command from the context is much cleaner + * if we are sure that there will always be enough room in the + * ringbuffer so restrict the maximum size of the context queue to + * ADRENO_CONTEXT_CMDQUEUE_SIZE - 1 + */ +static DISPATCHER_UINT_ATTR(context_cmdqueue_size, 0644, + ADRENO_CONTEXT_CMDQUEUE_SIZE - 1, _context_cmdqueue_size); +static DISPATCHER_UINT_ATTR(context_burst_count, 0644, 0, + _context_cmdbatch_burst); +static DISPATCHER_UINT_ATTR(cmdbatch_timeout, 0644, 0, _cmdbatch_timeout); +static DISPATCHER_UINT_ATTR(context_queue_wait, 0644, 0, _context_queue_wait); +static DISPATCHER_UINT_ATTR(fault_detect_interval, 0644, 0, + _fault_timer_interval); + +static struct attribute *dispatcher_attrs[] = { + &dispatcher_attr_inflight.attr, + &dispatcher_attr_context_cmdqueue_size.attr, + &dispatcher_attr_context_burst_count.attr, + &dispatcher_attr_cmdbatch_timeout.attr, + &dispatcher_attr_context_queue_wait.attr, + &dispatcher_attr_fault_detect_interval.attr, + NULL, +}; + +static ssize_t dispatcher_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct adreno_dispatcher *dispatcher = to_dispatcher(kobj); + struct dispatcher_attribute *pattr = to_dispatcher_attr(attr); + ssize_t ret = -EIO; + + if (pattr->show) + ret = pattr->show(dispatcher, pattr, buf); + + return ret; +} + +static ssize_t dispatcher_sysfs_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t count) +{ + struct adreno_dispatcher *dispatcher = to_dispatcher(kobj); + struct dispatcher_attribute *pattr = to_dispatcher_attr(attr); + ssize_t ret = -EIO; + + if (pattr->store) + ret = pattr->store(dispatcher, pattr, buf, count); + + return ret; +} + +static void dispatcher_sysfs_release(struct kobject *kobj) +{ +} + +static const struct sysfs_ops dispatcher_sysfs_ops = { + .show = dispatcher_sysfs_show, + .store = dispatcher_sysfs_store +}; + +static struct kobj_type ktype_dispatcher = { + .sysfs_ops = &dispatcher_sysfs_ops, + .default_attrs = dispatcher_attrs, + .release = dispatcher_sysfs_release +}; + +/** + * adreno_dispatcher_init() - Initialize the dispatcher + * @adreno_dev: pointer to the adreno device structure + * + * Initialize the dispatcher + */ +int adreno_dispatcher_init(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = &adreno_dev->dev; + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + int ret; + + memset(dispatcher, 0, sizeof(*dispatcher)); + + mutex_init(&dispatcher->mutex); + + setup_timer(&dispatcher->timer, adreno_dispatcher_timer, + (unsigned long) adreno_dev); + + setup_timer(&dispatcher->fault_timer, adreno_dispatcher_fault_timer, + (unsigned long) adreno_dev); + + INIT_WORK(&dispatcher->work, adreno_dispatcher_work); + + plist_head_init(&dispatcher->pending); + spin_lock_init(&dispatcher->plist_lock); + + dispatcher->state = ADRENO_DISPATCHER_ACTIVE; + + ret = kobject_init_and_add(&dispatcher->kobj, &ktype_dispatcher, + &device->dev->kobj, "dispatch"); + + fault_detect_regs = kzalloc(FT_DETECT_REGS_COUNT * sizeof(unsigned int), + GFP_KERNEL); + + if (fault_detect_regs == NULL) + ret = -ENOMEM; + + return ret; +} diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c index 6fbcdee9..dac4f8c7 100644 --- a/drivers/gpu/msm/adreno_drawctxt.c +++ b/drivers/gpu/msm/adreno_drawctxt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2013, The Linux Foundation. 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 and @@ -12,6 +12,7 @@ */ #include +#include #include "kgsl.h" #include "kgsl_sharedmem.h" @@ -143,11 +144,10 @@ void build_quad_vtxbuff(struct adreno_context *drawctxt, */ int adreno_drawctxt_create(struct kgsl_device *device, struct kgsl_pagetable *pagetable, - struct kgsl_context *context, uint32_t flags) + struct kgsl_context *context, uint32_t *flags) { struct adreno_context *drawctxt; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; int ret; drawctxt = kzalloc(sizeof(struct adreno_context), GFP_KERNEL); @@ -160,28 +160,38 @@ int adreno_drawctxt_create(struct kgsl_device *device, drawctxt->pagetable = pagetable; drawctxt->bin_base_offset = 0; drawctxt->id = context->id; - rb->timestamp[context->id] = 0; + drawctxt->timestamp = 0; - if (flags & KGSL_CONTEXT_PREAMBLE) + *flags &= (KGSL_CONTEXT_PREAMBLE | + KGSL_CONTEXT_NO_GMEM_ALLOC | + KGSL_CONTEXT_PER_CONTEXT_TS | + KGSL_CONTEXT_USER_GENERATED_TS | + KGSL_CONTEXT_NO_FAULT_TOLERANCE | + KGSL_CONTEXT_TYPE_MASK); + + if (*flags & KGSL_CONTEXT_PREAMBLE) drawctxt->flags |= CTXT_FLAGS_PREAMBLE; - if (flags & KGSL_CONTEXT_NO_GMEM_ALLOC) + if (*flags & KGSL_CONTEXT_NO_GMEM_ALLOC) drawctxt->flags |= CTXT_FLAGS_NOGMEMALLOC; - if (flags & KGSL_CONTEXT_PER_CONTEXT_TS) + if (*flags & KGSL_CONTEXT_PER_CONTEXT_TS) drawctxt->flags |= CTXT_FLAGS_PER_CONTEXT_TS; - if (flags & KGSL_CONTEXT_USER_GENERATED_TS) { - if (!(flags & KGSL_CONTEXT_PER_CONTEXT_TS)) { + if (*flags & KGSL_CONTEXT_USER_GENERATED_TS) { + if (!(*flags & KGSL_CONTEXT_PER_CONTEXT_TS)) { ret = -EINVAL; goto err; } drawctxt->flags |= CTXT_FLAGS_USER_GENERATED_TS; } - if (flags & KGSL_CONTEXT_NO_FAULT_TOLERANCE) + if (*flags & KGSL_CONTEXT_NO_FAULT_TOLERANCE) drawctxt->flags |= CTXT_FLAGS_NO_FAULT_TOLERANCE; + drawctxt->type = + (*flags & KGSL_CONTEXT_TYPE_MASK) >> KGSL_CONTEXT_TYPE_SHIFT; + ret = adreno_dev->gpudev->ctxt_create(adreno_dev, drawctxt); if (ret) goto err; diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h index c91bfc01..dfa4277a 100644 --- a/drivers/gpu/msm/adreno_drawctxt.h +++ b/drivers/gpu/msm/adreno_drawctxt.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2013, The Linux Foundation. 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 and @@ -57,6 +57,14 @@ /* Context no fault tolerance */ #define CTXT_FLAGS_NO_FAULT_TOLERANCE BIT(16) +/* Symbolic table for the adreno draw context type */ +#define ADRENO_DRAWCTXT_TYPES \ + { KGSL_CONTEXT_TYPE_ANY, "any" }, \ + { KGSL_CONTEXT_TYPE_GL, "GL" }, \ + { KGSL_CONTEXT_TYPE_CL, "CL" }, \ + { KGSL_CONTEXT_TYPE_C2D, "C2D" }, \ + { KGSL_CONTEXT_TYPE_RS, "RS" } + struct kgsl_device; struct adreno_device; struct kgsl_device_private; @@ -92,9 +100,11 @@ struct adreno_context { char pid_name[TASK_COMM_LEN]; unsigned int id; unsigned int ib_gpu_time_used; + unsigned int timestamp; uint32_t flags; uint32_t pagefault; unsigned long pagefault_ts; + unsigned int type; struct kgsl_pagetable *pagetable; struct kgsl_memdesc gpustate; unsigned int reg_restore[3]; @@ -127,7 +137,7 @@ struct adreno_context { int adreno_drawctxt_create(struct kgsl_device *device, struct kgsl_pagetable *pagetable, struct kgsl_context *context, - uint32_t flags); + uint32_t *flags); void adreno_drawctxt_destroy(struct kgsl_device *device, struct kgsl_context *context); diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c index 5fdcf194..ee82bd65 100644 --- a/drivers/gpu/msm/adreno_postmortem.c +++ b/drivers/gpu/msm/adreno_postmortem.c @@ -740,7 +740,9 @@ int adreno_dump(struct kgsl_device *device, int manual) (unsigned int *) &context_id, KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context)); - context = idr_find(&device->context_idr, context_id); + + context = kgsl_context_get(device, context_id); + if (context) { ts_processed = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED); @@ -749,6 +751,8 @@ int adreno_dump(struct kgsl_device *device, int manual) } else KGSL_LOG_DUMP(device, "BAD CTXT: %d\n", context_id); + kgsl_context_put(context); + num_item = adreno_ringbuffer_count(&adreno_dev->ringbuffer, cp_rb_rptr); if (num_item <= 0) diff --git a/drivers/gpu/msm/adreno_postmortem.h b/drivers/gpu/msm/adreno_postmortem.h new file mode 100644 index 00000000..a1745ba0 --- /dev/null +++ b/drivers/gpu/msm/adreno_postmortem.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2010-2011, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ADRENO_POSTMORTEM_H +#define __ADRENO_POSTMORTEM_H + +struct kgsl_device; + +int adreno_postmortem_dump(struct kgsl_device *device, int manual); + +#endif /* __ADRENO_POSTMORTEM_H */ diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c old mode 100644 new mode 100755 index 48367fae..edc4a720 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2013, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2014, The Linux Foundation. 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 and @@ -57,14 +57,14 @@ adreno_ringbuffer_waitspace(struct adreno_ringbuffer *rb, struct adreno_context *context, unsigned int numcmds, int wptr_ahead) { - int nopcount; + int nopcount, i; unsigned int freecmds; unsigned int *cmds; uint cmds_gpu; unsigned long wait_time; unsigned long wait_timeout = msecs_to_jiffies(ADRENO_IDLE_TIMEOUT); unsigned long wait_time_part; - unsigned int prev_reg_val[ft_detect_regs_count]; + unsigned int prev_reg_val[FT_DETECT_REGS_COUNT]; memset(prev_reg_val, 0, sizeof(prev_reg_val)); @@ -78,6 +78,13 @@ adreno_ringbuffer_waitspace(struct adreno_ringbuffer *rb, GSL_RB_WRITE(cmds, cmds_gpu, cp_nop_packet(nopcount)); + /* + * Fill remaining ring buffer data with KGSL_NOP_DATA_FILLER + * to avoid misinterpretation in recovery extraction logic. + */ + for (i = 0; i < nopcount; i++) + GSL_RB_WRITE(cmds, cmds_gpu, KGSL_NOP_DATA_FILLER); + /* Make sure that rptr is not 0 before submitting * commands at the end of ringbuffer. We do not * want the rptr and wptr to become equal when @@ -319,7 +326,7 @@ int adreno_ringbuffer_load_pfp_ucode(struct kgsl_device *device) return 0; } -int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) +int adreno_ringbuffer_start(struct adreno_ringbuffer *rb) { int status; /*cp_rb_cntl_u cp_rb_cntl; */ @@ -331,9 +338,6 @@ int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) if (rb->flags & KGSL_FLAGS_STARTED) return 0; - if (init_ram) - rb->timestamp[KGSL_MEMSTORE_GLOBAL] = 0; - kgsl_sharedmem_set(&rb->memptrs_desc, 0, 0, sizeof(struct kgsl_rbmemptrs)); @@ -383,32 +387,6 @@ int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) rb->memptrs_desc.gpuaddr + GSL_RB_MEMPTRS_RPTR_OFFSET); - if (adreno_is_a3xx(adreno_dev)) { - /* enable access protection to privileged registers */ - adreno_regwrite(device, A3XX_CP_PROTECT_CTRL, 0x00000007); - - /* RBBM registers */ - adreno_regwrite(device, A3XX_CP_PROTECT_REG_0, 0x63000040); - adreno_regwrite(device, A3XX_CP_PROTECT_REG_1, 0x62000080); - adreno_regwrite(device, A3XX_CP_PROTECT_REG_2, 0x600000CC); - adreno_regwrite(device, A3XX_CP_PROTECT_REG_3, 0x60000108); - adreno_regwrite(device, A3XX_CP_PROTECT_REG_4, 0x64000140); - adreno_regwrite(device, A3XX_CP_PROTECT_REG_5, 0x66000400); - - /* CP registers */ - adreno_regwrite(device, A3XX_CP_PROTECT_REG_6, 0x65000700); - adreno_regwrite(device, A3XX_CP_PROTECT_REG_7, 0x610007D8); - adreno_regwrite(device, A3XX_CP_PROTECT_REG_8, 0x620007E0); - adreno_regwrite(device, A3XX_CP_PROTECT_REG_9, 0x61001178); - adreno_regwrite(device, A3XX_CP_PROTECT_REG_A, 0x64001180); - - /* RB registers */ - adreno_regwrite(device, A3XX_CP_PROTECT_REG_B, 0x60003300); - - /* VBIF registers */ - adreno_regwrite(device, A3XX_CP_PROTECT_REG_C, 0x6B00C000); - } - if (adreno_is_a2xx(adreno_dev)) { /* explicitly clear all cp interrupts */ adreno_regwrite(device, REG_CP_INT_ACK, 0xFFFFFFFF); @@ -443,7 +421,9 @@ int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) adreno_regwrite(device, REG_CP_ME_CNTL, 0); /* ME init is GPU specific, so jump into the sub-function */ - adreno_dev->gpudev->rb_init(adreno_dev, rb); + status = adreno_dev->gpudev->rb_init(adreno_dev, rb); + if (status) + return status; /* idle device to validate ME INIT */ status = adreno_idle(device); @@ -481,6 +461,7 @@ int adreno_ringbuffer_init(struct kgsl_device *device) */ rb->sizedwords = KGSL_RB_SIZE >> 2; + rb->buffer_desc.flags = KGSL_MEMFLAGS_GPUREADONLY; /* allocate memory for ringbuffer */ status = kgsl_allocate_contiguous(&rb->buffer_desc, (rb->sizedwords << 2)); @@ -523,40 +504,32 @@ void adreno_ringbuffer_close(struct adreno_ringbuffer *rb) memset(rb, 0, sizeof(struct adreno_ringbuffer)); } -static uint32_t +static int adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, struct adreno_context *context, unsigned int flags, unsigned int *cmds, - int sizedwords, uint32_t timestamp) + int sizedwords) { struct adreno_device *adreno_dev = ADRENO_DEVICE(rb->device); unsigned int *ringcmds; unsigned int total_sizedwords = sizedwords; unsigned int i; unsigned int rcmd_gpu; - unsigned int context_id = KGSL_MEMSTORE_GLOBAL; + unsigned int context_id; unsigned int gpuaddr = rb->device->memstore.gpuaddr; + unsigned int timestamp; /* * if the context was not created with per context timestamp * support, we must use the global timestamp since issueibcmds - * will be returning that one. + * will be returning that one, or if an internal issue then + * use global timestamp. */ - if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS) + if ((context && (context->flags & CTXT_FLAGS_PER_CONTEXT_TS)) && + !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) context_id = context->id; - - if ((context && context->flags & CTXT_FLAGS_USER_GENERATED_TS) && - (!(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE))) { - if (timestamp_cmp(rb->timestamp[context_id], - timestamp) >= 0) { - KGSL_DRV_ERR(rb->device, - "Invalid user generated ts <%d:0x%x>, " - "less than last issued ts <%d:0x%x>\n", - context_id, timestamp, context_id, - rb->timestamp[context_id]); - return -ERANGE; - } - } + else + context_id = KGSL_MEMSTORE_GLOBAL; /* reserve space to temporarily turn off protected mode * error checking if needed @@ -564,38 +537,37 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, total_sizedwords += flags & KGSL_CMD_FLAGS_PMODE ? 4 : 0; /* 2 dwords to store the start of command sequence */ total_sizedwords += 2; + /* internal ib command identifier for the ringbuffer */ + total_sizedwords += (flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE) ? 2 : 0; /* Add CP_COND_EXEC commands to generate CP_INTERRUPT */ total_sizedwords += context ? 13 : 0; + if ((context) && (context->flags & CTXT_FLAGS_PER_CONTEXT_TS) && + (flags & (KGSL_CMD_FLAGS_INTERNAL_ISSUE | + KGSL_CMD_FLAGS_GET_INT))) + total_sizedwords += 2; + if (adreno_is_a3xx(adreno_dev)) total_sizedwords += 7; if (adreno_is_a2xx(adreno_dev)) total_sizedwords += 2; /* CP_WAIT_FOR_IDLE */ - total_sizedwords += 2; /* scratchpad ts for fault tolerance */ - if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS && - !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) { - total_sizedwords += 3; /* sop timestamp */ - total_sizedwords += 4; /* eop timestamp */ + total_sizedwords += 2; /* scratchpad ts for recovery */ + total_sizedwords += 3; /* sop timestamp */ + total_sizedwords += 4; /* eop timestamp */ + + if (KGSL_MEMSTORE_GLOBAL != context_id) total_sizedwords += 3; /* global timestamp without cache * flush for non-zero context */ - } else { - total_sizedwords += 4; /* global timestamp for fault tolerance*/ - } if (flags & KGSL_CMD_FLAGS_EOF) total_sizedwords += 2; ringcmds = adreno_ringbuffer_allocspace(rb, context, total_sizedwords); - if (!ringcmds) { - /* - * We could not allocate space in ringbuffer, just return the - * last timestamp - */ - return rb->timestamp[context_id]; - } + if (!ringcmds) + return -ENOSPC; rcmd_gpu = rb->buffer_desc.gpuaddr + sizeof(uint)*(rb->wptr-total_sizedwords); @@ -603,6 +575,29 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_nop_packet(1)); GSL_RB_WRITE(ringcmds, rcmd_gpu, KGSL_CMD_IDENTIFIER); + if (flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE) { + GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_nop_packet(1)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, KGSL_CMD_INTERNAL_IDENTIFIER); + } + + /* always increment the global timestamp. once. */ + rb->global_ts++; + + if (KGSL_MEMSTORE_GLOBAL != context_id) + timestamp = context->timestamp; + else + timestamp = rb->global_ts; + + /* scratchpad ts for recovery */ + GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_type0_packet(REG_CP_TIMESTAMP, 1)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->global_ts); + + /* start-of-pipeline timestamp */ + GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_type3_packet(CP_MEM_WRITE, 2)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr + + KGSL_MEMSTORE_OFFSET(context_id, soptimestamp))); + GSL_RB_WRITE(ringcmds, rcmd_gpu, timestamp); + if (flags & KGSL_CMD_FLAGS_PMODE) { /* disable protected mode error checking */ GSL_RB_WRITE(ringcmds, rcmd_gpu, @@ -622,21 +617,6 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, GSL_RB_WRITE(ringcmds, rcmd_gpu, 1); } - /* always increment the global timestamp. once. */ - rb->timestamp[KGSL_MEMSTORE_GLOBAL]++; - - /* Do not update context's timestamp for internal submissions */ - if (context && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) { - if (context_id == KGSL_MEMSTORE_GLOBAL) - rb->timestamp[context->id] = - rb->timestamp[KGSL_MEMSTORE_GLOBAL]; - else if (context->flags & CTXT_FLAGS_USER_GENERATED_TS) - rb->timestamp[context_id] = timestamp; - else - rb->timestamp[context_id]++; - } - timestamp = rb->timestamp[context_id]; - /* HW Workaround for MMU Page fault * due to memory getting free early before * GPU completes it. @@ -647,14 +627,10 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x00); } - /* scratchpad ts for fault tolerance */ - GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_type0_packet(REG_CP_TIMESTAMP, 1)); - GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->timestamp[KGSL_MEMSTORE_GLOBAL]); - if (adreno_is_a3xx(adreno_dev)) { /* - * FLush HLSQ lazy updates to make sure there are no - * rsources pending for indirect loads after the timestamp + * Flush HLSQ lazy updates to make sure there are no + * resources pending for indirect loads after the timestamp */ GSL_RB_WRITE(ringcmds, rcmd_gpu, @@ -665,39 +641,24 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x00); } - if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS - && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) { - /* start-of-pipeline timestamp */ - GSL_RB_WRITE(ringcmds, rcmd_gpu, - cp_type3_packet(CP_MEM_WRITE, 2)); - GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr + - KGSL_MEMSTORE_OFFSET(context_id, soptimestamp))); - GSL_RB_WRITE(ringcmds, rcmd_gpu, timestamp); - - /* end-of-pipeline timestamp */ - GSL_RB_WRITE(ringcmds, rcmd_gpu, - cp_type3_packet(CP_EVENT_WRITE, 3)); - GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH_TS); - GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr + - KGSL_MEMSTORE_OFFSET(context_id, eoptimestamp))); - GSL_RB_WRITE(ringcmds, rcmd_gpu, timestamp); + /* + * end-of-pipeline timestamp. If per context timestamps is not + * enabled, then context_id will be KGSL_MEMSTORE_GLOBAL so all + * eop timestamps will work out. + */ + GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_type3_packet(CP_EVENT_WRITE, 3)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH_TS); + GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr + + KGSL_MEMSTORE_OFFSET(context_id, eoptimestamp))); + GSL_RB_WRITE(ringcmds, rcmd_gpu, timestamp); + if (KGSL_MEMSTORE_GLOBAL != context_id) { GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_type3_packet(CP_MEM_WRITE, 2)); GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr + - KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, - eoptimestamp))); - GSL_RB_WRITE(ringcmds, rcmd_gpu, - rb->timestamp[KGSL_MEMSTORE_GLOBAL]); - } else { - GSL_RB_WRITE(ringcmds, rcmd_gpu, - cp_type3_packet(CP_EVENT_WRITE, 3)); - GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH_TS); - GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr + - KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, - eoptimestamp))); - GSL_RB_WRITE(ringcmds, rcmd_gpu, - rb->timestamp[KGSL_MEMSTORE_GLOBAL]); + KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, + eoptimestamp))); + GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->global_ts); } if (context) { /* Conditional execution based on memory values */ @@ -735,6 +696,19 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, GSL_RB_WRITE(ringcmds, rcmd_gpu, CP_INT_CNTL__RB_INT_MASK); } + /* + * If per context timestamps are enabled and any of the kgsl + * internal commands want INT to be generated trigger the INT + */ + if ((context) && (context->flags & CTXT_FLAGS_PER_CONTEXT_TS) && + (flags & (KGSL_CMD_FLAGS_INTERNAL_ISSUE | + KGSL_CMD_FLAGS_GET_INT))) { + GSL_RB_WRITE(ringcmds, rcmd_gpu, + cp_type3_packet(CP_INTERRUPT, 1)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, + CP_INT_CNTL__RB_INT_MASK); + } + if (adreno_is_a3xx(adreno_dev)) { /* Dummy set-constant to trigger context rollover */ GSL_RB_WRITE(ringcmds, rcmd_gpu, @@ -751,7 +725,7 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, adreno_ringbuffer_submit(rb); - return timestamp; + return 0; } unsigned int @@ -771,7 +745,7 @@ adreno_ringbuffer_issuecmds(struct kgsl_device *device, flags |= KGSL_CMD_FLAGS_INTERNAL_ISSUE; return adreno_ringbuffer_addcmds(rb, drawctxt, flags, cmds, - sizedwords, 0); + sizedwords); } static bool _parse_ibs(struct kgsl_device_private *dev_priv, uint gpuaddr, @@ -865,10 +839,8 @@ static bool _parse_ibs(struct kgsl_device_private *dev_priv, buffer */ struct kgsl_mem_entry *entry; - spin_lock(&dev_priv->process_priv->mem_lock); entry = kgsl_sharedmem_find_region(dev_priv->process_priv, gpuaddr, sizedwords * sizeof(uint)); - spin_unlock(&dev_priv->process_priv->mem_lock); if (entry == NULL) { KGSL_CMD_ERR(dev_priv->device, "no mapping for gpuaddr: 0x%08x\n", gpuaddr); @@ -976,25 +948,31 @@ adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, { struct kgsl_device *device = dev_priv->device; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - unsigned int *link; + unsigned int *link = 0; unsigned int *cmds; unsigned int i; - struct adreno_context *drawctxt; + struct adreno_context *drawctxt = NULL; unsigned int start_index = 0; + int ret = 0; - if (device->state & KGSL_STATE_HUNG) - return -EBUSY; - if (!(adreno_dev->ringbuffer.flags & KGSL_FLAGS_STARTED) || - context == NULL || ibdesc == 0 || numibs == 0) - return -EINVAL; + if (device->state & KGSL_STATE_HUNG) { + ret = -EBUSY; + goto done; + } + if (!(adreno_dev->ringbuffer.flags & KGSL_FLAGS_STARTED) || + context == NULL || ibdesc == 0 || numibs == 0) { + ret = -EINVAL; + goto done; + } drawctxt = context->devctxt; if (drawctxt->flags & CTXT_FLAGS_GPU_HANG) { KGSL_CTXT_ERR(device, "proc %s failed fault tolerance" " will not accept commands for context %d\n", drawctxt->pid_name, drawctxt->id); - return -EDEADLK; + ret = -EDEADLK; + goto done; } if (drawctxt->flags & CTXT_FLAGS_SKIP_EOF) { @@ -1010,9 +988,8 @@ adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, cmds = link = kzalloc(sizeof(unsigned int) * (numibs * 3 + 4), GFP_KERNEL); if (!link) { - KGSL_CORE_ERR("kzalloc(%d) failed\n", - sizeof(unsigned int) * (numibs * 3 + 4)); - return -ENOMEM; + ret = -ENOMEM; + goto done; } /*When preamble is enabled, the preamble buffer with state restoration @@ -1037,9 +1014,15 @@ adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, if (unlikely(adreno_dev->ib_check_level >= 1 && !_parse_ibs(dev_priv, ibdesc[i].gpuaddr, ibdesc[i].sizedwords))) { - kfree(link); - return -EINVAL; + ret = -EINVAL; + goto done; + } + + if (ibdesc[i].sizedwords == 0) { + ret = -EINVAL; + goto done; } + *cmds++ = CP_HDR_INDIRECT_BUFFER_PFD; *cmds++ = ibdesc[i].gpuaddr; *cmds++ = ibdesc[i].sizedwords; @@ -1054,15 +1037,30 @@ adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, adreno_drawctxt_switch(adreno_dev, drawctxt, flags); - *timestamp = adreno_ringbuffer_addcmds(&adreno_dev->ringbuffer, + if (drawctxt->flags & CTXT_FLAGS_USER_GENERATED_TS) { + if (timestamp_cmp(drawctxt->timestamp, *timestamp) >= 0) { + KGSL_DRV_ERR(device, + "Invalid user generated ts <%d:0x%x>, " + "less than last issued ts <%d:0x%x>\n", + drawctxt->id, *timestamp, drawctxt->id, + drawctxt->timestamp); + return -ERANGE; + } + drawctxt->timestamp = *timestamp; + } else + drawctxt->timestamp++; + + ret = adreno_ringbuffer_addcmds(&adreno_dev->ringbuffer, drawctxt, (flags & KGSL_CMD_FLAGS_EOF), - &link[0], (cmds - link), *timestamp); - - KGSL_CMD_INFO(device, "ctxt %d g %08x numibs %d ts %d\n", - context->id, (unsigned int)ibdesc, numibs, *timestamp); + &link[0], (cmds - link)); + if (ret) + goto done; - kfree(link); + if (drawctxt->flags & CTXT_FLAGS_PER_CONTEXT_TS) + *timestamp = drawctxt->timestamp; + else + *timestamp = adreno_dev->ringbuffer.global_ts; #ifdef CONFIG_MSM_KGSL_CFF_DUMP /* @@ -1079,9 +1077,16 @@ adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, */ if (drawctxt->flags & CTXT_FLAGS_GPU_HANG_FT) { drawctxt->flags &= ~CTXT_FLAGS_GPU_HANG_FT; - return -EPROTO; - } else - return 0; + ret = -EPROTO; + } + +done: + kgsl_trace_issueibcmds(device, context ? context->id : 0, ibdesc, + numibs, *timestamp, flags, ret, + drawctxt ? drawctxt->type : 0); + + kfree(link); + return ret; } static void _turn_preamble_on_for_ib_seq(struct adreno_ringbuffer *rb, @@ -1156,11 +1161,13 @@ void adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, if (0xFFFFFFFF == ft_data->start_of_replay_cmds) return; - k_ctxt = idr_find(&device->context_idr, ft_data->context_id); + k_ctxt = kgsl_context_get(device, ft_data->context_id); + if (k_ctxt) { a_ctxt = k_ctxt->devctxt; if (a_ctxt->flags & CTXT_FLAGS_PREAMBLE) _turn_preamble_on_for_ib_seq(rb, rb_rptr); + kgsl_context_put(k_ctxt); } k_ctxt = NULL; @@ -1191,7 +1198,8 @@ void adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, /* if context switches to a context that did not cause * hang then start saving the rb contents as those * commands can be executed */ - k_ctxt = idr_find(&rb->device->context_idr, val2); + k_ctxt = kgsl_context_get(rb->device, val2); + if (k_ctxt) { a_ctxt = k_ctxt->devctxt; @@ -1229,6 +1237,7 @@ void adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, copy_rb_contents = 0; } } + kgsl_context_put(k_ctxt); } if (copy_rb_contents) diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h index fa03c059..66ea3d6f 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.h +++ b/drivers/gpu/msm/adreno_ringbuffer.h @@ -56,7 +56,7 @@ struct adreno_ringbuffer { unsigned int wptr; /* write pointer offset in dwords from baseaddr */ unsigned int rptr; /* read pointer offset in dwords from baseaddr */ - unsigned int timestamp[KGSL_MEMSTORE_MAX]; + unsigned int global_ts; }; @@ -97,8 +97,7 @@ int adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, int adreno_ringbuffer_init(struct kgsl_device *device); -int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, - unsigned int init_ram); +int adreno_ringbuffer_start(struct adreno_ringbuffer *rb); void adreno_ringbuffer_stop(struct adreno_ringbuffer *rb); diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c index f23586ee..967e4abc 100644 --- a/drivers/gpu/msm/adreno_snapshot.c +++ b/drivers/gpu/msm/adreno_snapshot.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2013, The Linux Foundation. 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 and @@ -161,6 +161,12 @@ static unsigned int vfd_control_0; static unsigned int sp_vs_pvt_mem_addr; static unsigned int sp_fs_pvt_mem_addr; +/* + * Cached value of SP_VS_OBJ_START_REG and SP_FS_OBJ_START_REG. + */ +static unsigned int sp_vs_obj_start_reg; +static unsigned int sp_fs_obj_start_reg; + /* * Each load state block has two possible types. Each type has a different * number of dwords per unit. Use this handy lookup table to make sure @@ -373,6 +379,26 @@ static int ib_parse_draw_indx(struct kgsl_device *device, unsigned int *pkt, sp_fs_pvt_mem_addr = 0; } + if (sp_vs_obj_start_reg) { + ret = kgsl_snapshot_get_object(device, ptbase, + sp_vs_obj_start_reg & 0xFFFFFFE0, 0, + SNAPSHOT_GPU_OBJECT_GENERIC); + if (ret < 0) + return -EINVAL; + snapshot_frozen_objsize += ret; + sp_vs_obj_start_reg = 0; + } + + if (sp_fs_obj_start_reg) { + ret = kgsl_snapshot_get_object(device, ptbase, + sp_fs_obj_start_reg & 0xFFFFFFE0, 0, + SNAPSHOT_GPU_OBJECT_GENERIC); + if (ret < 0) + return -EINVAL; + snapshot_frozen_objsize += ret; + sp_fs_obj_start_reg = 0; + } + /* Finally: VBOs */ /* The number of active VBOs is stored in VFD_CONTROL_O[31:27] */ @@ -444,7 +470,7 @@ static void ib_parse_type0(struct kgsl_device *device, unsigned int *ptr, int offset = type0_pkt_offset(*ptr); int i; - for (i = 0; i < size; i++, offset++) { + for (i = 0; i < size - 1; i++, offset++) { /* Visiblity stream buffer */ @@ -505,11 +531,20 @@ static void ib_parse_type0(struct kgsl_device *device, unsigned int *ptr, case A3XX_SP_FS_PVT_MEM_ADDR_REG: sp_fs_pvt_mem_addr = ptr[i + 1]; break; + case A3XX_SP_VS_OBJ_START_REG: + sp_vs_obj_start_reg = ptr[i + 1]; + break; + case A3XX_SP_FS_OBJ_START_REG: + sp_fs_obj_start_reg = ptr[i + 1]; + break; } } } } +static inline int parse_ib(struct kgsl_device *device, unsigned int ptbase, + unsigned int gpuaddr, unsigned int dwords); + /* Add an IB as a GPU object, but first, parse it to find more goodies within */ static int ib_add_gpu_object(struct kgsl_device *device, unsigned int ptbase, @@ -549,32 +584,12 @@ static int ib_add_gpu_object(struct kgsl_device *device, unsigned int ptbase, if (adreno_cmd_is_ib(src[i])) { unsigned int gpuaddr = src[i + 1]; unsigned int size = src[i + 2]; - unsigned int ibbase; - /* Address of the last processed IB2 */ - kgsl_regread(device, REG_CP_IB2_BASE, &ibbase); + ret = parse_ib(device, ptbase, gpuaddr, size); - /* - * If this is the last IB2 that was executed, - * then push it to make sure it goes into the - * static space - */ - - if (ibbase == gpuaddr) - push_object(device, - SNAPSHOT_OBJ_TYPE_IB, ptbase, - gpuaddr, size); - else { - ret = ib_add_gpu_object(device, - ptbase, gpuaddr, size); - - /* - * If adding the IB failed then stop - * parsing - */ - if (ret < 0) - goto done; - } + /* If adding the IB failed then stop parsing */ + if (ret < 0) + goto done; } else { ret = ib_parse_type3(device, &src[i], ptbase); /* @@ -604,29 +619,34 @@ static int ib_add_gpu_object(struct kgsl_device *device, unsigned int ptbase, return ret; } -/* Snapshot the istore memory */ -static int snapshot_istore(struct kgsl_device *device, void *snapshot, - int remain, void *priv) +/* + * We want to store the last executed IB1 and IB2 in the static region to ensure + * that we get at least some information out of the snapshot even if we can't + * access the dynamic data from the sysfs file. Push all other IBs on the + * dynamic list + */ +static inline int parse_ib(struct kgsl_device *device, unsigned int ptbase, + unsigned int gpuaddr, unsigned int dwords) { - struct kgsl_snapshot_istore *header = snapshot; - unsigned int *data = snapshot + sizeof(*header); - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - int count, i; - - count = adreno_dev->istore_size * adreno_dev->instruction_size; + unsigned int ib1base, ib2base; + int ret = 0; - if (remain < (count * 4) + sizeof(*header)) { - KGSL_DRV_ERR(device, - "snapshot: Not enough memory for the istore section"); - return 0; - } + /* + * Check the IB address - if it is either the last executed IB1 or the + * last executed IB2 then push it into the static blob otherwise put + * it in the dynamic list + */ - header->count = adreno_dev->istore_size; + kgsl_regread(device, REG_CP_IB1_BASE, &ib1base); + kgsl_regread(device, REG_CP_IB2_BASE, &ib2base); - for (i = 0; i < count; i++) - kgsl_regread(device, ADRENO_ISTORE_START + i, &data[i]); + if (gpuaddr == ib1base || gpuaddr == ib2base) + push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase, + gpuaddr, dwords); + else + ret = ib_add_gpu_object(device, ptbase, gpuaddr, dwords); - return (count * 4) + sizeof(*header); + return ret; } /* Snapshot the ringbuffer memory */ @@ -765,13 +785,13 @@ static int snapshot_rb(struct kgsl_device *device, void *snapshot, struct kgsl_memdesc *memdesc = adreno_find_ctxtmem(device, ptbase, ibaddr, - ibsize); + ibsize << 2); /* IOMMU uses a NOP IB placed in setsate memory */ if (NULL == memdesc) if (kgsl_gpuaddr_in_memdesc( &device->mmu.setstate_memory, - ibaddr, ibsize)) + ibaddr, ibsize << 2)) memdesc = &device->mmu.setstate_memory; /* * The IB from CP_IB1_BASE and the IBs for legacy @@ -779,12 +799,11 @@ static int snapshot_rb(struct kgsl_device *device, void *snapshot, * others get marked at GPU objects */ - if (ibaddr == ibbase || memdesc != NULL) + if (memdesc != NULL) push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase, ibaddr, ibsize); else - ib_add_gpu_object(device, ptbase, ibaddr, - ibsize); + parse_ib(device, ptbase, ibaddr, ibsize); } index = index + 1; @@ -799,6 +818,64 @@ static int snapshot_rb(struct kgsl_device *device, void *snapshot, return size + sizeof(*header); } +static int snapshot_capture_mem_list(struct kgsl_device *device, void *snapshot, + int remain, void *priv) +{ + struct kgsl_snapshot_replay_mem_list *header = snapshot; + struct kgsl_process_private *private; + unsigned int ptbase; + struct rb_node *node; + struct kgsl_mem_entry *entry = NULL; + int num_mem; + unsigned int *data = snapshot + sizeof(*header); + + ptbase = kgsl_mmu_get_current_ptbase(&device->mmu); + mutex_lock(&kgsl_driver.process_mutex); + list_for_each_entry(private, &kgsl_driver.process_list, list) { + if (kgsl_mmu_pt_equal(&device->mmu, private->pagetable, + ptbase)) + break; + } + mutex_unlock(&kgsl_driver.process_mutex); + if (!private) { + KGSL_DRV_ERR(device, + "Failed to get pointer to process private structure\n"); + return 0; + } + /* We need to know the number of memory objects that the process has */ + spin_lock(&private->mem_lock); + for (node = rb_first(&private->mem_rb), num_mem = 0; node; ) { + entry = rb_entry(node, struct kgsl_mem_entry, node); + node = rb_next(&entry->node); + num_mem++; + } + + if (remain < ((num_mem * 3 * sizeof(unsigned int)) + + sizeof(*header))) { + KGSL_DRV_ERR(device, + "snapshot: Not enough memory for the mem list section"); + spin_unlock(&private->mem_lock); + return 0; + } + header->num_entries = num_mem; + header->ptbase = ptbase; + /* + * Walk throught the memory list and store the + * tuples(gpuaddr, size, memtype) in snapshot + */ + for (node = rb_first(&private->mem_rb); node; ) { + entry = rb_entry(node, struct kgsl_mem_entry, node); + node = rb_next(&entry->node); + + *data++ = entry->memdesc.gpuaddr; + *data++ = entry->memdesc.size; + *data++ = (entry->memdesc.priv & KGSL_MEMTYPE_MASK) >> + KGSL_MEMTYPE_SHIFT; + } + spin_unlock(&private->mem_lock); + return sizeof(*header) + (num_mem * 3 * sizeof(unsigned int)); +} + /* Snapshot the memory for an indirect buffer */ static int snapshot_ib(struct kgsl_device *device, void *snapshot, int remain, void *priv) @@ -829,15 +906,14 @@ static int snapshot_ib(struct kgsl_device *device, void *snapshot, continue; if (adreno_cmd_is_ib(*src)) - push_object(device, SNAPSHOT_OBJ_TYPE_IB, - obj->ptbase, src[1], src[2]); - else { + ret = parse_ib(device, obj->ptbase, src[1], + src[2]); + else ret = ib_parse_type3(device, src, obj->ptbase); - /* Stop parsing if the type3 decode fails */ - if (ret < 0) - break; - } + /* Stop parsing if the type3 decode fails */ + if (ret < 0) + break; } } @@ -902,6 +978,13 @@ void *adreno_snapshot(struct kgsl_device *device, void *snapshot, int *remain, snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_RB, snapshot, remain, snapshot_rb, NULL); + /* + * Add a section that lists (gpuaddr, size, memtype) tuples of the + * hanging process + */ + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_MEMLIST, snapshot, remain, + snapshot_capture_mem_list, NULL); /* * Make sure that the last IB1 that was being executed is dumped. * Since this was the last IB1 that was processed, we should have @@ -950,17 +1033,6 @@ void *adreno_snapshot(struct kgsl_device *device, void *snapshot, int *remain, for (i = 0; i < objbufptr; i++) snapshot = dump_object(device, i, snapshot, remain); - /* - * Only dump the istore on a hang - reading it on a running system - * has a non 0 chance of hanging the GPU - */ - - if (hang) { - snapshot = kgsl_snapshot_add_section(device, - KGSL_SNAPSHOT_SECTION_ISTORE, snapshot, remain, - snapshot_istore, NULL); - } - /* Add GPU specific sections - registers mainly, but other stuff too */ if (adreno_dev->gpudev->snapshot) snapshot = adreno_dev->gpudev->snapshot(adreno_dev, snapshot, diff --git a/drivers/gpu/msm/adreno_trace.c b/drivers/gpu/msm/adreno_trace.c new file mode 100644 index 00000000..607ba8c4 --- /dev/null +++ b/drivers/gpu/msm/adreno_trace.c @@ -0,0 +1,18 @@ +/* Copyright (c) 2013, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "adreno.h" + +/* Instantiate tracepoints */ +#define CREATE_TRACE_POINTS +#include "adreno_trace.h" diff --git a/drivers/gpu/msm/adreno_trace.h b/drivers/gpu/msm/adreno_trace.h new file mode 100644 index 00000000..8993afb3 --- /dev/null +++ b/drivers/gpu/msm/adreno_trace.h @@ -0,0 +1,169 @@ +/* Copyright (c) 2013, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#if !defined(_ADRENO_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _ADRENO_TRACE_H + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM kgsl +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE adreno_trace + +#include + +TRACE_EVENT(adreno_cmdbatch_queued, + TP_PROTO(struct kgsl_cmdbatch *cmdbatch, unsigned int queued), + TP_ARGS(cmdbatch, queued), + TP_STRUCT__entry( + __field(unsigned int, id) + __field(unsigned int, timestamp) + __field(unsigned int, queued) + ), + TP_fast_assign( + __entry->id = cmdbatch->context->id; + __entry->timestamp = cmdbatch->timestamp; + __entry->queued = queued; + ), + TP_printk( + "ctx=%u ts=%u queued=%u", + __entry->id, __entry->timestamp, __entry->queued + ) +); + +DECLARE_EVENT_CLASS(adreno_cmdbatch_template, + TP_PROTO(struct kgsl_cmdbatch *cmdbatch, int inflight), + TP_ARGS(cmdbatch, inflight), + TP_STRUCT__entry( + __field(unsigned int, id) + __field(unsigned int, timestamp) + __field(unsigned int, inflight) + ), + TP_fast_assign( + __entry->id = cmdbatch->context->id; + __entry->timestamp = cmdbatch->timestamp; + __entry->inflight = inflight; + ), + TP_printk( + "ctx=%u ts=%u inflight=%u", + __entry->id, __entry->timestamp, + __entry->inflight + ) +); + +DEFINE_EVENT(adreno_cmdbatch_template, adreno_cmdbatch_retired, + TP_PROTO(struct kgsl_cmdbatch *cmdbatch, int inflight), + TP_ARGS(cmdbatch, inflight) +); + +DEFINE_EVENT(adreno_cmdbatch_template, adreno_cmdbatch_submitted, + TP_PROTO(struct kgsl_cmdbatch *cmdbatch, int inflight), + TP_ARGS(cmdbatch, inflight) +); + +DECLARE_EVENT_CLASS(adreno_drawctxt_template, + TP_PROTO(struct adreno_context *drawctxt), + TP_ARGS(drawctxt), + TP_STRUCT__entry( + __field(unsigned int, id) + ), + TP_fast_assign( + __entry->id = drawctxt->base.id; + ), + TP_printk("ctx=%u", __entry->id) +); + +DEFINE_EVENT(adreno_drawctxt_template, adreno_context_sleep, + TP_PROTO(struct adreno_context *drawctxt), + TP_ARGS(drawctxt) +); + +DEFINE_EVENT(adreno_drawctxt_template, adreno_context_wake, + TP_PROTO(struct adreno_context *drawctxt), + TP_ARGS(drawctxt) +); + +DEFINE_EVENT(adreno_drawctxt_template, dispatch_queue_context, + TP_PROTO(struct adreno_context *drawctxt), + TP_ARGS(drawctxt) +); + +TRACE_EVENT(adreno_drawctxt_wait_start, + TP_PROTO(unsigned int id, unsigned int ts), + TP_ARGS(id, ts), + TP_STRUCT__entry( + __field(unsigned int, id) + __field(unsigned int, ts) + ), + TP_fast_assign( + __entry->id = id; + __entry->ts = ts; + ), + TP_printk( + "ctx=%u ts=%u", + __entry->id, __entry->ts + ) +); + +TRACE_EVENT(adreno_drawctxt_wait_done, + TP_PROTO(unsigned int id, unsigned int ts, int status), + TP_ARGS(id, ts, status), + TP_STRUCT__entry( + __field(unsigned int, id) + __field(unsigned int, ts) + __field(int, status) + ), + TP_fast_assign( + __entry->id = id; + __entry->ts = ts; + __entry->status = status; + ), + TP_printk( + "ctx=%u ts=%u status=%d", + __entry->id, __entry->ts, __entry->status + ) +); + +TRACE_EVENT(adreno_gpu_fault, + TP_PROTO(unsigned int status, unsigned int rptr, unsigned int wptr, + unsigned int ib1base, unsigned int ib1size, + unsigned int ib2base, unsigned int ib2size), + TP_ARGS(status, rptr, wptr, ib1base, ib1size, ib2base, ib2size), + TP_STRUCT__entry( + __field(unsigned int, status) + __field(unsigned int, rptr) + __field(unsigned int, wptr) + __field(unsigned int, ib1base) + __field(unsigned int, ib1size) + __field(unsigned int, ib2base) + __field(unsigned int, ib2size) + ), + TP_fast_assign( + __entry->status = status; + __entry->rptr = rptr; + __entry->wptr = wptr; + __entry->ib1base = ib1base; + __entry->ib1size = ib1size; + __entry->ib2base = ib2base; + __entry->ib2size = ib2size; + ), + TP_printk("status=%X RB=%X/%X IB1=%X/%X IB2=%X/%X", + __entry->status, __entry->wptr, __entry->rptr, + __entry->ib1base, __entry->ib1size, __entry->ib2base, + __entry->ib2size) +); + +#endif /* _ADRENO_TRACE_H */ + +/* This part must be outside protection */ +#include diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 52d6acbd..6b7add93 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2012,2014 The Linux Foundation. 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 and @@ -18,16 +18,17 @@ #include #include #include -#include + #include #include #include #include #include #include -#include +#include #include #include +#include #include "kgsl.h" #include "kgsl_debugfs.h" @@ -37,6 +38,7 @@ #include "kgsl_device.h" #include "kgsl_trace.h" #include "kgsl_sync.h" +#include "adreno.h" #undef MODULE_PARAM_PREFIX #define MODULE_PARAM_PREFIX "kgsl." @@ -52,15 +54,214 @@ MODULE_PARM_DESC(ksgl_mmu_type, static struct ion_client *kgsl_ion_client; +static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry); + +/* + * The memfree list contains the last N blocks of memory that have been freed. + * On a GPU fault we walk the list to see if the faulting address had been + * recently freed and print out a message to that effect + */ + +#define MEMFREE_ENTRIES 512 + +static DEFINE_SPINLOCK(memfree_lock); + +struct memfree_entry { + unsigned long gpuaddr; + unsigned long size; + pid_t pid; + unsigned int flags; +}; + +static struct { + struct memfree_entry *list; + unsigned int head; + unsigned int tail; +} memfree; + +/** + * kgsl_hang_check() - Check for GPU hang + * data: KGSL device structure + * + * This function is called every KGSL_TIMEOUT_HANG_DETECT time when + * GPU is active to check for hang. If a hang is detected we + * trigger fault tolerance. + */ +void kgsl_hang_check(struct work_struct *work) +{ + struct kgsl_device *device = container_of(work, struct kgsl_device, + hang_check_ws); + static unsigned int prev_reg_val[FT_DETECT_REGS_COUNT]; + + mutex_lock(&device->mutex); + + if (device->state == KGSL_STATE_ACTIVE) { + + /* Check to see if the GPU is hung */ + if (adreno_ft_detect(device, prev_reg_val)) + adreno_dump_and_exec_ft(device); + + mod_timer(&device->hang_timer, + (jiffies + msecs_to_jiffies(KGSL_TIMEOUT_HANG_DETECT))); + } + + mutex_unlock(&device->mutex); +} + +/** + * hang_timer() - Hang timer function + * data: KGSL device structure + * + * This function is called when hang timer expires, in this + * function we check if GPU is in active state and queue the + * work on device workqueue to check for the hang. We restart + * the timer after KGSL_TIMEOUT_HANG_DETECT time. + */ +void hang_timer(unsigned long data) +{ + struct kgsl_device *device = (struct kgsl_device *) data; + + /* check Hang only for 3d device */ + if (device->id == KGSL_DEVICE_3D0) { + if (device->state == KGSL_STATE_ACTIVE) { + + /* Have work run in a non-interrupt context. */ + queue_work(device->work_queue, &device->hang_check_ws); + } + } +} + +/** + * kgsl_trace_issueibcmds() - Call trace_issueibcmds by proxy + * device: KGSL device + * id: ID of the context submitting the command + * ibdesc: Pointer to the list of IB descriptors + * numib: Number of IBs in the list + * timestamp: Timestamp assigned to the command batch + * flags: Flags sent by the user + * result: Result of the submission attempt + * type: Type of context issuing the command + * + * Wrap the issueibcmds ftrace hook into a function that can be called from the + * GPU specific modules. + */ +void kgsl_trace_issueibcmds(struct kgsl_device *device, int id, + struct kgsl_ibdesc *ibdesc, int numibs, + unsigned int timestamp, unsigned int flags, + int result, unsigned int type) +{ + trace_kgsl_issueibcmds(device, id, ibdesc, numibs, + timestamp, flags, result, type); +} +EXPORT_SYMBOL(kgsl_trace_issueibcmds); + +/** + * kgsl_trace_regwrite - call regwrite ftrace function by proxy + * device: KGSL device + * offset: dword offset of the register being written + * value: Value of the register being written + * + * Wrap the regwrite ftrace hook into a function that can be called from the + * GPU specific modules. + */ +void kgsl_trace_regwrite(struct kgsl_device *device, unsigned int offset, + unsigned int value) +{ + trace_kgsl_regwrite(device, offset, value); +} +EXPORT_SYMBOL(kgsl_trace_regwrite); + +int kgsl_memfree_init(void) +{ + memfree.list = kzalloc(MEMFREE_ENTRIES * sizeof(struct memfree_entry), + GFP_KERNEL); + + return (memfree.list) ? 0 : -ENOMEM; +} + +void kgsl_memfree_exit(void) +{ + kfree(memfree.list); + memset(&memfree, 0, sizeof(memfree)); +} + +int kgsl_memfree_find_entry(pid_t pid, unsigned long *gpuaddr, + unsigned long *size, unsigned int *flags) +{ + int ptr; + + if (memfree.list == NULL) + return 0; + + spin_lock(&memfree_lock); + + ptr = memfree.head - 1; + if (ptr < 0) + ptr = MEMFREE_ENTRIES - 1; + + /* Walk backwards through the list looking for the last match */ + while (ptr != memfree.tail) { + struct memfree_entry *entry = &memfree.list[ptr]; + + if ((entry->pid == pid) && + (*gpuaddr >= entry->gpuaddr && + *gpuaddr < (entry->gpuaddr + entry->size))) { + *gpuaddr = entry->gpuaddr; + *flags = entry->flags; + *size = entry->size; + + spin_unlock(&memfree_lock); + return 1; + } + + ptr = ptr - 1; + + if (ptr < 0) + ptr = MEMFREE_ENTRIES - 1; + } + + spin_unlock(&memfree_lock); + return 0; +} + +static void kgsl_memfree_add(pid_t pid, unsigned int gpuaddr, + unsigned int size, int flags) + +{ + struct memfree_entry *entry; + + if (memfree.list == NULL) + return; + + spin_lock(&memfree_lock); + + entry = &memfree.list[memfree.head]; + + entry->pid = pid; + entry->gpuaddr = gpuaddr; + entry->size = size; + entry->flags = flags; + + memfree.head = (memfree.head + 1) % MEMFREE_ENTRIES; + + if (memfree.head == memfree.tail) + memfree.tail = (memfree.tail + 1) % MEMFREE_ENTRIES; + + spin_unlock(&memfree_lock); +} + /* kgsl_get_mem_entry - get the mem_entry structure for the specified object * @device - Pointer to the device structure * @ptbase - the pagetable base of the object * @gpuaddr - the GPU address of the object * @size - Size of the region to search + * + * Caller must kgsl_mem_entry_put() the returned entry when finished using it. */ -struct kgsl_mem_entry *kgsl_get_mem_entry(struct kgsl_device *device, - unsigned int ptbase, unsigned int gpuaddr, unsigned int size) +struct kgsl_mem_entry * __must_check +kgsl_get_mem_entry(struct kgsl_device *device, unsigned int ptbase, + unsigned int gpuaddr, unsigned int size) { struct kgsl_process_private *priv; struct kgsl_mem_entry *entry; @@ -70,15 +271,12 @@ struct kgsl_mem_entry *kgsl_get_mem_entry(struct kgsl_device *device, list_for_each_entry(priv, &kgsl_driver.process_list, list) { if (!kgsl_mmu_pt_equal(&device->mmu, priv->pagetable, ptbase)) continue; - spin_lock(&priv->mem_lock); entry = kgsl_sharedmem_find_region(priv, gpuaddr, size); if (entry) { - spin_unlock(&priv->mem_lock); mutex_unlock(&kgsl_driver.process_mutex); return entry; } - spin_unlock(&priv->mem_lock); } mutex_unlock(&kgsl_driver.process_mutex); @@ -106,6 +304,9 @@ kgsl_mem_entry_destroy(struct kref *kref) struct kgsl_mem_entry, refcount); + /* Detach from process list */ + kgsl_mem_entry_detach_process(entry); + if (entry->memtype != KGSL_MEM_ENTRY_KERNEL) kgsl_driver.stats.mapped -= entry->memdesc.size; @@ -136,14 +337,47 @@ kgsl_mem_entry_destroy(struct kref *kref) } EXPORT_SYMBOL(kgsl_mem_entry_destroy); -static -void kgsl_mem_entry_attach_process(struct kgsl_mem_entry *entry, - struct kgsl_process_private *process) +/** + * kgsl_mem_entry_track_gpuaddr - Insert a mem_entry in the address tree and + * assign it with a gpu address space before insertion + * @process: the process that owns the memory + * @entry: the memory entry + * + * @returns - 0 on succcess else error code + * + * Insert the kgsl_mem_entry in to the rb_tree for searching by GPU address. + * The assignment of gpu address and insertion into list needs to + * happen with the memory lock held to avoid race conditions between + * gpu address being selected and some other thread looking through the + * rb list in search of memory based on gpuaddr + * This function should be called with processes memory spinlock held + */ +static int +kgsl_mem_entry_track_gpuaddr(struct kgsl_process_private *process, + struct kgsl_mem_entry *entry) { + int ret = 0; struct rb_node **node; struct rb_node *parent = NULL; - spin_lock(&process->mem_lock); + assert_spin_locked(&process->mem_lock); + /* + * If cpu=gpu map is used then caller needs to set the + * gpu address + */ + if (kgsl_memdesc_use_cpu_map(&entry->memdesc)) { + if (!entry->memdesc.gpuaddr) + goto done; + } else if (entry->memdesc.gpuaddr) { + WARN_ONCE(1, "gpuaddr assigned w/o holding memory lock\n"); + ret = -EINVAL; + goto done; + } + if (!kgsl_memdesc_use_cpu_map(&entry->memdesc)) { + ret = kgsl_mmu_get_gpuaddr(process->pagetable, &entry->memdesc); + if (ret) + goto done; + } node = &process->mem_rb.rb_node; @@ -162,9 +396,89 @@ void kgsl_mem_entry_attach_process(struct kgsl_mem_entry *entry, rb_link_node(&entry->node, parent, node); rb_insert_color(&entry->node, &process->mem_rb); - spin_unlock(&process->mem_lock); +done: + return ret; +} + +/** + * kgsl_mem_entry_untrack_gpuaddr() - Untrack memory that is previously tracked + * process - Pointer to process private to which memory belongs + * entry - Memory entry to untrack + * + * Function just does the opposite of kgsl_mem_entry_track_gpuaddr. Needs to be + * called with processes spin lock held + */ +static void +kgsl_mem_entry_untrack_gpuaddr(struct kgsl_process_private *process, + struct kgsl_mem_entry *entry) +{ + assert_spin_locked(&process->mem_lock); + if (entry->memdesc.gpuaddr) { + kgsl_mmu_put_gpuaddr(process->pagetable, &entry->memdesc); + rb_erase(&entry->node, &entry->priv->mem_rb); + } +} +/** + * kgsl_mem_entry_attach_process - Attach a mem_entry to its owner process + * @entry: the memory entry + * @process: the owner process + * + * Attach a newly created mem_entry to its owner process so that + * it can be found later. The mem_entry will be added to mem_idr and have + * its 'id' field assigned. If the GPU address has been set, the entry + * will also be added to the mem_rb tree. + * + * @returns - 0 on success or error code on failure. + */ +static int +kgsl_mem_entry_attach_process(struct kgsl_mem_entry *entry, + struct kgsl_device_private *dev_priv) +{ + int ret; + struct kgsl_process_private *process = dev_priv->process_priv; + + ret = kgsl_process_private_get(process); + if (!ret) + return -EBADF; + + while (1) { + if (idr_pre_get(&process->mem_idr, GFP_KERNEL) == 0) { + ret = -ENOMEM; + goto err_put_proc_priv; + } + + spin_lock(&process->mem_lock); + ret = idr_get_new_above(&process->mem_idr, entry, 1, + &entry->id); + spin_unlock(&process->mem_lock); + + if (ret == 0) + break; + else if (ret != -EAGAIN) + goto err_put_proc_priv; + } entry->priv = process; + entry->dev_priv = dev_priv; + + spin_lock(&process->mem_lock); + ret = kgsl_mem_entry_track_gpuaddr(process, entry); + if (ret) + idr_remove(&process->mem_idr, entry->id); + spin_unlock(&process->mem_lock); + if (ret) + goto err_put_proc_priv; + /* map the memory after unlocking if gpuaddr has been assigned */ + if (entry->memdesc.gpuaddr) { + ret = kgsl_mmu_map(process->pagetable, &entry->memdesc); + if (ret) + kgsl_mem_entry_detach_process(entry); + } + return ret; + +err_put_proc_priv: + kgsl_process_private_put(process); + return ret; } /* Detach a memory entry from a process and unmap it from the MMU */ @@ -174,12 +488,21 @@ static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry) if (entry == NULL) return; - entry->priv->stats[entry->memtype].cur -= entry->memdesc.size; - entry->priv = NULL; + /* Unmap here so that below we can call kgsl_mmu_put_gpuaddr */ + kgsl_mmu_unmap(entry->priv->pagetable, &entry->memdesc); + + spin_lock(&entry->priv->mem_lock); - kgsl_mmu_unmap(entry->memdesc.pagetable, &entry->memdesc); + kgsl_mem_entry_untrack_gpuaddr(entry->priv, entry); + if (entry->id != 0) + idr_remove(&entry->priv->mem_idr, entry->id); + entry->id = 0; - kgsl_mem_entry_put(entry); + entry->priv->stats[entry->memtype].cur -= entry->memdesc.size; + spin_unlock(&entry->priv->mem_lock); + kgsl_process_private_put(entry->priv); + + entry->priv = NULL; } /* Allocate a new context id */ @@ -187,50 +510,49 @@ static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry) static struct kgsl_context * kgsl_create_context(struct kgsl_device_private *dev_priv) { + struct kgsl_device *device = dev_priv->device; struct kgsl_context *context; - int ret, id; + int ret, id = 0; context = kzalloc(sizeof(*context), GFP_KERNEL); if (context == NULL) - return NULL; + return ERR_PTR(-ENOMEM); + while (1) { - if (idr_pre_get(&dev_priv->device->context_idr, - GFP_KERNEL) == 0) { - kfree(context); - return NULL; + if (idr_pre_get(&device->context_idr, GFP_KERNEL) == 0) { + KGSL_DRV_INFO(device, "idr_pre_get: ENOMEM\n"); + ret = -ENOMEM; + break; } - ret = idr_get_new_above(&dev_priv->device->context_idr, - context, 1, &id); + write_lock(&device->context_lock); + ret = idr_get_new_above(&device->context_idr, context, 1, &id); + context->id = id; + write_unlock(&device->context_lock); if (ret != -EAGAIN) break; } - if (ret) { - kfree(context); - return NULL; - } + if (ret) + goto func_end; /* MAX - 1, there is one memdesc in memstore for device info */ if (id >= KGSL_MEMSTORE_MAX) { - KGSL_DRV_ERR(dev_priv->device, "cannot have more than %d " + KGSL_DRV_INFO(device, "cannot have more than %d " "ctxts due to memstore limitation\n", KGSL_MEMSTORE_MAX); - idr_remove(&dev_priv->device->context_idr, id); - kfree(context); - return NULL; + ret = -ENOSPC; + goto fail_free_id; } kref_init(&context->refcount); - context->id = id; context->dev_priv = dev_priv; - - if (kgsl_sync_timeline_create(context)) { - idr_remove(&dev_priv->device->context_idr, id); - goto func_end; + ret = kgsl_sync_timeline_create(context); + if (ret) { + goto fail_free_id; } /* Initialize the pending event list */ @@ -247,10 +569,17 @@ kgsl_create_context(struct kgsl_device_private *dev_priv) INIT_LIST_HEAD(&context->events_list); +fail_free_id: + if (ret) { + write_lock(&device->context_lock); + idr_remove(&device->context_idr, id); + write_unlock(&device->context_lock); + } + func_end: if (ret) { kfree(context); - return NULL; + return ERR_PTR(ret); } return context; @@ -286,9 +615,13 @@ kgsl_context_detach(struct kgsl_context *context) * destroyed, to avoid possibly freeing memory while * it is still in use by the GPU. */ - kgsl_cancel_events_ctxt(device, context); - idr_remove(&device->context_idr, id); + kgsl_context_cancel_events(device, context); + + write_lock(&device->context_lock); context->id = KGSL_CONTEXT_INVALID; + idr_remove(&device->context_idr, id); + write_unlock(&device->context_lock); + context->dev_priv = NULL; kgsl_context_put(context); } @@ -301,27 +634,6 @@ kgsl_context_destroy(struct kref *kref) kfree(context); } -static void kgsl_check_idle_locked(struct kgsl_device *device) -{ - if (device->pwrctrl.nap_allowed == true && - device->state == KGSL_STATE_ACTIVE && - device->requested_state == KGSL_STATE_NONE) { - kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP); - kgsl_pwrscale_idle(device); - if (kgsl_pwrctrl_sleep(device) != 0) - mod_timer(&device->idle_timer, - jiffies + - device->pwrctrl.interval_timeout); - } -} - -static void kgsl_check_idle(struct kgsl_device *device) -{ - mutex_lock(&device->mutex); - kgsl_check_idle_locked(device); - mutex_unlock(&device->mutex); -} - struct kgsl_device *kgsl_get_device(int dev_idx) { int i; @@ -368,7 +680,8 @@ int kgsl_check_timestamp(struct kgsl_device *device, EXPORT_SYMBOL(kgsl_check_timestamp); static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) -{ +{ +#if 0 int status = -EINVAL; unsigned int nap_allowed_saved; struct kgsl_pwrscale_policy *policy_saved; @@ -384,15 +697,16 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) policy_saved = device->pwrscale.policy; device->pwrscale.policy = NULL; kgsl_pwrctrl_request_state(device, KGSL_STATE_SUSPEND); - /* Make sure no user process is waiting for a timestamp * - * before supending */ - if (device->state == KGSL_STATE_ACTIVE && device->active_cnt != 0) { - mutex_unlock(&device->mutex); - wait_for_completion(&device->suspend_gate); - mutex_lock(&device->mutex); - } + + /* + * Make sure no user process is waiting for a timestamp + * before supending. + */ + kgsl_active_count_wait(device); + /* Don't let the timer wake us during suspended sleep. */ del_timer_sync(&device->idle_timer); + del_timer_sync(&device->hang_timer); switch (device->state) { case KGSL_STATE_INIT: break; @@ -401,6 +715,9 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) device->ftbl->idle(device); case KGSL_STATE_NAP: case KGSL_STATE_SLEEP: + /* make sure power is on to stop the device */ + kgsl_pwrctrl_enable(device); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); /* Get the completion ready to be waited upon. */ INIT_COMPLETION(device->hwaccess_gate); device->ftbl->suspend_context(device); @@ -425,14 +742,15 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) end: mutex_unlock(&device->mutex); +#endif KGSL_PWR_WARN(device, "suspend end\n"); - return status; + + return 0; } static int kgsl_resume_device(struct kgsl_device *device) { - int status = -EINVAL; - +#if 0 if (!device) return -EINVAL; @@ -440,14 +758,27 @@ static int kgsl_resume_device(struct kgsl_device *device) mutex_lock(&device->mutex); if (device->state == KGSL_STATE_SUSPEND) { kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER); - status = 0; complete_all(&device->hwaccess_gate); + } else if (device->state != KGSL_STATE_INIT) { + /* + * This is an error situation,so wait for the device + * to idle and then put the device to SLUMBER state. + * This will put the device to the right state when + * we resume. + */ + if (device->state == KGSL_STATE_ACTIVE) + device->ftbl->idle(device); + kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER); + kgsl_pwrctrl_sleep(device); + KGSL_PWR_ERR(device, + "resume invoked without a suspend\n"); } kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); mutex_unlock(&device->mutex); +#endif KGSL_PWR_WARN(device, "resume end\n"); - return status; + return 0; } static int kgsl_suspend(struct device *dev) @@ -481,21 +812,26 @@ const struct dev_pm_ops kgsl_pm_ops = { .runtime_resume = kgsl_runtime_resume, }; EXPORT_SYMBOL(kgsl_pm_ops); - +#if 0 void kgsl_early_suspend_driver(struct early_suspend *h) { struct kgsl_device *device = container_of(h, struct kgsl_device, display_off); KGSL_PWR_WARN(device, "early suspend start\n"); mutex_lock(&device->mutex); - device->pwrctrl.restore_slumber = true; - kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER); - kgsl_pwrctrl_sleep(device); + + /* Only go to slumber if active_cnt is 0 */ + if (device->active_cnt == 0) { + device->pwrctrl.restore_slumber = true; + kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER); + kgsl_pwrctrl_sleep(device); + } + mutex_unlock(&device->mutex); KGSL_PWR_WARN(device, "early suspend end\n"); } EXPORT_SYMBOL(kgsl_early_suspend_driver); - +#endif int kgsl_suspend_driver(struct platform_device *pdev, pm_message_t state) { @@ -510,7 +846,7 @@ int kgsl_resume_driver(struct platform_device *pdev) return kgsl_resume_device(device); } EXPORT_SYMBOL(kgsl_resume_driver); - +#if 0 void kgsl_late_resume_driver(struct early_suspend *h) { struct kgsl_device *device = container_of(h, @@ -520,96 +856,199 @@ void kgsl_late_resume_driver(struct early_suspend *h) device->pwrctrl.restore_slumber = false; if (device->pwrscale.policy == NULL) kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO); - kgsl_pwrctrl_wake(device); + + if (kgsl_pwrctrl_wake(device) != 0) { + mutex_unlock(&device->mutex); + return; + } + /* + * We don't have a way to go directly from + * a deeper sleep state to NAP, which is + * the desired state here. + * + * Except if active_cnt is non zero which means that + * we probably went to early_suspend with it non zero + * and thus the system is still in an active state. + */ + + if (device->active_cnt == 0) { + kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP); + kgsl_pwrctrl_sleep(device); + } + mutex_unlock(&device->mutex); - kgsl_check_idle(device); KGSL_PWR_WARN(device, "late resume end\n"); } EXPORT_SYMBOL(kgsl_late_resume_driver); +#endif +/* + * kgsl_destroy_process_private() - Cleanup function to free process private + * @kref: - Pointer to object being destroyed's kref struct + * Free struct object and all other resources attached to it. + * Since the function can be used when not all resources inside process + * private have been allocated, there is a check to (before each resource + * cleanup) see if the struct member being cleaned is in fact allocated or not. + * If the value is not NULL, resource is freed. + */ +static void kgsl_destroy_process_private(struct kref *kref) +{ + struct kgsl_process_private *private = container_of(kref, + struct kgsl_process_private, refcount); -/* file operations */ -static struct kgsl_process_private * -kgsl_get_process_private(struct kgsl_device_private *cur_dev_priv) + /* + * Remove this process from global process list + * We do not acquire a lock first as it is expected that + * kgsl_destroy_process_private() is only going to be called + * through kref_put() which is only called after acquiring + * the lock. + */ + if (!private) { + KGSL_CORE_ERR("Cannot destroy null process private\n"); + mutex_unlock(&kgsl_driver.process_mutex); + return; + } + + if (private->kobj.parent) + kgsl_process_uninit_sysfs(private); + if (private->debug_root) + debugfs_remove_recursive(private->debug_root); + + list_del(&private->list); + mutex_unlock(&kgsl_driver.process_mutex); + + kgsl_mmu_putpagetable(private->pagetable); + idr_destroy(&private->mem_idr); + + kfree(private); + return; +} + +void +kgsl_process_private_put(struct kgsl_process_private *private) +{ + mutex_lock(&kgsl_driver.process_mutex); + + /* + * kref_put() returns 1 when the refcnt has reached 0 and the destroy + * function is called. Mutex is released in the destroy function if + * its called, so only release mutex if kref_put() return 0 + */ + if (!kref_put(&private->refcount, kgsl_destroy_process_private)) + mutex_unlock(&kgsl_driver.process_mutex); + return; +} + +/** + * kgsl_process_private_find() - Find the process associated with the specified + * name + * @name: pid_t of the process to search for + * Return the process struct for the given ID. + */ +struct kgsl_process_private *kgsl_process_private_find(pid_t pid) +{ + struct kgsl_process_private *p, *private = NULL; + + mutex_lock(&kgsl_driver.process_mutex); + list_for_each_entry(p, &kgsl_driver.process_list, list) { + if (p->pid == pid) { + if (kgsl_process_private_get(p)) + private = p; + break; + } + } + mutex_unlock(&kgsl_driver.process_mutex); + return private; +} + +/** + * kgsl_process_private_new() - Helper function to search for process private + * Returns: Pointer to the found/newly created private struct + */ +static struct kgsl_process_private *kgsl_process_private_new(void) { struct kgsl_process_private *private; + /* Search in the process list */ mutex_lock(&kgsl_driver.process_mutex); list_for_each_entry(private, &kgsl_driver.process_list, list) { if (private->pid == task_tgid_nr(current)) { - private->refcnt++; - goto out; + if (!kgsl_process_private_get(private)) + private = NULL; + goto done; } } /* no existing process private found for this dev_priv, create one */ private = kzalloc(sizeof(struct kgsl_process_private), GFP_KERNEL); - if (private == NULL) { - KGSL_DRV_ERR(cur_dev_priv->device, "kzalloc(%d) failed\n", - sizeof(struct kgsl_process_private)); - goto out; - } - - spin_lock_init(&private->mem_lock); - private->refcnt = 1; - private->pid = task_tgid_nr(current); - private->mem_rb = RB_ROOT; - - if (kgsl_mmu_enabled()) - { - unsigned long pt_name; + if (private == NULL) + goto done; - pt_name = task_tgid_nr(current); - private->pagetable = kgsl_mmu_getpagetable(pt_name); - if (private->pagetable == NULL) { - kfree(private); - private = NULL; - goto out; - } - } + kref_init(&private->refcount); + private->pid = task_tgid_nr(current); + spin_lock_init(&private->mem_lock); + mutex_init(&private->process_private_mutex); + /* Add the newly created process struct obj to the process list */ list_add(&private->list, &kgsl_driver.process_list); - - kgsl_process_init_sysfs(private); - kgsl_process_init_debugfs(private); - -out: +done: mutex_unlock(&kgsl_driver.process_mutex); return private; } -static void -kgsl_put_process_private(struct kgsl_device *device, - struct kgsl_process_private *private) +/* + * kgsl_get_process_private() - Used to find the process private structure + * @cur_dev_priv: Current device pointer + * Finds or creates a new porcess private structire and initializes its members + * Returns: Pointer to the private process struct obj found/created or + * NULL if pagetable creation for this process private obj failed. + */ +static struct kgsl_process_private * +kgsl_get_process_private(struct kgsl_device *device) { - struct kgsl_mem_entry *entry = NULL; - struct rb_node *node; + struct kgsl_process_private *private; - if (!private) - return; + private = kgsl_process_private_new(); - mutex_lock(&kgsl_driver.process_mutex); + if (!private) + return NULL; - if (--private->refcnt) - goto unlock; + mutex_lock(&private->process_private_mutex); - kgsl_process_uninit_sysfs(private); - debugfs_remove_recursive(private->debug_root); + if (test_bit(KGSL_PROCESS_INIT, &private->priv)) + goto done; - list_del(&private->list); + private->mem_rb = RB_ROOT; + idr_init(&private->mem_idr); - for (node = rb_first(&private->mem_rb); node; ) { - entry = rb_entry(node, struct kgsl_mem_entry, node); - node = rb_next(&entry->node); + if ((!private->pagetable) && kgsl_mmu_enabled()) { + unsigned long pt_name; - rb_erase(&entry->node, &private->mem_rb); - kgsl_mem_entry_detach_process(entry); + pt_name = task_tgid_nr(current); + private->pagetable = + kgsl_mmu_getpagetable(pt_name); + if (private->pagetable == NULL) + goto error; } - kgsl_mmu_putpagetable(private->pagetable); - kfree(private); -unlock: - mutex_unlock(&kgsl_driver.process_mutex); + + if (kgsl_process_init_sysfs(private)) + goto error; + + if (kgsl_process_init_debugfs(private)) + goto error; + + set_bit(KGSL_PROCESS_INIT, &private->priv); + +done: + mutex_unlock(&private->process_private_mutex); + return private; + +error: + mutex_unlock(&private->process_private_mutex); + kgsl_process_private_put(private); + return NULL; } + static int kgsl_release(struct inode *inodep, struct file *filep) { int result = 0; @@ -617,15 +1056,19 @@ static int kgsl_release(struct inode *inodep, struct file *filep) struct kgsl_process_private *private = dev_priv->process_priv; struct kgsl_device *device = dev_priv->device; struct kgsl_context *context; + struct kgsl_mem_entry *entry; int next = 0; filep->private_data = NULL; mutex_lock(&device->mutex); - kgsl_check_suspended(device); + kgsl_active_count_get(device); while (1) { + read_lock(&device->context_lock); context = idr_get_next(&device->context_idr, &next); + read_unlock(&device->context_lock); + if (context == NULL) break; @@ -634,6 +1077,25 @@ static int kgsl_release(struct inode *inodep, struct file *filep) next = next + 1; } + next = 0; + while (1) { + spin_lock(&private->mem_lock); + entry = idr_get_next(&private->mem_idr, &next); + spin_unlock(&private->mem_lock); + if (entry == NULL) + break; + /* + * If the free pending flag is not set it means that user space + * did not free it's reference to this entry, in that case + * free a reference to this entry, other references are from + * within kgsl so they will be freed eventually by kgsl + */ + if (entry->dev_priv == dev_priv && !entry->pending_free) { + entry->pending_free = 1; + kgsl_mem_entry_put(entry); + } + next = next + 1; + } /* * Clean up any to-be-freed entries that belong to this * process and this device. This is done after the context @@ -644,14 +1106,21 @@ static int kgsl_release(struct inode *inodep, struct file *filep) device->open_count--; if (device->open_count == 0) { + BUG_ON(device->active_cnt > 1); result = device->ftbl->stop(device); kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT); + /* + * active_cnt special case: we just stopped the device, + * so no need to use kgsl_active_count_put() + */ + device->active_cnt--; + } else { + kgsl_active_count_put(device); } - mutex_unlock(&device->mutex); kfree(dev_priv); - kgsl_put_process_private(device, private); + kgsl_process_private_put(private); pm_runtime_put(device->parentdev); return result; @@ -692,40 +1161,68 @@ static int kgsl_open(struct inode *inodep, struct file *filep) dev_priv->device = device; filep->private_data = dev_priv; - /* Get file (per process) private struct */ - dev_priv->process_priv = kgsl_get_process_private(dev_priv); - if (dev_priv->process_priv == NULL) { - result = -ENOMEM; - goto err_freedevpriv; - } - mutex_lock(&device->mutex); - kgsl_check_suspended(device); if (device->open_count == 0) { + /* + * active_cnt special case: we are starting up for the first + * time, so use this sequence instead of the kgsl_pwrctrl_wake() + * which will be called by kgsl_active_count_get(). + */ + device->active_cnt++; kgsl_sharedmem_set(&device->memstore, 0, 0, device->memstore.size); - result = device->ftbl->start(device, true); + result = device->ftbl->init(device); + if (result) + goto err_freedevpriv; - if (result) { - mutex_unlock(&device->mutex); - goto err_putprocess; - } + result = device->ftbl->start(device); + if (result) + goto err_freedevpriv; + /* + * Make sure the gates are open, so they don't block until + * we start suspend or FT. + */ + complete_all(&device->ft_gate); + complete_all(&device->hwaccess_gate); kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE); + kgsl_active_count_put(device); } device->open_count++; mutex_unlock(&device->mutex); + /* + * Get file (per process) private struct. This must be done + * after the first start so that the global pagetable mappings + * are set up before we create the per-process pagetable. + */ + dev_priv->process_priv = kgsl_get_process_private(device); + if (dev_priv->process_priv == NULL) { + result = -ENOMEM; + goto err_stop; + } + KGSL_DRV_INFO(device, "Initialized %s: mmu=%s pagetable_count=%d\n", device->name, kgsl_mmu_enabled() ? "on" : "off", kgsl_pagetable_count); return result; -err_putprocess: - kgsl_put_process_private(device, dev_priv->process_priv); +err_stop: + mutex_lock(&device->mutex); + device->open_count--; + if (device->open_count == 0) { + /* make sure power is on to stop the device */ + kgsl_pwrctrl_enable(device); + result = device->ftbl->stop(device); + kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT); + } err_freedevpriv: + /* only the first open takes an active count */ + if (device->open_count == 0) + device->active_cnt--; + mutex_unlock(&device->mutex); filep->private_data = NULL; kfree(dev_priv); err_pmruntime: @@ -733,46 +1230,174 @@ static int kgsl_open(struct inode *inodep, struct file *filep) return result; } -/*call with private->mem_lock locked */ -struct kgsl_mem_entry * +/** + * kgsl_sharedmem_find_region() - Find a gpu memory allocation + * + * @private: private data for the process to check. + * @gpuaddr: start address of the region + * @size: size of the region + * + * Find a gpu allocation. Caller must kgsl_mem_entry_put() + * the returned entry when finished using it. + */ +struct kgsl_mem_entry * __must_check kgsl_sharedmem_find_region(struct kgsl_process_private *private, unsigned int gpuaddr, size_t size) { - struct rb_node *node = private->mem_rb.rb_node; + struct rb_node *node; if (!kgsl_mmu_gpuaddr_in_range(gpuaddr)) return NULL; + spin_lock(&private->mem_lock); + node = private->mem_rb.rb_node; while (node != NULL) { struct kgsl_mem_entry *entry; entry = rb_entry(node, struct kgsl_mem_entry, node); - - if (kgsl_gpuaddr_in_memdesc(&entry->memdesc, gpuaddr, size)) + if (kgsl_gpuaddr_in_memdesc(&entry->memdesc, gpuaddr, size)) { + if (!kgsl_mem_entry_get(entry)) + break; + spin_unlock(&private->mem_lock); return entry; - + } if (gpuaddr < entry->memdesc.gpuaddr) node = node->rb_left; else if (gpuaddr >= (entry->memdesc.gpuaddr + entry->memdesc.size)) node = node->rb_right; else { + spin_unlock(&private->mem_lock); return NULL; } } + spin_unlock(&private->mem_lock); return NULL; } EXPORT_SYMBOL(kgsl_sharedmem_find_region); -/*call with private->mem_lock locked */ -static inline struct kgsl_mem_entry * +/** + * kgsl_sharedmem_find() - Find a gpu memory allocation + * + * @private: private data for the process to check. + * @gpuaddr: start address of the region + * + * Find a gpu allocation. Caller must kgsl_mem_entry_put() + * the returned entry when finished using it. + */ +static inline struct kgsl_mem_entry * __must_check kgsl_sharedmem_find(struct kgsl_process_private *private, unsigned int gpuaddr) { return kgsl_sharedmem_find_region(private, gpuaddr, 1); } +/** + * kgsl_sharedmem_region_empty() - Check if an addression region is empty + * + * @private: private data for the process to check. + * @gpuaddr: start address of the region + * @size: length of the region. + * + * Checks that there are no existing allocations within an address + * region. This function should be called with processes spin lock + * held. + */ +static int +kgsl_sharedmem_region_empty(struct kgsl_process_private *private, + unsigned int gpuaddr, size_t size) +{ + int result = 1; + unsigned int gpuaddr_end = gpuaddr + size; + + struct rb_node *node; + + assert_spin_locked(&private->mem_lock); + + if (!kgsl_mmu_gpuaddr_in_range(gpuaddr)) + return 0; + + /* don't overflow */ + if (gpuaddr_end < gpuaddr) + return 0; + + node = private->mem_rb.rb_node; + while (node != NULL) { + struct kgsl_mem_entry *entry; + unsigned int memdesc_start, memdesc_end; + + entry = rb_entry(node, struct kgsl_mem_entry, node); + + memdesc_start = entry->memdesc.gpuaddr; + memdesc_end = memdesc_start + + kgsl_memdesc_mmapsize(&entry->memdesc); + + if (gpuaddr_end <= memdesc_start) + node = node->rb_left; + else if (memdesc_end <= gpuaddr) + node = node->rb_right; + else { + result = 0; + break; + } + } + return result; +} + +/** + * kgsl_sharedmem_find_id() - find a memory entry by id + * @process: the owning process + * @id: id to find + * + * @returns - the mem_entry or NULL + * + * Caller must kgsl_mem_entry_put() the returned entry, when finished using + * it. + */ +static inline struct kgsl_mem_entry * __must_check +kgsl_sharedmem_find_id(struct kgsl_process_private *process, unsigned int id) +{ + int result = 0; + struct kgsl_mem_entry *entry; + + rcu_read_lock(); + entry = idr_find(&process->mem_idr, id); + if (entry) + result = kgsl_mem_entry_get(entry); + rcu_read_unlock(); + + if (!result) + return NULL; + return entry; +} + +/** + * kgsl_mem_entry_set_pend() - Set the pending free flag of a memory entry + * @entry - The memory entry + * + * @returns - true if pending flag was 0 else false + * + * This function will set the pending free flag if it is previously unset. Used + * to prevent race condition between ioctls calling free/freememontimestamp + * on the same entry. Whichever thread set's the flag first will do the free. + */ +static inline bool kgsl_mem_entry_set_pend(struct kgsl_mem_entry *entry) +{ + bool ret = false; + + if (entry == NULL) + return false; + + spin_lock(&entry->priv->mem_lock); + if (!entry->pending_free) { + entry->pending_free = 1; + ret = true; + } + spin_unlock(&entry->priv->mem_lock); + return ret; +} + /*call all ioctl sub functions with driver locked*/ static long kgsl_ioctl_device_getproperty(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) @@ -815,7 +1440,7 @@ static long kgsl_ioctl_device_getproperty(struct kgsl_device_private *dev_priv, result = -EFAULT; break; } - context = kgsl_find_context(dev_priv, id); + context = kgsl_context_get_owner(dev_priv, id); if (!context) { result = -EINVAL; break; @@ -825,12 +1450,14 @@ static long kgsl_ioctl_device_getproperty(struct kgsl_device_private *dev_priv, * the out parameter */ if (copy_to_user(param->value, &(context->reset_status), - sizeof(unsigned int))) { + sizeof(unsigned int))) result = -EFAULT; - break; + else { + /* Clear reset status once its been queried */ + context->reset_status = KGSL_CTX_STAT_NO_ERROR; } - /* Clear reset status once its been queried */ - context->reset_status = KGSL_CTX_STAT_NO_ERROR; + + kgsl_context_put(context); break; } default: @@ -867,10 +1494,6 @@ static long _device_waittimestamp(struct kgsl_device_private *dev_priv, struct kgsl_device *device = dev_priv->device; unsigned int context_id = context ? context->id : KGSL_MEMSTORE_GLOBAL; - /* Set the active count so that suspend doesn't do the wrong thing */ - - device->active_cnt++; - trace_kgsl_waittimestamp_entry(device, context_id, kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED), @@ -884,9 +1507,6 @@ static long _device_waittimestamp(struct kgsl_device_private *dev_priv, KGSL_TIMESTAMP_RETIRED), result); - /* Fire off any pending suspend operations that are in flight */ - kgsl_active_count_put(dev_priv->device); - return result; } @@ -906,19 +1526,14 @@ static long kgsl_ioctl_device_waittimestamp_ctxtid(struct kgsl_device_private { struct kgsl_device_waittimestamp_ctxtid *param = data; struct kgsl_context *context; - int result; + long result = -EINVAL; - context = kgsl_find_context(dev_priv, param->context_id); - if (context == NULL) - return -EINVAL; - /* - * A reference count is needed here, because waittimestamp may - * block with the device mutex unlocked and userspace could - * request for the context to be destroyed during that time. - */ - kgsl_context_get(context); - result = _device_waittimestamp(dev_priv, context, + context = kgsl_context_get_owner(dev_priv, param->context_id); + + if (context) + result = _device_waittimestamp(dev_priv, context, param->timestamp, param->timeout); + kgsl_context_put(context); return result; } @@ -931,7 +1546,7 @@ static long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv, struct kgsl_ibdesc *ibdesc; struct kgsl_context *context; - context = kgsl_find_context(dev_priv, param->drawctxt_id); + context = kgsl_context_get_owner(dev_priv, param->drawctxt_id); if (context == NULL) { result = -EINVAL; goto done; @@ -1004,12 +1619,10 @@ static long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv, ¶m->timestamp, param->flags); - trace_kgsl_issueibcmds(dev_priv->device, param, ibdesc, result); - free_ibdesc: kfree(ibdesc); done: - + kgsl_context_put(context); return result; } @@ -1042,25 +1655,26 @@ static long kgsl_ioctl_cmdstream_readtimestamp_ctxtid(struct kgsl_device_private { struct kgsl_cmdstream_readtimestamp_ctxtid *param = data; struct kgsl_context *context; + long result = -EINVAL; - context = kgsl_find_context(dev_priv, param->context_id); - if (context == NULL) - return -EINVAL; - + context = kgsl_context_get_owner(dev_priv, param->context_id); - return _cmdstream_readtimestamp(dev_priv, context, + if (context) + result = _cmdstream_readtimestamp(dev_priv, context, param->type, ¶m->timestamp); + + kgsl_context_put(context); + return result; } static void kgsl_freemem_event_cb(struct kgsl_device *device, - void *priv, u32 id, u32 timestamp) + void *priv, u32 id, u32 timestamp, u32 type) { struct kgsl_mem_entry *entry = priv; - spin_lock(&entry->priv->mem_lock); - rb_erase(&entry->node, &entry->priv->mem_rb); - spin_unlock(&entry->priv->mem_lock); + + /* Free the memory for all event types */ trace_kgsl_mem_timestamp_free(device, entry, id, timestamp, 0); - kgsl_mem_entry_detach_process(entry); + kgsl_mem_entry_put(entry); } static long _cmdstream_freememontimestamp(struct kgsl_device_private *dev_priv, @@ -1072,15 +1686,18 @@ static long _cmdstream_freememontimestamp(struct kgsl_device_private *dev_priv, struct kgsl_device *device = dev_priv->device; unsigned int context_id = context ? context->id : KGSL_MEMSTORE_GLOBAL; - spin_lock(&dev_priv->process_priv->mem_lock); entry = kgsl_sharedmem_find(dev_priv->process_priv, gpuaddr); - spin_unlock(&dev_priv->process_priv->mem_lock); if (!entry) { KGSL_DRV_ERR(dev_priv->device, "invalid gpuaddr %08x\n", gpuaddr); - result = -EINVAL; - goto done; + return -EINVAL; + } + if (!kgsl_mem_entry_set_pend(entry)) { + KGSL_DRV_WARN(dev_priv->device, + "Cannot set pending bit for gpuaddr %08x\n", gpuaddr); + kgsl_mem_entry_put(entry); + return -EBUSY; } trace_kgsl_mem_timestamp_queue(device, entry, context_id, kgsl_readtimestamp(device, context, @@ -1088,7 +1705,7 @@ static long _cmdstream_freememontimestamp(struct kgsl_device_private *dev_priv, timestamp); result = kgsl_add_event(dev_priv->device, context_id, timestamp, kgsl_freemem_event_cb, entry, dev_priv); -done: + kgsl_mem_entry_put(entry); return result; } @@ -1109,16 +1726,14 @@ static long kgsl_ioctl_cmdstream_freememontimestamp_ctxtid( { struct kgsl_cmdstream_freememontimestamp_ctxtid *param = data; struct kgsl_context *context; + long result = -EINVAL; - context = kgsl_find_context(dev_priv, param->context_id); - if (context == NULL) { - KGSL_DRV_ERR(dev_priv->device, - "invalid drawctxt context_id %d\n", param->context_id); - return -EINVAL; - } - - return _cmdstream_freememontimestamp(dev_priv, param->gpuaddr, + context = kgsl_context_get_owner(dev_priv, param->context_id); + if (context) + result = _cmdstream_freememontimestamp(dev_priv, param->gpuaddr, context, param->timestamp, param->type); + kgsl_context_put(context); + return result; } static long kgsl_ioctl_drawctxt_create(struct kgsl_device_private *dev_priv, @@ -1130,22 +1745,22 @@ static long kgsl_ioctl_drawctxt_create(struct kgsl_device_private *dev_priv, context = kgsl_create_context(dev_priv); - if (context == NULL) { - result = -ENOMEM; + if (IS_ERR(context)) { + result = PTR_ERR(context); goto done; } if (dev_priv->device->ftbl->drawctxt_create) { result = dev_priv->device->ftbl->drawctxt_create( dev_priv->device, dev_priv->process_priv->pagetable, - context, param->flags); + context, ¶m->flags); if (result) goto done; } trace_kgsl_context_create(dev_priv->device, context, param->flags); param->drawctxt_id = context->id; done: - if (result && context) + if (result && !IS_ERR(context)) kgsl_context_detach(context); return result; @@ -1154,46 +1769,76 @@ static long kgsl_ioctl_drawctxt_create(struct kgsl_device_private *dev_priv, static long kgsl_ioctl_drawctxt_destroy(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) { - int result = 0; struct kgsl_drawctxt_destroy *param = data; struct kgsl_context *context; + long result = -EINVAL; - context = kgsl_find_context(dev_priv, param->drawctxt_id); + context = kgsl_context_get_owner(dev_priv, param->drawctxt_id); - if (context == NULL) { - result = -EINVAL; - goto done; + if (context) { + kgsl_context_detach(context); + result = 0; } - kgsl_context_detach(context); -done: + kgsl_context_put(context); return result; } +static long _sharedmem_free_entry(struct kgsl_mem_entry *entry) +{ + if (!kgsl_mem_entry_set_pend(entry)) { + kgsl_mem_entry_put(entry); + return -EBUSY; + } + + trace_kgsl_mem_free(entry); + + kgsl_memfree_add(entry->priv->pid, entry->memdesc.gpuaddr, + entry->memdesc.size, entry->memdesc.flags); + + /* + * First kgsl_mem_entry_put is for the reference that we took in + * this function when calling kgsl_sharedmem_find, second one is + * to free the memory since this is a free ioctl + */ + kgsl_mem_entry_put(entry); + kgsl_mem_entry_put(entry); + + return 0; +} + static long kgsl_ioctl_sharedmem_free(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) { - int result = 0; struct kgsl_sharedmem_free *param = data; struct kgsl_process_private *private = dev_priv->process_priv; struct kgsl_mem_entry *entry = NULL; - spin_lock(&private->mem_lock); entry = kgsl_sharedmem_find(private, param->gpuaddr); - if (entry) - rb_erase(&entry->node, &private->mem_rb); + if (!entry) { + KGSL_MEM_INFO(dev_priv->device, "invalid gpuaddr %08x\n", + param->gpuaddr); + return -EINVAL; + } - spin_unlock(&private->mem_lock); + return _sharedmem_free_entry(entry); +} - if (entry) { - trace_kgsl_mem_free(entry); - kgsl_mem_entry_detach_process(entry); - } else { - KGSL_CORE_ERR("invalid gpuaddr %08x\n", param->gpuaddr); - result = -EINVAL; +static long kgsl_ioctl_gpumem_free_id(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + struct kgsl_gpumem_free_id *param = data; + struct kgsl_process_private *private = dev_priv->process_priv; + struct kgsl_mem_entry *entry = NULL; + + entry = kgsl_sharedmem_find_id(private, param->id); + + if (!entry) { + KGSL_MEM_INFO(dev_priv->device, "invalid id %d\n", param->id); + return -EINVAL; } - return result; + return _sharedmem_free_entry(entry); } static struct vm_area_struct *kgsl_get_vma_from_start_addr(unsigned int addr) @@ -1224,11 +1869,10 @@ static int kgsl_get_phys_file(int fd, unsigned long *start, unsigned long *len, dev_t rdev; struct fb_info *info; + *start = 0; + *vstart = 0; + *len = 0; *filep = NULL; -#ifdef CONFIG_ANDROID_PMEM - if (!get_pmem_file(fd, start, vstart, len, filep)) - return 0; -#endif fbfile = fget(fd); if (fbfile == NULL) { @@ -1310,6 +1954,8 @@ static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, entry->memdesc.size = size; entry->memdesc.physaddr = phys + offset; entry->memdesc.hostptr = (void *) (virt + offset); + /* USE_CPU_MAP is not impemented for PMEM. */ + entry->memdesc.flags &= ~KGSL_MEMFLAGS_USE_CPU_MAP; ret = memdesc_sg_phys(&entry->memdesc, phys + offset, size); if (ret) @@ -1317,18 +1963,14 @@ static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, return 0; err: -#ifdef CONFIG_ANDROID_PMEM - put_pmem_file(filep); -#endif return ret; } static int memdesc_sg_virt(struct kgsl_memdesc *memdesc, - void *addr, int size) + unsigned long paddr, int size) { int i; int sglen = PAGE_ALIGN(size) / PAGE_SIZE; - unsigned long paddr = (unsigned long) addr; memdesc->sg = kgsl_sg_alloc(sglen); @@ -1379,34 +2021,33 @@ static int memdesc_sg_virt(struct kgsl_memdesc *memdesc, return -EINVAL; } -static int kgsl_setup_hostptr(struct kgsl_mem_entry *entry, +static int kgsl_setup_useraddr(struct kgsl_mem_entry *entry, struct kgsl_pagetable *pagetable, - void *hostptr, unsigned int offset, + unsigned long useraddr, unsigned int offset, size_t size) { struct vm_area_struct *vma; unsigned int len; down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, (unsigned int) hostptr); + vma = find_vma(current->mm, useraddr); up_read(¤t->mm->mmap_sem); if (!vma) { - KGSL_CORE_ERR("find_vma(%p) failed\n", hostptr); + KGSL_CORE_ERR("find_vma(%lx) failed\n", useraddr); return -EINVAL; } /* We don't necessarily start at vma->vm_start */ - len = vma->vm_end - (unsigned long) hostptr; + len = vma->vm_end - useraddr; if (offset >= len) return -EINVAL; - if (!KGSL_IS_PAGE_ALIGNED((unsigned long) hostptr) || + if (!KGSL_IS_PAGE_ALIGNED(useraddr) || !KGSL_IS_PAGE_ALIGNED(len)) { - KGSL_CORE_ERR("user address len(%u)" - "and start(%p) must be page" - "aligned\n", len, hostptr); + KGSL_CORE_ERR("bad alignment: start(%lx) len(%u)\n", + useraddr, len); return -EINVAL; } @@ -1427,28 +2068,29 @@ static int kgsl_setup_hostptr(struct kgsl_mem_entry *entry, entry->memdesc.pagetable = pagetable; entry->memdesc.size = size; - entry->memdesc.hostptr = hostptr + (offset & PAGE_MASK); + entry->memdesc.useraddr = useraddr + (offset & PAGE_MASK); + if (kgsl_memdesc_use_cpu_map(&entry->memdesc)) + entry->memdesc.gpuaddr = entry->memdesc.useraddr; - return memdesc_sg_virt(&entry->memdesc, - hostptr + (offset & PAGE_MASK), size); + return memdesc_sg_virt(&entry->memdesc, entry->memdesc.useraddr, + size); } #ifdef CONFIG_ASHMEM static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry, struct kgsl_pagetable *pagetable, - int fd, void *hostptr, size_t size) + int fd, unsigned long useraddr, size_t size) { int ret; struct vm_area_struct *vma; struct file *filep, *vmfile; unsigned long len; - unsigned int hostaddr = (unsigned int) hostptr; - vma = kgsl_get_vma_from_start_addr(hostaddr); + vma = kgsl_get_vma_from_start_addr(useraddr); if (vma == NULL) return -EINVAL; - if (vma->vm_pgoff || vma->vm_start != hostaddr) { + if (vma->vm_pgoff || vma->vm_start != useraddr) { KGSL_CORE_ERR("Invalid vma region\n"); return -EINVAL; } @@ -1459,8 +2101,8 @@ static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry, size = len; if (size != len) { - KGSL_CORE_ERR("Invalid size %d for vma region %p\n", - size, hostptr); + KGSL_CORE_ERR("Invalid size %d for vma region %lx\n", + size, useraddr); return -EINVAL; } @@ -1480,9 +2122,11 @@ static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry, entry->priv_data = filep; entry->memdesc.pagetable = pagetable; entry->memdesc.size = ALIGN(size, PAGE_SIZE); - entry->memdesc.hostptr = hostptr; + entry->memdesc.useraddr = useraddr; + if (kgsl_memdesc_use_cpu_map(&entry->memdesc)) + entry->memdesc.gpuaddr = entry->memdesc.useraddr; - ret = memdesc_sg_virt(&entry->memdesc, hostptr, size); + ret = memdesc_sg_virt(&entry->memdesc, useraddr, size); if (ret) goto err; @@ -1495,7 +2139,7 @@ static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry, #else static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry, struct kgsl_pagetable *pagetable, - int fd, void *hostptr, size_t size) + int fd, unsigned long useraddr, size_t size) { return -EINVAL; } @@ -1521,6 +2165,8 @@ static int kgsl_setup_ion(struct kgsl_mem_entry *entry, entry->priv_data = handle; entry->memdesc.pagetable = pagetable; entry->memdesc.size = 0; + /* USE_CPU_MAP is not impemented for ION. */ + entry->memdesc.flags &= ~KGSL_MEMFLAGS_USE_CPU_MAP; sg_table = ion_sg_table(kgsl_ion_client, handle); @@ -1544,6 +2190,13 @@ static int kgsl_setup_ion(struct kgsl_mem_entry *entry, return -ENOMEM; } +static inline int +can_use_cpu_map(void) +{ + return (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU + && kgsl_mmu_is_perprocess()); +} + static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) { @@ -1563,7 +2216,23 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, else memtype = param->memtype; + /* + * Mask off unknown flags from userspace. This way the caller can + * check if a flag is supported by looking at the returned flags. + * Note: CACHEMODE is ignored for this call. Caching should be + * determined by type of allocation being mapped. + */ + param->flags &= KGSL_MEMFLAGS_GPUREADONLY + | KGSL_MEMTYPE_MASK + | KGSL_MEMALIGN_MASK + | KGSL_MEMFLAGS_USE_CPU_MAP; + entry->memdesc.flags = param->flags; + if (!can_use_cpu_map()) + entry->memdesc.flags &= ~KGSL_MEMFLAGS_USE_CPU_MAP; + + if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU) + entry->memdesc.priv |= KGSL_MEMDESC_GUARD_PAGE; switch (memtype) { case KGSL_USER_MEM_TYPE_PMEM: @@ -1589,8 +2258,8 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, if (param->hostptr == 0) break; - result = kgsl_setup_hostptr(entry, private->pagetable, - (void *) param->hostptr, + result = kgsl_setup_useraddr(entry, private->pagetable, + param->hostptr, param->offset, param->len); entry->memtype = KGSL_MEM_ENTRY_USER; break; @@ -1607,7 +2276,7 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, break; result = kgsl_setup_ashmem(entry, private->pagetable, - param->fd, (void *) param->hostptr, + param->fd, param->hostptr, param->len); entry->memtype = KGSL_MEM_ENTRY_ASHMEM; @@ -1629,12 +2298,12 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, else if (entry->memdesc.size >= SZ_64K) kgsl_memdesc_set_align(&entry->memdesc, ilog2(SZ_64)); - result = kgsl_mmu_map(private->pagetable, - &entry->memdesc, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + /* echo back flags */ + param->flags = entry->memdesc.flags; + result = kgsl_mem_entry_attach_process(entry, dev_priv); if (result) - goto error_put_file_ptr; + goto error_attach; /* Adjust the returned value for a non 4k aligned offset */ param->gpuaddr = entry->memdesc.gpuaddr + (param->offset & ~PAGE_MASK); @@ -1644,13 +2313,11 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, kgsl_process_add_stats(private, entry->memtype, param->len); - kgsl_mem_entry_attach_process(entry, private); trace_kgsl_mem_map(entry, param->fd); - kgsl_check_idle(dev_priv->device); return result; -error_put_file_ptr: +error_attach: switch (entry->memtype) { case KGSL_MEM_ENTRY_PMEM: case KGSL_MEM_ENTRY_ASHMEM: @@ -1665,37 +2332,143 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, } error: kfree(entry); - kgsl_check_idle(dev_priv->device); return result; } -/*This function flushes a graphics memory allocation from CPU cache - *when caching is enabled with MMU*/ +static int _kgsl_gpumem_sync_cache(struct kgsl_mem_entry *entry, int op) +{ + int ret = 0; + int cacheop; + int mode; + + /* + * Flush is defined as (clean | invalidate). If both bits are set, then + * do a flush, otherwise check for the individual bits and clean or inv + * as requested + */ + + if ((op & KGSL_GPUMEM_CACHE_FLUSH) == KGSL_GPUMEM_CACHE_FLUSH) + cacheop = KGSL_CACHE_OP_FLUSH; + else if (op & KGSL_GPUMEM_CACHE_CLEAN) + cacheop = KGSL_CACHE_OP_CLEAN; + else if (op & KGSL_GPUMEM_CACHE_INV) + cacheop = KGSL_CACHE_OP_INV; + else { + ret = -EINVAL; + goto done; + } + + mode = kgsl_memdesc_get_cachemode(&entry->memdesc); + if (mode != KGSL_CACHEMODE_UNCACHED + && mode != KGSL_CACHEMODE_WRITECOMBINE) + kgsl_cache_range_op(&entry->memdesc, cacheop); + +done: + return ret; +} + +/* New cache sync function - supports both directions (clean and invalidate) */ + +static long +kgsl_ioctl_gpumem_sync_cache(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + struct kgsl_gpumem_sync_cache *param = data; + struct kgsl_process_private *private = dev_priv->process_priv; + struct kgsl_mem_entry *entry = NULL; + long ret; + + if (param->id != 0) { + entry = kgsl_sharedmem_find_id(private, param->id); + if (entry == NULL) { + KGSL_MEM_INFO(dev_priv->device, "can't find id %d\n", + param->id); + return -EINVAL; + } + } else if (param->gpuaddr != 0) { + entry = kgsl_sharedmem_find(private, param->gpuaddr); + if (entry == NULL) { + KGSL_MEM_INFO(dev_priv->device, + "can't find gpuaddr %x\n", + param->gpuaddr); + return -EINVAL; + } + } else { + return -EINVAL; + } + + ret = _kgsl_gpumem_sync_cache(entry, param->op); + kgsl_mem_entry_put(entry); + return ret; +} + +/* Legacy cache function, does a flush (clean + invalidate) */ + static long kgsl_ioctl_sharedmem_flush_cache(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) { - int result = 0; - struct kgsl_mem_entry *entry; struct kgsl_sharedmem_free *param = data; struct kgsl_process_private *private = dev_priv->process_priv; + struct kgsl_mem_entry *entry = NULL; + long ret; - spin_lock(&private->mem_lock); entry = kgsl_sharedmem_find(private, param->gpuaddr); - if (!entry) { - KGSL_CORE_ERR("invalid gpuaddr %08x\n", param->gpuaddr); - result = -EINVAL; - goto done; - } - if (!entry->memdesc.hostptr) { - KGSL_CORE_ERR("invalid hostptr with gpuaddr %08x\n", - param->gpuaddr); - goto done; + if (entry == NULL) { + KGSL_MEM_INFO(dev_priv->device, + "can't find gpuaddr %x\n", + param->gpuaddr); + return -EINVAL; } + ret = _kgsl_gpumem_sync_cache(entry, KGSL_GPUMEM_CACHE_FLUSH); + kgsl_mem_entry_put(entry); + return ret; +} - kgsl_cache_range_op(&entry->memdesc, KGSL_CACHE_OP_CLEAN); -done: - spin_unlock(&private->mem_lock); +/* + * The common parts of kgsl_ioctl_gpumem_alloc and kgsl_ioctl_gpumem_alloc_id. + */ +int +_gpumem_alloc(struct kgsl_device_private *dev_priv, + struct kgsl_mem_entry **ret_entry, + unsigned int size, unsigned int flags) +{ + int result; + struct kgsl_process_private *private = dev_priv->process_priv; + struct kgsl_mem_entry *entry; + + /* + * Mask off unknown flags from userspace. This way the caller can + * check if a flag is supported by looking at the returned flags. + */ + flags &= KGSL_MEMFLAGS_GPUREADONLY + | KGSL_CACHEMODE_MASK + | KGSL_MEMTYPE_MASK + | KGSL_MEMALIGN_MASK + | KGSL_MEMFLAGS_USE_CPU_MAP; + + if (!can_use_cpu_map()) + flags &= ~KGSL_MEMFLAGS_USE_CPU_MAP; + + entry = kgsl_mem_entry_create(); + if (entry == NULL) + return -ENOMEM; + + if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU) + entry->memdesc.priv |= KGSL_MEMDESC_GUARD_PAGE; + + result = kgsl_allocate_user(&entry->memdesc, private->pagetable, size, + flags); + if (result != 0) + goto err; + + entry->memtype = KGSL_MEM_ENTRY_KERNEL; + + *ret_entry = entry; + return result; +err: + kfree(entry); + *ret_entry = NULL; return result; } @@ -1705,29 +2478,105 @@ kgsl_ioctl_gpumem_alloc(struct kgsl_device_private *dev_priv, { struct kgsl_process_private *private = dev_priv->process_priv; struct kgsl_gpumem_alloc *param = data; - struct kgsl_mem_entry *entry; + struct kgsl_mem_entry *entry = NULL; int result; - entry = kgsl_mem_entry_create(); - if (entry == NULL) - return -ENOMEM; + param->flags &= ~KGSL_MEMFLAGS_USE_CPU_MAP; + result = _gpumem_alloc(dev_priv, &entry, param->size, param->flags); + if (result) + return result; + + result = kgsl_mem_entry_attach_process(entry, dev_priv); + if (result != 0) + goto err; + + kgsl_process_add_stats(private, entry->memtype, param->size); + trace_kgsl_mem_alloc(entry); + + param->gpuaddr = entry->memdesc.gpuaddr; + param->size = entry->memdesc.size; + param->flags = entry->memdesc.flags; + return result; +err: + kgsl_sharedmem_free(&entry->memdesc); + kfree(entry); + return result; +} + +static long +kgsl_ioctl_gpumem_alloc_id(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + struct kgsl_process_private *private = dev_priv->process_priv; + struct kgsl_gpumem_alloc_id *param = data; + struct kgsl_mem_entry *entry = NULL; + int result; + + if (!can_use_cpu_map()) + param->flags &= ~KGSL_MEMFLAGS_USE_CPU_MAP; + + result = _gpumem_alloc(dev_priv, &entry, param->size, param->flags); + if (result != 0) + goto err; + + result = kgsl_mem_entry_attach_process(entry, dev_priv); + if (result != 0) + goto err; - result = kgsl_allocate_user(&entry->memdesc, private->pagetable, - param->size, param->flags); + kgsl_process_add_stats(private, entry->memtype, param->size); + trace_kgsl_mem_alloc(entry); + + param->id = entry->id; + param->flags = entry->memdesc.flags; + param->size = entry->memdesc.size; + param->mmapsize = kgsl_memdesc_mmapsize(&entry->memdesc); + param->gpuaddr = entry->memdesc.gpuaddr; + return result; +err: + if (entry) + kgsl_sharedmem_free(&entry->memdesc); + kfree(entry); + return result; +} - if (result == 0) { - entry->memtype = KGSL_MEM_ENTRY_KERNEL; - kgsl_mem_entry_attach_process(entry, private); - param->gpuaddr = entry->memdesc.gpuaddr; +static long +kgsl_ioctl_gpumem_get_info(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + struct kgsl_process_private *private = dev_priv->process_priv; + struct kgsl_gpumem_get_info *param = data; + struct kgsl_mem_entry *entry = NULL; + int result = 0; - kgsl_process_add_stats(private, entry->memtype, param->size); - trace_kgsl_mem_alloc(entry); - } else - kfree(entry); + if (param->id != 0) { + entry = kgsl_sharedmem_find_id(private, param->id); + if (entry == NULL) { + KGSL_MEM_INFO(dev_priv->device, "can't find id %d\n", + param->id); + return -EINVAL; + } + } else if (param->gpuaddr != 0) { + entry = kgsl_sharedmem_find(private, param->gpuaddr); + if (entry == NULL) { + KGSL_MEM_INFO(dev_priv->device, + "can't find gpuaddr %lx\n", + param->gpuaddr); + return -EINVAL; + } + } else { + return -EINVAL; + } + param->gpuaddr = entry->memdesc.gpuaddr; + param->id = entry->id; + param->flags = entry->memdesc.flags; + param->size = entry->memdesc.size; + param->mmapsize = kgsl_memdesc_mmapsize(&entry->memdesc); + param->useraddr = entry->memdesc.useraddr; - kgsl_check_idle(dev_priv->device); + kgsl_mem_entry_put(entry); return result; } + static long kgsl_ioctl_cff_syncmem(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) { @@ -1736,14 +2585,14 @@ static long kgsl_ioctl_cff_syncmem(struct kgsl_device_private *dev_priv, struct kgsl_process_private *private = dev_priv->process_priv; struct kgsl_mem_entry *entry = NULL; - spin_lock(&private->mem_lock); entry = kgsl_sharedmem_find_region(private, param->gpuaddr, param->len); - if (entry) - kgsl_cffdump_syncmem(dev_priv, &entry->memdesc, param->gpuaddr, - param->len, true); - else - result = -EINVAL; - spin_unlock(&private->mem_lock); + if (!entry) + return -EINVAL; + + kgsl_cffdump_syncmem(dev_priv, &entry->memdesc, param->gpuaddr, + param->len, true); + + kgsl_mem_entry_put(entry); return result; } @@ -1776,11 +2625,12 @@ struct kgsl_genlock_event_priv { */ static void kgsl_genlock_event_cb(struct kgsl_device *device, - void *priv, u32 context_id, u32 timestamp) + void *priv, u32 context_id, u32 timestamp, u32 type) { struct kgsl_genlock_event_priv *ev = priv; int ret; + /* Signal the lock for every event type */ ret = genlock_lock(ev->handle, GENLOCK_UNLOCK, 0, 0); if (ret) KGSL_CORE_ERR("Error while unlocking genlock: %d\n", ret); @@ -1895,7 +2745,7 @@ static const struct { } kgsl_ioctl_funcs[] = { KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_GETPROPERTY, kgsl_ioctl_device_getproperty, - KGSL_IOCTL_LOCK | KGSL_IOCTL_WAKE), + KGSL_IOCTL_LOCK), KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_WAITTIMESTAMP, kgsl_ioctl_device_waittimestamp, KGSL_IOCTL_LOCK | KGSL_IOCTL_WAKE), @@ -1913,13 +2763,13 @@ static const struct { KGSL_IOCTL_LOCK), KGSL_IOCTL_FUNC(IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP, kgsl_ioctl_cmdstream_freememontimestamp, - KGSL_IOCTL_LOCK | KGSL_IOCTL_WAKE), + KGSL_IOCTL_LOCK), KGSL_IOCTL_FUNC(IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP_CTXTID, kgsl_ioctl_cmdstream_freememontimestamp_ctxtid, - KGSL_IOCTL_LOCK | KGSL_IOCTL_WAKE), + KGSL_IOCTL_LOCK), KGSL_IOCTL_FUNC(IOCTL_KGSL_DRAWCTXT_CREATE, kgsl_ioctl_drawctxt_create, - KGSL_IOCTL_LOCK | KGSL_IOCTL_WAKE), + KGSL_IOCTL_LOCK), KGSL_IOCTL_FUNC(IOCTL_KGSL_DRAWCTXT_DESTROY, kgsl_ioctl_drawctxt_destroy, KGSL_IOCTL_LOCK | KGSL_IOCTL_WAKE), @@ -1942,7 +2792,15 @@ static const struct { KGSL_IOCTL_LOCK), KGSL_IOCTL_FUNC(IOCTL_KGSL_SETPROPERTY, kgsl_ioctl_device_setproperty, - KGSL_IOCTL_LOCK | KGSL_IOCTL_WAKE) + KGSL_IOCTL_LOCK), + KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_ALLOC_ID, + kgsl_ioctl_gpumem_alloc_id, 0), + KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_FREE_ID, + kgsl_ioctl_gpumem_free_id, 0), + KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_GET_INFO, + kgsl_ioctl_gpumem_get_info, 0), + KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_SYNC_CACHE, + kgsl_ioctl_gpumem_sync_cache, 0), }; static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) @@ -2023,14 +2881,19 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) if (lock) { mutex_lock(&dev_priv->device->mutex); - if (use_hw) - kgsl_check_suspended(dev_priv->device); + if (use_hw) { + ret = kgsl_active_count_get(dev_priv->device); + if (ret < 0) + goto unlock; + } } ret = func(dev_priv, cmd, uptr); +unlock: if (lock) { - kgsl_check_idle_locked(dev_priv->device); + if (use_hw) + kgsl_active_count_put(dev_priv->device); mutex_unlock(&dev_priv->device->mutex); } @@ -2084,7 +2947,8 @@ kgsl_mmap_memstore(struct kgsl_device *device, struct vm_area_struct *vma) static void kgsl_gpumem_vm_open(struct vm_area_struct *vma) { struct kgsl_mem_entry *entry = vma->vm_private_data; - kgsl_mem_entry_get(entry); + if (!kgsl_mem_entry_get(entry)) + vma->vm_private_data = NULL; } static int @@ -2092,6 +2956,8 @@ kgsl_gpumem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct kgsl_mem_entry *entry = vma->vm_private_data; + if (!entry) + return VM_FAULT_SIGBUS; if (!entry->memdesc.ops || !entry->memdesc.ops->vmfault) return VM_FAULT_SIGBUS; @@ -2102,6 +2968,11 @@ static void kgsl_gpumem_vm_close(struct vm_area_struct *vma) { struct kgsl_mem_entry *entry = vma->vm_private_data; + + if (!entry) + return; + + entry->memdesc.useraddr = 0; kgsl_mem_entry_put(entry); } @@ -2111,8 +2982,186 @@ static struct vm_operations_struct kgsl_gpumem_vm_ops = { .close = kgsl_gpumem_vm_close, }; +static int +get_mmap_entry(struct kgsl_process_private *private, + struct kgsl_mem_entry **out_entry, unsigned long pgoff, + unsigned long len) +{ + int ret = 0; + struct kgsl_mem_entry *entry; + + entry = kgsl_sharedmem_find_id(private, pgoff); + if (entry == NULL) { + entry = kgsl_sharedmem_find(private, pgoff << PAGE_SHIFT); + } + + if (!entry) + return -EINVAL; + + if (!entry->memdesc.ops || + !entry->memdesc.ops->vmflags || + !entry->memdesc.ops->vmfault) { + ret = -EINVAL; + goto err_put; + } + + if (entry->memdesc.useraddr != 0) { + ret = -EBUSY; + goto err_put; + } + + if (len != kgsl_memdesc_mmapsize(&entry->memdesc)) { + ret = -ERANGE; + goto err_put; + } + + *out_entry = entry; + return 0; +err_put: + kgsl_mem_entry_put(entry); + return ret; +} + +static inline bool +mmap_range_valid(unsigned long addr, unsigned long len) +{ + return ((ULONG_MAX - addr) > len) && ((addr + len) < TASK_SIZE); +} + +static unsigned long +kgsl_get_unmapped_area(struct file *file, unsigned long addr, + unsigned long len, unsigned long pgoff, + unsigned long flags) +{ + unsigned long ret = 0, orig_len = len; + unsigned long vma_offset = pgoff << PAGE_SHIFT; + struct kgsl_device_private *dev_priv = file->private_data; + struct kgsl_process_private *private = dev_priv->process_priv; + struct kgsl_device *device = dev_priv->device; + struct kgsl_mem_entry *entry = NULL; + unsigned int align; + unsigned int retry = 0; + + if (vma_offset == device->memstore.gpuaddr) + return get_unmapped_area(NULL, addr, len, pgoff, flags); + + ret = get_mmap_entry(private, &entry, pgoff, len); + if (ret) + return ret; + + if (!kgsl_memdesc_use_cpu_map(&entry->memdesc) || (flags & MAP_FIXED)) { + /* + * If we're not going to use the same mapping on the gpu, + * any address is fine. + * For MAP_FIXED, hopefully the caller knows what they're doing, + * but we may fail in mmap() if there is already something + * at the virtual address chosen. + */ + ret = get_unmapped_area(NULL, addr, len, pgoff, flags); + goto put; + } + if (entry->memdesc.gpuaddr != 0) { + KGSL_MEM_INFO(device, + "pgoff %lx already mapped to gpuaddr %x\n", + pgoff, entry->memdesc.gpuaddr); + ret = -EBUSY; + goto put; + } + + align = kgsl_memdesc_get_align(&entry->memdesc); + if (align >= ilog2(SZ_1M)) + align = ilog2(SZ_1M); + else if (align >= ilog2(SZ_64K)) + align = ilog2(SZ_64K); + else if (align <= PAGE_SHIFT) + align = 0; + + if (align) + len += 1 << align; + + if (!mmap_range_valid(addr, len)) + addr = 0; + do { + ret = get_unmapped_area(NULL, addr, len, pgoff, flags); + if (IS_ERR_VALUE(ret)) { + /* + * If we are really fragmented, there may not be room + * for the alignment padding, so try again without it. + */ + if (!retry && (ret == (unsigned long)-ENOMEM) + && (align > PAGE_SHIFT)) { + align = 0; + addr = 0; + len = orig_len; + retry = 1; + continue; + } + break; + } + if (align) + ret = ALIGN(ret, (1 << align)); + + /*make sure there isn't a GPU only mapping at this address */ + spin_lock(&private->mem_lock); + if (kgsl_sharedmem_region_empty(private, ret, orig_len)) { + int ret_val; + /* + * We found a free memory map, claim it here with + * memory lock held + */ + entry->memdesc.gpuaddr = ret; + /* This should never fail */ + ret_val = kgsl_mem_entry_track_gpuaddr(private, entry); + spin_unlock(&private->mem_lock); + BUG_ON(ret_val); + /* map cannot be called with lock held */ + ret_val = kgsl_mmu_map(private->pagetable, + &entry->memdesc); + if (ret_val) { + spin_lock(&private->mem_lock); + kgsl_mem_entry_untrack_gpuaddr(private, entry); + spin_unlock(&private->mem_lock); + ret = ret_val; + } + break; + } + spin_unlock(&private->mem_lock); + + trace_kgsl_mem_unmapped_area_collision(entry, addr, orig_len, + ret); + + /* + * If we collided, bump the hint address so that + * get_umapped_area knows to look somewhere else. + */ + addr = (addr == 0) ? ret + orig_len : addr + orig_len; + + /* + * The addr hint can be set by userspace to be near + * the end of the address space. Make sure we search + * the whole address space at least once by wrapping + * back around once. + */ + if (!retry && !mmap_range_valid(addr, len)) { + addr = 0; + retry = 1; + } else { + ret = -EBUSY; + } + } while (mmap_range_valid(addr, len)); + + if (IS_ERR_VALUE(ret)) + KGSL_MEM_ERR(device, + "pid %d pgoff %lx len %ld failed error %ld\n", + private->pid, pgoff, len, ret); +put: + kgsl_mem_entry_put(entry); + return ret; +} + static int kgsl_mmap(struct file *file, struct vm_area_struct *vma) { + unsigned int ret, cache; unsigned long vma_offset = vma->vm_pgoff << PAGE_SHIFT; struct kgsl_device_private *dev_priv = file->private_data; struct kgsl_process_private *private = dev_priv->process_priv; @@ -2124,31 +3173,64 @@ static int kgsl_mmap(struct file *file, struct vm_area_struct *vma) if (vma_offset == device->memstore.gpuaddr) return kgsl_mmap_memstore(device, vma); - /* Find a chunk of GPU memory */ - - spin_lock(&private->mem_lock); - entry = kgsl_sharedmem_find(private, vma_offset); + /* + * The reference count on the entry that we get from + * get_mmap_entry() will be held until kgsl_gpumem_vm_close(). + */ + ret = get_mmap_entry(private, &entry, vma->vm_pgoff, + vma->vm_end - vma->vm_start); + if (ret) + return ret; - if (entry) - kgsl_mem_entry_get(entry); + vma->vm_flags |= entry->memdesc.ops->vmflags(&entry->memdesc); - spin_unlock(&private->mem_lock); + vma->vm_private_data = entry; - if (entry == NULL) - return -EINVAL; + /* Determine user-side caching policy */ - if (!entry->memdesc.ops || - !entry->memdesc.ops->vmflags || - !entry->memdesc.ops->vmfault) - return -EINVAL; + cache = kgsl_memdesc_get_cachemode(&entry->memdesc); - vma->vm_flags |= entry->memdesc.ops->vmflags(&entry->memdesc); + switch (cache) { + case KGSL_CACHEMODE_UNCACHED: + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + break; + case KGSL_CACHEMODE_WRITETHROUGH: + vma->vm_page_prot = pgprot_writethroughcache(vma->vm_page_prot); + break; + case KGSL_CACHEMODE_WRITEBACK: + vma->vm_page_prot = pgprot_writebackcache(vma->vm_page_prot); + break; + case KGSL_CACHEMODE_WRITECOMBINE: + default: + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + break; + } - vma->vm_private_data = entry; - vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); vma->vm_ops = &kgsl_gpumem_vm_ops; + + if (cache == KGSL_CACHEMODE_WRITEBACK + || cache == KGSL_CACHEMODE_WRITETHROUGH) { + struct scatterlist *s; + int i; + int sglen = entry->memdesc.sglen; + unsigned long addr = vma->vm_start; + + for_each_sg(entry->memdesc.sg, s, sglen, i) { + int j; + for (j = 0; j < (sg_dma_len(s) >> PAGE_SHIFT); j++) { + struct page *page = sg_page(s); + page = nth_page(page, j); + vm_insert_page(vma, addr, page); + addr += PAGE_SIZE; + } + } + } + vma->vm_file = file; + entry->memdesc.useraddr = vma->vm_start; + + trace_kgsl_mem_mmap(entry); return 0; } @@ -2165,6 +3247,7 @@ static const struct file_operations kgsl_fops = { .release = kgsl_release, .open = kgsl_open, .mmap = kgsl_mmap, + .get_unmapped_area = kgsl_get_unmapped_area, .unlocked_ioctl = kgsl_ioctl, }; @@ -2312,13 +3395,15 @@ int kgsl_device_platform_probe(struct kgsl_device *device) device->id, device->reg_phys, device->reg_len, device->reg_virt); + rwlock_init(&device->context_lock); + result = kgsl_drm_init(pdev); if (result) goto error_pwrctrl_close; - kgsl_cffdump_open(device->id); setup_timer(&device->idle_timer, kgsl_timer, (unsigned long) device); + setup_timer(&device->hang_timer, hang_timer, (unsigned long) device); status = kgsl_create_device_workqueue(device); if (status) goto error_pwrctrl_close; @@ -2374,11 +3459,7 @@ int kgsl_postmortem_dump(struct kgsl_device *device, int manual) /* For a manual dump, make sure that the system is idle */ if (manual) { - if (device->active_cnt != 0) { - mutex_unlock(&device->mutex); - wait_for_completion(&device->suspend_gate); - mutex_lock(&device->mutex); - } + kgsl_active_count_wait(device); if (device->state == KGSL_STATE_ACTIVE) kgsl_idle(device); @@ -2398,9 +3479,7 @@ int kgsl_postmortem_dump(struct kgsl_device *device, int manual) /* Disable the idle timer so we don't get interrupted */ del_timer_sync(&device->idle_timer); - mutex_unlock(&device->mutex); - flush_workqueue(device->work_queue); - mutex_lock(&device->mutex); + del_timer_sync(&device->hang_timer); /* Turn off napping to make sure we have the clocks full attention through the following process */ @@ -2440,7 +3519,6 @@ void kgsl_device_platform_remove(struct kgsl_device *device) { kgsl_device_snapshot_close(device); - kgsl_cffdump_close(device->id); kgsl_pwrctrl_uninit_sysfs(device); pm_qos_remove_request(&device->pm_qos_req_dma); @@ -2496,6 +3574,7 @@ static void kgsl_core_exit(void) kgsl_driver.class = NULL; } + kgsl_memfree_exit(); unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX); } @@ -2561,12 +3640,14 @@ static int __init kgsl_core_init(void) kgsl_mmu_set_mmutype(ksgl_mmu_type); - if (KGSL_MMU_TYPE_GPU == kgsl_mmu_get_mmutype()) { + if (KGSL_MMU_TYPE_GPU == kgsl_mmu_get_mmutype()) { result = kgsl_ptdata_init(); if (result) goto err; } + kgsl_memfree_init(); + return 0; err: diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 39351648..22fc1844 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2014, The Linux Foundation. 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 and @@ -129,11 +129,16 @@ struct kgsl_memdesc_ops { #define KGSL_MEMDESC_GUARD_PAGE BIT(0) /* Set if the memdesc is mapped into all pagetables */ #define KGSL_MEMDESC_GLOBAL BIT(1) +/* The memdesc is frozen during a snapshot */ +#define KGSL_MEMDESC_FROZEN BIT(2) +/* The memdesc is mapped into a pagetable */ +#define KGSL_MEMDESC_MAPPED BIT(3) /* shared memory allocation */ struct kgsl_memdesc { struct kgsl_pagetable *pagetable; - void *hostptr; + void *hostptr; /* kernel virtual address */ + unsigned long useraddr; /* userspace address */ unsigned int gpuaddr; unsigned int physaddr; unsigned int size; @@ -154,21 +159,20 @@ struct kgsl_memdesc { #define KGSL_MEM_ENTRY_ION 4 #define KGSL_MEM_ENTRY_MAX 5 -/* List of flags */ - -#define KGSL_MEM_ENTRY_FROZEN (1 << 0) - struct kgsl_mem_entry { struct kref refcount; struct kgsl_memdesc memdesc; int memtype; - int flags; void *priv_data; struct rb_node node; + unsigned int id; unsigned int context_id; /* back pointer to private structure under whose context this * allocation is made */ struct kgsl_process_private *priv; + /* Initialized to 0, set to 1 when entry is marked for freeing */ + int pending_free; + struct kgsl_device_private *dev_priv; }; #ifdef CONFIG_MSM_KGSL_MMU_PAGE_FAULT @@ -177,6 +181,7 @@ struct kgsl_mem_entry { #define MMU_CONFIG 1 #endif +void kgsl_hang_check(struct work_struct *work); void kgsl_mem_entry_destroy(struct kref *kref); int kgsl_postmortem_dump(struct kgsl_device *device, int manual); @@ -189,16 +194,16 @@ struct kgsl_mem_entry *kgsl_sharedmem_find_region( void kgsl_get_memory_usage(char *str, size_t len, unsigned int memflags); -int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts, - void (*cb)(struct kgsl_device *, void *, u32, u32), void *priv, - void *owner); +void kgsl_signal_event(struct kgsl_device *device, + struct kgsl_context *context, unsigned int timestamp, + unsigned int type); + +void kgsl_signal_events(struct kgsl_device *device, + struct kgsl_context *context, unsigned int type); void kgsl_cancel_events(struct kgsl_device *device, void *owner); -void kgsl_cancel_events_ctxt(struct kgsl_device *device, - struct kgsl_context *context); - extern const struct dev_pm_ops kgsl_pm_ops; struct early_suspend; @@ -207,6 +212,14 @@ int kgsl_resume_driver(struct platform_device *pdev); void kgsl_early_suspend_driver(struct early_suspend *h); void kgsl_late_resume_driver(struct early_suspend *h); +void kgsl_trace_regwrite(struct kgsl_device *device, unsigned int offset, + unsigned int value); + +void kgsl_trace_issueibcmds(struct kgsl_device *device, int id, + struct kgsl_ibdesc *ibdesc, int numibs, + unsigned int timestamp, unsigned int flags, + int result, unsigned int type); + #ifdef CONFIG_MSM_KGSL_DRM extern int kgsl_drm_init(struct platform_device *dev); extern void kgsl_drm_exit(void); @@ -224,6 +237,14 @@ static inline void kgsl_drm_exit(void) static inline int kgsl_gpuaddr_in_memdesc(const struct kgsl_memdesc *memdesc, unsigned int gpuaddr, unsigned int size) { + /* set a minimum size to search for */ + if (!size) + size = 1; + + /* don't overflow */ + if (size > UINT_MAX - gpuaddr) + return 0; + if (gpuaddr >= memdesc->gpuaddr && ((gpuaddr + size) <= (memdesc->gpuaddr + memdesc->size))) { return 1; @@ -272,10 +293,10 @@ static inline int timestamp_cmp(unsigned int a, unsigned int b) return ((a > b) && (a - b <= KGSL_TIMESTAMP_WINDOW)) ? 1 : -1; } -static inline void +static inline int kgsl_mem_entry_get(struct kgsl_mem_entry *entry) { - kref_get(&entry->refcount); + return kref_get_unless_zero(&entry->refcount); } static inline void diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c index e06c94d6..f1271dcc 100644 --- a/drivers/gpu/msm/kgsl_cffdump.c +++ b/drivers/gpu/msm/kgsl_cffdump.c @@ -28,6 +28,7 @@ #include "kgsl_log.h" #include "kgsl_sharedmem.h" #include "adreno_pm4types.h" +#include "adreno.h" static struct rchan *chan; static struct dentry *dir; @@ -334,7 +335,7 @@ void kgsl_cffdump_init() return; } - kgsl_cff_dump_enable = 1; + kgsl_cff_dump_enable = 0; spin_lock_init(&cffdump_lock); @@ -356,38 +357,60 @@ void kgsl_cffdump_destroy() debugfs_remove(dir); } -void kgsl_cffdump_open(enum kgsl_deviceid device_id) +void kgsl_cffdump_open(struct kgsl_device *device) { - kgsl_cffdump_memory_base(device_id, KGSL_PAGETABLE_BASE, - kgsl_mmu_get_ptsize(), SZ_256K); + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + if (!kgsl_cff_dump_enable) + return; + + if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) { + kgsl_cffdump_memory_base(device->id, + kgsl_mmu_get_base_addr(&device->mmu), + kgsl_mmu_get_ptsize(&device->mmu) + + KGSL_IOMMU_GLOBAL_MEM_SIZE, adreno_dev->gmem_size); + } else { + kgsl_cffdump_memory_base(device->id, + kgsl_mmu_get_base_addr(&device->mmu), + kgsl_mmu_get_ptsize(&device->mmu), + adreno_dev->gmem_size); + } } void kgsl_cffdump_memory_base(enum kgsl_deviceid device_id, unsigned int base, unsigned int range, unsigned gmemsize) { + if (!kgsl_cff_dump_enable) + return; cffdump_printline(device_id, CFF_OP_MEMORY_BASE, base, range, gmemsize, 0, 0); } void kgsl_cffdump_hang(enum kgsl_deviceid device_id) { + if (!kgsl_cff_dump_enable) + return; cffdump_printline(device_id, CFF_OP_HANG, 0, 0, 0, 0, 0); } void kgsl_cffdump_close(enum kgsl_deviceid device_id) { + if (!kgsl_cff_dump_enable) + return; cffdump_printline(device_id, CFF_OP_EOF, 0, 0, 0, 0, 0); } + void kgsl_cffdump_user_event(unsigned int cff_opcode, unsigned int op1, unsigned int op2, unsigned int op3, unsigned int op4, unsigned int op5) { + if (!kgsl_cff_dump_enable) + return; cffdump_printline(-1, cff_opcode, op1, op2, op3, op4, op5); } void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv, - const struct kgsl_memdesc *memdesc, uint gpuaddr, uint sizebytes, + struct kgsl_memdesc *memdesc, uint gpuaddr, uint sizebytes, bool clean_cache) { const void *src; @@ -399,10 +422,8 @@ void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv, if (memdesc == NULL) { struct kgsl_mem_entry *entry; - spin_lock(&dev_priv->process_priv->mem_lock); entry = kgsl_sharedmem_find_region(dev_priv->process_priv, gpuaddr, sizebytes); - spin_unlock(&dev_priv->process_priv->mem_lock); if (entry == NULL) { KGSL_CORE_ERR("did not find mapping " "for gpuaddr: 0x%08x\n", gpuaddr); @@ -522,7 +543,7 @@ static int subbuf_start_handler(struct rchan_buf *buf, } static struct dentry *create_buf_file_handler(const char *filename, - struct dentry *parent, int mode, struct rchan_buf *buf, + struct dentry *parent, unsigned short mode, struct rchan_buf *buf, int *is_global) { return debugfs_create_file(filename, mode, parent, buf, diff --git a/drivers/gpu/msm/kgsl_cffdump.h b/drivers/gpu/msm/kgsl_cffdump.h index 2733cc3f..d5656f80 100644 --- a/drivers/gpu/msm/kgsl_cffdump.h +++ b/drivers/gpu/msm/kgsl_cffdump.h @@ -22,10 +22,10 @@ void kgsl_cffdump_init(void); void kgsl_cffdump_destroy(void); -void kgsl_cffdump_open(enum kgsl_deviceid device_id); +void kgsl_cffdump_open(struct kgsl_device *device); void kgsl_cffdump_close(enum kgsl_deviceid device_id); void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv, - const struct kgsl_memdesc *memdesc, uint physaddr, uint sizebytes, + struct kgsl_memdesc *memdesc, uint physaddr, uint sizebytes, bool clean_cache); void kgsl_cffdump_setmem(uint addr, uint value, uint sizebytes); void kgsl_cffdump_regwrite(enum kgsl_deviceid device_id, uint addr, @@ -49,7 +49,7 @@ void kgsl_cffdump_hang(enum kgsl_deviceid device_id); #define kgsl_cffdump_init() (void)0 #define kgsl_cffdump_destroy() (void)0 -#define kgsl_cffdump_open(device_id) (void)0 +#define kgsl_cffdump_open(device) (void)0 #define kgsl_cffdump_close(device_id) (void)0 #define kgsl_cffdump_syncmem(dev_priv, memdesc, addr, sizebytes, clean_cache) \ (void) 0 diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c index b41bd6b4..3b20c233 100644 --- a/drivers/gpu/msm/kgsl_debugfs.c +++ b/drivers/gpu/msm/kgsl_debugfs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2008-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2008-2012,2014, The Linux Foundation. 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 and @@ -18,8 +18,6 @@ #include "kgsl_device.h" #include "kgsl_sharedmem.h" -/*default log levels is error for everything*/ -#define KGSL_LOG_LEVEL_DEFAULT 3 #define KGSL_LOG_LEVEL_MAX 7 struct dentry *kgsl_debugfs_dir; @@ -134,13 +132,6 @@ void kgsl_device_debugfs_init(struct kgsl_device *device) if (!device->d_debugfs || IS_ERR(device->d_debugfs)) return; - device->cmd_log = KGSL_LOG_LEVEL_DEFAULT; - device->ctxt_log = KGSL_LOG_LEVEL_DEFAULT; - device->drv_log = KGSL_LOG_LEVEL_DEFAULT; - device->mem_log = KGSL_LOG_LEVEL_DEFAULT; - device->pwr_log = KGSL_LOG_LEVEL_DEFAULT; - device->ft_log = KGSL_LOG_LEVEL_DEFAULT; - debugfs_create_file("log_level_cmd", 0644, device->d_debugfs, device, &cmd_log_fops); debugfs_create_file("log_level_ctxt", 0644, device->d_debugfs, device, @@ -151,8 +142,6 @@ void kgsl_device_debugfs_init(struct kgsl_device *device) &mem_log_fops); debugfs_create_file("log_level_pwr", 0644, device->d_debugfs, device, &pwr_log_fops); - debugfs_create_file("log_level_ft", 0644, device->d_debugfs, device, - &ft_log_fops); /* Create postmortem dump control files */ @@ -167,7 +156,6 @@ void kgsl_device_debugfs_init(struct kgsl_device *device) &pm_regs_enabled_fops); debugfs_create_file("ib_enabled", 0644, pm_d_debugfs, device, &pm_ib_enabled_fops); - device->pm_dump_enable = 0; debugfs_create_file("enable", 0644, pm_d_debugfs, device, &pm_enabled_fops); @@ -198,60 +186,146 @@ static char get_alignflag(const struct kgsl_memdesc *m) return '-'; } +static char get_cacheflag(const struct kgsl_memdesc *m) +{ + static const char table[] = { + [KGSL_CACHEMODE_WRITECOMBINE] = '-', + [KGSL_CACHEMODE_UNCACHED] = 'u', + [KGSL_CACHEMODE_WRITEBACK] = 'b', + [KGSL_CACHEMODE_WRITETHROUGH] = 't', + }; + return table[kgsl_memdesc_get_cachemode(m)]; +} + +static void print_mem_entry(struct seq_file *s, struct kgsl_mem_entry *entry) +{ + char flags[6]; + char usage[16]; + struct kgsl_memdesc *m = &entry->memdesc; + + flags[0] = kgsl_memdesc_is_global(m) ? 'g' : '-'; + flags[1] = m->flags & KGSL_MEMFLAGS_GPUREADONLY ? 'r' : '-'; + flags[2] = get_alignflag(m); + flags[3] = get_cacheflag(m); + flags[4] = kgsl_memdesc_use_cpu_map(m) ? 'p' : '-'; + flags[5] = '\0'; + + kgsl_get_memory_usage(usage, sizeof(usage), m->flags); + + seq_printf(s, "%pK %pK %8zd %5d %5s %10s %16s %5d\n", + (unsigned long *) m->gpuaddr, + (unsigned long *) m->useraddr, + m->size, entry->id, flags, + memtype_str(entry->memtype), usage, m->sglen); +} + static int process_mem_print(struct seq_file *s, void *unused) { struct kgsl_mem_entry *entry; struct rb_node *node; struct kgsl_process_private *private = s->private; - char flags[4]; - char usage[16]; + int next = 0; + + seq_printf(s, "%8s %8s %8s %5s %5s %10s %16s %5s\n", + "gpuaddr", "useraddr", "size", "id", "flags", "type", + "usage", "sglen"); + /* print all entries with a GPU address */ spin_lock(&private->mem_lock); - seq_printf(s, "%8s %8s %5s %10s %16s %5s\n", - "gpuaddr", "size", "flags", "type", "usage", "sglen"); - for (node = rb_first(&private->mem_rb); node; node = rb_next(node)) { - struct kgsl_memdesc *m; + for (node = rb_first(&private->mem_rb); node; node = rb_next(node)) { entry = rb_entry(node, struct kgsl_mem_entry, node); - m = &entry->memdesc; - - flags[0] = m->priv & KGSL_MEMDESC_GLOBAL ? 'g' : '-'; - flags[1] = m->flags & KGSL_MEMFLAGS_GPUREADONLY ? 'r' : '-'; - flags[2] = get_alignflag(m); - flags[3] = '\0'; + print_mem_entry(s, entry); + } - kgsl_get_memory_usage(usage, sizeof(usage), m->flags); + spin_unlock(&private->mem_lock); - seq_printf(s, "%08x %8d %5s %10s %16s %5d\n", - m->gpuaddr, m->size, flags, - memtype_str(entry->memtype), usage, m->sglen); + /* now print all the unbound entries */ + while (1) { + rcu_read_lock(); + entry = idr_get_next(&private->mem_idr, &next); + rcu_read_unlock(); + + if (entry == NULL) + break; + if (entry->memdesc.gpuaddr == 0) + print_mem_entry(s, entry); + next++; } - spin_unlock(&private->mem_lock); + return 0; } static int process_mem_open(struct inode *inode, struct file *file) { - return single_open(file, process_mem_print, inode->i_private); + int ret; + pid_t pid = (pid_t) (unsigned long) inode->i_private; + struct kgsl_process_private *private = NULL; + + private = kgsl_process_private_find(pid); + + if (!private) + return -ENODEV; + + ret = single_open(file, process_mem_print, private); + if (ret) + kgsl_process_private_put(private); + + return ret; +} + +static int process_mem_release(struct inode *inode, struct file *file) +{ + struct kgsl_process_private *private = + ((struct seq_file *)file->private_data)->private; + + if (private) + kgsl_process_private_put(private); + + return single_release(inode, file); } static const struct file_operations process_mem_fops = { .open = process_mem_open, .read = seq_read, .llseek = seq_lseek, - .release = single_release, + .release = process_mem_release, }; -void +int kgsl_process_init_debugfs(struct kgsl_process_private *private) { unsigned char name[16]; - + int ret = 0; + struct dentry *dentry; snprintf(name, sizeof(name), "%d", private->pid); private->debug_root = debugfs_create_dir(name, proc_d_debugfs); - debugfs_create_file("mem", 0400, private->debug_root, private, - &process_mem_fops); + + if (!private->debug_root) + return -EINVAL; + + /* + * debugfs_create_dir() and debugfs_create_file() both + * return -ENODEV if debugfs is disabled in the kernel. + * We make a distinction between these two functions + * failing and debugfs being disabled in the kernel. + * In the first case, we abort process private struct + * creation, in the second we continue without any changes. + * So if debugfs is disabled in kernel, return as + * success. + */ + dentry = debugfs_create_file("mem", 0444, private->debug_root, + (void *) ((unsigned long) private->pid), &process_mem_fops); + + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + + if (ret == -ENODEV) + ret = 0; + } + + return ret; } void kgsl_core_debugfs_init(void) diff --git a/drivers/gpu/msm/kgsl_debugfs.h b/drivers/gpu/msm/kgsl_debugfs.h index ae5601fb..8d6a9dd5 100644 --- a/drivers/gpu/msm/kgsl_debugfs.h +++ b/drivers/gpu/msm/kgsl_debugfs.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2008-2011, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2008-2011,2013, The Linux Foundation. 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 and @@ -35,7 +35,7 @@ static inline void kgsl_core_debugfs_init(void) { } static inline void kgsl_device_debugfs_init(struct kgsl_device *device) { } static inline void kgsl_core_debugfs_close(void) { } static inline struct dentry *kgsl_get_debugfs_dir(void) { return NULL; } -static inline int kgsl_process_init_debugfs(struct kgsl_process_private *) +static inline int kgsl_process_init_debugfs(struct kgsl_process_private *priv) { return 0; } diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index f6ff824a..6013e7d0 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2012,2014 The Linux Foundation. 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 and @@ -28,6 +28,7 @@ #define KGSL_TIMEOUT_DEFAULT 0xFFFFFFFF #define KGSL_TIMEOUT_PART 50 /* 50 msec */ #define KGSL_TIMEOUT_LONG_IB_DETECTION 2000 /* 2 sec*/ +#define KGSL_TIMEOUT_HANG_DETECT 200 /* 200 msec */ #define FIRST_TIMEOUT (HZ / 2) @@ -54,6 +55,14 @@ #define KGSL_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK))) +/* + * KGSL event types - these are passed to the event callback when the event + * expires or is cancelled + */ + +#define KGSL_EVENT_TIMESTAMP_RETIRED 0 +#define KGSL_EVENT_CANCELLED 1 + struct kgsl_device; struct platform_device; struct kgsl_device_private; @@ -73,7 +82,8 @@ struct kgsl_functable { int (*idle) (struct kgsl_device *device); unsigned int (*isidle) (struct kgsl_device *device); int (*suspend_context) (struct kgsl_device *device); - int (*start) (struct kgsl_device *device, unsigned int init_ram); + int (*init) (struct kgsl_device *device); + int (*start) (struct kgsl_device *device); int (*stop) (struct kgsl_device *device); int (*getproperty) (struct kgsl_device *device, enum kgsl_property_type type, void *value, @@ -105,7 +115,7 @@ struct kgsl_functable { uint32_t flags); int (*drawctxt_create) (struct kgsl_device *device, struct kgsl_pagetable *pagetable, struct kgsl_context *context, - uint32_t flags); + uint32_t *flags); void (*drawctxt_destroy) (struct kgsl_device *device, struct kgsl_context *context); long (*ioctl) (struct kgsl_device_private *dev_priv, @@ -127,10 +137,12 @@ struct kgsl_mh { int mpu_range; }; +typedef void (*kgsl_event_func)(struct kgsl_device *, void *, u32, u32, u32); + struct kgsl_event { struct kgsl_context *context; uint32_t timestamp; - void (*func)(struct kgsl_device *, void *, u32, u32); + kgsl_event_func func; void *priv; struct list_head list; void *owner; @@ -156,7 +168,9 @@ struct kgsl_device { struct completion hwaccess_gate; const struct kgsl_functable *ftbl; struct work_struct idle_check_ws; + struct work_struct hang_check_ws; struct timer_list idle_timer; + struct timer_list hang_timer; struct kgsl_pwrctrl pwrctrl; int open_count; @@ -174,6 +188,7 @@ struct kgsl_device { struct dentry *d_debugfs; struct idr context_idr; struct early_suspend display_off; + rwlock_t context_lock; void *snapshot; /* Pointer to the snapshot memory region */ int snapshot_maxsize; /* Max size of the snapshot region */ @@ -209,6 +224,8 @@ struct kgsl_device { /* Postmortem Control switches */ int pm_regs_enabled; int pm_ib_enabled; + + int reset_counter; /* Track how many GPU core resets have occured */ }; void kgsl_process_events(struct work_struct *work); @@ -220,6 +237,8 @@ void kgsl_check_fences(struct work_struct *work); .ft_gate = COMPLETION_INITIALIZER((_dev).ft_gate),\ .idle_check_ws = __WORK_INITIALIZER((_dev).idle_check_ws,\ kgsl_idle_check),\ + .hang_check_ws = __WORK_INITIALIZER((_dev).hang_check_ws,\ + kgsl_hang_check),\ .ts_expired_ws = __WORK_INITIALIZER((_dev).ts_expired_ws,\ kgsl_process_events),\ .context_idr = IDR_INIT((_dev).context_idr),\ @@ -259,11 +278,33 @@ struct kgsl_context { struct list_head events_list; }; +/** + * struct kgsl_process_private - Private structure for a KGSL process (across + * all devices) + * @priv: Internal flags, use KGSL_PROCESS_* values + * @pid: ID for the task owner of the process + * @mem_lock: Spinlock to protect the process memory lists + * @refcount: kref object for reference counting the process + * @process_private_mutex: Mutex to synchronize access to the process struct + * @mem_rb: RB tree node for the memory owned by this process + * @idr: Iterator for assigning IDs to memory allocations + * @pagetable: Pointer to the pagetable owned by this process + * @kobj: Pointer to a kobj for the sysfs directory for this process + * @debug_root: Pointer to the debugfs root for this process + * @stats: Memory allocation statistics for this process + */ struct kgsl_process_private { - unsigned int refcnt; + unsigned long priv; pid_t pid; spinlock_t mem_lock; + + /* General refcount for process private struct obj */ + struct kref refcount; + /* Mutex to synchronize access to each process_private struct obj */ + struct mutex process_private_mutex; + struct rb_root mem_rb; + struct idr mem_idr; struct kgsl_pagetable *pagetable; struct list_head list; struct kobject kobj; @@ -275,6 +316,14 @@ struct kgsl_process_private { } stats[KGSL_MEM_ENTRY_MAX]; }; +/** + * enum kgsl_process_priv_flags - Private flags for kgsl_process_private + * @KGSL_PROCESS_INIT: Set if the process structure has been set up + */ +enum kgsl_process_priv_flags { + KGSL_PROCESS_INIT = 0, +}; + struct kgsl_device_private { struct kgsl_device *device; struct kgsl_process_private *process_priv; @@ -287,6 +336,9 @@ struct kgsl_power_stats { struct kgsl_device *kgsl_get_device(int dev_idx); +int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts, + kgsl_event_func func, void *priv, void *owner); + static inline void kgsl_process_add_stats(struct kgsl_process_private *priv, unsigned int type, size_t size) { @@ -374,17 +426,7 @@ static inline int kgsl_create_device_workqueue(struct kgsl_device *device) return 0; } -static inline struct kgsl_context * -kgsl_find_context(struct kgsl_device_private *dev_priv, uint32_t id) -{ - struct kgsl_context *ctxt = - idr_find(&dev_priv->device->context_idr, id); - - /* Make sure that the context belongs to the current instance so - that other processes can't guess context IDs and mess things up */ - return (ctxt && ctxt->dev_priv == dev_priv) ? ctxt : NULL; -} int kgsl_check_timestamp(struct kgsl_device *device, struct kgsl_context *context, unsigned int timestamp); @@ -408,51 +450,155 @@ kgsl_device_get_drvdata(struct kgsl_device *dev) return pdev->dev.platform_data; } +void kgsl_context_destroy(struct kref *kref); + +int kgsl_memfree_find_entry(pid_t pid, unsigned long *gpuaddr, + unsigned long *size, unsigned int *flags); + /** - * kgsl_context_get - Get context reference count + * kgsl_context_put - Release context reference count * @context * - * Asynchronous code that holds a pointer to a context - * must hold a reference count on it. The kgsl device - * mutex must be held while the context reference count - * is changed. */ static inline void -kgsl_context_get(struct kgsl_context *context) +kgsl_context_put(struct kgsl_context *context) { - kref_get(&context->refcount); + if (context) + kref_put(&context->refcount, kgsl_context_destroy); } -void kgsl_context_destroy(struct kref *kref); +/** + * _kgsl_context_get() - lightweight function to just increment the ref count + * @context: Pointer to the KGSL context + * + * Get a reference to the specified KGSL context structure. This is a + * lightweight way to just increase the refcount on a known context rather then + * walking through kgsl_context_get and searching the iterator + */ +static inline int _kgsl_context_get(struct kgsl_context *context) +{ + int ret = 0; + if (context) + ret = kref_get_unless_zero(&context->refcount); + return ret; +} /** - * kgsl_context_put - Release context reference count - * @context + * kgsl_context_get - get a pointer to a KGSL context + * @devicex - Pointer to the KGSL device that owns the context + * @id - Context ID to return * + * Find the context associated with the given ID number, increase the reference + * count on it and return it. The caller must make sure that this call is + * paired with a kgsl_context_put. This function is for internal use because it + * doesn't validate the ownership of the context with the calling process - use + * kgsl_context_get_owner for that */ -static inline void -kgsl_context_put(struct kgsl_context *context) +static inline struct kgsl_context *kgsl_context_get(struct kgsl_device *device, + uint32_t id) { - kref_put(&context->refcount, kgsl_context_destroy); + int result = 0; + struct kgsl_context *context = NULL; + + read_lock(&device->context_lock); + + context = idr_find(&device->context_idr, id); + + result = _kgsl_context_get(context); + + read_unlock(&device->context_lock); + + if (!result) + return NULL; + return context; } /** - * kgsl_active_count_put - Decrease the device active count - * @device: Pointer to a KGSL device + * kgsl_context_get_owner - get a pointer to a KGSL context + * @dev_priv - Pointer to the owner of the requesting process + * @id - Context ID to return * - * Decrease the active count for the KGSL device and trigger the suspend_gate - * completion if it hits zero + * Find the context associated with the given ID number, increase the reference + * count on it and return it. The caller must make sure that this call is + * paired with a kgsl_context_put. This function validates that the context id + * given is owned by the dev_priv instancet that is passed in. see + * kgsl_context_get for the internal version that doesn't do the check */ -static inline void -kgsl_active_count_put(struct kgsl_device *device) +static inline struct kgsl_context *kgsl_context_get_owner( + struct kgsl_device_private *dev_priv, uint32_t id) +{ + struct kgsl_context *context; + + context = kgsl_context_get(dev_priv->device, id); + + /* Verify that the context belongs to the dev_priv instance */ + if (context && context->dev_priv != dev_priv) { + kgsl_context_put(context); + return NULL; + } + + return context; +} + +static inline void kgsl_context_cancel_events(struct kgsl_device *device, + struct kgsl_context *context) +{ + kgsl_signal_events(device, context, KGSL_EVENT_CANCELLED); +} + +/** + * kgsl_context_cancel_events_timestamp - cancel events for a given timestamp + * @device: Pointer to the KGSL device that owns the context + * @cotnext: Pointer to the context that owns the event or NULL for global + * @timestamp: Timestamp to cancel events for + * + * Cancel events pending for a specific timestamp + */ +static inline void kgsl_cancel_events_timestamp(struct kgsl_device *device, + struct kgsl_context *context, unsigned int timestamp) { - if (device->active_cnt == 1) - INIT_COMPLETION(device->suspend_gate); + kgsl_signal_event(device, context, timestamp, KGSL_EVENT_CANCELLED); +} - device->active_cnt--; - if (device->active_cnt == 0) - complete(&device->suspend_gate); +/** +* kgsl_process_private_get() - increment the refcount on a kgsl_process_private +* struct +* @process: Pointer to the KGSL process_private +* +* Returns 0 if the structure is invalid and a reference count could not be +* obtained, nonzero otherwise. +*/ +static inline int kgsl_process_private_get(struct kgsl_process_private *process) +{ + int ret = 0; + if (process != NULL) + ret = kref_get_unless_zero(&process->refcount); + return ret; } +void kgsl_process_private_put(struct kgsl_process_private *private); + + +struct kgsl_process_private *kgsl_process_private_find(pid_t pid); + +/** + * kgsl_sysfs_store() - parse a string from a sysfs store function + * @buf: Incoming string to parse + * @ptr: Pointer to an unsigned int to store the value + */ +static inline int kgsl_sysfs_store(const char *buf, unsigned int *ptr) +{ + unsigned int val; + int rc; + + rc = kstrtou32(buf, 0, &val); + if (rc) + return rc; + + if (ptr) + *ptr = val; + + return 0; +} #endif /* __KGSL_DEVICE_H */ diff --git a/drivers/gpu/msm/kgsl_drm.c b/drivers/gpu/msm/kgsl_drm.c index 2a5a5fac..f6dd6b3d 100644 --- a/drivers/gpu/msm/kgsl_drm.c +++ b/drivers/gpu/msm/kgsl_drm.c @@ -16,7 +16,6 @@ */ #include "drmP.h" #include "drm.h" -#include #include "kgsl.h" #include "kgsl_device.h" diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c index b1b11cc2..af803110 100644 --- a/drivers/gpu/msm/kgsl_events.c +++ b/drivers/gpu/msm/kgsl_events.c @@ -18,6 +18,12 @@ #include "kgsl_trace.h" +static inline struct list_head *_get_list_head(struct kgsl_device *device, + struct kgsl_context *context) +{ + return (context) ? &context->events : &device->events; +} + static void _add_event_to_list(struct list_head *head, struct kgsl_event *event) { struct list_head *n; @@ -36,30 +42,168 @@ static void _add_event_to_list(struct list_head *head, struct kgsl_event *event) list_add_tail(&event->list, head); } +static inline void _do_signal_event(struct kgsl_device *device, + struct kgsl_event *event, unsigned int timestamp, + unsigned int type) +{ + int id = event->context ? event->context->id : KGSL_MEMSTORE_GLOBAL; + + trace_kgsl_fire_event(id, timestamp, type, jiffies - event->created); + + if (event->func) + event->func(device, event->priv, id, timestamp, type); + + list_del(&event->list); + kgsl_context_put(event->context); + kfree(event); + + kgsl_active_count_put(device); +} + +static void _retire_events(struct kgsl_device *device, + struct list_head *head, unsigned int timestamp) +{ + struct kgsl_event *event, *tmp; + + list_for_each_entry_safe(event, tmp, head, list) { + if (timestamp_cmp(timestamp, event->timestamp) < 0) + break; + + _do_signal_event(device, event, event->timestamp, + KGSL_EVENT_TIMESTAMP_RETIRED); + } +} + +static struct kgsl_event *_find_event(struct kgsl_device *device, + struct list_head *head, unsigned int timestamp, + kgsl_event_func func, void *priv) +{ + struct kgsl_event *event, *tmp; + + list_for_each_entry_safe(event, tmp, head, list) { + if (timestamp == event->timestamp && func == event->func && + event->priv == priv) + return event; + } + + return NULL; +} + +/** + * _signal_event - send a signal to a specific event in the list + * @device - KGSL device + * @head - Pointer to the event list to process + * @timestamp - timestamp of the event to signal + * @cur - timestamp value to send to the callback + * @type - Signal ID to send to the callback + * + * Send the specified signal to the events in the list with the specified + * timestamp. The timestamp 'cur' is sent to the callback so it knows + * when the signal was delivered + */ +static void _signal_event(struct kgsl_device *device, + struct list_head *head, unsigned int timestamp, + unsigned int cur, unsigned int type) +{ + struct kgsl_event *event, *tmp; + + list_for_each_entry_safe(event, tmp, head, list) { + if (timestamp_cmp(timestamp, event->timestamp) == 0) + _do_signal_event(device, event, cur, type); + } +} + +/** + * _signal_events - send a signal to all the events in a list + * @device - KGSL device + * @head - Pointer to the event list to process + * @timestamp - Timestamp to pass to the events (this should be the current + * timestamp when the signal is sent) + * @type - Signal ID to send to the callback + * + * Send the specified signal to all the events in the list and destroy them + */ +static void _signal_events(struct kgsl_device *device, + struct list_head *head, uint32_t timestamp, + unsigned int type) +{ + struct kgsl_event *event, *tmp; + + list_for_each_entry_safe(event, tmp, head, list) + _do_signal_event(device, event, timestamp, type); + +} + +void kgsl_signal_event(struct kgsl_device *device, + struct kgsl_context *context, unsigned int timestamp, + unsigned int type) +{ + struct list_head *head = _get_list_head(device, context); + uint32_t cur; + + BUG_ON(!mutex_is_locked(&device->mutex)); + + cur = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED); + _signal_event(device, head, timestamp, cur, type); + + if (context && list_empty(&context->events)) + list_del_init(&context->events_list); +} +EXPORT_SYMBOL(kgsl_signal_event); + +void kgsl_signal_events(struct kgsl_device *device, + struct kgsl_context *context, unsigned int type) +{ + struct list_head *head = _get_list_head(device, context); + uint32_t cur; + + BUG_ON(!mutex_is_locked(&device->mutex)); + + /* + * Send the current timestamp to the callback so it knows when the + * signal occured + */ + + cur = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED); + + _signal_events(device, head, cur, type); + + /* + * Remove the context from the master list since we know everything on + * it has been removed + */ + + if (context) + list_del_init(&context->events_list); +} +EXPORT_SYMBOL(kgsl_signal_events); + /** * kgsl_add_event - Add a new timstamp event for the KGSL device * @device - KGSL device for the new event * @id - the context ID that the event should be added to * @ts - the timestamp to trigger the event on - * @cb - callback function to call when the timestamp expires + * @func - callback function to call when the timestamp expires * @priv - private data for the specific event type * @owner - driver instance that owns this event * * @returns - 0 on success or error code on failure */ int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts, - void (*cb)(struct kgsl_device *, void *, u32, u32), void *priv, - void *owner) + kgsl_event_func func, void *priv, void *owner) { + int ret; struct kgsl_event *event; unsigned int cur_ts; struct kgsl_context *context = NULL; - if (cb == NULL) + BUG_ON(!mutex_is_locked(&device->mutex)); + + if (func == NULL) return -EINVAL; if (id != KGSL_MEMSTORE_GLOBAL) { - context = idr_find(&device->context_idr, id); + context = kgsl_context_get(device, id); if (context == NULL) return -EINVAL; } @@ -73,28 +217,38 @@ int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts, */ if (timestamp_cmp(cur_ts, ts) >= 0) { - trace_kgsl_fire_event(id, ts, 0); - cb(device, priv, id, ts); + trace_kgsl_fire_event(id, cur_ts, ts, 0); + + func(device, priv, id, ts, KGSL_EVENT_TIMESTAMP_RETIRED); + kgsl_context_put(context); return 0; } event = kzalloc(sizeof(*event), GFP_KERNEL); - if (event == NULL) + if (event == NULL) { + kgsl_context_put(context); return -ENOMEM; + } + + /* + * Increase the active count on the device to avoid going into power + * saving modes while events are pending + */ + ret = kgsl_active_count_get(device); + if (ret < 0) { + kfree(event); + return ret; + } event->context = context; event->timestamp = ts; event->priv = priv; - event->func = cb; + event->func = func; event->owner = owner; event->created = jiffies; trace_kgsl_register_event(id, ts); - /* inc refcount to avoid race conditions in cleanup */ - if (context) - kgsl_context_get(context); - /* Add the event to either the owning context or the global list */ if (context) { @@ -112,144 +266,52 @@ int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts, } else _add_event_to_list(&device->events, event); - /* - * Increase the active count on the device to avoid going into power - * saving modes while events are pending - */ - - device->active_cnt++; - queue_work(device->work_queue, &device->ts_expired_ws); return 0; } EXPORT_SYMBOL(kgsl_add_event); -/** - * kgsl_cancel_events_ctxt - Cancel all events for a context - * @device - KGSL device for the events to cancel - * @context - context whose events we want to cancel - * - */ -void kgsl_cancel_events_ctxt(struct kgsl_device *device, - struct kgsl_context *context) -{ - struct kgsl_event *event, *event_tmp; - unsigned int id, cur; - - cur = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED); - id = context->id; - - /* - * Increment the refcount to avoid freeing the context while - * cancelling its events - */ - kgsl_context_get(context); - - /* Remove ourselves from the master pending list */ - list_del_init(&context->events_list); - - list_for_each_entry_safe(event, event_tmp, &context->events, list) { - /* - * "cancel" the events by calling their callback. - * Currently, events are used for lock and memory - * management, so if the process is dying the right - * thing to do is release or free. - * - * Send the current timestamp so the event knows how far the - * system got before the event was canceled - */ - list_del(&event->list); - - trace_kgsl_fire_event(id, cur, jiffies - event->created); - - if (event->func) - event->func(device, event->priv, id, cur); - - kgsl_context_put(context); - kfree(event); - - kgsl_active_count_put(device); - } - kgsl_context_put(context); -} - /** * kgsl_cancel_events - Cancel all generic events for a process * @device - KGSL device for the events to cancel * @owner - driver instance that owns the events to cancel * */ -void kgsl_cancel_events(struct kgsl_device *device, - void *owner) +void kgsl_cancel_events(struct kgsl_device *device, void *owner) { struct kgsl_event *event, *event_tmp; unsigned int cur; + BUG_ON(!mutex_is_locked(&device->mutex)); + cur = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED); list_for_each_entry_safe(event, event_tmp, &device->events, list) { if (event->owner != owner) continue; - /* - * "cancel" the events by calling their callback. - * Currently, events are used for lock and memory - * management, so if the process is dying the right - * thing to do is release or free. Send the current timestamp so - * the callback knows how far the GPU made it before things went - * explosion - */ - list_del(&event->list); - - trace_kgsl_fire_event(KGSL_MEMSTORE_GLOBAL, cur, - jiffies - event->created); - - if (event->func) - event->func(device, event->priv, KGSL_MEMSTORE_GLOBAL, - cur); - - if (event->context) - kgsl_context_put(event->context); - kfree(event); - - kgsl_active_count_put(device); + _do_signal_event(device, event, cur, KGSL_EVENT_CANCELLED); } } EXPORT_SYMBOL(kgsl_cancel_events); -static void _process_event_list(struct kgsl_device *device, - struct list_head *head, unsigned int timestamp) +void kgsl_cancel_event(struct kgsl_device *device, struct kgsl_context *context, + unsigned int timestamp, kgsl_event_func func, + void *priv) { - struct kgsl_event *event, *tmp; - unsigned int id; - - list_for_each_entry_safe(event, tmp, head, list) { - if (timestamp_cmp(timestamp, event->timestamp) < 0) - break; - - id = event->context ? event->context->id : KGSL_MEMSTORE_GLOBAL; - - /* - * Send the timestamp of the expired event, not the current - * timestamp. This prevents the event handlers from getting - * confused if they don't bother comparing the current timetamp - * to the timestamp they wanted - */ - list_del(&event->list); - - trace_kgsl_fire_event(id, event->timestamp, - jiffies - event->created); + struct kgsl_event *event; + struct list_head *head = _get_list_head(device, context); - if (event->func) - event->func(device, event->priv, id, event->timestamp); + event = _find_event(device, head, timestamp, func, priv); - if (event->context) - kgsl_context_put(event->context); - kfree(event); + if (event) { + unsigned int cur = kgsl_readtimestamp(device, context, + KGSL_TIMESTAMP_RETIRED); - kgsl_active_count_put(device); + _do_signal_event(device, event, cur, KGSL_EVENT_CANCELLED); } } +EXPORT_SYMBOL(kgsl_cancel_event); static inline int _mark_next_event(struct kgsl_device *device, struct list_head *head) @@ -278,7 +340,7 @@ static int kgsl_process_context_events(struct kgsl_device *device, unsigned int timestamp = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED); - _process_event_list(device, &context->events, timestamp); + _retire_events(device, &context->events, timestamp); /* * _mark_next event will return 1 as long as the next event @@ -309,7 +371,7 @@ void kgsl_process_events(struct work_struct *work) /* Process expired global events */ timestamp = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED); - _process_event_list(device, &device->events, timestamp); + _retire_events(device, &device->events, timestamp); _mark_next_event(device, &device->events); /* Now process all of the pending contexts */ @@ -320,7 +382,7 @@ void kgsl_process_events(struct work_struct *work) * Increment the refcount to make sure that the list_del_init * is called with a valid context's list */ - kgsl_context_get(context); + _kgsl_context_get(context); /* * If kgsl_timestamp_expired_context returns 0 then it no longer * has any pending events and can be removed from the list diff --git a/drivers/gpu/msm/kgsl_gpummu.c b/drivers/gpu/msm/kgsl_gpummu.c index 8f285053..48c5feae 100644 --- a/drivers/gpu/msm/kgsl_gpummu.c +++ b/drivers/gpu/msm/kgsl_gpummu.c @@ -598,7 +598,7 @@ kgsl_gpummu_unmap(void *mmu_specific_pt, { unsigned int numpages; unsigned int pte, ptefirst, ptelast, superpte; - unsigned int range = kgsl_sg_size(memdesc->sg, memdesc->sglen); + unsigned int range = memdesc->size; struct kgsl_gpummu_pt *gpummu_pt = mmu_specific_pt; /* All GPU addresses as assigned are page aligned, but some diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index 28542af4..ca00d160 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2014, The Linux Foundation. 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 and @@ -11,6 +11,7 @@ * */ #include +#include #include #include #include @@ -31,6 +32,7 @@ #include "adreno.h" #include "kgsl_trace.h" #include "z180.h" +#include "kgsl_cffdump.h" static struct kgsl_iommu_register_list kgsl_iommuv1_reg[KGSL_IOMMU_REG_MAX] = { @@ -40,6 +42,10 @@ static struct kgsl_iommu_register_list kgsl_iommuv1_reg[KGSL_IOMMU_REG_MAX] = { { 0x20, 0, 0 }, /* FSR */ { 0x800, 0, 0 }, /* TLBIALL */ { 0x820, 0, 0 }, /* RESUME */ + { 0x03C, 0, 0 }, /* TLBLKCR */ + { 0x818, 0, 0 }, /* V2PUR */ + { 0x2C, 0, 0 }, /* FSYNR0 */ + { 0x2C, 0, 0 }, /* FSYNR0 */ }; static struct kgsl_iommu_register_list kgsl_iommuv2_reg[KGSL_IOMMU_REG_MAX] = { @@ -48,11 +54,22 @@ static struct kgsl_iommu_register_list kgsl_iommuv2_reg[KGSL_IOMMU_REG_MAX] = { { 0x28, 0x00FFFFFF, 14 }, /* TTBR1 */ { 0x58, 0, 0 }, /* FSR */ { 0x618, 0, 0 }, /* TLBIALL */ - { 0x008, 0, 0 } /* RESUME */ + { 0x008, 0, 0 }, /* RESUME */ + { 0, 0, 0 }, /* TLBLKCR */ + { 0, 0, 0 }, /* V2PUR */ + { 0x68, 0, 0 }, /* FSYNR0 */ + { 0x6C, 0, 0 } /* FSYNR1 */ }; struct remote_iommu_petersons_spinlock kgsl_iommu_sync_lock_vars; +/* + * One page allocation for a guard region to protect against over-zealous + * GPU pre-fetch + */ + +static struct page *kgsl_guard_page; + static int get_iommu_unit(struct device *dev, struct kgsl_mmu **mmu_out, struct kgsl_iommu_unit **iommu_unit_out) { @@ -100,6 +117,156 @@ static struct kgsl_iommu_device *get_iommu_device(struct kgsl_iommu_unit *unit, return NULL; } +/* These functions help find the nearest allocated memory entries on either side + * of a faulting address. If we know the nearby allocations memory we can + * get a better determination of what we think should have been located in the + * faulting region + */ + +/* + * A local structure to make it easy to store the interesting bits for the + * memory entries on either side of the faulting address + */ + +struct _mem_entry { + unsigned int gpuaddr; + unsigned int size; + unsigned int flags; + unsigned int priv; + pid_t pid; +}; + +/* + * Find the closest alloated memory block with an smaller GPU address then the + * given address + */ + +static void _prev_entry(struct kgsl_process_private *priv, + unsigned int faultaddr, struct _mem_entry *ret) +{ + struct rb_node *node; + struct kgsl_mem_entry *entry; + + for (node = rb_first(&priv->mem_rb); node; ) { + entry = rb_entry(node, struct kgsl_mem_entry, node); + + if (entry->memdesc.gpuaddr > faultaddr) + break; + + /* + * If this is closer to the faulting address, then copy + * the entry + */ + + if (entry->memdesc.gpuaddr > ret->gpuaddr) { + ret->gpuaddr = entry->memdesc.gpuaddr; + ret->size = entry->memdesc.size; + ret->flags = entry->memdesc.flags; + ret->priv = entry->memdesc.priv; + ret->pid = priv->pid; + } + + node = rb_next(&entry->node); + } +} + +/* + * Find the closest alloated memory block with a greater starting GPU address + * then the given address + */ + +static void _next_entry(struct kgsl_process_private *priv, + unsigned int faultaddr, struct _mem_entry *ret) +{ + struct rb_node *node; + struct kgsl_mem_entry *entry; + + for (node = rb_last(&priv->mem_rb); node; ) { + entry = rb_entry(node, struct kgsl_mem_entry, node); + + if (entry->memdesc.gpuaddr < faultaddr) + break; + + /* + * If this is closer to the faulting address, then copy + * the entry + */ + + if (entry->memdesc.gpuaddr < ret->gpuaddr) { + ret->gpuaddr = entry->memdesc.gpuaddr; + ret->size = entry->memdesc.size; + ret->flags = entry->memdesc.flags; + ret->priv = entry->memdesc.priv; + ret->pid = priv->pid; + } + + node = rb_prev(&entry->node); + } +} + +static void _find_mem_entries(struct kgsl_mmu *mmu, unsigned int faultaddr, + unsigned int ptbase, struct _mem_entry *preventry, + struct _mem_entry *nextentry) +{ + struct kgsl_process_private *private; + int id = kgsl_mmu_get_ptname_from_ptbase(mmu, ptbase); + + memset(preventry, 0, sizeof(*preventry)); + memset(nextentry, 0, sizeof(*nextentry)); + + /* Set the maximum possible size as an initial value */ + nextentry->gpuaddr = 0xFFFFFFFF; + + mutex_lock(&kgsl_driver.process_mutex); + + list_for_each_entry(private, &kgsl_driver.process_list, list) { + + if (private->pagetable->name != id) + continue; + + spin_lock(&private->mem_lock); + _prev_entry(private, faultaddr, preventry); + _next_entry(private, faultaddr, nextentry); + spin_unlock(&private->mem_lock); + } + + mutex_unlock(&kgsl_driver.process_mutex); +} + +static void _print_entry(struct kgsl_device *device, struct _mem_entry *entry) +{ + char name[32]; + memset(name, 0, sizeof(name)); + + kgsl_get_memory_usage(name, sizeof(name) - 1, entry->flags); + + KGSL_LOG_DUMP(device, + "[%8.8X - %8.8X] %s (pid = %d) (%s)\n", + entry->gpuaddr, + entry->gpuaddr + entry->size, + entry->priv & KGSL_MEMDESC_GUARD_PAGE ? "(+guard)" : "", + entry->pid, name); +} + +static void _check_if_freed(struct kgsl_iommu_device *iommu_dev, + unsigned long addr, unsigned int pid) +{ + unsigned long gpuaddr = addr; + unsigned long size = 0; + unsigned int flags = 0; + char name[32]; + memset(name, 0, sizeof(name)); + + if (kgsl_memfree_find_entry(pid, &gpuaddr, &size, &flags)) { + kgsl_get_memory_usage(name, sizeof(name) - 1, flags); + KGSL_LOG_DUMP(iommu_dev->kgsldev, + "---- premature free ----\n"); + KGSL_LOG_DUMP(iommu_dev->kgsldev, + "[%8.8lX-%8.8lX] (%s) was already freed by pid %d\n", + gpuaddr, gpuaddr + size, name, pid); + } +} + static int kgsl_iommu_fault_handler(struct iommu_domain *domain, struct device *dev, unsigned long addr, int flags) { @@ -114,8 +281,11 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain, unsigned int no_page_fault_log = 0; unsigned int curr_context_id = 0; unsigned int curr_global_ts = 0; - static struct adreno_context *curr_context; - static struct kgsl_context *context; + struct kgsl_context *context; + unsigned int pid; + unsigned int fsynr0, fsynr1; + int write; + struct _mem_entry prev, next; ret = get_iommu_unit(dev, &mmu, &iommu_unit); if (ret) @@ -135,39 +305,85 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain, fsr = KGSL_IOMMU_GET_CTX_REG(iommu, iommu_unit, iommu_dev->ctx_id, FSR); + fsynr0 = KGSL_IOMMU_GET_CTX_REG(iommu, iommu_unit, + iommu_dev->ctx_id, FSYNR0); + fsynr1 = KGSL_IOMMU_GET_CTX_REG(iommu, iommu_unit, + iommu_dev->ctx_id, FSYNR1); + + if (!msm_soc_version_supports_iommu_v1()) + write = ((fsynr1 & (KGSL_IOMMU_FSYNR1_AWRITE_MASK << + KGSL_IOMMU_FSYNR1_AWRITE_SHIFT)) ? 1 : 0); + else + write = ((fsynr0 & (KGSL_IOMMU_V1_FSYNR0_WNR_MASK << + KGSL_IOMMU_V1_FSYNR0_WNR_SHIFT)) ? 1 : 0); if (adreno_dev->ft_pf_policy & KGSL_FT_PAGEFAULT_LOG_ONE_PER_PAGE) no_page_fault_log = kgsl_mmu_log_fault_addr(mmu, ptbase, addr); + pid = kgsl_mmu_get_ptname_from_ptbase(mmu, ptbase); if (!no_page_fault_log) { KGSL_MEM_CRIT(iommu_dev->kgsldev, - "GPU PAGE FAULT: addr = %lX pid = %d\n", - addr, kgsl_mmu_get_ptname_from_ptbase(mmu, ptbase)); - KGSL_MEM_CRIT(iommu_dev->kgsldev, "context = %d FSR = %X\n", - iommu_dev->ctx_id, fsr); + "GPU PAGE FAULT: addr = %lX pid = %d\n", addr, pid); + KGSL_MEM_CRIT(iommu_dev->kgsldev, + "context = %d FSR = %X FSYNR0 = %X FSYNR1 = %X(%s fault)\n", + iommu_dev->ctx_id, fsr, fsynr0, fsynr1, + write ? "write" : "read"); } + _check_if_freed(iommu_dev, addr, pid); + + KGSL_LOG_DUMP(iommu_dev->kgsldev, "---- nearby memory ----\n"); + + _find_mem_entries(mmu, addr, ptbase, &prev, &next); + + if (prev.gpuaddr) + _print_entry(iommu_dev->kgsldev, &prev); + else + KGSL_LOG_DUMP(iommu_dev->kgsldev, "*EMPTY*\n"); + + KGSL_LOG_DUMP(iommu_dev->kgsldev, " <- fault @ %8.8lX\n", addr); + + if (next.gpuaddr != 0xFFFFFFFF) + _print_entry(iommu_dev->kgsldev, &next); + else + KGSL_LOG_DUMP(iommu_dev->kgsldev, "*EMPTY*\n"); + mmu->fault = 1; iommu_dev->fault = 1; kgsl_sharedmem_readl(&device->memstore, &curr_context_id, KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context)); - context = idr_find(&device->context_idr, curr_context_id); - if (context != NULL) - curr_context = context->devctxt; + context = kgsl_context_get(device, curr_context_id); - kgsl_sharedmem_readl(&device->memstore, &curr_global_ts, - KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, eoptimestamp)); + if (context != NULL && (context->devctxt != NULL)) { + struct adreno_context *drawctxt = context->devctxt; + + ret = kgsl_sharedmem_readl(&device->memstore, &curr_global_ts, + KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, eoptimestamp)); + + if (ret < 0) { + KGSL_CORE_ERR("Invalid curr_global_ts = %d\n", curr_global_ts); + kgsl_context_put(context); + goto done; + } + + /* + * Store pagefault's timestamp in adreno context, + * this information will be used in GFT + */ + + if (drawctxt != NULL) { + drawctxt->pagefault = 1; + drawctxt->pagefault_ts = curr_global_ts; + } + + kgsl_context_put(context); + } - /* - * Store pagefault's timestamp and ib1 addr in context, - * this information is used in GFT - */ - curr_context->pagefault = 1; - curr_context->pagefault_ts = curr_global_ts; trace_kgsl_mmu_pagefault(iommu_dev->kgsldev, addr, - kgsl_mmu_get_ptname_from_ptbase(mmu, ptbase), 0); + kgsl_mmu_get_ptname_from_ptbase(mmu, ptbase), + write ? "write" : "read"); /* * We do not want the h/w to resume fetching data from an iommu unit @@ -224,7 +440,8 @@ static void kgsl_iommu_disable_clk(struct kgsl_mmu *mmu) * Return - void */ static void kgsl_iommu_clk_disable_event(struct kgsl_device *device, void *data, - unsigned int id, unsigned int ts) + unsigned int id, unsigned int ts, + u32 type) { struct kgsl_mmu *mmu = data; struct kgsl_iommu *iommu = mmu->priv; @@ -582,6 +799,46 @@ static int _get_iommu_ctxs(struct kgsl_mmu *mmu, return 0; } +/* + * kgsl_iommu_start_sync_lock - Initialize some variables during MMU start up + * for GPU CPU synchronization + * @mmu - Pointer to mmu device + * + * Return - 0 on success else error code + */ +static int kgsl_iommu_start_sync_lock(struct kgsl_mmu *mmu) +{ + struct kgsl_iommu *iommu = mmu->priv; + uint32_t lock_gpu_addr = 0; + + if (KGSL_DEVICE_3D0 != mmu->device->id || + !msm_soc_version_supports_iommu_v1() || + !kgsl_mmu_is_perprocess() || + iommu->sync_lock_vars) + return 0; + + if (!(mmu->flags & KGSL_MMU_FLAGS_IOMMU_SYNC)) { + KGSL_DRV_ERR(mmu->device, + "The GPU microcode does not support IOMMUv1 sync opcodes\n"); + return -ENXIO; + } + /* Store Lock variables GPU address */ + lock_gpu_addr = (iommu->sync_lock_desc.gpuaddr + + iommu->sync_lock_offset); + + kgsl_iommu_sync_lock_vars.flag[PROC_APPS] = (lock_gpu_addr + + (offsetof(struct remote_iommu_petersons_spinlock, + flag[PROC_APPS]))); + kgsl_iommu_sync_lock_vars.flag[PROC_GPU] = (lock_gpu_addr + + (offsetof(struct remote_iommu_petersons_spinlock, + flag[PROC_GPU]))); + kgsl_iommu_sync_lock_vars.turn = (lock_gpu_addr + + (offsetof(struct remote_iommu_petersons_spinlock, turn))); + + iommu->sync_lock_vars = &kgsl_iommu_sync_lock_vars; + + return 0; +} /* * kgsl_get_sync_lock - Init Sync Lock between GPU and CPU @@ -591,21 +848,31 @@ static int _get_iommu_ctxs(struct kgsl_mmu *mmu, */ static int kgsl_iommu_init_sync_lock(struct kgsl_mmu *mmu) { - struct kgsl_iommu *iommu = mmu->device->mmu.priv; + struct kgsl_iommu *iommu = mmu->priv; int status = 0; - struct kgsl_pagetable *pagetable = NULL; - uint32_t lock_gpu_addr = 0; uint32_t lock_phy_addr = 0; uint32_t page_offset = 0; - iommu->sync_lock_initialized = 0; + if (KGSL_DEVICE_3D0 != mmu->device->id || + !msm_soc_version_supports_iommu_v1() || + !kgsl_mmu_is_perprocess()) + return status; - if (!(mmu->flags & KGSL_MMU_FLAGS_IOMMU_SYNC)) { - KGSL_DRV_ERR(mmu->device, - "The GPU microcode does not support IOMMUv1 sync opcodes\n"); - return -ENXIO; + /* + * For 2D devices cpu side sync lock is required. For 3D device, + * since we only have a single 3D core and we always ensure that + * 3D core is idle while writing to IOMMU register using CPU this + * lock is not required + */ + if (KGSL_DEVICE_2D0 == mmu->device->id || + KGSL_DEVICE_2D1 == mmu->device->id) { + return status; } + /* Return if already initialized */ + if (iommu->sync_lock_initialized) + return status; + /* Get the physical address of the Lock variables */ lock_phy_addr = (msm_iommu_lock_initialize() - MSM_SHARED_RAM_BASE + msm_shared_ram_phys); @@ -620,6 +887,7 @@ static int kgsl_iommu_init_sync_lock(struct kgsl_mmu *mmu) page_offset = (lock_phy_addr & (PAGE_SIZE - 1)); lock_phy_addr = (lock_phy_addr & ~(PAGE_SIZE - 1)); iommu->sync_lock_desc.physaddr = (unsigned int)lock_phy_addr; + iommu->sync_lock_offset = page_offset; iommu->sync_lock_desc.size = PAGE_ALIGN(sizeof(kgsl_iommu_sync_lock_vars)); @@ -630,34 +898,6 @@ static int kgsl_iommu_init_sync_lock(struct kgsl_mmu *mmu) if (status) return status; - /* Map Lock variables to GPU pagetable */ - iommu->sync_lock_desc.priv |= KGSL_MEMDESC_GLOBAL; - - pagetable = mmu->priv_bank_table ? mmu->priv_bank_table : - mmu->defaultpagetable; - - status = kgsl_mmu_map(pagetable, &iommu->sync_lock_desc, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); - - if (status) { - kgsl_mmu_unmap(pagetable, &iommu->sync_lock_desc); - iommu->sync_lock_desc.priv &= ~KGSL_MEMDESC_GLOBAL; - return status; - } - - /* Store Lock variables GPU address */ - lock_gpu_addr = (iommu->sync_lock_desc.gpuaddr + page_offset); - - kgsl_iommu_sync_lock_vars.flag[PROC_APPS] = (lock_gpu_addr + - (offsetof(struct remote_iommu_petersons_spinlock, - flag[PROC_APPS]))); - kgsl_iommu_sync_lock_vars.flag[PROC_GPU] = (lock_gpu_addr + - (offsetof(struct remote_iommu_petersons_spinlock, - flag[PROC_GPU]))); - kgsl_iommu_sync_lock_vars.turn = (lock_gpu_addr + - (offsetof(struct remote_iommu_petersons_spinlock, turn))); - - iommu->sync_lock_vars = &kgsl_iommu_sync_lock_vars; /* Flag Sync Lock is Initialized */ iommu->sync_lock_initialized = 1; @@ -699,6 +939,10 @@ inline unsigned int kgsl_iommu_sync_lock(struct kgsl_mmu *mmu, *cmds++ = 0x1; *cmds++ = 0x1; + /* WAIT_REG_MEM turns back on protected mode - push it off */ + *cmds++ = cp_type3_packet(CP_SET_PROTECTED_MODE, 1); + *cmds++ = 0; + *cmds++ = cp_type3_packet(CP_MEM_WRITE, 2); *cmds++ = lock_vars->turn; *cmds++ = 0; @@ -713,11 +957,19 @@ inline unsigned int kgsl_iommu_sync_lock(struct kgsl_mmu *mmu, *cmds++ = 0x1; *cmds++ = 0x1; + /* WAIT_REG_MEM turns back on protected mode - push it off */ + *cmds++ = cp_type3_packet(CP_SET_PROTECTED_MODE, 1); + *cmds++ = 0; + *cmds++ = cp_type3_packet(CP_TEST_TWO_MEMS, 3); *cmds++ = lock_vars->flag[PROC_APPS]; *cmds++ = lock_vars->turn; *cmds++ = 0; + /* TEST_TWO_MEMS turns back on protected mode - push it off */ + *cmds++ = cp_type3_packet(CP_SET_PROTECTED_MODE, 1); + *cmds++ = 0; + cmds += adreno_add_idle_cmds(adreno_dev, cmds); return cmds - start; @@ -755,6 +1007,10 @@ inline unsigned int kgsl_iommu_sync_unlock(struct kgsl_mmu *mmu, *cmds++ = 0x1; *cmds++ = 0x1; + /* WAIT_REG_MEM turns back on protected mode - push it off */ + *cmds++ = cp_type3_packet(CP_SET_PROTECTED_MODE, 1); + *cmds++ = 0; + cmds += adreno_add_idle_cmds(adreno_dev, cmds); return cmds - start; @@ -917,6 +1173,76 @@ static void kgsl_iommu_setstate(struct kgsl_mmu *mmu, } } +/* + * kgsl_iommu_setup_regs - map iommu registers into a pagetable + * @mmu: Pointer to mmu structure + * @pt: the pagetable + * + * To do pagetable switches from the GPU command stream, the IOMMU + * registers need to be mapped into the GPU's pagetable. This function + * is used differently on different targets. On 8960, the registers + * are mapped into every pagetable during kgsl_setup_pt(). On + * all other targets, the registers are mapped only into the second + * context bank. + * + * Return - 0 on success else error code + */ +static int kgsl_iommu_setup_regs(struct kgsl_mmu *mmu, + struct kgsl_pagetable *pt) +{ + int status; + int i = 0; + struct kgsl_iommu *iommu = mmu->priv; + + if (!msm_soc_version_supports_iommu_v1()) + return 0; + + for (i = 0; i < iommu->unit_count; i++) { + status = kgsl_mmu_map_global(pt, + &(iommu->iommu_units[i].reg_map)); + if (status) + goto err; + } + + /* Map Lock variables to GPU pagetable */ + if (iommu->sync_lock_initialized) { + status = kgsl_mmu_map_global(pt, + &iommu->sync_lock_desc); + if (status) + goto err; + } + + return 0; +err: + for (i--; i >= 0; i--) + kgsl_mmu_unmap(pt, + &(iommu->iommu_units[i].reg_map)); + + return status; +} + +/* + * kgsl_iommu_cleanup_regs - unmap iommu registers from a pagetable + * @mmu: Pointer to mmu structure + * @pt: the pagetable + * + * Removes mappings created by kgsl_iommu_setup_regs(). + * + * Return - 0 on success else error code + */ +static void kgsl_iommu_cleanup_regs(struct kgsl_mmu *mmu, + struct kgsl_pagetable *pt) +{ + struct kgsl_iommu *iommu = mmu->priv; + int i; + for (i = 0; i < iommu->unit_count; i++) + kgsl_mmu_unmap(pt, &(iommu->iommu_units[i].reg_map)); + + if (iommu->sync_lock_desc.gpuaddr) + kgsl_mmu_unmap(pt, &iommu->sync_lock_desc); +} + + static int kgsl_iommu_init(struct kgsl_mmu *mmu) { /* @@ -942,6 +1268,10 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu) if (status) goto done; + status = kgsl_iommu_init_sync_lock(mmu); + if (status) + goto done; + iommu->iommu_reg_list = kgsl_iommuv1_reg; iommu->ctx_offset = KGSL_IOMMU_CTX_OFFSET_V1; @@ -959,6 +1289,24 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu) KGSL_IOMMU_SETSTATE_NOP_OFFSET, cp_nop_packet(1)); + if (cpu_is_msm8960()) { + /* + * 8960 doesn't have a second context bank, so the IOMMU + * registers must be mapped into every pagetable. + */ + iommu_ops.mmu_setup_pt = kgsl_iommu_setup_regs; + iommu_ops.mmu_cleanup_pt = kgsl_iommu_cleanup_regs; + } + + if (kgsl_guard_page == NULL) { + kgsl_guard_page = alloc_page(GFP_KERNEL | __GFP_ZERO | + __GFP_HIGHMEM); + if (kgsl_guard_page == NULL) { + status = -ENOMEM; + goto done; + } + } + dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n", __func__); done: @@ -981,9 +1329,6 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu) static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu) { int status = 0; - int i = 0; - struct kgsl_iommu *iommu = mmu->priv; - struct kgsl_pagetable *pagetable = NULL; /* If chip is not 8960 then we use the 2nd context bank for pagetable * switching on the 3D side for which a separate table is allocated */ @@ -994,6 +1339,9 @@ static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu) status = -ENOMEM; goto err; } + status = kgsl_iommu_setup_regs(mmu, mmu->priv_bank_table); + if (status) + goto err; } mmu->defaultpagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT); /* Return error if the default pagetable doesn't exist */ @@ -1001,31 +1349,10 @@ static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu) status = -ENOMEM; goto err; } - pagetable = mmu->priv_bank_table ? mmu->priv_bank_table : - mmu->defaultpagetable; - /* Map the IOMMU regsiters to only defaultpagetable */ - if (msm_soc_version_supports_iommu_v1()) { - for (i = 0; i < iommu->unit_count; i++) { - iommu->iommu_units[i].reg_map.priv |= - KGSL_MEMDESC_GLOBAL; - status = kgsl_mmu_map(pagetable, - &(iommu->iommu_units[i].reg_map), - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); - if (status) { - iommu->iommu_units[i].reg_map.priv &= - ~KGSL_MEMDESC_GLOBAL; - goto err; - } - } - } return status; err: - for (i--; i >= 0; i--) { - kgsl_mmu_unmap(pagetable, - &(iommu->iommu_units[i].reg_map)); - iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMDESC_GLOBAL; - } if (mmu->priv_bank_table) { + kgsl_iommu_cleanup_regs(mmu, mmu->priv_bank_table); kgsl_mmu_putpagetable(mmu->priv_bank_table); mmu->priv_bank_table = NULL; } @@ -1036,9 +1363,114 @@ static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu) return status; } -static int kgsl_iommu_start(struct kgsl_mmu *mmu) +/* + * kgsl_iommu_lock_rb_in_tlb - Allocates tlb entries and locks the + * virtual to physical address translation of ringbuffer for 3D + * device into tlb. + * @mmu - Pointer to mmu structure + * + * Return - void + */ +static void kgsl_iommu_lock_rb_in_tlb(struct kgsl_mmu *mmu) { struct kgsl_device *device = mmu->device; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_ringbuffer *rb; + struct kgsl_iommu *iommu = mmu->priv; + unsigned int num_tlb_entries; + unsigned int tlblkcr = 0; + unsigned int v2pxx = 0; + unsigned int vaddr = 0; + int i, j, k, l; + + if (!iommu->sync_lock_initialized) + return; + + rb = &adreno_dev->ringbuffer; + num_tlb_entries = rb->buffer_desc.size / PAGE_SIZE; + + for (i = 0; i < iommu->unit_count; i++) { + struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i]; + for (j = 0; j < iommu_unit->dev_count; j++) { + tlblkcr = 0; + if (cpu_is_msm8960()) + tlblkcr |= ((num_tlb_entries & + KGSL_IOMMU_TLBLKCR_FLOOR_MASK) << + KGSL_IOMMU_TLBLKCR_FLOOR_SHIFT); + else + tlblkcr |= (((num_tlb_entries * + iommu_unit->dev_count) & + KGSL_IOMMU_TLBLKCR_FLOOR_MASK) << + KGSL_IOMMU_TLBLKCR_FLOOR_SHIFT); + /* Do not invalidate locked entries on tlbiall flush */ + tlblkcr |= ((1 & KGSL_IOMMU_TLBLKCR_TLBIALLCFG_MASK) + << KGSL_IOMMU_TLBLKCR_TLBIALLCFG_SHIFT); + tlblkcr |= ((1 & KGSL_IOMMU_TLBLKCR_TLBIASIDCFG_MASK) + << KGSL_IOMMU_TLBLKCR_TLBIASIDCFG_SHIFT); + tlblkcr |= ((1 & KGSL_IOMMU_TLBLKCR_TLBIVAACFG_MASK) + << KGSL_IOMMU_TLBLKCR_TLBIVAACFG_SHIFT); + /* Enable tlb locking */ + tlblkcr |= ((1 & KGSL_IOMMU_TLBLKCR_LKE_MASK) + << KGSL_IOMMU_TLBLKCR_LKE_SHIFT); + KGSL_IOMMU_SET_CTX_REG(iommu, iommu_unit, + iommu_unit->dev[j].ctx_id, + TLBLKCR, tlblkcr); + } + for (j = 0; j < iommu_unit->dev_count; j++) { + /* skip locking entries for private bank on 8960 */ + if (cpu_is_msm8960() && KGSL_IOMMU_CONTEXT_PRIV == j) + continue; + /* Lock the ringbuffer virtual address into tlb */ + vaddr = rb->buffer_desc.gpuaddr; + for (k = 0; k < num_tlb_entries; k++) { + v2pxx = 0; + v2pxx |= (((k + j * num_tlb_entries) & + KGSL_IOMMU_V2PXX_INDEX_MASK) + << KGSL_IOMMU_V2PXX_INDEX_SHIFT); + v2pxx |= vaddr & (KGSL_IOMMU_V2PXX_VA_MASK << + KGSL_IOMMU_V2PXX_VA_SHIFT); + + KGSL_IOMMU_SET_CTX_REG(iommu, iommu_unit, + iommu_unit->dev[j].ctx_id, + V2PUR, v2pxx); + mb(); + vaddr += PAGE_SIZE; + for (l = 0; l < iommu_unit->dev_count; l++) { + tlblkcr = KGSL_IOMMU_GET_CTX_REG(iommu, + iommu_unit, + iommu_unit->dev[l].ctx_id, + TLBLKCR); + mb(); + tlblkcr &= + ~(KGSL_IOMMU_TLBLKCR_VICTIM_MASK + << KGSL_IOMMU_TLBLKCR_VICTIM_SHIFT); + tlblkcr |= (((k + 1 + + (j * num_tlb_entries)) & + KGSL_IOMMU_TLBLKCR_VICTIM_MASK) << + KGSL_IOMMU_TLBLKCR_VICTIM_SHIFT); + KGSL_IOMMU_SET_CTX_REG(iommu, + iommu_unit, + iommu_unit->dev[l].ctx_id, + TLBLKCR, tlblkcr); + } + } + } + for (j = 0; j < iommu_unit->dev_count; j++) { + tlblkcr = KGSL_IOMMU_GET_CTX_REG(iommu, iommu_unit, + iommu_unit->dev[j].ctx_id, + TLBLKCR); + mb(); + /* Disable tlb locking */ + tlblkcr &= ~(KGSL_IOMMU_TLBLKCR_LKE_MASK + << KGSL_IOMMU_TLBLKCR_LKE_SHIFT); + KGSL_IOMMU_SET_CTX_REG(iommu, iommu_unit, + iommu_unit->dev[j].ctx_id, TLBLKCR, tlblkcr); + } + } +} + +static int kgsl_iommu_start(struct kgsl_mmu *mmu) +{ int status; struct kgsl_iommu *iommu = mmu->priv; int i, j; @@ -1050,23 +1482,22 @@ static int kgsl_iommu_start(struct kgsl_mmu *mmu) status = kgsl_iommu_setup_defaultpagetable(mmu); if (status) return -ENOMEM; - - /* Initialize the sync lock between GPU and CPU */ - if (msm_soc_version_supports_iommu_v1() && - (device->id == KGSL_DEVICE_3D0)) - kgsl_iommu_init_sync_lock(mmu); } + status = kgsl_iommu_start_sync_lock(mmu); + if (status) + return status; + /* We use the GPU MMU to control access to IOMMU registers on 8960 with * a225, hence we still keep the MMU active on 8960 */ - if (cpu_is_msm8960()) { + if (cpu_is_msm8960() && KGSL_DEVICE_3D0 == mmu->device->id) { struct kgsl_mh *mh = &(mmu->device->mh); + BUG_ON(iommu->iommu_units[0].reg_map.gpuaddr != 0 && + mh->mpu_base > iommu->iommu_units[0].reg_map.gpuaddr); kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000001); + kgsl_regwrite(mmu->device, MH_MMU_MPU_END, - mh->mpu_base + - iommu->iommu_units[0].reg_map.gpuaddr); - } else { - kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000); + mh->mpu_base + mh->mpu_range); } mmu->hwpagetable = mmu->defaultpagetable; @@ -1091,6 +1522,7 @@ static int kgsl_iommu_start(struct kgsl_mmu *mmu) * changing pagetables we can use this lsb value of the pagetable w/o * having to read it again */ + msm_iommu_lock(); for (i = 0; i < iommu->unit_count; i++) { struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i]; for (j = 0; j < iommu_unit->dev_count; j++) { @@ -1101,6 +1533,13 @@ static int kgsl_iommu_start(struct kgsl_mmu *mmu) TTBR0)); } } + kgsl_iommu_lock_rb_in_tlb(mmu); + msm_iommu_unlock(); + + /* For complete CFF */ + kgsl_cffdump_setmem(mmu->setstate_memory.gpuaddr + + KGSL_IOMMU_SETSTATE_NOP_OFFSET, + cp_nop_packet(1), sizeof(unsigned int)); kgsl_iommu_disable_clk_on_ts(mmu, 0, false); mmu->flags |= KGSL_FLAGS_STARTED; @@ -1119,32 +1558,33 @@ kgsl_iommu_unmap(void *mmu_specific_pt, unsigned int *tlb_flags) { int ret; - unsigned int range = kgsl_sg_size(memdesc->sg, memdesc->sglen); + unsigned int range = memdesc->size; struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt; /* All GPU addresses as assigned are page aligned, but some functions purturb the gpuaddr with an offset, so apply the mask here to make sure we have the right address */ - unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK; + unsigned int gpuaddr = memdesc->gpuaddr & KGSL_IOMMU_ALIGN_MASK; if (range == 0 || gpuaddr == 0) return 0; + if (kgsl_memdesc_has_guard_page(memdesc)) + range += PAGE_SIZE; + ret = iommu_unmap_range(iommu_pt->domain, gpuaddr, range); if (ret) KGSL_CORE_ERR("iommu_unmap_range(%p, %x, %d) failed " "with err: %d\n", iommu_pt->domain, gpuaddr, range, ret); -#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE /* * Flushing only required if per process pagetables are used. With * global case, flushing will happen inside iommu_map function */ - if (!ret && msm_soc_version_supports_iommu_v1()) + if (!ret && kgsl_mmu_is_perprocess()) *tlb_flags = UINT_MAX; -#endif return 0; } @@ -1157,23 +1597,35 @@ kgsl_iommu_map(void *mmu_specific_pt, int ret; unsigned int iommu_virt_addr; struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt; - int size = kgsl_sg_size(memdesc->sg, memdesc->sglen); + int size = memdesc->size; BUG_ON(NULL == iommu_pt); - iommu_virt_addr = memdesc->gpuaddr; ret = iommu_map_range(iommu_pt->domain, iommu_virt_addr, memdesc->sg, - size, (IOMMU_READ | IOMMU_WRITE)); + size, protflags); if (ret) { - KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) " - "failed with err: %d\n", iommu_pt->domain, - iommu_virt_addr, memdesc->sg, size, - (IOMMU_READ | IOMMU_WRITE), ret); + KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %x) err: %d\n", + iommu_pt->domain, iommu_virt_addr, memdesc->sg, size, + protflags, ret); return ret; } - + if (kgsl_memdesc_has_guard_page(memdesc)) { + ret = iommu_map(iommu_pt->domain, iommu_virt_addr + size, + page_to_phys(kgsl_guard_page), PAGE_SIZE, + protflags & ~IOMMU_WRITE); + if (ret) { + KGSL_CORE_ERR("iommu_map(%p, %x, %x, %x) err: %d\n", + iommu_pt->domain, iommu_virt_addr + size, + page_to_phys(kgsl_guard_page), + protflags & ~IOMMU_WRITE, + ret); + /* cleanup the partial mapping */ + iommu_unmap_range(iommu_pt->domain, iommu_virt_addr, + size); + } + } return ret; } @@ -1186,7 +1638,6 @@ static void kgsl_iommu_stop(struct kgsl_mmu *mmu) * * call this with the global lock held */ - if (mmu->flags & KGSL_FLAGS_STARTED) { /* detach iommu attachment */ kgsl_detach_pagetable_iommu_domain(mmu); @@ -1201,10 +1652,12 @@ static void kgsl_iommu_stop(struct kgsl_mmu *mmu) for (j = 0; j < iommu_unit->dev_count; j++) { if (iommu_unit->dev[j].fault) { kgsl_iommu_enable_clk(mmu, j); + msm_iommu_lock(); KGSL_IOMMU_SET_CTX_REG(iommu, iommu_unit, iommu_unit->dev[j].ctx_id, RESUME, 1); + msm_iommu_unlock(); iommu_unit->dev[j].fault = 0; } } @@ -1222,24 +1675,35 @@ static int kgsl_iommu_close(struct kgsl_mmu *mmu) { struct kgsl_iommu *iommu = mmu->priv; int i; - for (i = 0; i < iommu->unit_count; i++) { - struct kgsl_pagetable *pagetable = (mmu->priv_bank_table ? - mmu->priv_bank_table : mmu->defaultpagetable); - if (iommu->iommu_units[i].reg_map.gpuaddr) - kgsl_mmu_unmap(pagetable, - &(iommu->iommu_units[i].reg_map)); - if (iommu->iommu_units[i].reg_map.hostptr) - iounmap(iommu->iommu_units[i].reg_map.hostptr); - kgsl_sg_free(iommu->iommu_units[i].reg_map.sg, - iommu->iommu_units[i].reg_map.sglen); - } - if (mmu->priv_bank_table) + if (mmu->priv_bank_table != NULL) { + kgsl_iommu_cleanup_regs(mmu, mmu->priv_bank_table); kgsl_mmu_putpagetable(mmu->priv_bank_table); - if (mmu->defaultpagetable) + } + + if (mmu->defaultpagetable != NULL) kgsl_mmu_putpagetable(mmu->defaultpagetable); + + for (i = 0; i < iommu->unit_count; i++) { + struct kgsl_memdesc *reg_map = &iommu->iommu_units[i].reg_map; + + if (reg_map->hostptr) + iounmap(reg_map->hostptr); + kgsl_sg_free(reg_map->sg, reg_map->sglen); + reg_map->priv &= ~KGSL_MEMDESC_GLOBAL; + } + /* clear IOMMU GPU CPU sync structures */ + kgsl_sg_free(iommu->sync_lock_desc.sg, iommu->sync_lock_desc.sglen); + memset(&iommu->sync_lock_desc, 0, sizeof(iommu->sync_lock_desc)); + iommu->sync_lock_vars = NULL; + kfree(iommu); + if (kgsl_guard_page != NULL) { + __free_page(kgsl_guard_page); + kgsl_guard_page = NULL; + } + return 0; } @@ -1382,6 +1846,9 @@ struct kgsl_mmu_ops iommu_ops = { .mmu_get_pt_base_addr = kgsl_iommu_get_pt_base_addr, .mmu_sync_lock = kgsl_iommu_sync_lock, .mmu_sync_unlock = kgsl_iommu_sync_unlock, + /* These callbacks will be set on some chipsets */ + .mmu_setup_pt = NULL, + .mmu_cleanup_pt = NULL, }; struct kgsl_mmu_pt_ops iommu_pt_ops = { diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h index 45077007..f3dd28cb 100644 --- a/drivers/gpu/msm/kgsl_iommu.h +++ b/drivers/gpu/msm/kgsl_iommu.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2013, The Linux Foundation. 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 and @@ -19,6 +19,33 @@ #define KGSL_IOMMU_CTX_OFFSET_V2 0x8000 #define KGSL_IOMMU_CTX_SHIFT 12 +/* TLBLKCR fields */ +#define KGSL_IOMMU_TLBLKCR_LKE_MASK 0x00000001 +#define KGSL_IOMMU_TLBLKCR_LKE_SHIFT 0 +#define KGSL_IOMMU_TLBLKCR_TLBIALLCFG_MASK 0x00000001 +#define KGSL_IOMMU_TLBLKCR_TLBIALLCFG_SHIFT 1 +#define KGSL_IOMMU_TLBLKCR_TLBIASIDCFG_MASK 0x00000001 +#define KGSL_IOMMU_TLBLKCR_TLBIASIDCFG_SHIFT 2 +#define KGSL_IOMMU_TLBLKCR_TLBIVAACFG_MASK 0x00000001 +#define KGSL_IOMMU_TLBLKCR_TLBIVAACFG_SHIFT 3 +#define KGSL_IOMMU_TLBLKCR_FLOOR_MASK 0x000000FF +#define KGSL_IOMMU_TLBLKCR_FLOOR_SHIFT 8 +#define KGSL_IOMMU_TLBLKCR_VICTIM_MASK 0x000000FF +#define KGSL_IOMMU_TLBLKCR_VICTIM_SHIFT 16 + +/* V2PXX fields */ +#define KGSL_IOMMU_V2PXX_INDEX_MASK 0x000000FF +#define KGSL_IOMMU_V2PXX_INDEX_SHIFT 0 +#define KGSL_IOMMU_V2PXX_VA_MASK 0x000FFFFF +#define KGSL_IOMMU_V2PXX_VA_SHIFT 12 + +/* FSYNR1 V0 fields */ +#define KGSL_IOMMU_FSYNR1_AWRITE_MASK 0x00000001 +#define KGSL_IOMMU_FSYNR1_AWRITE_SHIFT 8 +/* FSYNR0 V1 fields */ +#define KGSL_IOMMU_V1_FSYNR0_WNR_MASK 0x00000001 +#define KGSL_IOMMU_V1_FSYNR0_WNR_SHIFT 4 + enum kgsl_iommu_reg_map { KGSL_IOMMU_GLOBAL_BASE = 0, KGSL_IOMMU_CTX_TTBR0, @@ -26,6 +53,10 @@ enum kgsl_iommu_reg_map { KGSL_IOMMU_CTX_FSR, KGSL_IOMMU_CTX_TLBIALL, KGSL_IOMMU_CTX_RESUME, + KGSL_IOMMU_CTX_TLBLKCR, + KGSL_IOMMU_CTX_V2PUR, + KGSL_IOMMU_CTX_FSYNR0, + KGSL_IOMMU_CTX_FSYNR1, KGSL_IOMMU_REG_MAX }; @@ -124,6 +155,8 @@ struct kgsl_iommu_unit { * IOMMU registers * @sync_lock_desc: GPU Memory descriptor for the memory containing the * spinlocks + * @sync_lock_offset: The page offset within a page at which the sync + * variables are located * @sync_lock_initialized: True if the sync_lock feature is enabled */ struct kgsl_iommu { @@ -136,6 +169,7 @@ struct kgsl_iommu { struct kgsl_iommu_register_list *iommu_reg_list; struct remote_iommu_petersons_spinlock *sync_lock_vars; struct kgsl_memdesc sync_lock_desc; + unsigned int sync_lock_offset; bool sync_lock_initialized; }; diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index f11511fc..c96a3609 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2014, The Linux Foundation. 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 and @@ -27,9 +27,6 @@ #include "kgsl_sharedmem.h" #include "adreno.h" -#define KGSL_MMU_ALIGN_SHIFT 13 -#define KGSL_MMU_ALIGN_MASK (~((1 << KGSL_MMU_ALIGN_SHIFT) - 1)) - static enum kgsl_mmutype kgsl_mmu_type; static void pagetable_remove_sysfs_objects(struct kgsl_pagetable *pagetable); @@ -37,17 +34,18 @@ static void pagetable_remove_sysfs_objects(struct kgsl_pagetable *pagetable); static int kgsl_cleanup_pt(struct kgsl_pagetable *pt) { int i; - /* For IOMMU only unmap the global structures to global pt */ - if ((KGSL_MMU_TYPE_NONE != kgsl_mmu_type) && - (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) && - (KGSL_MMU_GLOBAL_PT != pt->name) && - (KGSL_MMU_PRIV_BANK_TABLE_NAME != pt->name)) - return 0; + struct kgsl_device *device; + for (i = 0; i < KGSL_DEVICE_MAX; i++) { - struct kgsl_device *device = kgsl_driver.devp[i]; + device = kgsl_driver.devp[i]; if (device) device->ftbl->cleanup_pt(device, pt); } + /* Only the 3d device needs mmu specific pt entries */ + device = kgsl_driver.devp[KGSL_DEVICE_3D0]; + if (device->mmu.mmu_ops->mmu_cleanup_pt != NULL) + device->mmu.mmu_ops->mmu_cleanup_pt(&device->mmu, pt); + return 0; } @@ -56,21 +54,25 @@ static int kgsl_setup_pt(struct kgsl_pagetable *pt) { int i = 0; int status = 0; + struct kgsl_device *device; - /* For IOMMU only map the global structures to global pt */ - if ((KGSL_MMU_TYPE_NONE != kgsl_mmu_type) && - (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) && - (KGSL_MMU_GLOBAL_PT != pt->name) && - (KGSL_MMU_PRIV_BANK_TABLE_NAME != pt->name)) - return 0; for (i = 0; i < KGSL_DEVICE_MAX; i++) { - struct kgsl_device *device = kgsl_driver.devp[i]; + device = kgsl_driver.devp[i]; if (device) { status = device->ftbl->setup_pt(device, pt); if (status) goto error_pt; } } + /* Only the 3d device needs mmu specific pt entries */ + device = kgsl_driver.devp[KGSL_DEVICE_3D0]; + if (device->mmu.mmu_ops->mmu_setup_pt != NULL) { + status = device->mmu.mmu_ops->mmu_setup_pt(&device->mmu, pt); + if (status) { + i = KGSL_DEVICE_MAX - 1; + goto error_pt; + } + } return status; error_pt: while (i >= 0) { @@ -82,16 +84,8 @@ static int kgsl_setup_pt(struct kgsl_pagetable *pt) return status; } -static void kgsl_destroy_pagetable(struct kref *kref) +static void _kgsl_destroy_pagetable(struct kgsl_pagetable *pagetable) { - struct kgsl_pagetable *pagetable = container_of(kref, - struct kgsl_pagetable, refcount); - unsigned long flags; - - spin_lock_irqsave(&kgsl_driver.ptlock, flags); - list_del(&pagetable->list); - spin_unlock_irqrestore(&kgsl_driver.ptlock, flags); - pagetable_remove_sysfs_objects(pagetable); kgsl_cleanup_pt(pagetable); @@ -106,6 +100,29 @@ static void kgsl_destroy_pagetable(struct kref *kref) kfree(pagetable); } +static void kgsl_destroy_pagetable(struct kref *kref) +{ + struct kgsl_pagetable *pagetable = container_of(kref, + struct kgsl_pagetable, refcount); + unsigned long flags; + + spin_lock_irqsave(&kgsl_driver.ptlock, flags); + list_del(&pagetable->list); + spin_unlock_irqrestore(&kgsl_driver.ptlock, flags); + + _kgsl_destroy_pagetable(pagetable); +} + +static void kgsl_destroy_pagetable_locked(struct kref *kref) +{ + struct kgsl_pagetable *pagetable = container_of(kref, + struct kgsl_pagetable, refcount); + + list_del(&pagetable->list); + + _kgsl_destroy_pagetable(pagetable); +} + static inline void kgsl_put_pagetable(struct kgsl_pagetable *pagetable) { if (pagetable) @@ -120,10 +137,12 @@ kgsl_get_pagetable(unsigned long name) spin_lock_irqsave(&kgsl_driver.ptlock, flags); list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) { - if (pt->name == name) { - ret = pt; - kref_get(&ret->refcount); - break; + if (kref_get_unless_zero(&pt->refcount)) { + if (pt->name == name) { + ret = pt; + break; + } + kref_put(&pt->refcount, kgsl_destroy_pagetable_locked); } } @@ -134,12 +153,12 @@ kgsl_get_pagetable(unsigned long name) static struct kgsl_pagetable * _get_pt_from_kobj(struct kobject *kobj) { - unsigned long ptname; + unsigned int ptname; if (!kobj) return NULL; - if (sscanf(kobj->name, "%ld", &ptname) != 1) + if (kstrtou32(kobj->name, 0, &ptname)) return NULL; return kgsl_get_pagetable(ptname); @@ -310,22 +329,6 @@ pagetable_add_sysfs_objects(struct kgsl_pagetable *pagetable) return ret; } -unsigned int kgsl_mmu_get_ptsize(void) -{ - /* - * For IOMMU, we could do up to 4G virtual range if we wanted to, but - * it makes more sense to return a smaller range and leave the rest of - * the virtual range for future improvements - */ - - if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type) - return CONFIG_MSM_KGSL_PAGE_TABLE_SIZE; - else if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) - return SZ_2G - KGSL_PAGETABLE_BASE; - else - return 0; -} - int kgsl_mmu_get_ptname_from_ptbase(struct kgsl_mmu *mmu, unsigned int pt_base) { @@ -336,9 +339,14 @@ kgsl_mmu_get_ptname_from_ptbase(struct kgsl_mmu *mmu, unsigned int pt_base) return KGSL_MMU_GLOBAL_PT; spin_lock(&kgsl_driver.ptlock); list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) { - if (mmu->mmu_ops->mmu_pt_equal(mmu, pt, pt_base)) { - ptid = (int) pt->name; - break; + if (kref_get_unless_zero(&pt->refcount)) { + if (mmu->mmu_ops->mmu_pt_equal(mmu, pt, pt_base)) { + ptid = (int) pt->name; + kref_put(&pt->refcount, + kgsl_destroy_pagetable_locked); + break; + } + kref_put(&pt->refcount, kgsl_destroy_pagetable_locked); } } spin_unlock(&kgsl_driver.ptlock); @@ -358,16 +366,23 @@ kgsl_mmu_log_fault_addr(struct kgsl_mmu *mmu, unsigned int pt_base, return KGSL_MMU_GLOBAL_PT; spin_lock(&kgsl_driver.ptlock); list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) { - if (mmu->mmu_ops->mmu_pt_equal(mmu, pt, pt_base)) { - if ((addr & ~(PAGE_SIZE-1)) == pt->fault_addr) { - ret = 1; - break; - } else { - pt->fault_addr = (addr & ~(PAGE_SIZE-1)); - ret = 0; - break; + if (kref_get_unless_zero(&pt->refcount)) { + if (mmu->mmu_ops->mmu_pt_equal(mmu, pt, pt_base)) { + if ((addr & ~(PAGE_SIZE-1)) == pt->fault_addr) { + ret = 1; + kref_put(&pt->refcount, + kgsl_destroy_pagetable_locked); + break; + } else { + pt->fault_addr = + (addr & ~(PAGE_SIZE-1)); + ret = 0; + kref_put(&pt->refcount, + kgsl_destroy_pagetable_locked); + break; + } } - + kref_put(&pt->refcount, kgsl_destroy_pagetable_locked); } } spin_unlock(&kgsl_driver.ptlock); @@ -385,6 +400,10 @@ int kgsl_mmu_init(struct kgsl_device *device) status = kgsl_allocate_contiguous(&mmu->setstate_memory, PAGE_SIZE); if (status) return status; + + /* Mark the setstate memory as read only */ + mmu->setstate_memory.flags |= KGSL_MEMFLAGS_GPUREADONLY; + kgsl_sharedmem_set(&mmu->setstate_memory, 0, 0, mmu->setstate_memory.size); @@ -490,10 +509,10 @@ static struct kgsl_pagetable *kgsl_mmu_createpagetableobject( if ((KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) && ((KGSL_MMU_GLOBAL_PT == name) || (KGSL_MMU_PRIV_BANK_TABLE_NAME == name))) { - pagetable->kgsl_pool = gen_pool_create(PAGE_SHIFT, -1); + pagetable->kgsl_pool = gen_pool_create(ilog2(SZ_8K), -1); if (pagetable->kgsl_pool == NULL) { KGSL_CORE_ERR("gen_pool_create(%d) failed\n", - KGSL_MMU_ALIGN_SHIFT); + ilog2(SZ_8K)); goto err_alloc; } if (gen_pool_add(pagetable->kgsl_pool, @@ -511,7 +530,7 @@ static struct kgsl_pagetable *kgsl_mmu_createpagetableobject( goto err_kgsl_pool; } - if (gen_pool_add(pagetable->pool, KGSL_PAGETABLE_BASE, + if (gen_pool_add(pagetable->pool, kgsl_mmu_get_base_addr(), ptsize, -1)) { KGSL_CORE_ERR("gen_pool_add failed\n"); goto err_pool; @@ -559,12 +578,9 @@ struct kgsl_pagetable *kgsl_mmu_getpagetable(unsigned long name) if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type) return (void *)(-1); -#ifndef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE + if (!kgsl_mmu_is_perprocess()) + name = KGSL_MMU_GLOBAL_PT; -#endif - /* We presently do not support per-process for IOMMU-v2 */ - if (!msm_soc_version_supports_iommu_v1()) - name = KGSL_MMU_GLOBAL_PT; pt = kgsl_get_pagetable(name); @@ -625,23 +641,20 @@ void kgsl_mh_start(struct kgsl_device *device) * kgsl_pwrctrl_irq() is called */ } +EXPORT_SYMBOL(kgsl_mh_start); -static inline struct gen_pool * -_get_pool(struct kgsl_pagetable *pagetable, unsigned int flags) -{ - if (pagetable->kgsl_pool && - (KGSL_MEMDESC_GLOBAL & flags)) - return pagetable->kgsl_pool; - return pagetable->pool; -} - +/** + * kgsl_mmu_get_gpuaddr - Assign a memdesc with a gpuadddr from the gen pool + * @pagetable - pagetable whose pool is to be used + * @memdesc - memdesc to which gpuaddr is assigned + * + * returns - 0 on success else error code + */ int -kgsl_mmu_map(struct kgsl_pagetable *pagetable, - struct kgsl_memdesc *memdesc, - unsigned int protflags) +kgsl_mmu_get_gpuaddr(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc) { - int ret; - struct gen_pool *pool; + struct gen_pool *pool = NULL; int size; int page_align = ilog2(PAGE_SIZE); @@ -663,31 +676,80 @@ kgsl_mmu_map(struct kgsl_pagetable *pagetable, } } - size = kgsl_sg_size(memdesc->sg, memdesc->sglen); - - /* Allocate from kgsl pool if it exists for global mappings */ - pool = _get_pool(pagetable, memdesc->priv); - - /* Allocate aligned virtual addresses for iommu. This allows - * more efficient pagetable entries if the physical memory - * is also aligned. Don't do this for GPUMMU, because - * the address space is so small. - */ - if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype() && - kgsl_memdesc_get_align(memdesc) > 0) - page_align = kgsl_memdesc_get_align(memdesc); - - memdesc->gpuaddr = gen_pool_alloc_aligned(pool, size, page_align); - if (memdesc->gpuaddr == 0) { - KGSL_CORE_ERR("gen_pool_alloc(%d) failed from pool: %s\n", - size, - (pool == pagetable->kgsl_pool) ? - "kgsl_pool" : "general_pool"); - KGSL_CORE_ERR(" [%d] allocated=%d, entries=%d\n", - pagetable->name, pagetable->stats.mapped, - pagetable->stats.entries); - return -ENOMEM; + /* Add space for the guard page when allocating the mmu VA. */ + size = memdesc->size; + if (kgsl_memdesc_has_guard_page(memdesc)) + size += PAGE_SIZE; + + pool = pagetable->pool; + + if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) { + /* Allocate aligned virtual addresses for iommu. This allows + * more efficient pagetable entries if the physical memory + * is also aligned. Don't do this for GPUMMU, because + * the address space is so small. + */ + if (kgsl_memdesc_get_align(memdesc) > 0) + page_align = kgsl_memdesc_get_align(memdesc); + if (kgsl_memdesc_is_global(memdesc)) { + /* + * Only the default pagetable has a kgsl_pool, and + * it is responsible for creating the mapping for + * each global buffer. The mapping will be reused + * in all other pagetables and it must already exist + * when we're creating other pagetables which do not + * have a kgsl_pool. + */ + pool = pagetable->kgsl_pool; + if (pool == NULL && memdesc->gpuaddr == 0) { + KGSL_CORE_ERR( + "No address for global mapping into pt %d\n", + pagetable->name); + return -EINVAL; + } + } else if (kgsl_memdesc_use_cpu_map(memdesc)) { + if (memdesc->gpuaddr == 0) + return -EINVAL; + pool = NULL; + } } + if (pool) { + memdesc->gpuaddr = gen_pool_alloc_aligned(pool, size, + page_align); + if (memdesc->gpuaddr == 0) { + KGSL_CORE_ERR("gen_pool_alloc(%d) failed, pool: %s\n", + size, + (pool == pagetable->kgsl_pool) ? + "kgsl_pool" : "general_pool"); + KGSL_CORE_ERR(" [%d] allocated=%d, entries=%d\n", + pagetable->name, + pagetable->stats.mapped, + pagetable->stats.entries); + return -ENOMEM; + } + } + return 0; +} +EXPORT_SYMBOL(kgsl_mmu_get_gpuaddr); + +int +kgsl_mmu_map(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc) +{ + int ret = 0; + int size; + unsigned int protflags = kgsl_memdesc_protflags(memdesc); + + if (!memdesc->gpuaddr) + return -EINVAL; + /* Only global mappings should be mapped multiple times */ + if (!kgsl_memdesc_is_global(memdesc) && + (KGSL_MEMDESC_MAPPED & memdesc->priv)) + return -EINVAL; + /* Add space for the guard page when allocating the mmu VA. */ + size = memdesc->size; + if (kgsl_memdesc_has_guard_page(memdesc)) + size += PAGE_SIZE; if (KGSL_MMU_TYPE_IOMMU != kgsl_mmu_get_mmutype()) spin_lock(&pagetable->lock); @@ -697,7 +759,7 @@ kgsl_mmu_map(struct kgsl_pagetable *pagetable, spin_lock(&pagetable->lock); if (ret) - goto err_free_gpuaddr; + goto done; /* Keep track of the statistics for the sysfs files */ @@ -708,35 +770,81 @@ kgsl_mmu_map(struct kgsl_pagetable *pagetable, pagetable->stats.max_mapped); spin_unlock(&pagetable->lock); + memdesc->priv |= KGSL_MEMDESC_MAPPED; return 0; -err_free_gpuaddr: +done: spin_unlock(&pagetable->lock); - gen_pool_free(pool, memdesc->gpuaddr, size); - memdesc->gpuaddr = 0; return ret; } EXPORT_SYMBOL(kgsl_mmu_map); +/** + * kgsl_mmu_put_gpuaddr - Free a gpuaddress from memory pool + * @pagetable - pagetable whose pool memory is freed from + * @memdesc - memdesc whose gpuaddress is freed + * + * returns - 0 on success else error code + */ int -kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, - struct kgsl_memdesc *memdesc) +kgsl_mmu_put_gpuaddr(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc) { struct gen_pool *pool; int size; - unsigned int start_addr = 0; - unsigned int end_addr = 0; if (memdesc->size == 0 || memdesc->gpuaddr == 0) return 0; - if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) { + if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) + goto done; + + /* Add space for the guard page when freeing the mmu VA. */ + size = memdesc->size; + if (kgsl_memdesc_has_guard_page(memdesc)) + size += PAGE_SIZE; + + pool = pagetable->pool; + + if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) { + if (kgsl_memdesc_is_global(memdesc)) + pool = pagetable->kgsl_pool; + else if (kgsl_memdesc_use_cpu_map(memdesc)) + pool = NULL; + } + if (pool) + gen_pool_free(pool, memdesc->gpuaddr, size); + /* + * Don't clear the gpuaddr on global mappings because they + * may be in use by other pagetables + */ +done: + if (!kgsl_memdesc_is_global(memdesc)) memdesc->gpuaddr = 0; + return 0; +} +EXPORT_SYMBOL(kgsl_mmu_put_gpuaddr); + +int +kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc) +{ + int size; + unsigned int start_addr = 0; + unsigned int end_addr = 0; + + if (memdesc->size == 0 || memdesc->gpuaddr == 0 || + !(KGSL_MEMDESC_MAPPED & memdesc->priv)) + return -EINVAL; + + if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) return 0; - } - size = kgsl_sg_size(memdesc->sg, memdesc->sglen); + /* Add space for the guard page when freeing the mmu VA. */ + size = memdesc->size; + if (kgsl_memdesc_has_guard_page(memdesc)) + size += PAGE_SIZE; start_addr = memdesc->gpuaddr; end_addr = (memdesc->gpuaddr + size); @@ -758,22 +866,14 @@ kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, pagetable->stats.mapped -= size; spin_unlock(&pagetable->lock); - - pool = _get_pool(pagetable, memdesc->priv); - gen_pool_free(pool, memdesc->gpuaddr, size); - - /* - * Don't clear the gpuaddr on global mappings because they - * may be in use by other pagetables - */ - if (!(memdesc->priv & KGSL_MEMDESC_GLOBAL)) - memdesc->gpuaddr = 0; + if (!kgsl_memdesc_is_global(memdesc)) + memdesc->priv &= ~KGSL_MEMDESC_MAPPED; return 0; } EXPORT_SYMBOL(kgsl_mmu_unmap); int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable, - struct kgsl_memdesc *memdesc, unsigned int protflags) + struct kgsl_memdesc *memdesc) { int result = -EINVAL; unsigned int gpuaddr = 0; @@ -785,13 +885,15 @@ int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable, /* Not all global mappings are needed for all MMU types */ if (!memdesc->size) return 0; - gpuaddr = memdesc->gpuaddr; memdesc->priv |= KGSL_MEMDESC_GLOBAL; - result = kgsl_mmu_map(pagetable, memdesc, protflags); + result = kgsl_mmu_get_gpuaddr(pagetable, memdesc); if (result) goto error; + result = kgsl_mmu_map(pagetable, memdesc); + if (result) + goto error_put_gpuaddr; /*global mappings must have the same gpu address in all pagetables*/ if (gpuaddr && gpuaddr != memdesc->gpuaddr) { @@ -803,6 +905,8 @@ int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable, return result; error_unmap: kgsl_mmu_unmap(pagetable, memdesc); +error_put_gpuaddr: + kgsl_mmu_put_gpuaddr(pagetable, memdesc); error: return result; } @@ -894,8 +998,13 @@ int kgsl_mmu_gpuaddr_in_range(unsigned int gpuaddr) { if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type) return 1; - return ((gpuaddr >= KGSL_PAGETABLE_BASE) && - (gpuaddr < (KGSL_PAGETABLE_BASE + kgsl_mmu_get_ptsize()))); + if (gpuaddr >= kgsl_mmu_get_base_addr() && + gpuaddr < kgsl_mmu_get_base_addr() + kgsl_mmu_get_ptsize()) + return 1; + if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU + && kgsl_mmu_is_perprocess()) + return (gpuaddr > 0 && gpuaddr < TASK_SIZE); + return 0; } EXPORT_SYMBOL(kgsl_mmu_gpuaddr_in_range); diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h index 377f3423..f2b476a5 100644 --- a/drivers/gpu/msm/kgsl_mmu.h +++ b/drivers/gpu/msm/kgsl_mmu.h @@ -13,13 +13,15 @@ #ifndef __KGSL_MMU_H #define __KGSL_MMU_H +#include + /* - * These defines control the split between ttbr1 and ttbr0 pagetables of IOMMU - * and what ranges of memory we map to them + * These defines control the address range for allocations that + * are mapped into all pagetables. */ -#define KGSL_IOMMU_GLOBAL_MEM_BASE 0xC0000000 +#define KGSL_IOMMU_GLOBAL_MEM_BASE 0xf8000000 #define KGSL_IOMMU_GLOBAL_MEM_SIZE SZ_4M -#define KGSL_IOMMU_TTBR1_SPLIT 2 +#define KGSL_IOMMU_ALIGN_MASK (~((1 << PAGE_SHIFT) - 1)) #define KGSL_MMU_ALIGN_SHIFT 13 #define KGSL_MMU_ALIGN_MASK (~((1 << KGSL_MMU_ALIGN_SHIFT) - 1)) @@ -155,6 +157,10 @@ struct kgsl_mmu_ops { unsigned int (*mmu_sync_unlock) (struct kgsl_mmu *mmu, unsigned int *cmds); + int (*mmu_setup_pt) (struct kgsl_mmu *mmu, + struct kgsl_pagetable *pt); + void (*mmu_cleanup_pt) (struct kgsl_mmu *mmu, + struct kgsl_pagetable *pt); }; struct kgsl_mmu_pt_ops { @@ -200,12 +206,15 @@ int kgsl_mmu_init(struct kgsl_device *device); int kgsl_mmu_start(struct kgsl_device *device); int kgsl_mmu_close(struct kgsl_device *device); int kgsl_mmu_map(struct kgsl_pagetable *pagetable, - struct kgsl_memdesc *memdesc, - unsigned int protflags); + struct kgsl_memdesc *memdesc); +int kgsl_mmu_get_gpuaddr(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc); int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable, - struct kgsl_memdesc *memdesc, unsigned int protflags); + struct kgsl_memdesc *memdesc); int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc); +int kgsl_mmu_put_gpuaddr(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc); unsigned int kgsl_virtaddr_to_physaddr(void *virtaddr); void kgsl_setstate(struct kgsl_mmu *mmu, unsigned int context_id, uint32_t flags); @@ -220,7 +229,6 @@ void *kgsl_mmu_ptpool_init(int entries); int kgsl_mmu_enabled(void); void kgsl_mmu_set_mmutype(char *mmutype); enum kgsl_mmutype kgsl_mmu_get_mmutype(void); -unsigned int kgsl_mmu_get_ptsize(void); int kgsl_mmu_gpuaddr_in_range(unsigned int gpuaddr); /* @@ -352,4 +360,78 @@ static inline int kgsl_mmu_sync_unlock(struct kgsl_mmu *mmu, return 0; } +/* + * kgsl_mmu_is_perprocess() - Runtime check for per-process + * pagetables. + * + * Returns non-zero if per-process pagetables are enabled, + * 0 if not. + */ +#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE +static inline int kgsl_mmu_is_perprocess(void) +{ + + /* We presently do not support per-process for IOMMU-v2 */ + return (kgsl_mmu_get_mmutype() != KGSL_MMU_TYPE_IOMMU) + || msm_soc_version_supports_iommu_v1(); +} +#else +static inline int kgsl_mmu_is_perprocess(void) +{ + return 0; +} +#endif + +/* + * kgsl_mmu_base_addr() - Get gpu virtual address base. + * + * Returns the start address of the allocatable gpu + * virtual address space. Other mappings that mirror + * the CPU address space are possible outside this range. + */ +static inline unsigned int kgsl_mmu_get_base_addr(void) +{ + if (KGSL_MMU_TYPE_GPU == kgsl_mmu_get_mmutype() + || !kgsl_mmu_is_perprocess()) + return KGSL_PAGETABLE_BASE; + /* + * This is the start of the kernel address + * space, so allocations from this range will + * never conflict with userpace addresses + */ + return PAGE_OFFSET; +} + +/* + * kgsl_mmu_get_ptsize() - Get gpu pagetable size + * + * Returns the usable size of the gpu allocatable + * address space. + */ +static inline unsigned int kgsl_mmu_get_ptsize(void) +{ + /* + * For IOMMU per-process pagetables, the allocatable range + * and the kernel global range must both be outside + * the userspace address range. There is a 1Mb gap + * between these address ranges to make overrun + * detection easier. + * For the shared pagetable case use 2GB and because + * mirroring the CPU address space is not possible and + * we're better off with extra room. + */ + enum kgsl_mmutype mmu_type = kgsl_mmu_get_mmutype(); + + if (KGSL_MMU_TYPE_GPU == mmu_type) + return CONFIG_MSM_KGSL_PAGE_TABLE_SIZE; + else if (KGSL_MMU_TYPE_IOMMU == mmu_type) { + if (kgsl_mmu_is_perprocess()) + return KGSL_IOMMU_GLOBAL_MEM_BASE + - kgsl_mmu_get_base_addr() - SZ_1M; + else + return SZ_2G; + } + return 0; +} + #endif /* __KGSL_MMU_H */ diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 3b9b689c..faad5785 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2012,2014 The Linux Foundation. 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 and @@ -23,6 +23,7 @@ #include "kgsl_pwrscale.h" #include "kgsl_device.h" #include "kgsl_trace.h" +#include "kgsl_sharedmem.h" #define KGSL_PWRFLAGS_POWER_ON 0 #define KGSL_PWRFLAGS_CLK_ON 1 @@ -178,19 +179,18 @@ static int kgsl_pwrctrl_thermal_pwrlevel_store(struct device *dev, { struct kgsl_device *device = kgsl_device_from_dev(dev); struct kgsl_pwrctrl *pwr; - int ret, level; + int ret; + unsigned int level = 0; if (device == NULL) return 0; pwr = &device->pwrctrl; - ret = sscanf(buf, "%d", &level); - if (ret != 1) - return count; + ret = kgsl_sysfs_store(buf, &level); - if (level < 0) - return count; + if (ret) + return ret; mutex_lock(&device->mutex); @@ -235,20 +235,17 @@ static int kgsl_pwrctrl_max_pwrlevel_store(struct device *dev, { struct kgsl_device *device = kgsl_device_from_dev(dev); struct kgsl_pwrctrl *pwr; - int ret, level, max_level; + int ret, max_level; + unsigned int level = 0; if (device == NULL) return 0; pwr = &device->pwrctrl; - ret = sscanf(buf, "%d", &level); - if (ret != 1) - return count; - - /* If the use specifies a negative number, then don't change anything */ - if (level < 0) - return count; + ret = kgsl_sysfs_store(buf, &level); + if (ret) + return ret; mutex_lock(&device->mutex); @@ -293,20 +290,17 @@ static int kgsl_pwrctrl_min_pwrlevel_store(struct device *dev, const char *buf, size_t count) { struct kgsl_device *device = kgsl_device_from_dev(dev); struct kgsl_pwrctrl *pwr; - int ret, level, min_level; + int ret, min_level; + unsigned int level = 0; if (device == NULL) return 0; pwr = &device->pwrctrl; - ret = sscanf(buf, "%d", &level); - if (ret != 1) - return count; - - /* Don't do anything on obviously incorrect values */ - if (level < 0) - return count; + ret = kgsl_sysfs_store(buf, &level); + if (ret) + return ret; mutex_lock(&device->mutex); if (level > pwr->num_pwrlevels - 2) @@ -377,7 +371,7 @@ static int kgsl_pwrctrl_max_gpuclk_store(struct device *dev, { struct kgsl_device *device = kgsl_device_from_dev(dev); struct kgsl_pwrctrl *pwr; - unsigned long val; + unsigned int val = 0; int ret, level; if (device == NULL) @@ -385,9 +379,9 @@ static int kgsl_pwrctrl_max_gpuclk_store(struct device *dev, pwr = &device->pwrctrl; - ret = sscanf(buf, "%ld", &val); - if (ret != 1) - return count; + ret = kgsl_sysfs_store(buf, &val); + if (ret) + return ret; mutex_lock(&device->mutex); level = _get_nearest_pwrlevel(pwr, val); @@ -429,7 +423,7 @@ static int kgsl_pwrctrl_gpuclk_store(struct device *dev, { struct kgsl_device *device = kgsl_device_from_dev(dev); struct kgsl_pwrctrl *pwr; - unsigned long val; + unsigned int val = 0; int ret, level; if (device == NULL) @@ -437,9 +431,9 @@ static int kgsl_pwrctrl_gpuclk_store(struct device *dev, pwr = &device->pwrctrl; - ret = sscanf(buf, "%ld", &val); - if (ret != 1) - return count; + ret = kgsl_sysfs_store(buf, &val); + if (ret) + return ret; mutex_lock(&device->mutex); level = _get_nearest_pwrlevel(pwr, val); @@ -467,21 +461,18 @@ static int kgsl_pwrctrl_pwrnap_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - char temp[20]; - unsigned long val; + unsigned int val = 0; struct kgsl_device *device = kgsl_device_from_dev(dev); struct kgsl_pwrctrl *pwr; - int rc; + int ret; if (device == NULL) return 0; pwr = &device->pwrctrl; - snprintf(temp, sizeof(temp), "%.*s", - (int)min(count, sizeof(temp) - 1), buf); - rc = strict_strtoul(temp, 0, &val); - if (rc) - return rc; + ret = kgsl_sysfs_store(buf, &val); + if (ret) + return ret; mutex_lock(&device->mutex); @@ -510,23 +501,20 @@ static int kgsl_pwrctrl_idle_timer_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - char temp[20]; - unsigned long val; + unsigned int val = 0; struct kgsl_device *device = kgsl_device_from_dev(dev); struct kgsl_pwrctrl *pwr; const long div = 1000/HZ; static unsigned int org_interval_timeout = 1; - int rc; + int ret; if (device == NULL) return 0; pwr = &device->pwrctrl; - snprintf(temp, sizeof(temp), "%.*s", - (int)min(count, sizeof(temp) - 1), buf); - rc = strict_strtoul(temp, 0, &val); - if (rc) - return rc; + ret = kgsl_sysfs_store(buf, &val); + if (ret) + return ret; if (org_interval_timeout == 1) org_interval_timeout = pwr->interval_timeout; @@ -560,7 +548,11 @@ static int kgsl_pwrctrl_gpubusy_show(struct device *dev, { int ret; struct kgsl_device *device = kgsl_device_from_dev(dev); - struct kgsl_clk_stats *clkstats = &device->pwrctrl.clk_stats; + struct kgsl_clk_stats *clkstats; + + if (device == NULL) + return 0; + clkstats = &device->pwrctrl.clk_stats; ret = snprintf(buf, PAGE_SIZE, "%7d %7d\n", clkstats->on_time_old, clkstats->elapsed_old); if (!test_bit(KGSL_PWRFLAGS_AXI_ON, &device->pwrctrl.power_flags)) { @@ -576,10 +568,13 @@ static int kgsl_pwrctrl_gputop_show(struct device *dev, { int ret; struct kgsl_device *device = kgsl_device_from_dev(dev); - struct kgsl_clk_stats *clkstats = &device->pwrctrl.clk_stats; + struct kgsl_clk_stats *clkstats; int i = 0; char *ptr = buf; + if (device == NULL) + return 0; + clkstats = &device->pwrctrl.clk_stats; ret = snprintf(buf, PAGE_SIZE, "%7d %7d ", clkstats->on_time_old, clkstats->elapsed_old); for (i = 0, ptr += ret; i < device->pwrctrl.num_pwrlevels; @@ -615,6 +610,16 @@ static int kgsl_pwrctrl_gpu_available_frequencies_show( return num_chars; } +static int kgsl_pwrctrl_reset_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + if (device == NULL) + return 0; + return snprintf(buf, PAGE_SIZE, "%d\n", device->reset_counter); +} + DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show, kgsl_pwrctrl_gpuclk_store); DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show, kgsl_pwrctrl_max_gpuclk_store); @@ -640,6 +645,9 @@ DEVICE_ATTR(thermal_pwrlevel, 0644, DEVICE_ATTR(num_pwrlevels, 0444, kgsl_pwrctrl_num_pwrlevels_show, NULL); +DEVICE_ATTR(reset_count, 0444, + kgsl_pwrctrl_reset_count_show, + NULL); static const struct device_attribute *pwrctrl_attr_list[] = { &dev_attr_gpuclk, @@ -653,6 +661,7 @@ static const struct device_attribute *pwrctrl_attr_list[] = { &dev_attr_min_pwrlevel, &dev_attr_thermal_pwrlevel, &dev_attr_num_pwrlevels, + &dev_attr_reset_count, NULL }; @@ -878,7 +887,8 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) if (pdata->set_grp_async != NULL) pdata->set_grp_async(); - if (pdata->num_levels > KGSL_MAX_PWRLEVELS) { + if (pdata->num_levels > KGSL_MAX_PWRLEVELS || + pdata->num_levels < 1) { KGSL_PWR_ERR(device, "invalid power level count: %d\n", pdata->num_levels); result = -EINVAL; @@ -998,6 +1008,14 @@ void kgsl_pwrctrl_close(struct kgsl_device *device) pwr->power_flags = 0; } +/** + * kgsl_idle_check() - Work function for GPU interrupts and idle timeouts. + * @device: The device + * + * This function is called for work that is queued by the interrupt + * handler or the idle timer. It attempts to transition to a clocks + * off state if the active_cnt is 0 and the hardware is idle. + */ void kgsl_idle_check(struct work_struct *work) { struct kgsl_device *device = container_of(work, struct kgsl_device, @@ -1007,15 +1025,22 @@ void kgsl_idle_check(struct work_struct *work) return; mutex_lock(&device->mutex); - if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP)) { - kgsl_pwrscale_idle(device); - if (kgsl_pwrctrl_sleep(device) != 0) { + kgsl_pwrscale_idle(device); + + if (device->state == KGSL_STATE_ACTIVE + || device->state == KGSL_STATE_NAP) { + if (device->active_cnt > 0 || kgsl_pwrctrl_sleep(device) != 0) { + + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); + mod_timer(&device->idle_timer, jiffies + device->pwrctrl.interval_timeout); - /* If the GPU has been too busy to sleep, make sure * - * that is acurately reflected in the % busy numbers. */ + /* + * If the GPU has been too busy to sleep, make sure + * that is acurately reflected in the % busy numbers. + */ device->pwrctrl.clk_stats.no_nap_cnt++; if (device->pwrctrl.clk_stats.no_nap_cnt > UPDATE_BUSY) { @@ -1030,6 +1055,7 @@ void kgsl_idle_check(struct work_struct *work) mutex_unlock(&device->mutex); } +EXPORT_SYMBOL(kgsl_idle_check); void kgsl_timer(unsigned long data) { @@ -1047,53 +1073,30 @@ void kgsl_timer(unsigned long data) } } -void kgsl_pre_hwaccess(struct kgsl_device *device) +bool kgsl_pwrctrl_isenabled(struct kgsl_device *device) { - BUG_ON(!mutex_is_locked(&device->mutex)); - switch (device->state) { - case KGSL_STATE_ACTIVE: - return; - case KGSL_STATE_NAP: - case KGSL_STATE_SLEEP: - case KGSL_STATE_SLUMBER: - kgsl_pwrctrl_wake(device); - break; - case KGSL_STATE_SUSPEND: - kgsl_check_suspended(device); - break; - case KGSL_STATE_INIT: - case KGSL_STATE_HUNG: - case KGSL_STATE_DUMP_AND_FT: - if (test_bit(KGSL_PWRFLAGS_CLK_ON, - &device->pwrctrl.power_flags)) - break; - else - KGSL_PWR_ERR(device, - "hw access while clocks off from state %d\n", - device->state); - break; - default: - KGSL_PWR_ERR(device, "hw access while in unknown state %d\n", - device->state); - break; - } + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + return (test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags) != 0); } -EXPORT_SYMBOL(kgsl_pre_hwaccess); -void kgsl_check_suspended(struct kgsl_device *device) +/** + * kgsl_pre_hwaccess - Enforce preconditions for touching registers + * @device: The device + * + * This function ensures that the correct lock is held and that the GPU + * clock is on immediately before a register is read or written. Note + * that this function does not check active_cnt because the registers + * must be accessed during device start and stop, when the active_cnt + * may legitimately be 0. + */ +void kgsl_pre_hwaccess(struct kgsl_device *device) { - if (device->requested_state == KGSL_STATE_SUSPEND || - device->state == KGSL_STATE_SUSPEND) { - mutex_unlock(&device->mutex); - wait_for_completion(&device->hwaccess_gate); - mutex_lock(&device->mutex); - } else if (device->state == KGSL_STATE_DUMP_AND_FT) { - mutex_unlock(&device->mutex); - wait_for_completion(&device->ft_gate); - mutex_lock(&device->mutex); - } else if (device->state == KGSL_STATE_SLUMBER) - kgsl_pwrctrl_wake(device); + /* In order to touch a register you must hold the device mutex...*/ + BUG_ON(!mutex_is_locked(&device->mutex)); + /* and have the clock on! */ + BUG_ON(!kgsl_pwrctrl_isenabled(device)); } +EXPORT_SYMBOL(kgsl_pre_hwaccess); static int _nap(struct kgsl_device *device) @@ -1104,6 +1107,7 @@ _nap(struct kgsl_device *device) kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); return -EBUSY; } + del_timer_sync(&device->hang_timer); kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF, KGSL_STATE_NAP); kgsl_pwrctrl_set_state(device, KGSL_STATE_NAP); @@ -1173,6 +1177,10 @@ _slumber(struct kgsl_device *device) case KGSL_STATE_NAP: case KGSL_STATE_SLEEP: del_timer_sync(&device->idle_timer); + del_timer_sync(&device->hang_timer); + /* make sure power is on to stop the device*/ + kgsl_pwrctrl_enable(device); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); device->ftbl->suspend_context(device); device->ftbl->stop(device); _sleep_accounting(device); @@ -1222,13 +1230,18 @@ EXPORT_SYMBOL(kgsl_pwrctrl_sleep); /******************************************************************/ /* Caller must hold the device mutex. */ -void kgsl_pwrctrl_wake(struct kgsl_device *device) +int kgsl_pwrctrl_wake(struct kgsl_device *device) { - int status; + int status = 0; + unsigned int context_id; + unsigned int state = device->state; + unsigned int ts_processed = 0xdeaddead; + struct kgsl_context *context; + kgsl_pwrctrl_request_state(device, KGSL_STATE_ACTIVE); switch (device->state) { case KGSL_STATE_SLUMBER: - status = device->ftbl->start(device, 0); + status = device->ftbl->start(device); if (status) { kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); KGSL_DRV_ERR(device, "start failed %d\n", status); @@ -1238,6 +1251,18 @@ void kgsl_pwrctrl_wake(struct kgsl_device *device) case KGSL_STATE_SLEEP: kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON); kgsl_pwrscale_wake(device); + kgsl_sharedmem_readl(&device->memstore, + (unsigned int *) &context_id, + KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, + current_context)); + context = kgsl_context_get(device, context_id); + if (context) + ts_processed = kgsl_readtimestamp(device, context, + KGSL_TIMESTAMP_RETIRED); + KGSL_PWR_INFO(device, "Wake from %s state. CTXT: %d RTRD TS: %08X\n", + kgsl_pwrstate_to_str(state), + context ? context->id : -1, ts_processed); + kgsl_context_put(context); /* fall through */ case KGSL_STATE_NAP: /* Turn on the core clocks */ @@ -1248,17 +1273,23 @@ void kgsl_pwrctrl_wake(struct kgsl_device *device) /* Re-enable HW access */ mod_timer(&device->idle_timer, jiffies + device->pwrctrl.interval_timeout); + mod_timer(&device->hang_timer, + (jiffies + msecs_to_jiffies(KGSL_TIMEOUT_HANG_DETECT))); pm_qos_update_request(&device->pm_qos_req_dma, GPU_SWFI_LATENCY); case KGSL_STATE_ACTIVE: kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); break; + case KGSL_STATE_INIT: + break; default: KGSL_PWR_WARN(device, "unhandled state %s\n", kgsl_pwrstate_to_str(device->state)); kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); + status = -EINVAL; break; } + return status; } EXPORT_SYMBOL(kgsl_pwrctrl_wake); @@ -1324,3 +1355,122 @@ const char *kgsl_pwrstate_to_str(unsigned int state) } EXPORT_SYMBOL(kgsl_pwrstate_to_str); + +/** + * kgsl_active_count_get() - Increase the device active count + * @device: Pointer to a KGSL device + * + * Increase the active count for the KGSL device and turn on + * clocks if this is the first reference. Code paths that need + * to touch the hardware or wait for the hardware to complete + * an operation must hold an active count reference until they + * are finished. An error code will be returned if waking the + * device fails. The device mutex must be held while *calling + * this function. + */ +int kgsl_active_count_get(struct kgsl_device *device) +{ + int ret = 0; + BUG_ON(!mutex_is_locked(&device->mutex)); + + if (device->active_cnt == 0) { + + if (device->state != KGSL_STATE_DUMP_AND_FT) { + mutex_unlock(&device->mutex); + wait_for_completion(&device->hwaccess_gate); + wait_for_completion(&device->ft_gate); + mutex_lock(&device->mutex); + } + + ret = kgsl_pwrctrl_wake(device); + } + if (ret == 0) + device->active_cnt++; + return ret; +} +EXPORT_SYMBOL(kgsl_active_count_get); + +/** + * kgsl_active_count_get_light() - Increase the device active count + * @device: Pointer to a KGSL device + * + * Increase the active count for the KGSL device WITHOUT + * turning on the clocks. Currently this is only used for creating + * kgsl_events. The device mutex must be held while calling this function. + */ +int kgsl_active_count_get_light(struct kgsl_device *device) +{ + BUG_ON(!mutex_is_locked(&device->mutex)); + + if (device->state != KGSL_STATE_ACTIVE) { + dev_WARN_ONCE(device->dev, 1, "device in unexpected state %s\n", + kgsl_pwrstate_to_str(device->state)); + return -EINVAL; + } + + if (device->active_cnt == 0) { + dev_WARN_ONCE(device->dev, 1, "active count is 0!\n"); + return -EINVAL; + } + + device->active_cnt++; + return 0; +} +EXPORT_SYMBOL(kgsl_active_count_get_light); + +/** + * kgsl_active_count_put() - Decrease the device active count + * @device: Pointer to a KGSL device + * + * Decrease the active count for the KGSL device and turn off + * clocks if there are no remaining references. This function will + * transition the device to NAP if there are no other pending state + * changes. It also completes the suspend gate. The device mutex must + * be held while calling this function. + */ +void kgsl_active_count_put(struct kgsl_device *device) +{ + BUG_ON(!mutex_is_locked(&device->mutex)); + BUG_ON(device->active_cnt == 0); + + kgsl_pwrscale_idle(device); + if (device->active_cnt > 1) { + device->active_cnt--; + return; + } + + INIT_COMPLETION(device->suspend_gate); + + if (device->pwrctrl.nap_allowed == true && + (device->state == KGSL_STATE_ACTIVE && + device->requested_state == KGSL_STATE_NONE)) { + kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP); + if (kgsl_pwrctrl_sleep(device) != 0) + mod_timer(&device->idle_timer, + jiffies + + device->pwrctrl.interval_timeout); + } + device->active_cnt--; + + if (device->active_cnt == 0) + complete(&device->suspend_gate); +} +EXPORT_SYMBOL(kgsl_active_count_put); + +/** + * kgsl_active_count_wait() - Wait for activity to finish. + * @device: Pointer to a KGSL device + * + * Block until all active_cnt users put() their reference. + */ +void kgsl_active_count_wait(struct kgsl_device *device) +{ + BUG_ON(!mutex_is_locked(&device->mutex)); + + if (device->active_cnt != 0) { + mutex_unlock(&device->mutex); + wait_for_completion(&device->suspend_gate); + mutex_lock(&device->mutex); + } +} +EXPORT_SYMBOL(kgsl_active_count_wait); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 8d66505c..532bb4f6 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -93,15 +93,16 @@ void kgsl_pwrctrl_close(struct kgsl_device *device); void kgsl_timer(unsigned long data); void kgsl_idle_check(struct work_struct *work); void kgsl_pre_hwaccess(struct kgsl_device *device); -void kgsl_check_suspended(struct kgsl_device *device); int kgsl_pwrctrl_sleep(struct kgsl_device *device); -void kgsl_pwrctrl_wake(struct kgsl_device *device); +int kgsl_pwrctrl_wake(struct kgsl_device *device); void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, unsigned int level); int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device); void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device); void kgsl_pwrctrl_enable(struct kgsl_device *device); void kgsl_pwrctrl_disable(struct kgsl_device *device); +bool kgsl_pwrctrl_isenabled(struct kgsl_device *device); + static inline unsigned long kgsl_get_clkrate(struct clk *clk) { return (clk != NULL) ? clk_get_rate(clk) : 0; @@ -109,4 +110,10 @@ static inline unsigned long kgsl_get_clkrate(struct clk *clk) void kgsl_pwrctrl_set_state(struct kgsl_device *device, unsigned int state); void kgsl_pwrctrl_request_state(struct kgsl_device *device, unsigned int state); + +int kgsl_active_count_get(struct kgsl_device *device); +int kgsl_active_count_get_light(struct kgsl_device *device); +void kgsl_active_count_put(struct kgsl_device *device); +void kgsl_active_count_wait(struct kgsl_device *device); + #endif /* __KGSL_PWRCTRL_H */ diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index 02ada386..afef62e5 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2013, The Linux Foundation. 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 and @@ -240,6 +240,7 @@ void kgsl_pwrscale_busy(struct kgsl_device *device) device->pwrscale.policy->busy(device, &device->pwrscale); } +EXPORT_SYMBOL(kgsl_pwrscale_busy); void kgsl_pwrscale_idle(struct kgsl_device *device) { diff --git a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c index a6473614..db9054ba 100644 --- a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c +++ b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c @@ -85,20 +85,14 @@ static ssize_t tz_governor_store(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale, const char *buf, size_t count) { - char str[20]; struct tz_priv *priv = pwrscale->priv; struct kgsl_pwrctrl *pwr = &device->pwrctrl; - int ret; - - ret = sscanf(buf, "%20s", str); - if (ret != 1) - return -EINVAL; mutex_lock(&device->mutex); - if (!strncmp(str, "ondemand", 8)) + if (!strncmp(buf, "ondemand", 8)) priv->governor = TZ_GOVERNOR_ONDEMAND; - else if (!strncmp(str, "performance", 11)) + else if (!strncmp(buf, "performance", 11)) priv->governor = TZ_GOVERNOR_PERFORMANCE; if (priv->governor == TZ_GOVERNOR_PERFORMANCE) diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c old mode 100755 new mode 100644 index b8dda845..424269d3 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2012,2014, The Linux Foundation. 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 and @@ -65,14 +65,6 @@ struct mem_entry_stats { mem_entry_max_show), \ } - -/* - * One page allocation for a guard region to protect against over-zealous - * GPU pre-fetch - */ - -static struct page *kgsl_guard_page; - /** * Given a kobj, find the process structure attached to it */ @@ -81,12 +73,12 @@ static struct kgsl_process_private * _get_priv_from_kobj(struct kobject *kobj) { struct kgsl_process_private *private; - unsigned long name; + unsigned int name; if (!kobj) return NULL; - if (sscanf(kobj->name, "%ld", &name) != 1) + if (kstrtou32(kobj->name, 0, &name)) return NULL; list_for_each_entry(private, &kgsl_driver.process_list, list) { @@ -154,9 +146,7 @@ static struct kobj_type ktype_mem_entry = { static struct mem_entry_stats mem_stats[] = { MEM_ENTRY_STAT(KGSL_MEM_ENTRY_KERNEL, kernel), -#ifdef CONFIG_ANDROID_PMEM MEM_ENTRY_STAT(KGSL_MEM_ENTRY_PMEM, pmem), -#endif #ifdef CONFIG_ASHMEM MEM_ENTRY_STAT(KGSL_MEM_ENTRY_ASHMEM, ashmem), #endif @@ -180,17 +170,19 @@ kgsl_process_uninit_sysfs(struct kgsl_process_private *private) kobject_put(&private->kobj); } -void +int kgsl_process_init_sysfs(struct kgsl_process_private *private) { unsigned char name[16]; - int i, ret; + int i, ret = 0; snprintf(name, sizeof(name), "%d", private->pid); - if (kobject_init_and_add(&private->kobj, &ktype_mem_entry, - kgsl_driver.prockobj, name)) - return; + ret = kobject_init_and_add(&private->kobj, &ktype_mem_entry, + kgsl_driver.prockobj, name); + + if (ret) + return ret; for (i = 0; i < ARRAY_SIZE(mem_stats); i++) { /* We need to check the value of sysfs_create_file, but we @@ -201,6 +193,7 @@ kgsl_process_init_sysfs(struct kgsl_process_private *private) ret = sysfs_create_file(&private->kobj, &mem_stats[i].max_attr.attr); } + return ret; } static int kgsl_drv_memstat_show(struct device *dev, @@ -366,10 +359,6 @@ static void kgsl_page_alloc_free(struct kgsl_memdesc *memdesc) struct scatterlist *sg; int sglen = memdesc->sglen; - /* Don't free the guard page if it was used */ - if (memdesc->priv & KGSL_MEMDESC_GUARD_PAGE) - sglen--; - kgsl_driver.stats.page_alloc -= memdesc->size; if (memdesc->hostptr) { @@ -407,10 +396,6 @@ static int kgsl_page_alloc_map_kernel(struct kgsl_memdesc *memdesc) int sglen = memdesc->sglen; int i, count = 0; - /* Don't map the guard page if it exists */ - if (memdesc->priv & KGSL_MEMDESC_GUARD_PAGE) - sglen--; - /* create a list of pages to call vmap */ pages = vmalloc(npages * sizeof(struct page *)); if (!pages) { @@ -514,21 +499,29 @@ static struct kgsl_memdesc_ops kgsl_coherent_ops = { void kgsl_cache_range_op(struct kgsl_memdesc *memdesc, int op) { - void *addr = memdesc->hostptr; + /* + * If the buffer is mapped in the kernel operate on that address + * otherwise use the user address + */ + + void *addr = (memdesc->hostptr) ? + memdesc->hostptr : (void *) memdesc->useraddr; + int size = memdesc->size; - switch (op) { - case KGSL_CACHE_OP_FLUSH: - dmac_flush_range(addr, addr + size); - break; - case KGSL_CACHE_OP_CLEAN: - dmac_clean_range(addr, addr + size); - break; - case KGSL_CACHE_OP_INV: - dmac_inv_range(addr, addr + size); - break; + if (addr != NULL) { + switch (op) { + case KGSL_CACHE_OP_FLUSH: + dmac_flush_range(addr, addr + size); + break; + case KGSL_CACHE_OP_CLEAN: + dmac_clean_range(addr, addr + size); + break; + case KGSL_CACHE_OP_INV: + dmac_inv_range(addr, addr + size); + break; + } } - outer_cache_range_op_sg(memdesc->sg, memdesc->sglen, op); } EXPORT_SYMBOL(kgsl_cache_range_op); @@ -536,7 +529,7 @@ EXPORT_SYMBOL(kgsl_cache_range_op); static int _kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc, struct kgsl_pagetable *pagetable, - size_t size, unsigned int protflags) + size_t size) { int pcount = 0, order, ret = 0; int j, len, page_size, sglen_alloc, sglen = 0; @@ -550,7 +543,8 @@ _kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc, page_size = (align >= ilog2(SZ_64K) && size >= SZ_64K) ? SZ_64K : PAGE_SIZE; /* update align flags for what we actually use */ - kgsl_memdesc_set_align(memdesc, ilog2(page_size)); + if (page_size != PAGE_SIZE) + kgsl_memdesc_set_align(memdesc, ilog2(page_size)); /* * There needs to be enough room in the sg structure to be able to @@ -559,15 +553,6 @@ _kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc, sglen_alloc = PAGE_ALIGN(size) >> PAGE_SHIFT; - /* - * Add guard page to the end of the allocation when the - * IOMMU is in use. - */ - - if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU) - sglen_alloc++; - - memdesc->size = size; memdesc->pagetable = pagetable; memdesc->ops = &kgsl_page_alloc_ops; @@ -581,13 +566,16 @@ _kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc, /* * Allocate space to store the list of pages to send to vmap. - * This is an array of pointers so we can track 1024 pages per page of - * allocation which means we can handle up to a 8MB buffer request with - * two pages; well within the acceptable limits for using kmalloc. + * This is an array of pointers so we can track 1024 pages per page + * of allocation. Since allocations can be as large as the user dares, + * we have to use the kmalloc/vmalloc trick here to make sure we can + * get the memory we need. */ - pages = kmalloc(memdesc->sglen_alloc * sizeof(struct page *), - GFP_KERNEL); + if ((memdesc->sglen_alloc * sizeof(struct page *)) > PAGE_SIZE) + pages = vmalloc(memdesc->sglen_alloc * sizeof(struct page *)); + else + pages = kmalloc(PAGE_SIZE, GFP_KERNEL); if (pages == NULL) { ret = -ENOMEM; @@ -627,6 +615,14 @@ _kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc, continue; } + /* + * Update sglen and memdesc size,as requested allocation + * not served fully. So that they can be correctly freed + * in kgsl_sharedmem_free(). + */ + memdesc->sglen = sglen; + memdesc->size = (size - len); + KGSL_CORE_ERR( "Out of memory: only allocated %dKB of %dKB requested\n", (size - len) >> 10, size >> 10); @@ -642,27 +638,8 @@ _kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc, len -= page_size; } - /* Add the guard page to the end of the sglist */ - - if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU) { - /* - * It doesn't matter if we use GFP_ZERO here, this never - * gets mapped, and we only allocate it once in the life - * of the system - */ - - if (kgsl_guard_page == NULL) - kgsl_guard_page = alloc_page(GFP_KERNEL | __GFP_ZERO | - __GFP_HIGHMEM); - - if (kgsl_guard_page != NULL) { - sg_set_page(&memdesc->sg[sglen++], kgsl_guard_page, - PAGE_SIZE, 0); - memdesc->priv |= KGSL_MEMDESC_GUARD_PAGE; - } - } - memdesc->sglen = sglen; + memdesc->size = size; /* * All memory that goes to the user has to be zeroed out before it gets @@ -703,21 +680,19 @@ _kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc, outer_cache_range_op_sg(memdesc->sg, memdesc->sglen, KGSL_CACHE_OP_FLUSH); - ret = kgsl_mmu_map(pagetable, memdesc, protflags); - - if (ret) - goto done; - - KGSL_STATS_ADD(size, kgsl_driver.stats.page_alloc, - kgsl_driver.stats.page_alloc_max); - order = get_order(size); if (order < 16) kgsl_driver.stats.histogram[order]++; done: - kfree(pages); + KGSL_STATS_ADD(memdesc->size, kgsl_driver.stats.page_alloc, + kgsl_driver.stats.page_alloc_max); + + if ((memdesc->sglen_alloc * sizeof(struct page *)) > PAGE_SIZE) + vfree(pages); + else + kfree(pages); if (ret) kgsl_sharedmem_free(memdesc); @@ -734,8 +709,7 @@ kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc, size = ALIGN(size, PAGE_SIZE * 2); - ret = _kgsl_sharedmem_page_alloc(memdesc, pagetable, size, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + ret = _kgsl_sharedmem_page_alloc(memdesc, pagetable, size); if (!ret) ret = kgsl_page_alloc_map_kernel(memdesc); if (ret) @@ -749,17 +723,7 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, struct kgsl_pagetable *pagetable, size_t size) { - unsigned int protflags; - - if (size == 0) - return -EINVAL; - - protflags = GSL_PT_PAGE_RV; - if (!(memdesc->flags & KGSL_MEMFLAGS_GPUREADONLY)) - protflags |= GSL_PT_PAGE_WV; - - return _kgsl_sharedmem_page_alloc(memdesc, pagetable, size, - protflags); + return _kgsl_sharedmem_page_alloc(memdesc, pagetable, PAGE_ALIGN(size)); } EXPORT_SYMBOL(kgsl_sharedmem_page_alloc_user); @@ -803,8 +767,10 @@ void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc) if (memdesc == NULL || memdesc->size == 0) return; - if (memdesc->gpuaddr) + if (memdesc->gpuaddr) { kgsl_mmu_unmap(memdesc->pagetable, memdesc); + kgsl_mmu_put_gpuaddr(memdesc->pagetable, memdesc); + } if (memdesc->ops && memdesc->ops->free) memdesc->ops->free(memdesc); @@ -834,12 +800,6 @@ _kgsl_sharedmem_ebimem(struct kgsl_memdesc *memdesc, result = memdesc_sg_phys(memdesc, memdesc->physaddr, size); - if (result) - goto err; - - result = kgsl_mmu_map(pagetable, memdesc, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); - if (result) goto err; diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h index a895a754..c5bb8cee 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.h +++ b/drivers/gpu/msm/kgsl_sharedmem.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2013, The Linux Foundation. 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 and @@ -19,6 +19,7 @@ #include "kgsl_mmu.h" #include #include +#include #include "kgsl_log.h" @@ -64,7 +65,7 @@ int kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, void kgsl_cache_range_op(struct kgsl_memdesc *memdesc, int op); -void kgsl_process_init_sysfs(struct kgsl_process_private *private); +int kgsl_process_init_sysfs(struct kgsl_process_private *private); void kgsl_process_uninit_sysfs(struct kgsl_process_private *private); int kgsl_sharedmem_init_sysfs(void); @@ -82,6 +83,18 @@ kgsl_memdesc_get_align(const struct kgsl_memdesc *memdesc) return (memdesc->flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT; } +/* + * kgsl_memdesc_get_cachemode - Get cache mode of a memdesc + * @memdesc: the memdesc + * + * Returns a KGSL_CACHEMODE* value. + */ +static inline int +kgsl_memdesc_get_cachemode(const struct kgsl_memdesc *memdesc) +{ + return (memdesc->flags & KGSL_CACHEMODE_MASK) >> KGSL_CACHEMODE_SHIFT; +} + /* * kgsl_memdesc_set_align - Set alignment flags of a memdesc * @memdesc - the memdesc @@ -125,6 +138,9 @@ kgsl_sharedmem_map_vma(struct vm_area_struct *vma, static inline void *kgsl_sg_alloc(unsigned int sglen) { + if ((sglen == 0) || (sglen >= ULONG_MAX / sizeof(struct scatterlist))) + return NULL; + if ((sglen * sizeof(struct scatterlist)) < PAGE_SIZE) return kzalloc(sglen * sizeof(struct scatterlist), GFP_KERNEL); else { @@ -162,14 +178,103 @@ memdesc_sg_phys(struct kgsl_memdesc *memdesc, return 0; } +/* + * kgsl_memdesc_is_global - is this a globally mapped buffer? + * @memdesc: the memdesc + * + * Returns nonzero if this is a global mapping, 0 otherwise + */ +static inline int kgsl_memdesc_is_global(const struct kgsl_memdesc *memdesc) +{ + return (memdesc->priv & KGSL_MEMDESC_GLOBAL) != 0; +} + +/* + * kgsl_memdesc_has_guard_page - is the last page a guard page? + * @memdesc - the memdesc + * + * Returns nonzero if there is a guard page, 0 otherwise + */ +static inline int +kgsl_memdesc_has_guard_page(const struct kgsl_memdesc *memdesc) +{ + return (memdesc->priv & KGSL_MEMDESC_GUARD_PAGE) != 0; +} + +/* + * kgsl_memdesc_protflags - get mmu protection flags + * @memdesc - the memdesc + * Returns a mask of GSL_PT_PAGE* or IOMMU* values based + * on the memdesc flags. + */ +static inline unsigned int +kgsl_memdesc_protflags(const struct kgsl_memdesc *memdesc) +{ + unsigned int protflags = 0; + enum kgsl_mmutype mmutype = kgsl_mmu_get_mmutype(); + + if (mmutype == KGSL_MMU_TYPE_GPU) { + protflags = GSL_PT_PAGE_RV; + if (!(memdesc->flags & KGSL_MEMFLAGS_GPUREADONLY)) + protflags |= GSL_PT_PAGE_WV; + } else if (mmutype == KGSL_MMU_TYPE_IOMMU) { + protflags = IOMMU_READ; + if (!(memdesc->flags & KGSL_MEMFLAGS_GPUREADONLY)) + protflags |= IOMMU_WRITE; + } + return protflags; +} + +/* + * kgsl_memdesc_use_cpu_map - use the same virtual mapping on CPU and GPU? + * @memdesc - the memdesc + */ +static inline int +kgsl_memdesc_use_cpu_map(const struct kgsl_memdesc *memdesc) +{ + return (memdesc->flags & KGSL_MEMFLAGS_USE_CPU_MAP) != 0; +} + +/* + * kgsl_memdesc_mmapsize - get the size of the mmap region + * @memdesc - the memdesc + * + * The entire memdesc must be mapped. Additionally if the + * CPU mapping is going to be mirrored, there must be room + * for the guard page to be mapped so that the address spaces + * match up. + */ +static inline unsigned int +kgsl_memdesc_mmapsize(const struct kgsl_memdesc *memdesc) +{ + unsigned int size = memdesc->size; + if (kgsl_memdesc_use_cpu_map(memdesc) && + kgsl_memdesc_has_guard_page(memdesc)) + size += SZ_4K; + return size; +} + static inline int kgsl_allocate(struct kgsl_memdesc *memdesc, struct kgsl_pagetable *pagetable, size_t size) { + int ret; + memdesc->priv |= (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT); if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE) return kgsl_sharedmem_ebimem(memdesc, pagetable, size); - memdesc->flags |= (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT); - return kgsl_sharedmem_page_alloc(memdesc, pagetable, size); + + ret = kgsl_sharedmem_page_alloc(memdesc, pagetable, size); + if (ret) + return ret; + ret = kgsl_mmu_get_gpuaddr(pagetable, memdesc); + if (ret) { + kgsl_sharedmem_free(memdesc); + return ret; + } + ret = kgsl_mmu_map(pagetable, memdesc); + if (ret) + kgsl_sharedmem_free(memdesc); + return ret; } static inline int @@ -179,6 +284,9 @@ kgsl_allocate_user(struct kgsl_memdesc *memdesc, { int ret; + if (size == 0) + return -EINVAL; + memdesc->flags = flags; if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE) @@ -200,15 +308,4 @@ kgsl_allocate_contiguous(struct kgsl_memdesc *memdesc, size_t size) return ret; } -static inline int kgsl_sg_size(struct scatterlist *sg, int sglen) -{ - int i, size = 0; - struct scatterlist *s; - - for_each_sg(sg, s, sglen, i) { - size += s->length; - } - - return size; -} #endif /* __KGSL_SHAREDMEM_H */ diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c index a5aa42f2..7ae65f0f 100644 --- a/drivers/gpu/msm/kgsl_snapshot.c +++ b/drivers/gpu/msm/kgsl_snapshot.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2013, The Linux Foundation. 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 and @@ -106,7 +106,12 @@ static int snapshot_context_info(int id, void *ptr, void *data) { struct kgsl_snapshot_linux_context *header = _ctxtptr; struct kgsl_context *context = ptr; - struct kgsl_device *device = context->dev_priv->device; + struct kgsl_device *device; + + if (context) + device = context->dev_priv->device; + else + device = (struct kgsl_device *)data; header->id = id; @@ -139,7 +144,12 @@ static int snapshot_os(struct kgsl_device *device, /* Figure out how many active contexts there are - these will * be appended on the end of the structure */ + read_lock(&device->context_lock); idr_for_each(&device->context_idr, snapshot_context_count, &ctxtcount); + read_unlock(&device->context_lock); + + /* Increment ctxcount for the global memstore */ + ctxtcount++; size += ctxtcount * sizeof(struct kgsl_snapshot_linux_context); @@ -169,8 +179,9 @@ static int snapshot_os(struct kgsl_device *device, header->grpclk = kgsl_get_clkrate(pwr->grp_clks[0]); header->busclk = kgsl_get_clkrate(pwr->ebi1_clk); - /* Future proof for per-context timestamps */ - header->current_context = -1; + /* Save the last active context */ + kgsl_sharedmem_readl(&device->memstore, &header->current_context, + KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context)); /* Get the current PT base */ header->ptbase = kgsl_mmu_get_current_ptbase(&device->mmu); @@ -185,9 +196,16 @@ static int snapshot_os(struct kgsl_device *device, header->ctxtcount = ctxtcount; - /* append information for each context */ _ctxtptr = snapshot + sizeof(*header); + + /* append information for the global context */ + snapshot_context_info(KGSL_MEMSTORE_GLOBAL, NULL, device); + + /* append information for each context */ + + read_lock(&device->context_lock); idr_for_each(&device->context_idr, snapshot_context_info, NULL); + read_unlock(&device->context_lock); /* Return the size of the data segment */ return size; @@ -283,7 +301,7 @@ static void kgsl_snapshot_put_object(struct kgsl_device *device, { list_del(&obj->node); - obj->entry->flags &= ~KGSL_MEM_ENTRY_FROZEN; + obj->entry->memdesc.priv &= ~KGSL_MEMDESC_FROZEN; kgsl_mem_entry_put(obj->entry); kfree(obj); @@ -314,6 +332,7 @@ int kgsl_snapshot_have_object(struct kgsl_device *device, unsigned int ptbase, return 0; } +EXPORT_SYMBOL(kgsl_snapshot_have_object); /* kgsl_snapshot_get_object - Mark a GPU buffer to be frozen * @device - the device that is being snapshotted @@ -333,6 +352,10 @@ int kgsl_snapshot_get_object(struct kgsl_device *device, unsigned int ptbase, struct kgsl_mem_entry *entry; struct kgsl_snapshot_object *obj; int offset; + int ret = -EINVAL; + + if (!gpuaddr) + return 0; entry = kgsl_get_mem_entry(device, ptbase, gpuaddr, size); @@ -346,7 +369,7 @@ int kgsl_snapshot_get_object(struct kgsl_device *device, unsigned int ptbase, if (entry->memtype != KGSL_MEM_ENTRY_KERNEL) { KGSL_DRV_ERR(device, "Only internal GPU buffers can be frozen\n"); - return -EINVAL; + goto err_put; } /* @@ -369,36 +392,33 @@ int kgsl_snapshot_get_object(struct kgsl_device *device, unsigned int ptbase, if (size + offset > entry->memdesc.size) { KGSL_DRV_ERR(device, "Invalid size for GPU buffer %8.8X\n", gpuaddr); - return -EINVAL; + goto err_put; } /* If the buffer is already on the list, skip it */ list_for_each_entry(obj, &device->snapshot_obj_list, node) { if (obj->gpuaddr == gpuaddr && obj->ptbase == ptbase) { - /* If the size is different, use the new size */ - if (obj->size != size) + /* If the size is different, use the bigger size */ + if (obj->size < size) obj->size = size; - - return 0; + ret = 0; + goto err_put; } } if (kgsl_memdesc_map(&entry->memdesc) == NULL) { KGSL_DRV_ERR(device, "Unable to map GPU buffer %X\n", gpuaddr); - return -EINVAL; + goto err_put; } obj = kzalloc(sizeof(*obj), GFP_KERNEL); if (obj == NULL) { KGSL_DRV_ERR(device, "Unable to allocate memory\n"); - return -EINVAL; + goto err_put; } - /* Ref count the mem entry */ - kgsl_mem_entry_get(entry); - obj->type = type; obj->entry = entry; obj->gpuaddr = gpuaddr; @@ -416,12 +436,15 @@ int kgsl_snapshot_get_object(struct kgsl_device *device, unsigned int ptbase, * 0 so it doesn't get counted twice */ - if (entry->flags & KGSL_MEM_ENTRY_FROZEN) - return 0; + ret = (entry->memdesc.priv & KGSL_MEMDESC_FROZEN) ? 0 + : entry->memdesc.size; - entry->flags |= KGSL_MEM_ENTRY_FROZEN; + entry->memdesc.priv |= KGSL_MEMDESC_FROZEN; - return entry->memdesc.size; + return ret; +err_put: + kgsl_mem_entry_put(entry); + return ret; } EXPORT_SYMBOL(kgsl_snapshot_get_object); @@ -519,6 +542,16 @@ int kgsl_device_snapshot(struct kgsl_device *device, int hang) int remain = device->snapshot_maxsize - sizeof(*header); void *snapshot; struct timespec boot; + int ret = 0; + + /* + * Bail if failed to get active count for GPU, + * try again + */ + if (kgsl_active_count_get(device)) { + KGSL_DRV_ERR(device, "Failed to get GPU active count"); + return -EINVAL; + } /* * The first hang is always the one we are interested in. To @@ -529,19 +562,23 @@ int kgsl_device_snapshot(struct kgsl_device *device, int hang) * of the state and never frozen. */ - if (hang && device->snapshot_frozen == 1) - return 0; + if (hang && device->snapshot_frozen == 1) { + ret = 0; + goto done; + } if (device->snapshot == NULL) { KGSL_DRV_ERR(device, "snapshot: No snapshot memory available\n"); - return -ENOMEM; + ret = -ENOMEM; + goto done; } if (remain < sizeof(*header)) { KGSL_DRV_ERR(device, "snapshot: Not enough memory for the header\n"); - return -ENOMEM; + ret = -ENOMEM; + goto done; } header->magic = SNAPSHOT_MAGIC; @@ -577,7 +614,10 @@ int kgsl_device_snapshot(struct kgsl_device *device, int hang) __pa(device->snapshot), device->snapshot_size); if (hang) sysfs_notify(&device->snapshot_kobj, NULL, "timestamp"); - return 0; + +done: + kgsl_active_count_put(device); + return ret; } EXPORT_SYMBOL(kgsl_device_snapshot); diff --git a/drivers/gpu/msm/kgsl_snapshot.h b/drivers/gpu/msm/kgsl_snapshot.h index 327d18a1..4db2815c 100644 --- a/drivers/gpu/msm/kgsl_snapshot.h +++ b/drivers/gpu/msm/kgsl_snapshot.h @@ -52,6 +52,7 @@ struct kgsl_snapshot_section_header { #define KGSL_SNAPSHOT_SECTION_DEBUG 0x0901 #define KGSL_SNAPSHOT_SECTION_DEBUGBUS 0x0A01 #define KGSL_SNAPSHOT_SECTION_GPU_OBJECT 0x0B01 +#define KGSL_SNAPSHOT_SECTION_MEMLIST 0x0E01 #define KGSL_SNAPSHOT_SECTION_END 0xFFFF @@ -103,6 +104,17 @@ struct kgsl_snapshot_rb { int count; /* Number of dwords in the dump */ } __packed; +/* Replay or Memory list section, both sections have same header */ +struct kgsl_snapshot_replay_mem_list { + /* + * Number of IBs to replay for replay section or + * number of memory list entries for mem list section + */ + int num_entries; + /* Pagetable base to which the replay IBs or memory entries belong */ + __u32 ptbase; +} __packed; + /* Indirect buffer sub-section header */ struct kgsl_snapshot_ib { __u32 gpuaddr; /* GPU address of the the IB */ diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c index 813305ad..8fa8502d 100644 --- a/drivers/gpu/msm/kgsl_sync.c +++ b/drivers/gpu/msm/kgsl_sync.c @@ -12,9 +12,12 @@ */ #include +#include #include #include +#include + #include "kgsl_sync.h" struct sync_pt *kgsl_sync_pt_create(struct sync_timeline *timeline, @@ -83,9 +86,11 @@ struct kgsl_fence_event_priv { */ static inline void kgsl_fence_event_cb(struct kgsl_device *device, - void *priv, u32 context_id, u32 timestamp) + void *priv, u32 context_id, u32 timestamp, u32 type) { struct kgsl_fence_event_priv *ev = priv; + + /* Signal event time timeline for every event type */ kgsl_sync_timeline_signal(ev->context->timeline, ev->timestamp); kgsl_context_put(ev->context); kfree(ev); @@ -118,16 +123,19 @@ int kgsl_add_fence_event(struct kgsl_device *device, if (len != sizeof(priv)) return -EINVAL; - context = kgsl_find_context(owner, context_id); - if (context == NULL) - return -EINVAL; - event = kzalloc(sizeof(*event), GFP_KERNEL); if (event == NULL) return -ENOMEM; + + context = kgsl_context_get_owner(owner, context_id); + + if (context == NULL) { + kfree(event); + return -EINVAL; + } + event->context = context; event->timestamp = timestamp; - kgsl_context_get(context); pt = kgsl_sync_pt_create(context->timeline, timestamp); if (pt == NULL) { @@ -158,6 +166,10 @@ int kgsl_add_fence_event(struct kgsl_device *device, goto fail_copy_fd; } + /* + * Hold the context ref-count for the event - it will get released in + * the callback + */ ret = kgsl_add_event(device, context_id, timestamp, kgsl_fence_event_cb, event, owner); if (ret) @@ -168,7 +180,6 @@ int kgsl_add_fence_event(struct kgsl_device *device, fail_event: fail_copy_fd: /* clean up sync_fence_install */ - sync_fence_put(fence); put_unused_fd(priv.fence_fd); fail_fd: /* clean up sync_fence_create */ @@ -180,24 +191,81 @@ int kgsl_add_fence_event(struct kgsl_device *device, return ret; } +static unsigned int kgsl_sync_get_timestamp( + struct kgsl_sync_timeline *ktimeline, enum kgsl_timestamp_type type) +{ + unsigned int ret = 0; + + struct kgsl_context *context = kgsl_context_get(ktimeline->device, + ktimeline->context_id); + + if (context) + ret = kgsl_readtimestamp(ktimeline->device, context, type); + + kgsl_context_put(context); + return ret; +} + +static void kgsl_sync_timeline_value_str(struct sync_timeline *sync_timeline, + char *str, int size) +{ + struct kgsl_sync_timeline *ktimeline = + (struct kgsl_sync_timeline *) sync_timeline; + unsigned int timestamp_retired = kgsl_sync_get_timestamp(ktimeline, + KGSL_TIMESTAMP_RETIRED); + snprintf(str, size, "%u retired:%u", ktimeline->last_timestamp, + timestamp_retired); +} + +static void kgsl_sync_pt_value_str(struct sync_pt *sync_pt, + char *str, int size) +{ + struct kgsl_sync_pt *kpt = (struct kgsl_sync_pt *) sync_pt; + snprintf(str, size, "%u", kpt->timestamp); +} + +static void kgsl_sync_timeline_release_obj(struct sync_timeline *sync_timeline) +{ + /* + * Make sure to free the timeline only after destroy flag is set. + * This is to avoid further accessing to the timeline from KGSL and + * also to catch any unbalanced kref of timeline. + */ + BUG_ON(sync_timeline && (sync_timeline->destroyed != true)); +} static const struct sync_timeline_ops kgsl_sync_timeline_ops = { .driver_name = "kgsl-timeline", .dup = kgsl_sync_pt_dup, .has_signaled = kgsl_sync_pt_has_signaled, .compare = kgsl_sync_pt_compare, + .timeline_value_str = kgsl_sync_timeline_value_str, + .pt_value_str = kgsl_sync_pt_value_str, + .release_obj = kgsl_sync_timeline_release_obj, }; int kgsl_sync_timeline_create(struct kgsl_context *context) { struct kgsl_sync_timeline *ktimeline; + /* Generate a name which includes the thread name, thread id, process + * name, process id, and context id. This makes it possible to + * identify the context of a timeline in the sync dump. */ + char ktimeline_name[sizeof(context->timeline->name)] = {}; + snprintf(ktimeline_name, sizeof(ktimeline_name), + "%s_%.15s(%d)-%.15s(%d)-%d", + context->dev_priv->device->name, + current->group_leader->comm, current->group_leader->pid, + current->comm, current->pid, context->id); + context->timeline = sync_timeline_create(&kgsl_sync_timeline_ops, - (int) sizeof(struct kgsl_sync_timeline), "kgsl-timeline"); + (int) sizeof(struct kgsl_sync_timeline), ktimeline_name); if (context->timeline == NULL) return -EINVAL; ktimeline = (struct kgsl_sync_timeline *) context->timeline; ktimeline->last_timestamp = 0; + ktimeline->device = context->dev_priv->device; + ktimeline->context_id = context->id; return 0; } diff --git a/drivers/gpu/msm/kgsl_sync.h b/drivers/gpu/msm/kgsl_sync.h index 06b3ad0d..63adf06a 100644 --- a/drivers/gpu/msm/kgsl_sync.h +++ b/drivers/gpu/msm/kgsl_sync.h @@ -19,6 +19,8 @@ struct kgsl_sync_timeline { struct sync_timeline timeline; unsigned int last_timestamp; + struct kgsl_device *device; + u32 context_id; }; struct kgsl_sync_pt { diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h index c54445ca..7943be80 100644 --- a/drivers/gpu/msm/kgsl_trace.h +++ b/drivers/gpu/msm/kgsl_trace.h @@ -24,6 +24,8 @@ #include #include "kgsl_device.h" +#include "adreno_drawctxt.h" + struct kgsl_device; struct kgsl_ringbuffer_issueibcmds; struct kgsl_device_waittimestamp; @@ -34,11 +36,16 @@ struct kgsl_device_waittimestamp; TRACE_EVENT(kgsl_issueibcmds, TP_PROTO(struct kgsl_device *device, - struct kgsl_ringbuffer_issueibcmds *cmd, + int drawctxt_id, struct kgsl_ibdesc *ibdesc, - int result), + int numibs, + int timestamp, + int flags, + int result, + unsigned int type), - TP_ARGS(device, cmd, ibdesc, result), + TP_ARGS(device, drawctxt_id, ibdesc, numibs, timestamp, flags, + result, type), TP_STRUCT__entry( __string(device_name, device->name) @@ -48,21 +55,23 @@ TRACE_EVENT(kgsl_issueibcmds, __field(unsigned int, timestamp) __field(unsigned int, flags) __field(int, result) + __field(unsigned int, drawctxt_type) ), TP_fast_assign( __assign_str(device_name, device->name); - __entry->drawctxt_id = cmd->drawctxt_id; + __entry->drawctxt_id = drawctxt_id; __entry->ibdesc_addr = ibdesc[0].gpuaddr; - __entry->numibs = cmd->numibs; - __entry->timestamp = cmd->timestamp; - __entry->flags = cmd->flags; + __entry->numibs = numibs; + __entry->timestamp = timestamp; + __entry->flags = flags; __entry->result = result; + __entry->drawctxt_type = type; ), TP_printk( "d_name=%s ctx=%u ib=0x%u numibs=%u timestamp=0x%x " - "flags=0x%x(%s) result=%d", + "flags=0x%x(%s) result=%d type=%s", __get_str(device_name), __entry->drawctxt_id, __entry->ibdesc_addr, @@ -74,7 +83,9 @@ TRACE_EVENT(kgsl_issueibcmds, { KGSL_CONTEXT_SUBMIT_IB_LIST, "IB_LIST" }, { KGSL_CONTEXT_CTX_SWITCH, "CTX_SWITCH" }) : "None", - __entry->result + __entry->result, + __print_symbolic(__entry->drawctxt_type, + ADRENO_DRAWCTXT_TYPES) ) ); @@ -317,6 +328,8 @@ TRACE_EVENT(kgsl_mem_alloc, __field(unsigned int, size) __field(unsigned int, tgid) __array(char, usage, 16) + __field(unsigned int, id) + __field(unsigned int, flags) ), TP_fast_assign( @@ -325,12 +338,76 @@ TRACE_EVENT(kgsl_mem_alloc, __entry->tgid = mem_entry->priv->pid; kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage), mem_entry->memdesc.flags); + __entry->id = mem_entry->id; + __entry->flags = mem_entry->memdesc.flags; ), TP_printk( - "gpuaddr=0x%08x size=%d tgid=%d usage=%s", + "gpuaddr=0x%08x size=%d tgid=%d usage=%s id=%d flags=0x%08x", __entry->gpuaddr, __entry->size, __entry->tgid, - __entry->usage + __entry->usage, __entry->id, __entry->flags + ) +); + +TRACE_EVENT(kgsl_mem_mmap, + + TP_PROTO(struct kgsl_mem_entry *mem_entry), + + TP_ARGS(mem_entry), + + TP_STRUCT__entry( + __field(unsigned long, useraddr) + __field(unsigned int, gpuaddr) + __field(unsigned int, size) + __array(char, usage, 16) + __field(unsigned int, id) + __field(unsigned int, flags) + ), + + TP_fast_assign( + __entry->useraddr = mem_entry->memdesc.useraddr; + __entry->gpuaddr = mem_entry->memdesc.gpuaddr; + __entry->size = mem_entry->memdesc.size; + kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage), + mem_entry->memdesc.flags); + __entry->id = mem_entry->id; + __entry->flags = mem_entry->memdesc.flags; + ), + + TP_printk( + "useraddr=%lx gpuaddr=0x%08x size=%d usage=%s id=%d" + " flags=0x%08x", + __entry->useraddr, __entry->gpuaddr, __entry->size, + __entry->usage, __entry->id, __entry->flags + ) +); + +TRACE_EVENT(kgsl_mem_unmapped_area_collision, + + TP_PROTO(struct kgsl_mem_entry *mem_entry, + unsigned long hint, + unsigned long len, + unsigned long addr), + + TP_ARGS(mem_entry, hint, len, addr), + + TP_STRUCT__entry( + __field(unsigned int, id) + __field(unsigned long, hint) + __field(unsigned long, len) + __field(unsigned long, addr) + ), + + TP_fast_assign( + __entry->id = mem_entry->id; + __entry->hint = hint; + __entry->len = len; + __entry->addr = addr; + ), + + TP_printk( + "id=%d hint=0x%lx len=%ld addr=0x%lx", + __entry->id, __entry->hint, __entry->len, __entry->addr ) ); @@ -347,6 +424,7 @@ TRACE_EVENT(kgsl_mem_map, __field(int, type) __field(unsigned int, tgid) __array(char, usage, 16) + __field(unsigned int, id) ), TP_fast_assign( @@ -357,13 +435,14 @@ TRACE_EVENT(kgsl_mem_map, __entry->tgid = mem_entry->priv->pid; kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage), mem_entry->memdesc.flags); + __entry->id = mem_entry->id; ), TP_printk( - "gpuaddr=0x%08x size=%d type=%d fd=%d tgid=%d usage %s", + "gpuaddr=0x%08x size=%d type=%d fd=%d tgid=%d usage=%s id=%d", __entry->gpuaddr, __entry->size, __entry->type, __entry->fd, __entry->tgid, - __entry->usage + __entry->usage, __entry->id ) ); @@ -380,6 +459,7 @@ TRACE_EVENT(kgsl_mem_free, __field(int, fd) __field(unsigned int, tgid) __array(char, usage, 16) + __field(unsigned int, id) ), TP_fast_assign( @@ -389,12 +469,13 @@ TRACE_EVENT(kgsl_mem_free, __entry->tgid = mem_entry->priv->pid; kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage), mem_entry->memdesc.flags); + __entry->id = mem_entry->id; ), TP_printk( - "gpuaddr=0x%08x size=%d type=%d tgid=%d usage=%s", + "gpuaddr=0x%08x size=%d type=%d tgid=%d usage=%s id=%d", __entry->gpuaddr, __entry->size, __entry->type, - __entry->tgid, __entry->usage + __entry->tgid, __entry->usage, __entry->id ) ); @@ -411,6 +492,7 @@ DECLARE_EVENT_CLASS(kgsl_mem_timestamp_template, __field(unsigned int, size) __field(int, type) __array(char, usage, 16) + __field(unsigned int, id) __field(unsigned int, drawctxt_id) __field(unsigned int, curr_ts) __field(unsigned int, free_ts) @@ -422,6 +504,7 @@ DECLARE_EVENT_CLASS(kgsl_mem_timestamp_template, __entry->size = mem_entry->memdesc.size; kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage), mem_entry->memdesc.flags); + __entry->id = mem_entry->id; __entry->drawctxt_id = id; __entry->type = mem_entry->memtype; __entry->curr_ts = curr_ts; @@ -429,13 +512,14 @@ DECLARE_EVENT_CLASS(kgsl_mem_timestamp_template, ), TP_printk( - "d_name=%s gpuaddr=0x%08x size=%d type=%d usage=%s ctx=%u" + "d_name=%s gpuaddr=0x%08x size=%d type=%d usage=%s id=%d ctx=%u" " curr_ts=0x%x free_ts=0x%x", __get_str(device_name), __entry->gpuaddr, __entry->size, __entry->type, __entry->usage, + __entry->id, __entry->drawctxt_id, __entry->curr_ts, __entry->free_ts @@ -553,21 +637,48 @@ TRACE_EVENT(kgsl_register_event, TRACE_EVENT(kgsl_fire_event, TP_PROTO(unsigned int id, unsigned int ts, - unsigned int age), - TP_ARGS(id, ts, age), + unsigned int type, unsigned int age), + TP_ARGS(id, ts, type, age), TP_STRUCT__entry( __field(unsigned int, id) __field(unsigned int, ts) + __field(unsigned int, type) __field(unsigned int, age) ), TP_fast_assign( __entry->id = id; __entry->ts = ts; + __entry->type = type; __entry->age = age; ), TP_printk( - "ctx=%d ts=%d age=%u", - __entry->id, __entry->ts, __entry->age) + "ctx=%d ts=%d type=%d age=%u", + __entry->id, __entry->ts, __entry->type, __entry->age) +); + +TRACE_EVENT(kgsl_regwrite, + + TP_PROTO(struct kgsl_device *device, unsigned int offset, + unsigned int value), + + TP_ARGS(device, offset, value), + + TP_STRUCT__entry( + __string(device_name, device->name) + __field(unsigned int, offset) + __field(unsigned int, value) + ), + + TP_fast_assign( + __assign_str(device_name, device->name); + __entry->offset = offset; + __entry->value = value; + ), + + TP_printk( + "d_name=%s reg=%x value=%x", + __get_str(device_name), __entry->offset, __entry->value + ) ); #endif /* _KGSL_TRACE_H */ diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c index cfa39b2b..95b3abbd 100644 --- a/drivers/gpu/msm/z180.c +++ b/drivers/gpu/msm/z180.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2014, The Linux Foundation. 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 and @@ -93,7 +93,8 @@ enum z180_cmdwindow_type { #define Z180_CMDWINDOW_TARGET_SHIFT 0 #define Z180_CMDWINDOW_ADDR_SHIFT 8 -static int z180_start(struct kgsl_device *device, unsigned int init_ram); +static int z180_init(struct kgsl_device *device); +static int z180_start(struct kgsl_device *device); static int z180_stop(struct kgsl_device *device); static int z180_wait(struct kgsl_device *device, struct kgsl_context *context, @@ -123,6 +124,9 @@ static void z180_cmdwindow_write(struct kgsl_device *device, | (MMU_CONFIG << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT) \ | (MMU_CONFIG << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT)) +/*default log levels is error for everything*/ +#define KGSL_LOG_LEVEL_DEFAULT 3 + static const struct kgsl_functable z180_functable; static struct z180_device device_2d0 = { @@ -148,6 +152,13 @@ static struct z180_device device_2d0 = { }, .iomemname = KGSL_2D0_REG_MEMORY, .ftbl = &z180_functable, + .cmd_log = KGSL_LOG_LEVEL_DEFAULT, + .ctxt_log = KGSL_LOG_LEVEL_DEFAULT, + .drv_log = KGSL_LOG_LEVEL_DEFAULT, + .mem_log = KGSL_LOG_LEVEL_DEFAULT, + .pwr_log = KGSL_LOG_LEVEL_DEFAULT, + .ft_log = KGSL_LOG_LEVEL_DEFAULT, + .pm_dump_enable = 0, }, .cmdwin_lock = __SPIN_LOCK_INITIALIZER(device_2d1.cmdwin_lock), }; @@ -175,6 +186,13 @@ static struct z180_device device_2d1 = { }, .iomemname = KGSL_2D1_REG_MEMORY, .ftbl = &z180_functable, + .cmd_log = KGSL_LOG_LEVEL_DEFAULT, + .ctxt_log = KGSL_LOG_LEVEL_DEFAULT, + .drv_log = KGSL_LOG_LEVEL_DEFAULT, + .mem_log = KGSL_LOG_LEVEL_DEFAULT, + .pwr_log = KGSL_LOG_LEVEL_DEFAULT, + .ft_log = KGSL_LOG_LEVEL_DEFAULT, + .pm_dump_enable = 0, }, .cmdwin_lock = __SPIN_LOCK_INITIALIZER(device_2d1.cmdwin_lock), }; @@ -244,22 +262,26 @@ static int z180_setup_pt(struct kgsl_device *device, int result = 0; struct z180_device *z180_dev = Z180_DEVICE(device); - result = kgsl_mmu_map_global(pagetable, &device->mmu.setstate_memory, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + result = kgsl_mmu_map_global(pagetable, &device->mmu.setstate_memory); if (result) goto error; - result = kgsl_mmu_map_global(pagetable, &device->memstore, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + result = kgsl_mmu_map_global(pagetable, &device->memstore); if (result) goto error_unmap_dummy; result = kgsl_mmu_map_global(pagetable, - &z180_dev->ringbuffer.cmdbufdesc, - GSL_PT_PAGE_RV); + &z180_dev->ringbuffer.cmdbufdesc); if (result) goto error_unmap_memstore; + /* + * Set the mpu end to the last "normal" global memory we use. + * For the IOMMU, this will be used to restrict access to the + * mapped registers. + */ + device->mh.mpu_range = z180_dev->ringbuffer.cmdbufdesc.gpuaddr + + z180_dev->ringbuffer.cmdbufdesc.size; return result; error_unmap_dummy: @@ -315,16 +337,11 @@ static void addcmd(struct z180_ringbuffer *rb, unsigned int timestamp, *p++ = ADDR_VGV3_LAST << 24; } -static void z180_cmdstream_start(struct kgsl_device *device, int init_ram) +static void z180_cmdstream_start(struct kgsl_device *device) { struct z180_device *z180_dev = Z180_DEVICE(device); unsigned int cmd = VGV3_NEXTCMD_JUMP << VGV3_NEXTCMD_NEXTCMD_FSHIFT; - if (init_ram) { - z180_dev->timestamp = 0; - z180_dev->current_timestamp = 0; - } - addmarker(&z180_dev->ringbuffer, 0); z180_cmdwindow_write(device, ADDR_VGV3_MODE, 4); @@ -424,7 +441,7 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, "Cannot make kernel mapping for gpuaddr 0x%x\n", cmd); result = -EINVAL; - goto error; + goto error_put; } KGSL_CMD_INFO(device, "ctxt %d ibaddr 0x%08x sizedwords %d\n", @@ -450,7 +467,7 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, if (result < 0) { KGSL_CMD_ERR(device, "wait_event_interruptible_timeout " "failed: %ld\n", result); - goto error; + goto error_put; } result = 0; @@ -482,7 +499,13 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, z180_cmdwindow_write(device, ADDR_VGV3_CONTROL, cmd); z180_cmdwindow_write(device, ADDR_VGV3_CONTROL, 0); +error_put: + kgsl_mem_entry_put(entry); error: + + kgsl_trace_issueibcmds(device, context->id, ibdesc, numibs, + *timestamp, ctrl, result, 0); + return (int)result; } @@ -491,6 +514,7 @@ static int z180_ringbuffer_init(struct kgsl_device *device) struct z180_device *z180_dev = Z180_DEVICE(device); memset(&z180_dev->ringbuffer, 0, sizeof(struct z180_ringbuffer)); z180_dev->ringbuffer.prevctx = Z180_INVALID_CONTEXT; + z180_dev->ringbuffer.cmdbufdesc.flags = KGSL_MEMFLAGS_GPUREADONLY; return kgsl_allocate_contiguous(&z180_dev->ringbuffer.cmdbufdesc, Z180_RB_SIZE); } @@ -547,7 +571,17 @@ static int __devexit z180_remove(struct platform_device *pdev) return 0; } -static int z180_start(struct kgsl_device *device, unsigned int init_ram) +static int z180_init(struct kgsl_device *device) +{ + struct z180_device *z180_dev = Z180_DEVICE(device); + + z180_dev->timestamp = 0; + z180_dev->current_timestamp = 0; + + return 0; +} + +static int z180_start(struct kgsl_device *device) { int status = 0; @@ -564,11 +598,14 @@ static int z180_start(struct kgsl_device *device, unsigned int init_ram) if (status) goto error_clk_off; - z180_cmdstream_start(device, init_ram); + z180_cmdstream_start(device); mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT); kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); device->ftbl->irqctrl(device, 1); + + device->reset_counter++; + return 0; error_clk_off: @@ -806,13 +843,29 @@ static int z180_waittimestamp(struct kgsl_device *device, unsigned int msecs) { int status = -EINVAL; + long timeout = 0; - /* Don't wait forever, set a max (10 sec) value for now */ + /* Don't wait forever, set a max (20 sec) value for now */ if (msecs == -1) - msecs = 10 * MSEC_PER_SEC; + msecs = 20 * MSEC_PER_SEC; mutex_unlock(&device->mutex); - status = z180_wait(device, context, timestamp, msecs); + timeout = wait_io_event_interruptible_timeout( + device->wait_queue, + kgsl_check_timestamp(device, context, timestamp), + msecs_to_jiffies(msecs)); + + if (timeout > 0) + status = 0; + else if (timeout == 0) { + status = -ETIMEDOUT; + mutex_lock(&device->mutex); + kgsl_pwrctrl_set_state(device, KGSL_STATE_HUNG); + kgsl_postmortem_dump(device, 0); + mutex_unlock(&device->mutex); + } else + status = timeout; + mutex_lock(&device->mutex); return status; @@ -911,6 +964,7 @@ static const struct kgsl_functable z180_functable = { .idle = z180_idle, .isidle = z180_isidle, .suspend_context = z180_suspend_context, + .init = z180_init, .start = z180_start, .stop = z180_stop, .getproperty = z180_getproperty, diff --git a/drivers/gpu/msm/z180.h b/drivers/gpu/msm/z180.h index 268aac3e..e247d10a 100644 --- a/drivers/gpu/msm/z180.h +++ b/drivers/gpu/msm/z180.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2012,2014 The Linux Foundation. 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 and @@ -28,8 +28,8 @@ #define Z180_DEFAULT_PWRSCALE_POLICY NULL -/* Wait a maximum of 10 seconds when trying to idle the core */ -#define Z180_IDLE_TIMEOUT (10 * 1000) +/* Wait a maximum of 20 seconds when trying to idle the core */ +#define Z180_IDLE_TIMEOUT (20 * 1000) struct z180_ringbuffer { unsigned int prevctx; diff --git a/drivers/gpu/msm/z180_postmortem.c b/drivers/gpu/msm/z180_postmortem.c index c1e5f07c..55b8faa3 100644 --- a/drivers/gpu/msm/z180_postmortem.c +++ b/drivers/gpu/msm/z180_postmortem.c @@ -168,6 +168,7 @@ static void z180_dump_ib(struct kgsl_device *device) KGSL_LOG_DUMP(device, "Could not map IB to kernel memory, Ringbuffer Slot: %d\n", rb_slot_num); + kgsl_mem_entry_put(entry); continue; } @@ -190,6 +191,7 @@ static void z180_dump_ib(struct kgsl_device *device) linebuf); } KGSL_LOG_DUMP(device, "IB Dump Finished\n"); + kgsl_mem_entry_put(entry); } } } diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h index c000fa47..0eb21dc8 100644 --- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h +++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h @@ -13,7 +13,7 @@ #ifndef _MPQ_DMX_PLUGIN_COMMON_H #define _MPQ_DMX_PLUGIN_COMMON_H -#include +#include #include "dvbdev.h" #include "dmxdev.h" diff --git a/drivers/media/video/msm/actuators/msm_actuator.c b/drivers/media/video/msm/actuators/msm_actuator.c index 0f44897d..4244ea30 100644 --- a/drivers/media/video/msm/actuators/msm_actuator.c +++ b/drivers/media/video/msm/actuators/msm_actuator.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2012, Code Aurora Forum. 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 and @@ -67,11 +67,9 @@ int32_t msm_actuator_i2c_write(struct msm_actuator_ctrl_t *a_ctrl, struct msm_actuator_reg_params_t *write_arr = a_ctrl->reg_tbl; uint32_t hw_dword = hw_params; uint16_t i2c_byte1 = 0, i2c_byte2 = 0; - uint32_t value = 0; + uint16_t value = 0; uint32_t size = a_ctrl->reg_tbl_size, i = 0; int32_t rc = 0; - unsigned char buf[4]; - int length = 0; CDBG("%s: IN\n", __func__); for (i = 0; i < size; i++) { if (write_arr[i].reg_write_type == MSM_ACTUATOR_WRITE_DAC) { @@ -84,7 +82,7 @@ int32_t msm_actuator_i2c_write(struct msm_actuator_ctrl_t *a_ctrl, i2c_byte1 = write_arr[i].reg_addr; i2c_byte2 = value; if (size != (i+1)) { - i2c_byte2 = value & 0xFF; + i2c_byte2 = (i2c_byte2 & 0xFF00) >> 8; CDBG("%s: byte1:0x%x, byte2:0x%x\n", __func__, i2c_byte1, i2c_byte2); rc = msm_camera_i2c_write( @@ -99,33 +97,13 @@ int32_t msm_actuator_i2c_write(struct msm_actuator_ctrl_t *a_ctrl, i++; i2c_byte1 = write_arr[i].reg_addr; - i2c_byte2 = (value & 0xFF00) >> 8; + i2c_byte2 = value & 0xFF; } } else { i2c_byte1 = (value & 0xFF00) >> 8; i2c_byte2 = value & 0xFF; } - } - else if (write_arr[i].reg_write_type == MSM_ACTUATOR_WRITE_DAC_AD5823) { - value = (next_lens_position << - write_arr[i].data_shift) | - ((hw_dword & write_arr[i].hw_mask) << - write_arr[i].hw_shift); - - buf[0] = write_arr[i].reg_addr; - buf[1] = (value & 0xff00)>>8;//MSB - buf[2] = value & 0xff;//LSB - length = 3; - CDBG("%s: position is 0x%x, MSB0x%x, LSB:0x%x\r\n", __func__, value, buf[1], buf[2]); - rc = msm_camera_i2c_txdata(&a_ctrl->i2c_client, buf, length); - if (rc < 0) { - pr_err("%s: i2c write error:%d\n", - __func__, rc); - return rc; - } - break; - } - else { + } else { i2c_byte1 = write_arr[i].reg_addr; i2c_byte2 = (hw_dword & write_arr[i].hw_mask) >> write_arr[i].hw_shift; @@ -215,6 +193,8 @@ int32_t msm_actuator_write_focus( } curr_lens_pos = next_lens_pos; usleep(wait_time); + if(0 == damping_code_step) + break; } if (curr_lens_pos != code_boundary) { @@ -315,6 +295,7 @@ int32_t msm_actuator_move_focus( target_step_pos = dest_step_pos; target_lens_pos = a_ctrl->step_position_table[target_step_pos]; + /*change from Qualcomm. delete some codes*/ rc = a_ctrl->func_tbl-> actuator_write_focus( a_ctrl, @@ -334,6 +315,7 @@ int32_t msm_actuator_move_focus( target_step_pos = step_boundary; target_lens_pos = a_ctrl->step_position_table[target_step_pos]; + /*change from Qualcomm. delete some codes*/ rc = a_ctrl->func_tbl-> actuator_write_focus( a_ctrl, @@ -469,8 +451,7 @@ int32_t msm_actuator_init(struct msm_actuator_ctrl_t *a_ctrl, } a_ctrl->total_steps = set_info->af_tuning_params.total_steps; a_ctrl->pwd_step = set_info->af_tuning_params.pwd_step; - a_ctrl->total_steps = set_info->af_tuning_params.total_steps; - + //delete this line for it is a duplicated written as the line before if (copy_from_user(&a_ctrl->region_params, (void *)set_info->af_tuning_params.region_params, a_ctrl->region_size * sizeof(struct region_params_t))) diff --git a/drivers/media/video/msm/gemini/msm_gemini_platform.c b/drivers/media/video/msm/gemini/msm_gemini_platform.c index 6550c92c..3c038cc0 100644 --- a/drivers/media/video/msm/gemini/msm_gemini_platform.c +++ b/drivers/media/video/msm/gemini/msm_gemini_platform.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -35,9 +34,6 @@ void msm_gemini_platform_p2v(struct file *file, ion_unmap_iommu(gemini_client, *ionhandle, CAMERA_DOMAIN, GEN_POOL); ion_free(gemini_client, *ionhandle); *ionhandle = NULL; -#elif CONFIG_ANDROID_PMEM - put_pmem_file(file); -#endif } uint32_t msm_gemini_platform_v2p(int fd, uint32_t len, struct file **file_p, @@ -53,9 +49,6 @@ uint32_t msm_gemini_platform_v2p(int fd, uint32_t len, struct file **file_p, rc = ion_map_iommu(gemini_client, *ionhandle, CAMERA_DOMAIN, GEN_POOL, SZ_4K, 0, &paddr, (unsigned long *)&size, UNCACHED, 0); -#elif CONFIG_ANDROID_PMEM - unsigned long kvstart; - rc = get_pmem_file(fd, &paddr, &kvstart, &size, file_p); #else rc = 0; paddr = 0; diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c index 656630a8..ffa5ee93 100644 --- a/drivers/media/video/msm/msm_camera.c +++ b/drivers/media/video/msm/msm_camera.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -318,15 +317,6 @@ static int msm_pmem_table_add(struct hlist_head *ptype, goto out1; ion_phys(client_for_ion, region->handle, &paddr, (size_t *)&len); -#else - rc = get_pmem_file(info->fd, &paddr, &kvstart, &len, &file); - if (rc < 0) { - pr_err("%s: get_pmem_file fd %d error %d\n", - __func__, - info->fd, rc); - goto out1; - } - region->file = file; #endif if (!info->len) info->len = len; @@ -363,8 +353,6 @@ static int msm_pmem_table_add(struct hlist_head *ptype, out2: #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION ion_free(client_for_ion, region->handle); -#else - put_pmem_file(region->file); #endif out1: kfree(region); @@ -648,8 +636,6 @@ static int __msm_pmem_table_del(struct msm_sync *sync, hlist_del(node); #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION ion_free(client_for_ion, region->handle); -#else - put_pmem_file(region->file); #endif kfree(region); CDBG("%s: type %d, vaddr 0x%p\n", @@ -672,8 +658,6 @@ static int __msm_pmem_table_del(struct msm_sync *sync, hlist_del(node); #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION ion_free(client_for_ion, region->handle); -#else - put_pmem_file(region->file); #endif kfree(region); CDBG("%s: type %d, vaddr 0x%p\n", @@ -695,8 +679,7 @@ static int __msm_pmem_table_del(struct msm_sync *sync, hlist_del(node); #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION ion_free(client_for_ion, region->handle); -#else - put_pmem_file(region->file); + #endif kfree(region); CDBG("%s: type %d, vaddr 0x%p\n", @@ -3066,8 +3049,7 @@ static int __msm_release(struct msm_sync *sync) hlist_del(hnode); #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION ion_free(client_for_ion, region->handle); -#else - put_pmem_file(region->file); + #endif kfree(region); } @@ -3077,8 +3059,7 @@ static int __msm_release(struct msm_sync *sync) hlist_del(hnode); #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION ion_free(client_for_ion, region->handle); -#else - put_pmem_file(region->file); + #endif kfree(region); } diff --git a/drivers/media/video/msm/msm_isp.c b/drivers/media/video/msm/msm_isp.c index 3a86fd07..e7245aa7 100644 --- a/drivers/media/video/msm/msm_isp.c +++ b/drivers/media/video/msm/msm_isp.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include diff --git a/drivers/media/video/msm/msm_mctl.c b/drivers/media/video/msm/msm_mctl.c index 3beaf702..f4ae3154 100644 --- a/drivers/media/video/msm/msm_mctl.c +++ b/drivers/media/video/msm/msm_mctl.c @@ -25,8 +25,6 @@ #include #include -#include - #include #include "msm.h" diff --git a/drivers/media/video/msm/msm_mctl_buf.c b/drivers/media/video/msm/msm_mctl_buf.c index e89c28c1..53c32551 100644 --- a/drivers/media/video/msm/msm_mctl_buf.c +++ b/drivers/media/video/msm/msm_mctl_buf.c @@ -23,8 +23,6 @@ #include #include -#include - #include "msm.h" #include "msm_ispif.h" diff --git a/drivers/media/video/msm/msm_mctl_pp.c b/drivers/media/video/msm/msm_mctl_pp.c index 2af95df5..676e623e 100644 --- a/drivers/media/video/msm/msm_mctl_pp.c +++ b/drivers/media/video/msm/msm_mctl_pp.c @@ -25,8 +25,6 @@ #include #include -#include - #include "msm.h" #include "msm_vpe.h" diff --git a/drivers/media/video/msm/msm_mem.c b/drivers/media/video/msm/msm_mem.c index 938b5cd6..be82d0ea 100644 --- a/drivers/media/video/msm/msm_mem.c +++ b/drivers/media/video/msm/msm_mem.c @@ -26,7 +26,6 @@ #include #include -#include #include "msm.h" @@ -72,26 +71,6 @@ static DEFINE_MUTEX(hlist_mut); -#ifdef CONFIG_ANDROID_PMEM -static int check_pmem_info(struct msm_pmem_info *info, int len) -{ - if (info->offset < len && - info->offset + info->len <= len && - info->planar0_off < len && - info->planar1_off < len) - return 0; - - pr_err("%s: check failed: off %d len %d y %d cbcr %d (total len %d)\n", - __func__, - info->offset, - info->len, - info->planar0_off, - info->planar1_off, - len); - return -EINVAL; -} -#endif - static int check_overlap(struct hlist_head *ptype, unsigned long paddr, unsigned long len) @@ -137,16 +116,8 @@ static int msm_pmem_table_add(struct hlist_head *ptype, if (IS_ERR_OR_NULL(region->handle)) goto out1; if (ion_map_iommu(client, region->handle, CAMERA_DOMAIN, GEN_POOL, - SZ_4K, 0, &paddr, &len, UNCACHED, 0) < 0) + SZ_4K, 0, &paddr, &len, 0, 0) < 0) goto out2; -#elif CONFIG_ANDROID_PMEM - rc = get_pmem_file(info->fd, &paddr, &kvstart, &len, &file); - if (rc < 0) { - pr_err("%s: get_pmem_file fd %d error %d\n", - __func__, info->fd, rc); - goto out1; - } - region->file = file; #else paddr = 0; file = NULL; @@ -154,9 +125,6 @@ static int msm_pmem_table_add(struct hlist_head *ptype, #endif if (!info->len) info->len = len; - rc = check_pmem_info(info, len); - if (rc < 0) - goto out3; paddr += info->offset; len = info->len; @@ -186,8 +154,6 @@ static int msm_pmem_table_add(struct hlist_head *ptype, #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION out2: ion_free(client, region->handle); -#elif CONFIG_ANDROID_PMEM - put_pmem_file(region->file); #endif out1: kfree(region); @@ -247,8 +213,7 @@ static int __msm_pmem_table_del(struct hlist_head *ptype, ion_unmap_iommu(client, region->handle, CAMERA_DOMAIN, GEN_POOL); ion_free(client, region->handle); -#else - put_pmem_file(region->file); + #endif kfree(region); } diff --git a/drivers/media/video/msm/msm_v4l2_video.c b/drivers/media/video/msm/msm_v4l2_video.c index fb46cc1a..c082daf3 100644 --- a/drivers/media/video/msm/msm_v4l2_video.c +++ b/drivers/media/video/msm/msm_v4l2_video.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2013, The Linux Foundation. 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 and @@ -22,7 +22,7 @@ #include #include #include - +#include #include #include #include @@ -104,13 +104,22 @@ static int msm_v4l2_overlay_mapformat(uint32_t pixelformat) mdp_format = MDP_RGB_888; break; case V4L2_PIX_FMT_NV12: - mdp_format = MDP_Y_CRCB_H2V2; + mdp_format = MDP_Y_CBCR_H2V2; break; case V4L2_PIX_FMT_NV21: - mdp_format = MDP_Y_CBCR_H2V2; + mdp_format = MDP_Y_CRCB_H2V2; break; case V4L2_PIX_FMT_YUV420: - mdp_format = MDP_Y_CR_CB_H2V2; + mdp_format = MDP_Y_CB_CR_H2V2; + break; + case V4L2_PIX_FMT_UYVY: + mdp_format = MDP_CBYCRY_H2V1; + break; + case V4L2_PIX_FMT_YUYV: + mdp_format = MDP_YCBYCR_H2V1; + break; + case V4L2_PIX_FMT_YVU420: + mdp_format = MDP_Y_CR_CB_GH2V2; break; default: pr_err("%s:Unrecognized format %u\n", __func__, pixelformat); @@ -140,20 +149,22 @@ msm_v4l2_overlay_fb_update(struct msm_v4l2_overlay_device *vout, src_addr = (unsigned long)v4l2_ram_phys + vout->bufs[buffer->index].offset; src_size = buffer->bytesused; - ret = msm_fb_v4l2_update(vout->par, src_addr, src_size, + ret = msm_fb_v4l2_update(vout->par, false, src_addr, src_size, 0, 0, 0, 0); break; case V4L2_MEMORY_USERPTR: if (copy_from_user(&up_buffer, (void __user *)buffer->m.userptr, sizeof(struct msm_v4l2_overlay_userptr_buffer))) { + pr_err("%s:copy_from_user for userptr failed\n", + __func__); mutex_unlock(&msmfb_lock); return -EINVAL; } - ret = msm_fb_v4l2_update(vout->par, - (unsigned long)up_buffer.base[0], up_buffer.length[0], - (unsigned long)up_buffer.base[1], up_buffer.length[1], - (unsigned long)up_buffer.base[2], up_buffer.length[2]); + ret = msm_fb_v4l2_update(vout->par, true, + (unsigned long)up_buffer.fd[0], up_buffer.offset[0], + (unsigned long)up_buffer.fd[1], up_buffer.offset[1], + (unsigned long)up_buffer.fd[2], up_buffer.offset[2]); break; default: mutex_unlock(&msmfb_lock); @@ -420,6 +431,7 @@ msm_v4l2_overlay_do_ioctl(struct file *file, struct msm_v4l2_overlay_fh *fh = file->private_data; struct msm_v4l2_overlay_device *vout = fh->vout; int ret; + struct v4l2_buffer *buffer = arg; switch (cmd) { case VIDIOC_QUERYCAP: @@ -439,7 +451,14 @@ msm_v4l2_overlay_do_ioctl(struct file *file, case VIDIOC_QBUF: mutex_lock(&vout->update_lock); - ret = msm_v4l2_overlay_vidioc_qbuf(file, fh, arg, false); + if (buffer->memory == V4L2_MEMORY_USERPTR) { + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + ret = msm_v4l2_overlay_vidioc_qbuf(file, fh, arg, true); + } else { + ret = msm_v4l2_overlay_vidioc_qbuf(file, fh, arg, + false); + } mutex_unlock(&vout->update_lock); return ret; @@ -723,6 +742,9 @@ msm_v4l2_overlay_mmap(struct file *filp, struct vm_area_struct * vma) unsigned long off = vma->vm_pgoff << PAGE_SHIFT; u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + v4l2_ram_size); + if (!start) + return -EINVAL; + /* * This is probably unnecessary now - the last PAGE_SHIFT * bits of start should be 0 now, since we are page aligning @@ -730,16 +752,20 @@ msm_v4l2_overlay_mmap(struct file *filp, struct vm_area_struct * vma) */ start &= PAGE_MASK; + if ((vma->vm_end <= vma->vm_start) || + (off >= len) || + ((vma->vm_end - vma->vm_start) > (len - off))) { + pr_err("v4l2 map request, memory requested out of bounds\n"); + return -EINVAL; + } + pr_debug("v4l2 map req for phys(%p,%p) offset %u to virt (%p,%p)\n", (void *)(start+off), (void *)(start+off+(vma->vm_end - vma->vm_start)), (unsigned int)off, (void *)vma->vm_start, (void *)vma->vm_end); - if ((vma->vm_end - vma->vm_start + off) > len) { - pr_err("v4l2 map request, memory requested too big\n"); - return -EINVAL; - } - start += off; + if (start < off) + return -EINVAL; vma->vm_pgoff = start >> PAGE_SHIFT; /* This is an IO map - tell maydump to skip this VMA */ vma->vm_flags |= VM_IO | VM_RESERVED; @@ -825,7 +851,7 @@ msm_v4l2_overlay_open(struct file *file) | V4L2_FBUF_CAP_LOCAL_ALPHA; vout->fb.flags = V4L2_FBUF_FLAG_LOCAL_ALPHA; vout->fb.base = 0; - memcpy(&vout->fb.fmt, pix, sizeof(struct v4l2_format)); + memcpy(&vout->fb.fmt, pix, sizeof(struct v4l2_pix_format)); vout->bufs = 0; vout->numbufs = 0; diff --git a/drivers/media/video/msm/msm_v4l2_video.h b/drivers/media/video/msm/msm_v4l2_video.h index 0c09a3f8..6de6c80e 100644 --- a/drivers/media/video/msm/msm_v4l2_video.h +++ b/drivers/media/video/msm/msm_v4l2_video.h @@ -52,8 +52,8 @@ struct msm_v4l2_overlay_fh { }; struct msm_v4l2_overlay_userptr_buffer { - uint base[3]; - size_t length[3]; + uint fd[3]; /* ION fd of frame or plane */ + size_t offset[3]; /* offset of frame or plane in multi buffer case */ }; #endif diff --git a/drivers/media/video/msm/msm_vfe7x.c b/drivers/media/video/msm/msm_vfe7x.c index bbf9d1b0..8354671c 100644 --- a/drivers/media/video/msm/msm_vfe7x.c +++ b/drivers/media/video/msm/msm_vfe7x.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/media/video/msm/msm_vfe7x27a.c b/drivers/media/video/msm/msm_vfe7x27a.c index 0279c780..dae2b6e7 100644 --- a/drivers/media/video/msm/msm_vfe7x27a.c +++ b/drivers/media/video/msm/msm_vfe7x27a.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/media/video/msm/msm_vfe7x27a_v4l2.c b/drivers/media/video/msm/msm_vfe7x27a_v4l2.c index ef11e4b7..f24df39b 100644 --- a/drivers/media/video/msm/msm_vfe7x27a_v4l2.c +++ b/drivers/media/video/msm/msm_vfe7x27a_v4l2.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/media/video/msm/msm_vfe_stats_buf.c b/drivers/media/video/msm/msm_vfe_stats_buf.c index c21664b3..4eccd8e4 100644 --- a/drivers/media/video/msm/msm_vfe_stats_buf.c +++ b/drivers/media/video/msm/msm_vfe_stats_buf.c @@ -25,7 +25,6 @@ #include #include -#include #include #include #include "msm.h" @@ -162,25 +161,6 @@ static int msm_stats_deinit(struct msm_stats_bufq_ctrl *stats_ctrl) return rc; } -#ifdef CONFIG_ANDROID_PMEM -static int msm_stats_check_pmem_info(struct msm_stats_buf_info *info, int len) -{ - if (info->offset < len && - info->offset + info->len <= len && - info->planar0_off < len && info->planar1_off < len) - return 0; - - pr_err("%s: check failed: off %d len %d y %d cbcr %d (total len %d)\n", - __func__, - info->offset, - info->len, - info->planar0_off, - info->planar1_off, - len); - return -EINVAL; -} -#endif - static int msm_stats_buf_prepare(struct msm_stats_bufq_ctrl *stats_ctrl, struct msm_stats_buf_info *info, struct ion_client *client) { @@ -225,14 +205,7 @@ static int msm_stats_buf_prepare(struct msm_stats_bufq_ctrl *stats_ctrl, pr_err("%s: cannot map address", __func__); goto out2; } -#elif CONFIG_ANDROID_PMEM - rc = get_pmem_file(info->fd, &paddr, &kvstart, &len, &file); - if (rc < 0) { - pr_err("%s: get_pmem_file fd %d error %d\n", - __func__, info->fd, rc); - goto out1; - } - stats_buf->file = file; + #else paddr = 0; file = NULL; @@ -240,11 +213,6 @@ static int msm_stats_buf_prepare(struct msm_stats_bufq_ctrl *stats_ctrl, #endif if (!info->len) info->len = len; - rc = msm_stats_check_pmem_info(info, len); - if (rc < 0) { - pr_err("%s: msm_stats_check_pmem_info err = %d", __func__, rc); - goto out3; - } paddr += info->offset; len = info->len; stats_buf->paddr = paddr; @@ -255,15 +223,14 @@ static int msm_stats_buf_prepare(struct msm_stats_bufq_ctrl *stats_ctrl, D("%s pmem_stats address is 0x%ld\n", __func__, paddr); stats_buf->state = MSM_STATS_BUFFER_STATE_PREPARED; return 0; -out3: + #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION ion_unmap_iommu(client, stats_buf->handle, CAMERA_DOMAIN, GEN_POOL); #endif #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION out2: ion_free(client, stats_buf->handle); -#elif CONFIG_ANDROID_PMEM - put_pmem_file(stats_buf->file); + #endif out1: return rc; @@ -294,8 +261,7 @@ static int msm_stats_buf_unprepare(struct msm_stats_bufq_ctrl *stats_ctrl, ion_unmap_iommu(client, stats_buf->handle, CAMERA_DOMAIN, GEN_POOL); ion_free(client, stats_buf->handle); -#else - put_pmem_file(stats_buf->file); + #endif if (stats_buf->state == MSM_STATS_BUFFER_STATE_QUEUED) { /* buf queued need delete from list */ diff --git a/drivers/media/video/msm/wfd/wfd-ioctl.c b/drivers/media/video/msm/wfd/wfd-ioctl.c index 887af958..4b60970a 100644 --- a/drivers/media/video/msm/wfd/wfd-ioctl.c +++ b/drivers/media/video/msm/wfd/wfd-ioctl.c @@ -18,8 +18,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/drivers/media/video/videobuf-msm-mem.c b/drivers/media/video/videobuf-msm-mem.c index 9e2cc222..28fed735 100644 --- a/drivers/media/video/videobuf-msm-mem.c +++ b/drivers/media/video/videobuf-msm-mem.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -140,55 +139,6 @@ static const struct vm_operations_struct videobuf_vm_ops = { .close = videobuf_vm_close, }; -/** - * videobuf_pmem_contig_user_put() - reset pointer to user space buffer - * @mem: per-buffer private videobuf-contig-pmem data - * - * This function resets the user space pointer - */ -static void videobuf_pmem_contig_user_put(struct videobuf_contig_pmem *mem) -{ - if (mem->phyaddr) { - put_pmem_file(mem->file); - mem->is_userptr = 0; - mem->phyaddr = 0; - mem->size = 0; - } -} - -/** - * videobuf_pmem_contig_user_get() - setup user space memory pointer - * @mem: per-buffer private videobuf-contig-pmem data - * @vb: video buffer to map - * - * This function validates and sets up a pointer to user space memory. - * Only physically contiguous pfn-mapped memory is accepted. - * - * Returns 0 if successful. - */ -static int videobuf_pmem_contig_user_get(struct videobuf_contig_pmem *mem, - struct videobuf_buffer *vb) -{ - unsigned long kvstart; - unsigned long len; - int rc; - - mem->size = PAGE_ALIGN(vb->size); - rc = get_pmem_file(vb->baddr, (unsigned long *)&mem->phyaddr, - &kvstart, &len, &mem->file); - if (rc < 0) { - pr_err("%s: get_pmem_file fd %lu error %d\n", - __func__, vb->baddr, - rc); - return rc; - } - mem->phyaddr += vb->boff; - mem->y_off = 0; - mem->cbcr_off = (vb->size)*2/3; - mem->is_userptr = 1; - return rc; -} - static struct videobuf_buffer *__videobuf_alloc(size_t size) { struct videobuf_contig_pmem *mem; @@ -229,12 +179,6 @@ static int __videobuf_iolock(struct videobuf_queue *q, /* All handling should be done by __videobuf_mmap_mapper() */ break; - case V4L2_MEMORY_USERPTR: - D("%s memory method USERPTR\n", __func__); - - /* handle pointer from user space */ - rc = videobuf_pmem_contig_user_get(mem, vb); - break; case V4L2_MEMORY_OVERLAY: default: pr_err("%s memory method OVERLAY/unknown\n", __func__); @@ -383,7 +327,6 @@ int videobuf_pmem_contig_free(struct videobuf_queue *q, /* handle user space pointer case */ if (buf->baddr) { - videobuf_pmem_contig_user_put(mem); return 0; } else { /* don't support read() method */ diff --git a/drivers/media/video/videobuf2-msm-mem.c b/drivers/media/video/videobuf2-msm-mem.c index 8114d7a9..d753c42a 100644 --- a/drivers/media/video/videobuf2-msm-mem.c +++ b/drivers/media/video/videobuf2-msm-mem.c @@ -24,10 +24,11 @@ #include #include #include -#include + #include #include #include +#include #include #include #include @@ -56,7 +57,7 @@ static unsigned long msm_mem_allocate(struct videobuf2_contig_pmem *mem) goto client_failed; } mem->ion_handle = ion_alloc(mem->client, mem->size, SZ_4K, - (0x1 << ION_CP_MM_HEAP_ID | 0x1 << ION_IOMMU_HEAP_ID)); + (0x1 << ION_CP_MM_HEAP_ID | 0x1 << ION_IOMMU_HEAP_ID), 0); if (IS_ERR((void *)mem->ion_handle)) { pr_err("%s Could not allocate\n", __func__); goto alloc_failed; @@ -64,7 +65,7 @@ static unsigned long msm_mem_allocate(struct videobuf2_contig_pmem *mem) rc = ion_map_iommu(mem->client, mem->ion_handle, CAMERA_DOMAIN, GEN_POOL, SZ_4K, 0, (unsigned long *)&phyaddr, - (unsigned long *)&len, UNCACHED, 0); + (unsigned long *)&len, 0, 0); if (rc < 0) { pr_err("%s Could not get physical address\n", __func__); goto phys_failed; @@ -176,35 +177,25 @@ int videobuf2_pmem_contig_user_get(struct videobuf2_contig_pmem *mem, uint32_t addr_offset, int path, struct ion_client *client) { - unsigned long len; int rc = 0; -#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION - unsigned long kvstart; +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + unsigned long len; #endif unsigned long paddr = 0; if (mem->phyaddr != 0) return 0; -#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION +#if defined(CONFIG_MSM_MULTIMEDIA_USE_ION) mem->ion_handle = ion_import_dma_buf(client, (int)mem->vaddr); if (IS_ERR_OR_NULL(mem->ion_handle)) { pr_err("%s ION import failed\n", __func__); return PTR_ERR(mem->ion_handle); } rc = ion_map_iommu(client, mem->ion_handle, CAMERA_DOMAIN, GEN_POOL, - SZ_4K, 0, (unsigned long *)&mem->phyaddr, &len, UNCACHED, 0); + SZ_4K, 0, (unsigned long *)&mem->phyaddr, &len, 0, 0); if (rc < 0) ion_free(client, mem->ion_handle); -#elif CONFIG_ANDROID_PMEM - rc = get_pmem_file((int)mem->vaddr, (unsigned long *)&mem->phyaddr, - &kvstart, &len, &mem->file); - if (rc < 0) { - pr_err("%s: get_pmem_file fd %d error %d\n", - __func__, (int)mem->vaddr, rc); - return rc; - } #else paddr = 0; - kvstart = 0; #endif if (offset) mem->offset = *offset; @@ -223,12 +214,10 @@ void videobuf2_pmem_contig_user_put(struct videobuf2_contig_pmem *mem, struct ion_client *client) { if (mem->is_userptr) { -#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION +#if defined(CONFIG_MSM_MULTIMEDIA_USE_ION) ion_unmap_iommu(client, mem->ion_handle, CAMERA_DOMAIN, GEN_POOL); ion_free(client, mem->ion_handle); -#elif CONFIG_ANDROID_PMEM - put_pmem_file(mem->file); #endif } mem->is_userptr = 0; @@ -298,9 +287,9 @@ static int msm_vb2_mem_ops_mmap(void *buf_priv, struct vm_area_struct *vma) vma->vm_flags |= VM_DONTEXPAND; vma->vm_private_data = mem; - D("mmap %p: %08lx-%08lx (%lx) pgoff %08lx\n", - map, vma->vm_start, vma->vm_end, - (long int)mem->bsize, vma->vm_pgoff); + D("mmap: %08lx-%08lx (%lx) pgoff %08lx\n", + vma->vm_start, vma->vm_end, + (long int)mem->size, vma->vm_pgoff); videobuf2_vm_open(vma); return 0; error: diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 5df8afbe..14df87dd 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index c8c1cdea..40877a8b 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -6957,14 +6957,6 @@ int msm_nand_scan(struct mtd_info *mtd, int maxchips) mtd->_unpoint = NULL; mtd->_read = msm_nand_read; mtd->_write = msm_nand_write; -<<<<<<< HEAD -======= -#if 0 -//#ifdef CONFIG_HUAWEI_KERNEL - /* add for android panic support*/ - mtd->_panic_write = msm_nand_write_apanic; -#endif ->>>>>>> d7fe698... msm: dma: upstream to CAF Ifaa5efdde8150f932a12d4f9eeccfb36ecb8f88f mtd->_read_oob = msm_nand_read_oob; mtd->_write_oob = msm_nand_write_oob; if (dual_nand_ctlr_present) { diff --git a/drivers/net/ethernet/msm/msm_rmnet.c b/drivers/net/ethernet/msm/msm_rmnet.c index f8f5a55c..87de1778 100644 --- a/drivers/net/ethernet/msm/msm_rmnet.c +++ b/drivers/net/ethernet/msm/msm_rmnet.c @@ -94,6 +94,7 @@ struct rmnet_private struct sk_buff *skb; spinlock_t lock; struct tasklet_struct tsklt; + struct tasklet_struct rx_tasklet; u32 operation_mode; /* IOCTL specified mode (protocol, QoS header) */ struct platform_driver pdrv; struct completion complete; @@ -257,7 +258,6 @@ static __be16 rmnet_ip_type_trans(struct sk_buff *skb, struct net_device *dev) } static void smd_net_data_handler(unsigned long arg); -static DECLARE_TASKLET(smd_net_data_tasklet, smd_net_data_handler, 0); /* Called in soft-irq context */ static void smd_net_data_handler(unsigned long arg) @@ -280,8 +280,8 @@ static void smd_net_data_handler(unsigned long arg) pr_err("[%s] rmnet_recv() cannot allocate skb\n", dev->name); /* out of memory, reschedule a later attempt */ - smd_net_data_tasklet.data = (unsigned long)dev; - tasklet_schedule(&smd_net_data_tasklet); + p->rx_tasklet.data = (unsigned long)dev; + tasklet_schedule(&p->rx_tasklet); break; } else { skb->dev = dev; @@ -449,8 +449,8 @@ static void smd_net_notify(void *_dev, unsigned event) if (smd_read_avail(p->ch) && (smd_read_avail(p->ch) >= smd_cur_packet_size(p->ch))) { - smd_net_data_tasklet.data = (unsigned long) _dev; - tasklet_schedule(&smd_net_data_tasklet); + p->rx_tasklet.data = (unsigned long) _dev; + tasklet_schedule(&p->rx_tasklet); } break; @@ -501,16 +501,10 @@ static int __rmnet_open(struct net_device *dev) static int __rmnet_close(struct net_device *dev) { struct rmnet_private *p = netdev_priv(dev); - int rc; - unsigned long flags; - if (p->ch) { - rc = smd_close(p->ch); - spin_lock_irqsave(&p->lock, flags); - p->ch = 0; - spin_unlock_irqrestore(&p->lock, flags); - return rc; - } else + if (p->ch) + return 0; + else return -EBADF; } @@ -529,12 +523,9 @@ static int rmnet_open(struct net_device *dev) static int rmnet_stop(struct net_device *dev) { - struct rmnet_private *p = netdev_priv(dev); - DBG0("[%s] rmnet_stop()\n", dev->name); netif_stop_queue(dev); - tasklet_kill(&p->tsklt); /* TODO: unload modem safely, currently, this causes unnecessary unloads */ @@ -797,6 +788,8 @@ static int __init rmnet_init(void) spin_lock_init(&p->lock); tasklet_init(&p->tsklt, _rmnet_resume_flow, (unsigned long)dev); + tasklet_init(&p->rx_tasklet, smd_net_data_handler, + (unsigned long)dev); wake_lock_init(&p->wake_lock, WAKE_LOCK_SUSPEND, ch_name[n]); #ifdef CONFIG_MSM_RMNET_DEBUG p->timeout_us = timeout_us; diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 09602241..07354883 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -1,6 +1,6 @@ menuconfig ATH_COMMON tristate "Atheros Wireless Cards" - depends on CFG80211 && (!UML || BROKEN) + depends on CFG80211 ---help--- This will enable the support for the Atheros wireless drivers. ath5k, ath9k, ath9k_htc and ar9170 drivers share some common code, this option diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index d716b748..f4eb545b 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -1,14 +1,17 @@ +NOSTDINC_FLAGS := -I$(srctree)/include/compat/ \ + -include $(srctree)/include/compat/linux/compat-2.6.h \ + $(CFLAGS) + obj-$(CONFIG_ATH5K) += ath5k/ +obj-$(CONFIG_ATH6KL) += ath6kl/ obj-$(CONFIG_ATH9K_HW) += ath9k/ obj-$(CONFIG_CARL9170) += carl9170/ -obj-$(CONFIG_ATH6KL) += ath6kl/ -obj-$(CONFIG_ATH_COMMON) += ath.o +# obj-$(CONFIG_ATH_COMMON) += ath.o ath-objs := main.o \ regd.o \ hw.o \ key.o -ath-$(CONFIG_ATH_DEBUG) += debug.o -ccflags-y += -D__CHECK_ENDIAN__ +ath-$(CONFIG_ATH_DEBUG) += debug.o \ No newline at end of file diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h old mode 100644 new mode 100755 diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c index aec33cc2..e5be7e70 100644 --- a/drivers/net/wireless/ath/ath5k/ahb.c +++ b/drivers/net/wireless/ath/ath5k/ahb.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include "ath5k.h" #include "debug.h" @@ -120,7 +119,7 @@ static int ath_ahb_probe(struct platform_device *pdev) if (res == NULL) { dev_err(&pdev->dev, "no IRQ resource found\n"); ret = -ENXIO; - goto err_iounmap; + goto err_out; } irq = res->start; @@ -129,7 +128,7 @@ static int ath_ahb_probe(struct platform_device *pdev) if (hw == NULL) { dev_err(&pdev->dev, "no memory for ieee80211_hw\n"); ret = -ENOMEM; - goto err_iounmap; + goto err_out; } ah = hw->priv; @@ -141,23 +140,23 @@ static int ath_ahb_probe(struct platform_device *pdev) if (bcfg->devid >= AR5K_SREV_AR2315_R6) { /* Enable WMAC AHB arbitration */ - reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL); + reg = __raw_readl((void __iomem *) AR5K_AR2315_AHB_ARB_CTL); reg |= AR5K_AR2315_AHB_ARB_CTL_WLAN; - iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL); + __raw_writel(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL); /* Enable global WMAC swapping */ - reg = ioread32((void __iomem *) AR5K_AR2315_BYTESWAP); + reg = __raw_readl((void __iomem *) AR5K_AR2315_BYTESWAP); reg |= AR5K_AR2315_BYTESWAP_WMAC; - iowrite32(reg, (void __iomem *) AR5K_AR2315_BYTESWAP); + __raw_writel(reg, (void __iomem *) AR5K_AR2315_BYTESWAP); } else { /* Enable WMAC DMA access (assuming 5312 or 231x*/ /* TODO: check other platforms */ - reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE); + reg = __raw_readl((void __iomem *) AR5K_AR5312_ENABLE); if (to_platform_device(ah->dev)->id == 0) reg |= AR5K_AR5312_ENABLE_WLAN0; else reg |= AR5K_AR5312_ENABLE_WLAN1; - iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE); + __raw_writel(reg, (void __iomem *) AR5K_AR5312_ENABLE); /* * On a dual-band AR5312, the multiband radio is only @@ -167,9 +166,7 @@ static int ath_ahb_probe(struct platform_device *pdev) if (to_platform_device(ah->dev)->id == 0 && (bcfg->config->flags & (BD_WLAN0 | BD_WLAN1)) == (BD_WLAN1 | BD_WLAN0)) - ah->ah_capabilities.cap_needs_2GHz_ovr = true; - else - ah->ah_capabilities.cap_needs_2GHz_ovr = false; + __set_bit(ATH_STAT_2G_DISABLED, ah->status); } ret = ath5k_init_ah(ah, &ath_ahb_bus_ops); @@ -186,8 +183,6 @@ static int ath_ahb_probe(struct platform_device *pdev) err_free_hw: ieee80211_free_hw(hw); platform_set_drvdata(pdev, NULL); - err_iounmap: - iounmap(mem); err_out: return ret; } @@ -206,21 +201,20 @@ static int ath_ahb_remove(struct platform_device *pdev) if (bcfg->devid >= AR5K_SREV_AR2315_R6) { /* Disable WMAC AHB arbitration */ - reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL); + reg = __raw_readl((void __iomem *) AR5K_AR2315_AHB_ARB_CTL); reg &= ~AR5K_AR2315_AHB_ARB_CTL_WLAN; - iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL); + __raw_writel(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL); } else { /*Stop DMA access */ - reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE); + reg = __raw_readl((void __iomem *) AR5K_AR5312_ENABLE); if (to_platform_device(ah->dev)->id == 0) reg &= ~AR5K_AR5312_ENABLE_WLAN0; else reg &= ~AR5K_AR5312_ENABLE_WLAN1; - iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE); + __raw_writel(reg, (void __iomem *) AR5K_AR5312_ENABLE); } ath5k_deinit_ah(ah); - iounmap(ah->iobase); platform_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); diff --git a/drivers/net/wireless/ath/ath5k/ani.c b/drivers/net/wireless/ath/ath5k/ani.c index 35e93704..bea90e6b 100644 --- a/drivers/net/wireless/ath/ath5k/ani.c +++ b/drivers/net/wireless/ath/ath5k/ani.c @@ -27,21 +27,15 @@ * or reducing sensitivity as necessary. * * The parameters are: - * * - "noise immunity" - * * - "spur immunity" - * * - "firstep level" - * * - "OFDM weak signal detection" - * * - "CCK weak signal detection" * * Basically we look at the amount of ODFM and CCK timing errors we get and then * raise or lower immunity accordingly by setting one or more of these * parameters. - * * Newer chipsets have PHY error counters in hardware which will generate a MIB * interrupt when they overflow. Older hardware has too enable PHY error frames * by setting a RX flag and then count every single PHY error. When a specified @@ -51,13 +45,11 @@ */ -/***********************\ -* ANI parameter control * -\***********************/ +/*** ANI parameter control ***/ /** * ath5k_ani_set_noise_immunity_level() - Set noise immunity level - * @ah: The &struct ath5k_hw + * * @level: level between 0 and @ATH5K_ANI_MAX_NOISE_IMM_LVL */ void @@ -99,11 +91,12 @@ ath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level) ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); } + /** * ath5k_ani_set_spur_immunity_level() - Set spur immunity level - * @ah: The &struct ath5k_hw + * * @level: level between 0 and @max_spur_level (the maximum level is dependent - * on the chip revision). + * on the chip revision). */ void ath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level) @@ -124,9 +117,10 @@ ath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level) ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); } + /** * ath5k_ani_set_firstep_level() - Set "firstep" level - * @ah: The &struct ath5k_hw + * * @level: level between 0 and @ATH5K_ANI_MAX_FIRSTEP_LVL */ void @@ -146,9 +140,11 @@ ath5k_ani_set_firstep_level(struct ath5k_hw *ah, int level) ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); } + /** - * ath5k_ani_set_ofdm_weak_signal_detection() - Set OFDM weak signal detection - * @ah: The &struct ath5k_hw + * ath5k_ani_set_ofdm_weak_signal_detection() - Control OFDM weak signal + * detection + * * @on: turn on or off */ void @@ -186,9 +182,10 @@ ath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw *ah, bool on) on ? "on" : "off"); } + /** - * ath5k_ani_set_cck_weak_signal_detection() - Set CCK weak signal detection - * @ah: The &struct ath5k_hw + * ath5k_ani_set_cck_weak_signal_detection() - control CCK weak signal detection + * * @on: turn on or off */ void @@ -203,16 +200,13 @@ ath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw *ah, bool on) } -/***************\ -* ANI algorithm * -\***************/ +/*** ANI algorithm ***/ /** * ath5k_ani_raise_immunity() - Increase noise immunity - * @ah: The &struct ath5k_hw - * @as: The &struct ath5k_ani_state + * * @ofdm_trigger: If this is true we are called because of too many OFDM errors, - * the algorithm will tune more parameters then. + * the algorithm will tune more parameters then. * * Try to raise noise immunity (=decrease sensitivity) in several steps * depending on the average RSSI of the beacons we received. @@ -257,7 +251,7 @@ ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as, "beacon RSSI high"); /* only OFDM: beacon RSSI is high, we can disable ODFM weak * signal detection */ - if (ofdm_trigger && as->ofdm_weak_sig) { + if (ofdm_trigger && as->ofdm_weak_sig == true) { ath5k_ani_set_ofdm_weak_signal_detection(ah, false); ath5k_ani_set_spur_immunity_level(ah, 0); return; @@ -272,7 +266,7 @@ ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as, * but can raise firstep level */ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "beacon RSSI mid"); - if (ofdm_trigger && !as->ofdm_weak_sig) + if (ofdm_trigger && as->ofdm_weak_sig == false) ath5k_ani_set_ofdm_weak_signal_detection(ah, true); if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) ath5k_ani_set_firstep_level(ah, as->firstep_level + 1); @@ -282,7 +276,7 @@ ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as, * detect and zero firstep level to maximize CCK sensitivity */ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "beacon RSSI low, 2GHz"); - if (ofdm_trigger && as->ofdm_weak_sig) + if (ofdm_trigger && as->ofdm_weak_sig == true) ath5k_ani_set_ofdm_weak_signal_detection(ah, false); if (as->firstep_level > 0) ath5k_ani_set_firstep_level(ah, 0); @@ -296,10 +290,9 @@ ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as, */ } + /** * ath5k_ani_lower_immunity() - Decrease noise immunity - * @ah: The &struct ath5k_hw - * @as: The &struct ath5k_ani_state * * Try to lower noise immunity (=increase sensitivity) in several steps * depending on the average RSSI of the beacons we received. @@ -326,7 +319,7 @@ ath5k_ani_lower_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as) } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) { /* beacon RSSI is mid-range: turn on ODFM weak signal * detection and next, lower firstep level */ - if (!as->ofdm_weak_sig) { + if (as->ofdm_weak_sig == false) { ath5k_ani_set_ofdm_weak_signal_detection(ah, true); return; @@ -359,10 +352,9 @@ ath5k_ani_lower_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as) } } + /** * ath5k_hw_ani_get_listen_time() - Update counters and return listening time - * @ah: The &struct ath5k_hw - * @as: The &struct ath5k_ani_state * * Return an approximation of the time spent "listening" in milliseconds (ms) * since the last call of this function. @@ -387,10 +379,9 @@ ath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as) return listen; } + /** * ath5k_ani_save_and_clear_phy_errors() - Clear and save PHY error counters - * @ah: The &struct ath5k_hw - * @as: The &struct ath5k_ani_state * * Clear the PHY error counters as soon as possible, since this might be called * from a MIB interrupt and we want to make sure we don't get interrupted again. @@ -438,14 +429,14 @@ ath5k_ani_save_and_clear_phy_errors(struct ath5k_hw *ah, return 1; } + /** * ath5k_ani_period_restart() - Restart ANI period - * @as: The &struct ath5k_ani_state * * Just reset counters, so they are clear for the next "ani period". */ static void -ath5k_ani_period_restart(struct ath5k_ani_state *as) +ath5k_ani_period_restart(struct ath5k_hw *ah, struct ath5k_ani_state *as) { /* keep last values for debugging */ as->last_ofdm_errors = as->ofdm_errors; @@ -457,9 +448,9 @@ ath5k_ani_period_restart(struct ath5k_ani_state *as) as->listen_time = 0; } + /** * ath5k_ani_calibration() - The main ANI calibration function - * @ah: The &struct ath5k_hw * * We count OFDM and CCK errors relative to the time where we did not send or * receive ("listen" time) and raise or lower immunity accordingly. @@ -501,7 +492,7 @@ ath5k_ani_calibration(struct ath5k_hw *ah) /* too many PHY errors - we have to raise immunity */ bool ofdm_flag = as->ofdm_errors > ofdm_high ? true : false; ath5k_ani_raise_immunity(ah, as, ofdm_flag); - ath5k_ani_period_restart(as); + ath5k_ani_period_restart(ah, as); } else if (as->listen_time > 5 * ATH5K_ANI_LISTEN_PERIOD) { /* If more than 5 (TODO: why 5?) periods have passed and we got @@ -513,18 +504,15 @@ ath5k_ani_calibration(struct ath5k_hw *ah) if (as->ofdm_errors <= ofdm_low && as->cck_errors <= cck_low) ath5k_ani_lower_immunity(ah, as); - ath5k_ani_period_restart(as); + ath5k_ani_period_restart(ah, as); } } -/*******************\ -* Interrupt handler * -\*******************/ +/*** INTERRUPT HANDLER ***/ /** * ath5k_ani_mib_intr() - Interrupt handler for ANI MIB counters - * @ah: The &struct ath5k_hw * * Just read & reset the registers quickly, so they don't generate more * interrupts, save the counters and schedule the tasklet to decide whether @@ -561,11 +549,9 @@ ath5k_ani_mib_intr(struct ath5k_hw *ah) tasklet_schedule(&ah->ani_tasklet); } + /** - * ath5k_ani_phy_error_report - Used by older HW to report PHY errors - * - * @ah: The &struct ath5k_hw - * @phyerr: One of enum ath5k_phy_error_code + * ath5k_ani_phy_error_report() - Used by older HW to report PHY errors * * This is used by hardware without PHY error counters to report PHY errors * on a frame-by-frame basis, instead of the interrupt. @@ -588,13 +574,10 @@ ath5k_ani_phy_error_report(struct ath5k_hw *ah, } -/****************\ -* Initialization * -\****************/ +/*** INIT ***/ /** * ath5k_enable_phy_err_counters() - Enable PHY error counters - * @ah: The &struct ath5k_hw * * Enable PHY error counters for OFDM and CCK timing errors. */ @@ -613,9 +596,9 @@ ath5k_enable_phy_err_counters(struct ath5k_hw *ah) ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); } + /** * ath5k_disable_phy_err_counters() - Disable PHY error counters - * @ah: The &struct ath5k_hw * * Disable PHY error counters for OFDM and CCK timing errors. */ @@ -632,10 +615,10 @@ ath5k_disable_phy_err_counters(struct ath5k_hw *ah) ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); } + /** * ath5k_ani_init() - Initialize ANI - * @ah: The &struct ath5k_hw - * @mode: One of enum ath5k_ani_mode + * @mode: Which mode to use (auto, manual high, manual low, off) * * Initialize ANI according to mode. */ @@ -712,18 +695,10 @@ ath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode) } -/**************\ -* Debug output * -\**************/ +/*** DEBUG ***/ #ifdef CONFIG_ATH5K_DEBUG -/** - * ath5k_ani_print_counters() - Print ANI counters - * @ah: The &struct ath5k_hw - * - * Used for debugging ANI - */ void ath5k_ani_print_counters(struct ath5k_hw *ah) { diff --git a/drivers/net/wireless/ath/ath5k/ani.h b/drivers/net/wireless/ath/ath5k/ani.h index 21aa3554..7358b6c8 100644 --- a/drivers/net/wireless/ath/ath5k/ani.h +++ b/drivers/net/wireless/ath/ath5k/ani.h @@ -40,13 +40,13 @@ enum ath5k_phy_error_code; * enum ath5k_ani_mode - mode for ANI / noise sensitivity * * @ATH5K_ANI_MODE_OFF: Turn ANI off. This can be useful to just stop the ANI - * algorithm after it has been on auto mode. - * @ATH5K_ANI_MODE_MANUAL_LOW: Manually set all immunity parameters to low, - * maximizing sensitivity. ANI will not run. - * @ATH5K_ANI_MODE_MANUAL_HIGH: Manually set all immunity parameters to high, - * minimizing sensitivity. ANI will not run. - * @ATH5K_ANI_MODE_AUTO: Automatically control immunity parameters based on the - * amount of OFDM and CCK frame errors (default). + * algorithm after it has been on auto mode. + * ATH5K_ANI_MODE_MANUAL_LOW: Manually set all immunity parameters to low, + * maximizing sensitivity. ANI will not run. + * ATH5K_ANI_MODE_MANUAL_HIGH: Manually set all immunity parameters to high, + * minimizing sensitivity. ANI will not run. + * ATH5K_ANI_MODE_AUTO: Automatically control immunity parameters based on the + * amount of OFDM and CCK frame errors (default). */ enum ath5k_ani_mode { ATH5K_ANI_MODE_OFF = 0, @@ -58,22 +58,8 @@ enum ath5k_ani_mode { /** * struct ath5k_ani_state - ANI state and associated counters - * @ani_mode: One of enum ath5k_ani_mode - * @noise_imm_level: Noise immunity level - * @spur_level: Spur immunity level - * @firstep_level: FIRstep level - * @ofdm_weak_sig: OFDM weak signal detection state (on/off) - * @cck_weak_sig: CCK weak signal detection state (on/off) - * @max_spur_level: Max spur immunity level (chip specific) - * @listen_time: Listen time - * @ofdm_errors: OFDM timing error count - * @cck_errors: CCK timing error count - * @last_cc: The &struct ath_cycle_counters (for stats) - * @last_listen: Listen time from previous run (for stats) - * @last_ofdm_errors: OFDM timing error count from previous run (for tats) - * @last_cck_errors: CCK timing error count from previous run (for stats) - * @sum_ofdm_errors: Sum of OFDM timing errors (for stats) - * @sum_cck_errors: Sum of all CCK timing errors (for stats) + * + * @max_spur_level: the maximum spur level is chip dependent */ struct ath5k_ani_state { enum ath5k_ani_mode ani_mode; diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 8d434b8f..fecbcd9a 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -187,9 +187,10 @@ #define AR5K_TUNE_MAX_TXPOWER 63 #define AR5K_TUNE_DEFAULT_TXPOWER 25 #define AR5K_TUNE_TPC_TXPOWER false -#define ATH5K_TUNE_CALIBRATION_INTERVAL_FULL 60000 /* 60 sec */ -#define ATH5K_TUNE_CALIBRATION_INTERVAL_SHORT 10000 /* 10 sec */ +#define ATH5K_TUNE_CALIBRATION_INTERVAL_FULL 10000 /* 10 sec */ #define ATH5K_TUNE_CALIBRATION_INTERVAL_ANI 1000 /* 1 sec */ +#define ATH5K_TUNE_CALIBRATION_INTERVAL_NF 60000 /* 60 sec */ + #define ATH5K_TX_COMPLETE_POLL_INT 3000 /* 3 sec */ #define AR5K_INIT_CARR_SENSE_EN 1 @@ -261,34 +262,16 @@ #define AR5K_AGC_SETTLING_TURBO 37 +/* GENERIC CHIPSET DEFINITIONS */ -/*****************************\ -* GENERIC CHIPSET DEFINITIONS * -\*****************************/ - -/** - * enum ath5k_version - MAC Chips - * @AR5K_AR5210: AR5210 (Crete) - * @AR5K_AR5211: AR5211 (Oahu/Maui) - * @AR5K_AR5212: AR5212 (Venice) and newer - */ +/* MAC Chips */ enum ath5k_version { AR5K_AR5210 = 0, AR5K_AR5211 = 1, AR5K_AR5212 = 2, }; -/** - * enum ath5k_radio - PHY Chips - * @AR5K_RF5110: RF5110 (Fez) - * @AR5K_RF5111: RF5111 (Sombrero) - * @AR5K_RF5112: RF2112/5112(A) (Derby/Derby2) - * @AR5K_RF2413: RF2413/2414 (Griffin/Griffin-Lite) - * @AR5K_RF5413: RF5413/5414/5424 (Eagle/Condor) - * @AR5K_RF2316: RF2315/2316 (Cobra SoC) - * @AR5K_RF2317: RF2317 (Spider SoC) - * @AR5K_RF2425: RF2425/2417 (Swan/Nalla) - */ +/* PHY Chips */ enum ath5k_radio { AR5K_RF5110 = 0, AR5K_RF5111 = 1, @@ -320,11 +303,11 @@ enum ath5k_radio { #define AR5K_SREV_AR5213A 0x59 /* Hainan */ #define AR5K_SREV_AR2413 0x78 /* Griffin lite */ #define AR5K_SREV_AR2414 0x70 /* Griffin */ -#define AR5K_SREV_AR2315_R6 0x86 /* AP51-Light */ -#define AR5K_SREV_AR2315_R7 0x87 /* AP51-Full */ +#define AR5K_SREV_AR2315_R6 0x86 /* AP51-Light */ +#define AR5K_SREV_AR2315_R7 0x87 /* AP51-Full */ #define AR5K_SREV_AR5424 0x90 /* Condor */ -#define AR5K_SREV_AR2317_R1 0x90 /* AP61-Light */ -#define AR5K_SREV_AR2317_R2 0x91 /* AP61-Full */ +#define AR5K_SREV_AR2317_R1 0x90 /* AP61-Light */ +#define AR5K_SREV_AR2317_R2 0x91 /* AP61-Full */ #define AR5K_SREV_AR5413 0xa4 /* Eagle lite */ #define AR5K_SREV_AR5414 0xa0 /* Eagle */ #define AR5K_SREV_AR2415 0xb0 /* Talon */ @@ -361,40 +344,32 @@ enum ath5k_radio { /* TODO add support to mac80211 for vendor-specific rates and modes */ -/** - * DOC: Atheros XR - * +/* * Some of this information is based on Documentation from: * * http://madwifi-project.org/wiki/ChipsetFeatures/SuperAG * - * Atheros' eXtended Range - range enhancing extension is a modulation scheme - * that is supposed to double the link distance between an Atheros XR-enabled - * client device with an Atheros XR-enabled access point. This is achieved - * by increasing the receiver sensitivity up to, -105dBm, which is about 20dB - * above what the 802.11 specifications demand. In addition, new (proprietary) - * data rates are introduced: 3, 2, 1, 0.5 and 0.25 MBit/s. + * Modulation for Atheros' eXtended Range - range enhancing extension that is + * supposed to double the distance an Atheros client device can keep a + * connection with an Atheros access point. This is achieved by increasing + * the receiver sensitivity up to, -105dBm, which is about 20dB above what + * the 802.11 specifications demand. In addition, new (proprietary) data rates + * are introduced: 3, 2, 1, 0.5 and 0.25 MBit/s. * * Please note that can you either use XR or TURBO but you cannot use both, * they are exclusive. * - * Also note that we do not plan to support XR mode at least for now. You can - * get a mode similar to XR by using 5MHz bwmode. */ - - -/** - * DOC: Atheros SuperAG - * - * In addition to XR we have another modulation scheme called TURBO mode - * that is supposed to provide a throughput transmission speed up to 40Mbit/s - * -60Mbit/s at a 108Mbit/s signaling rate achieved through the bonding of two - * 54Mbit/s 802.11g channels. To use this feature both ends must support it. +#define MODULATION_XR 0x00000200 +/* + * Modulation for Atheros' Turbo G and Turbo A, its supposed to provide a + * throughput transmission speed up to 40Mbit/s-60Mbit/s at a 108Mbit/s + * signaling rate achieved through the bonding of two 54Mbit/s 802.11g + * channels. To use this feature your Access Point must also support it. * There is also a distinction between "static" and "dynamic" turbo modes: * * - Static: is the dumb version: devices set to this mode stick to it until * the mode is turned off. - * * - Dynamic: is the intelligent version, the network decides itself if it * is ok to use turbo. As soon as traffic is detected on adjacent channels * (which would get used in turbo mode), or when a non-turbo station joins @@ -408,39 +383,24 @@ enum ath5k_radio { * * http://www.pcworld.com/article/id,113428-page,1/article.html * - * The channel bonding seems to be driver specific though. - * - * In addition to TURBO modes we also have the following features for even - * greater speed-up: + * The channel bonding seems to be driver specific though. In addition to + * deciding what channels will be used, these "Turbo" modes are accomplished + * by also enabling the following features: * * - Bursting: allows multiple frames to be sent at once, rather than pausing * after each frame. Bursting is a standards-compliant feature that can be * used with any Access Point. - * * - Fast frames: increases the amount of information that can be sent per * frame, also resulting in a reduction of transmission overhead. It is a * proprietary feature that needs to be supported by the Access Point. - * * - Compression: data frames are compressed in real time using a Lempel Ziv * algorithm. This is done transparently. Once this feature is enabled, * compression and decompression takes place inside the chipset, without * putting additional load on the host CPU. * - * As with XR we also don't plan to support SuperAG features for now. You can - * get a mode similar to TURBO by using 40MHz bwmode. */ +#define MODULATION_TURBO 0x00000080 - -/** - * enum ath5k_driver_mode - PHY operation mode - * @AR5K_MODE_11A: 802.11a - * @AR5K_MODE_11B: 802.11b - * @AR5K_MODE_11G: 801.11g - * @AR5K_MODE_MAX: Used for boundary checks - * - * Do not change the order here, we use these as - * array indices and it also maps EEPROM structures. - */ enum ath5k_driver_mode { AR5K_MODE_11A = 0, AR5K_MODE_11B = 1, @@ -448,64 +408,30 @@ enum ath5k_driver_mode { AR5K_MODE_MAX = 3 }; -/** - * enum ath5k_ant_mode - Antenna operation mode - * @AR5K_ANTMODE_DEFAULT: Default antenna setup - * @AR5K_ANTMODE_FIXED_A: Only antenna A is present - * @AR5K_ANTMODE_FIXED_B: Only antenna B is present - * @AR5K_ANTMODE_SINGLE_AP: STA locked on a single ap - * @AR5K_ANTMODE_SECTOR_AP: AP with tx antenna set on tx desc - * @AR5K_ANTMODE_SECTOR_STA: STA with tx antenna set on tx desc - * @AR5K_ANTMODE_DEBUG: Debug mode -A -> Rx, B-> Tx- - * @AR5K_ANTMODE_MAX: Used for boundary checks - * - * For more infos on antenna control check out phy.c - */ enum ath5k_ant_mode { - AR5K_ANTMODE_DEFAULT = 0, - AR5K_ANTMODE_FIXED_A = 1, - AR5K_ANTMODE_FIXED_B = 2, - AR5K_ANTMODE_SINGLE_AP = 3, - AR5K_ANTMODE_SECTOR_AP = 4, - AR5K_ANTMODE_SECTOR_STA = 5, - AR5K_ANTMODE_DEBUG = 6, + AR5K_ANTMODE_DEFAULT = 0, /* default antenna setup */ + AR5K_ANTMODE_FIXED_A = 1, /* only antenna A is present */ + AR5K_ANTMODE_FIXED_B = 2, /* only antenna B is present */ + AR5K_ANTMODE_SINGLE_AP = 3, /* sta locked on a single ap */ + AR5K_ANTMODE_SECTOR_AP = 4, /* AP with tx antenna set on tx desc */ + AR5K_ANTMODE_SECTOR_STA = 5, /* STA with tx antenna set on tx desc */ + AR5K_ANTMODE_DEBUG = 6, /* Debug mode -A -> Rx, B-> Tx- */ AR5K_ANTMODE_MAX, }; -/** - * enum ath5k_bw_mode - Bandwidth operation mode - * @AR5K_BWMODE_DEFAULT: 20MHz, default operation - * @AR5K_BWMODE_5MHZ: Quarter rate - * @AR5K_BWMODE_10MHZ: Half rate - * @AR5K_BWMODE_40MHZ: Turbo - */ enum ath5k_bw_mode { - AR5K_BWMODE_DEFAULT = 0, - AR5K_BWMODE_5MHZ = 1, - AR5K_BWMODE_10MHZ = 2, - AR5K_BWMODE_40MHZ = 3 + AR5K_BWMODE_DEFAULT = 0, /* 20MHz, default operation */ + AR5K_BWMODE_5MHZ = 1, /* Quarter rate */ + AR5K_BWMODE_10MHZ = 2, /* Half rate */ + AR5K_BWMODE_40MHZ = 3 /* Turbo */ }; - - /****************\ TX DEFINITIONS \****************/ -/** - * struct ath5k_tx_status - TX Status descriptor - * @ts_seqnum: Sequence number - * @ts_tstamp: Timestamp - * @ts_status: Status code - * @ts_final_idx: Final transmission series index - * @ts_final_retry: Final retry count - * @ts_rssi: RSSI for received ACK - * @ts_shortretry: Short retry count - * @ts_virtcol: Virtual collision count - * @ts_antenna: Antenna used - * - * TX status descriptor gets filled by the hw - * on each transmission attempt. +/* + * TX Status descriptor */ struct ath5k_tx_status { u16 ts_seqnum; @@ -528,6 +454,7 @@ struct ath5k_tx_status { * enum ath5k_tx_queue - Queue types used to classify tx queues. * @AR5K_TX_QUEUE_INACTIVE: q is unused -- see ath5k_hw_release_tx_queue * @AR5K_TX_QUEUE_DATA: A normal data queue + * @AR5K_TX_QUEUE_XR_DATA: An XR-data queue * @AR5K_TX_QUEUE_BEACON: The beacon queue * @AR5K_TX_QUEUE_CAB: The after-beacon queue * @AR5K_TX_QUEUE_UAPSD: Unscheduled Automatic Power Save Delivery queue @@ -535,6 +462,7 @@ struct ath5k_tx_status { enum ath5k_tx_queue { AR5K_TX_QUEUE_INACTIVE = 0, AR5K_TX_QUEUE_DATA, + AR5K_TX_QUEUE_XR_DATA, AR5K_TX_QUEUE_BEACON, AR5K_TX_QUEUE_CAB, AR5K_TX_QUEUE_UAPSD, @@ -543,46 +471,36 @@ enum ath5k_tx_queue { #define AR5K_NUM_TX_QUEUES 10 #define AR5K_NUM_TX_QUEUES_NOQCU 2 -/** - * enum ath5k_tx_queue_subtype - Queue sub-types to classify normal data queues - * @AR5K_WME_AC_BK: Background traffic - * @AR5K_WME_AC_BE: Best-effort (normal) traffic - * @AR5K_WME_AC_VI: Video traffic - * @AR5K_WME_AC_VO: Voice traffic - * +/* + * Queue syb-types to classify normal data queues. * These are the 4 Access Categories as defined in * WME spec. 0 is the lowest priority and 4 is the * highest. Normal data that hasn't been classified * goes to the Best Effort AC. */ enum ath5k_tx_queue_subtype { - AR5K_WME_AC_BK = 0, - AR5K_WME_AC_BE, - AR5K_WME_AC_VI, - AR5K_WME_AC_VO, + AR5K_WME_AC_BK = 0, /*Background traffic*/ + AR5K_WME_AC_BE, /*Best-effort (normal) traffic*/ + AR5K_WME_AC_VI, /*Video traffic*/ + AR5K_WME_AC_VO, /*Voice traffic*/ }; -/** - * enum ath5k_tx_queue_id - Queue ID numbers as returned by the hw functions - * @AR5K_TX_QUEUE_ID_NOQCU_DATA: Data queue on AR5210 (no QCU available) - * @AR5K_TX_QUEUE_ID_NOQCU_BEACON: Beacon queue on AR5210 (no QCU available) - * @AR5K_TX_QUEUE_ID_DATA_MIN: Data queue min index - * @AR5K_TX_QUEUE_ID_DATA_MAX: Data queue max index - * @AR5K_TX_QUEUE_ID_CAB: Content after beacon queue - * @AR5K_TX_QUEUE_ID_BEACON: Beacon queue - * @AR5K_TX_QUEUE_ID_UAPSD: Urgent Automatic Power Save Delivery, - * - * Each number represents a hw queue. If hw does not support hw queues - * (eg 5210) all data goes in one queue. +/* + * Queue ID numbers as returned by the hw functions, each number + * represents a hw queue. If hw does not support hw queues + * (eg 5210) all data goes in one queue. These match + * d80211 definitions (net80211/MadWiFi don't use them). */ enum ath5k_tx_queue_id { AR5K_TX_QUEUE_ID_NOQCU_DATA = 0, AR5K_TX_QUEUE_ID_NOQCU_BEACON = 1, - AR5K_TX_QUEUE_ID_DATA_MIN = 0, - AR5K_TX_QUEUE_ID_DATA_MAX = 3, - AR5K_TX_QUEUE_ID_UAPSD = 7, - AR5K_TX_QUEUE_ID_CAB = 8, - AR5K_TX_QUEUE_ID_BEACON = 9, + AR5K_TX_QUEUE_ID_DATA_MIN = 0, /*IEEE80211_TX_QUEUE_DATA0*/ + AR5K_TX_QUEUE_ID_DATA_MAX = 3, /*IEEE80211_TX_QUEUE_DATA3*/ + AR5K_TX_QUEUE_ID_DATA_SVP = 5, /*IEEE80211_TX_QUEUE_SVP - Spectralink Voice Protocol*/ + AR5K_TX_QUEUE_ID_CAB = 6, /*IEEE80211_TX_QUEUE_AFTER_BEACON*/ + AR5K_TX_QUEUE_ID_BEACON = 7, /*IEEE80211_TX_QUEUE_BEACON*/ + AR5K_TX_QUEUE_ID_UAPSD = 8, + AR5K_TX_QUEUE_ID_XR_DATA = 9, }; /* @@ -603,70 +521,46 @@ enum ath5k_tx_queue_id { #define AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS 0x1000 /* Disable backoff while bursting */ #define AR5K_TXQ_FLAG_COMPRESSION_ENABLE 0x2000 /* Enable hw compression -not implemented-*/ -/** - * struct ath5k_txq - Transmit queue state - * @qnum: Hardware q number - * @link: Link ptr in last TX desc - * @q: Transmit queue (&struct list_head) - * @lock: Lock on q and link - * @setup: Is the queue configured - * @txq_len:Number of queued buffers - * @txq_max: Max allowed num of queued buffers - * @txq_poll_mark: Used to check if queue got stuck - * @txq_stuck: Queue stuck counter - * - * One of these exists for each hardware transmit queue. - * Packets sent to us from above are assigned to queues based - * on their priority. Not all devices support a complete set - * of hardware transmit queues. For those devices the array - * sc_ac2q will map multiple priorities to fewer hardware queues - * (typically all to one hardware queue). +/* + * Data transmit queue state. One of these exists for each + * hardware transmit queue. Packets sent to us from above + * are assigned to queues based on their priority. Not all + * devices support a complete set of hardware transmit queues. + * For those devices the array sc_ac2q will map multiple + * priorities to fewer hardware queues (typically all to one + * hardware queue). */ struct ath5k_txq { - unsigned int qnum; - u32 *link; - struct list_head q; - spinlock_t lock; + unsigned int qnum; /* hardware q number */ + u32 *link; /* link ptr in last TX desc */ + struct list_head q; /* transmit queue */ + spinlock_t lock; /* lock on q and link */ bool setup; - int txq_len; - int txq_max; + int txq_len; /* number of queued buffers */ + int txq_max; /* max allowed num of queued buffers */ bool txq_poll_mark; - unsigned int txq_stuck; + unsigned int txq_stuck; /* informational counter */ }; -/** - * struct ath5k_txq_info - A struct to hold TX queue's parameters - * @tqi_type: One of enum ath5k_tx_queue - * @tqi_subtype: One of enum ath5k_tx_queue_subtype - * @tqi_flags: TX queue flags (see above) - * @tqi_aifs: Arbitrated Inter-frame Space - * @tqi_cw_min: Minimum Contention Window - * @tqi_cw_max: Maximum Contention Window - * @tqi_cbr_period: Constant bit rate period - * @tqi_ready_time: Time queue waits after an event when RDYTIME is enabled +/* + * A struct to hold tx queue's parameters */ struct ath5k_txq_info { enum ath5k_tx_queue tqi_type; enum ath5k_tx_queue_subtype tqi_subtype; - u16 tqi_flags; - u8 tqi_aifs; - u16 tqi_cw_min; - u16 tqi_cw_max; - u32 tqi_cbr_period; + u16 tqi_flags; /* Tx queue flags (see above) */ + u8 tqi_aifs; /* Arbitrated Interframe Space */ + u16 tqi_cw_min; /* Minimum Contention Window */ + u16 tqi_cw_max; /* Maximum Contention Window */ + u32 tqi_cbr_period; /* Constant bit rate period */ u32 tqi_cbr_overflow_limit; u32 tqi_burst_time; - u32 tqi_ready_time; + u32 tqi_ready_time; /* Time queue waits after an event */ }; -/** - * enum ath5k_pkt_type - Transmit packet types - * @AR5K_PKT_TYPE_NORMAL: Normal data - * @AR5K_PKT_TYPE_ATIM: ATIM - * @AR5K_PKT_TYPE_PSPOLL: PS-Poll - * @AR5K_PKT_TYPE_BEACON: Beacon - * @AR5K_PKT_TYPE_PROBE_RESP: Probe response - * @AR5K_PKT_TYPE_PIFS: PIFS - * Used on tx control descriptor +/* + * Transmit packet types. + * used on tx control descriptor */ enum ath5k_pkt_type { AR5K_PKT_TYPE_NORMAL = 0, @@ -689,23 +583,27 @@ enum ath5k_pkt_type { (ah->ah_txpower.txp_rates_power_table[(_r)] & 0x3f) << (_v) \ ) +/* + * DMA size definitions (2^(n+2)) + */ +enum ath5k_dmasize { + AR5K_DMASIZE_4B = 0, + AR5K_DMASIZE_8B, + AR5K_DMASIZE_16B, + AR5K_DMASIZE_32B, + AR5K_DMASIZE_64B, + AR5K_DMASIZE_128B, + AR5K_DMASIZE_256B, + AR5K_DMASIZE_512B +}; /****************\ RX DEFINITIONS \****************/ -/** - * struct ath5k_rx_status - RX Status descriptor - * @rs_datalen: Data length - * @rs_tstamp: Timestamp - * @rs_status: Status code - * @rs_phyerr: PHY error mask - * @rs_rssi: RSSI in 0.5dbm units - * @rs_keyix: Index to the key used for decrypting - * @rs_rate: Rate used to decode the frame - * @rs_antenna: Antenna used to receive the frame - * @rs_more: Indicates this is a frame fragment (Fast frames) +/* + * RX Status descriptor */ struct ath5k_rx_status { u16 rs_datalen; @@ -747,18 +645,10 @@ struct ath5k_rx_status { #define TSF_TO_TU(_tsf) (u32)((_tsf) >> 10) - /*******************************\ GAIN OPTIMIZATION DEFINITIONS \*******************************/ -/** - * enum ath5k_rfgain - RF Gain optimization engine state - * @AR5K_RFGAIN_INACTIVE: Engine disabled - * @AR5K_RFGAIN_ACTIVE: Probe active - * @AR5K_RFGAIN_READ_REQUESTED: Probe requested - * @AR5K_RFGAIN_NEED_CHANGE: Gain_F needs change - */ enum ath5k_rfgain { AR5K_RFGAIN_INACTIVE = 0, AR5K_RFGAIN_ACTIVE, @@ -766,16 +656,6 @@ enum ath5k_rfgain { AR5K_RFGAIN_NEED_CHANGE, }; -/** - * struct ath5k_gain - RF Gain optimization engine state data - * @g_step_idx: Current step index - * @g_current: Current gain - * @g_target: Target gain - * @g_low: Low gain boundary - * @g_high: High gain boundary - * @g_f_corr: Gain_F correction - * @g_state: One of enum ath5k_rfgain - */ struct ath5k_gain { u8 g_step_idx; u8 g_current; @@ -786,8 +666,6 @@ struct ath5k_gain { u8 g_state; }; - - /********************\ COMMON DEFINITIONS \********************/ @@ -796,14 +674,9 @@ struct ath5k_gain { #define AR5K_SLOT_TIME_20 880 #define AR5K_SLOT_TIME_MAX 0xffff -/** - * struct ath5k_athchan_2ghz - 2GHz to 5GHZ map for RF5111 - * @a2_flags: Channel flags (internal) - * @a2_athchan: HW channel number (internal) - * - * This structure is used to map 2GHz channels to - * 5GHz Atheros channels on 2111 frequency converter - * that comes together with RF5111 +/* + * The following structure is used to map 2GHz channels to + * 5GHz Atheros channels. * TODO: Clean up */ struct ath5k_athchan_2ghz { @@ -811,80 +684,36 @@ struct ath5k_athchan_2ghz { u16 a2_athchan; }; -/** - * enum ath5k_dmasize - DMA size definitions (2^(n+2)) - * @AR5K_DMASIZE_4B: 4Bytes - * @AR5K_DMASIZE_8B: 8Bytes - * @AR5K_DMASIZE_16B: 16Bytes - * @AR5K_DMASIZE_32B: 32Bytes - * @AR5K_DMASIZE_64B: 64Bytes (Default) - * @AR5K_DMASIZE_128B: 128Bytes - * @AR5K_DMASIZE_256B: 256Bytes - * @AR5K_DMASIZE_512B: 512Bytes - * - * These are used to set DMA burst size on hw - * - * Note: Some platforms can't handle more than 4Bytes - * be careful on embedded boards. - */ -enum ath5k_dmasize { - AR5K_DMASIZE_4B = 0, - AR5K_DMASIZE_8B, - AR5K_DMASIZE_16B, - AR5K_DMASIZE_32B, - AR5K_DMASIZE_64B, - AR5K_DMASIZE_128B, - AR5K_DMASIZE_256B, - AR5K_DMASIZE_512B -}; - - /******************\ RATE DEFINITIONS \******************/ /** - * DOC: Rate codes - * * Seems the ar5xxx hardware supports up to 32 rates, indexed by 1-32. * * The rate code is used to get the RX rate or set the TX rate on the * hardware descriptors. It is also used for internal modulation control * and settings. * - * This is the hardware rate map we are aware of (html unfriendly): + * This is the hardware rate map we are aware of: + * + * rate_code 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 + * rate_kbps 3000 1000 ? ? ? 2000 500 48000 + * + * rate_code 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 + * rate_kbps 24000 12000 6000 54000 36000 18000 9000 ? * - * Rate code Rate (Kbps) - * --------- ----------- - * 0x01 3000 (XR) - * 0x02 1000 (XR) - * 0x03 250 (XR) - * 0x04 - 05 -Reserved- - * 0x06 2000 (XR) - * 0x07 500 (XR) - * 0x08 48000 (OFDM) - * 0x09 24000 (OFDM) - * 0x0A 12000 (OFDM) - * 0x0B 6000 (OFDM) - * 0x0C 54000 (OFDM) - * 0x0D 36000 (OFDM) - * 0x0E 18000 (OFDM) - * 0x0F 9000 (OFDM) - * 0x10 - 17 -Reserved- - * 0x18 11000L (CCK) - * 0x19 5500L (CCK) - * 0x1A 2000L (CCK) - * 0x1B 1000L (CCK) - * 0x1C 11000S (CCK) - * 0x1D 5500S (CCK) - * 0x1E 2000S (CCK) - * 0x1F -Reserved- + * rate_code 17 18 19 20 21 22 23 24 + * rate_kbps ? ? ? ? ? ? ? 11000 * - * "S" indicates CCK rates with short preamble and "L" with long preamble. + * rate_code 25 26 27 28 29 30 31 32 + * rate_kbps 5500 2000 1000 11000S 5500S 2000S ? ? + * + * "S" indicates CCK rates with short preamble. * * AR5211 has different rate codes for CCK (802.11B) rates. It only uses the - * lowest 4 bits, so they are the same as above with a 0xF mask. + * lowest 4 bits, so they are the same as below with a 0xF mask. * (0xB, 0xA, 0x9 and 0x8 for 1M, 2M, 5.5M and 11M). * We handle this in ath5k_setup_bands(). */ @@ -904,9 +733,13 @@ enum ath5k_dmasize { #define ATH5K_RATE_CODE_36M 0x0D #define ATH5K_RATE_CODE_48M 0x08 #define ATH5K_RATE_CODE_54M 0x0C +/* XR */ +#define ATH5K_RATE_CODE_XR_500K 0x07 +#define ATH5K_RATE_CODE_XR_1M 0x02 +#define ATH5K_RATE_CODE_XR_2M 0x06 +#define ATH5K_RATE_CODE_XR_3M 0x01 -/* Adding this flag to rate_code on B rates - * enables short preamble */ +/* adding this flag to rate_code enables short preamble */ #define AR5K_SET_SHORT_PREAMBLE 0x04 /* @@ -914,7 +747,7 @@ enum ath5k_dmasize { */ #define AR5K_KEYCACHE_SIZE 8 -extern bool ath5k_modparam_nohwcrypt; +extern int ath5k_modparam_nohwcrypt; /***********************\ HW RELATED DEFINITIONS @@ -936,65 +769,49 @@ extern bool ath5k_modparam_nohwcrypt; /** * enum ath5k_int - Hardware interrupt masks helpers - * @AR5K_INT_RXOK: Frame successfully received - * @AR5K_INT_RXDESC: Request RX descriptor/Read RX descriptor - * @AR5K_INT_RXERR: Frame reception failed - * @AR5K_INT_RXNOFRM: No frame received within a specified time period - * @AR5K_INT_RXEOL: Reached "End Of List", means we need more RX descriptors - * @AR5K_INT_RXORN: Indicates we got RX FIFO overrun. Note that Rx overrun is - * not always fatal, on some chips we can continue operation - * without resetting the card, that's why %AR5K_INT_FATAL is not - * common for all chips. - * @AR5K_INT_RX_ALL: Mask to identify all RX related interrupts - * - * @AR5K_INT_TXOK: Frame transmission success - * @AR5K_INT_TXDESC: Request TX descriptor/Read TX status descriptor - * @AR5K_INT_TXERR: Frame transmission failure - * @AR5K_INT_TXEOL: Received End Of List for VEOL (Virtual End Of List). The - * Queue Control Unit (QCU) signals an EOL interrupt only if a - * descriptor's LinkPtr is NULL. For more details, refer to: - * "http://www.freepatentsonline.com/20030225739.html" - * @AR5K_INT_TXNOFRM: No frame was transmitted within a specified time period - * @AR5K_INT_TXURN: Indicates we got TX FIFO underrun. In such case we should - * increase the TX trigger threshold. - * @AR5K_INT_TX_ALL: Mask to identify all TX related interrupts * + * @AR5K_INT_RX: mask to identify received frame interrupts, of type + * AR5K_ISR_RXOK or AR5K_ISR_RXERR + * @AR5K_INT_RXDESC: Request RX descriptor/Read RX descriptor (?) + * @AR5K_INT_RXNOFRM: No frame received (?) + * @AR5K_INT_RXEOL: received End Of List for VEOL (Virtual End Of List). The + * Queue Control Unit (QCU) signals an EOL interrupt only if a descriptor's + * LinkPtr is NULL. For more details, refer to: + * http://www.freepatentsonline.com/20030225739.html + * @AR5K_INT_RXORN: Indicates we got RX overrun (eg. no more descriptors). + * Note that Rx overrun is not always fatal, on some chips we can continue + * operation without resetting the card, that's why int_fatal is not + * common for all chips. + * @AR5K_INT_TX: mask to identify received frame interrupts, of type + * AR5K_ISR_TXOK or AR5K_ISR_TXERR + * @AR5K_INT_TXDESC: Request TX descriptor/Read TX status descriptor (?) + * @AR5K_INT_TXURN: received when we should increase the TX trigger threshold + * We currently do increments on interrupt by + * (AR5K_TUNE_MAX_TX_FIFO_THRES - current_trigger_level) / 2 * @AR5K_INT_MIB: Indicates the either Management Information Base counters or - * one of the PHY error counters reached the maximum value and - * should be read and cleared. - * @AR5K_INT_SWI: Software triggered interrupt. + * one of the PHY error counters reached the maximum value and should be + * read and cleared. * @AR5K_INT_RXPHY: RX PHY Error * @AR5K_INT_RXKCM: RX Key cache miss * @AR5K_INT_SWBA: SoftWare Beacon Alert - indicates its time to send a - * beacon that must be handled in software. The alternative is if - * you have VEOL support, in that case you let the hardware deal - * with things. - * @AR5K_INT_BRSSI: Beacon received with an RSSI value below our threshold + * beacon that must be handled in software. The alternative is if you + * have VEOL support, in that case you let the hardware deal with things. * @AR5K_INT_BMISS: If in STA mode this indicates we have stopped seeing - * beacons from the AP have associated with, we should probably - * try to reassociate. When in IBSS mode this might mean we have - * not received any beacons from any local stations. Note that - * every station in an IBSS schedules to send beacons at the - * Target Beacon Transmission Time (TBTT) with a random backoff. - * @AR5K_INT_BNR: Beacon queue got triggered (DMA beacon alert) while empty. - * @AR5K_INT_TIM: Beacon with local station's TIM bit set - * @AR5K_INT_DTIM: Beacon with DTIM bit and zero DTIM count received - * @AR5K_INT_DTIM_SYNC: DTIM sync lost - * @AR5K_INT_GPIO: GPIO interrupt is used for RF Kill switches connected to - * our GPIO pins. - * @AR5K_INT_BCN_TIMEOUT: Beacon timeout, we waited after TBTT but got noting - * @AR5K_INT_CAB_TIMEOUT: We waited for CAB traffic after the beacon but got - * nothing or an incomplete CAB frame sequence. - * @AR5K_INT_QCBRORN: A queue got it's CBR counter expired - * @AR5K_INT_QCBRURN: A queue got triggered wile empty - * @AR5K_INT_QTRIG: A queue got triggered - * - * @AR5K_INT_FATAL: Fatal errors were encountered, typically caused by bus/DMA - * errors. Indicates we need to reset the card. + * beacons from the AP have associated with, we should probably try to + * reassociate. When in IBSS mode this might mean we have not received + * any beacons from any local stations. Note that every station in an + * IBSS schedules to send beacons at the Target Beacon Transmission Time + * (TBTT) with a random backoff. + * @AR5K_INT_BNR: Beacon Not Ready interrupt - ?? + * @AR5K_INT_GPIO: GPIO interrupt is used for RF Kill, disabled for now + * until properly handled + * @AR5K_INT_FATAL: Fatal errors were encountered, typically caused by DMA + * errors. These types of errors we can enable seem to be of type + * AR5K_SIMR2_MCABT, AR5K_SIMR2_SSERR and AR5K_SIMR2_DPERR. * @AR5K_INT_GLOBAL: Used to clear and set the IER - * @AR5K_INT_NOCARD: Signals the card has been removed - * @AR5K_INT_COMMON: Common interrupts shared among MACs with the same - * bit value + * @AR5K_INT_NOCARD: signals the card has been removed + * @AR5K_INT_COMMON: common interrupts shared among MACs with the same + * bit value * * These are mapped to take advantage of some common bits * between the MACs, to be able to set intr properties @@ -1030,15 +847,15 @@ enum ath5k_int { AR5K_INT_GPIO = 0x01000000, AR5K_INT_BCN_TIMEOUT = 0x02000000, /* Non common */ AR5K_INT_CAB_TIMEOUT = 0x04000000, /* Non common */ - AR5K_INT_QCBRORN = 0x08000000, /* Non common */ - AR5K_INT_QCBRURN = 0x10000000, /* Non common */ - AR5K_INT_QTRIG = 0x20000000, /* Non common */ + AR5K_INT_RX_DOPPLER = 0x08000000, /* Non common */ + AR5K_INT_QCBRORN = 0x10000000, /* Non common */ + AR5K_INT_QCBRURN = 0x20000000, /* Non common */ + AR5K_INT_QTRIG = 0x40000000, /* Non common */ AR5K_INT_GLOBAL = 0x80000000, AR5K_INT_TX_ALL = AR5K_INT_TXOK | AR5K_INT_TXDESC | AR5K_INT_TXERR - | AR5K_INT_TXNOFRM | AR5K_INT_TXEOL | AR5K_INT_TXURN, @@ -1074,32 +891,15 @@ enum ath5k_int { AR5K_INT_NOCARD = 0xffffffff }; -/** - * enum ath5k_calibration_mask - Mask which calibration is active at the moment - * @AR5K_CALIBRATION_FULL: Full calibration (AGC + SHORT) - * @AR5K_CALIBRATION_SHORT: Short calibration (NF + I/Q) - * @AR5K_CALIBRATION_NF: Noise Floor calibration - * @AR5K_CALIBRATION_ANI: Adaptive Noise Immunity - */ +/* mask which calibration is active at the moment */ enum ath5k_calibration_mask { AR5K_CALIBRATION_FULL = 0x01, AR5K_CALIBRATION_SHORT = 0x02, - AR5K_CALIBRATION_NF = 0x04, - AR5K_CALIBRATION_ANI = 0x08, + AR5K_CALIBRATION_ANI = 0x04, }; -/** - * enum ath5k_power_mode - Power management modes - * @AR5K_PM_UNDEFINED: Undefined - * @AR5K_PM_AUTO: Allow card to sleep if possible - * @AR5K_PM_AWAKE: Force card to wake up - * @AR5K_PM_FULL_SLEEP: Force card to full sleep (DANGEROUS) - * @AR5K_PM_NETWORK_SLEEP: Allow to sleep for a specified duration - * - * Currently only PM_AWAKE is used, FULL_SLEEP and NETWORK_SLEEP/AUTO - * are also known to have problems on some cards. This is not a big - * problem though because we can have almost the same effect as - * FULL_SLEEP by putting card on warm reset (it's almost powered down). +/* + * Power management */ enum ath5k_power_mode { AR5K_PM_UNDEFINED = 0, @@ -1157,8 +957,6 @@ struct ath5k_capabilities { } cap_queues; bool cap_has_phyerr_counters; - bool cap_has_mrr_support; - bool cap_needs_2GHz_ovr; }; /* size of noise floor history (keep it a power of two) */ @@ -1274,11 +1072,13 @@ struct ath5k_hw { dma_addr_t desc_daddr; /* DMA (physical) address */ size_t desc_len; /* size of TX/RX descriptors */ - DECLARE_BITMAP(status, 4); + DECLARE_BITMAP(status, 6); #define ATH_STAT_INVALID 0 /* disable hardware accesses */ -#define ATH_STAT_PROMISC 1 -#define ATH_STAT_LEDSOFT 2 /* enable LED gpio status */ -#define ATH_STAT_STARTED 3 /* opened & irqs enabled */ +#define ATH_STAT_MRRETRY 1 /* multi-rate retry support */ +#define ATH_STAT_PROMISC 2 +#define ATH_STAT_LEDSOFT 3 /* enable LED gpio status */ +#define ATH_STAT_STARTED 4 /* opened & irqs enabled */ +#define ATH_STAT_2G_DISABLED 5 /* multiband radio without 2G */ unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */ struct ieee80211_channel *curchan; /* current h/w channel */ @@ -1297,7 +1097,6 @@ struct ath5k_hw { led_on; /* pin setting for LED on */ struct work_struct reset_work; /* deferred chip reset */ - struct work_struct calib_work; /* deferred phy calibration */ struct list_head rxbuf; /* receive buffer */ spinlock_t rxbuflock; @@ -1314,13 +1113,14 @@ struct ath5k_hw { struct ath5k_rfkill rf_kill; + struct tasklet_struct calib; /* calibration tasklet */ + spinlock_t block; /* protects beacon */ struct tasklet_struct beacontq; /* beacon intr tasklet */ struct list_head bcbuf; /* beacon buffer */ struct ieee80211_vif *bslot[ATH_BCBUF]; u16 num_ap_vifs; u16 num_adhoc_vifs; - u16 num_mesh_vifs; unsigned int bhalq, /* SW q for outgoing beacons */ bmisscount, /* missed beacon transmits */ bintval, /* beacon interval in TU */ @@ -1344,7 +1144,7 @@ struct ath5k_hw { enum ath5k_int ah_imr; struct ieee80211_channel *ah_current_channel; - bool ah_iq_cal_needed; + bool ah_calibration; bool ah_single_chip; enum ath5k_version ah_version; @@ -1387,13 +1187,7 @@ struct ath5k_hw { u32 ah_txq_imr_cbrurn; u32 ah_txq_imr_qtrig; u32 ah_txq_imr_nofrm; - - u32 ah_txq_isr_txok_all; - u32 ah_txq_isr_txurn; - u32 ah_txq_isr_qcborn; - u32 ah_txq_isr_qcburn; - u32 ah_txq_isr_qtrig; - + u32 ah_txq_isr; u32 *ah_rf_banks; size_t ah_rf_banks_size; size_t ah_rf_regs_count; @@ -1434,8 +1228,8 @@ struct ath5k_hw { /* Calibration timestamp */ unsigned long ah_cal_next_full; - unsigned long ah_cal_next_short; unsigned long ah_cal_next_ani; + unsigned long ah_cal_next_nf; /* Calibration mask */ u8 ah_cal_mask; @@ -1544,11 +1338,11 @@ void ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah); u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah); void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64); void ath5k_hw_reset_tsf(struct ath5k_hw *ah); -void ath5k_hw_init_beacon_timers(struct ath5k_hw *ah, u32 next_beacon, - u32 interval); +void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval); bool ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval); /* Init function */ -void ath5k_hw_pcu_init(struct ath5k_hw *ah, enum nl80211_iftype op_mode); +void ath5k_hw_pcu_init(struct ath5k_hw *ah, enum nl80211_iftype op_mode, + u8 mode); /* Queue Control Unit, DFS Control Unit Functions */ int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, @@ -1657,12 +1451,12 @@ static inline void __iomem *ath5k_ahb_reg(struct ath5k_hw *ah, u16 reg) static inline u32 ath5k_hw_reg_read(struct ath5k_hw *ah, u16 reg) { - return ioread32(ath5k_ahb_reg(ah, reg)); + return __raw_readl(ath5k_ahb_reg(ah, reg)); } static inline void ath5k_hw_reg_write(struct ath5k_hw *ah, u32 val, u16 reg) { - iowrite32(val, ath5k_ahb_reg(ah, reg)); + __raw_writel(val, ath5k_ahb_reg(ah, reg)); } #else diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c index d7114c75..91627dd2 100644 --- a/drivers/net/wireless/ath/ath5k/attach.c +++ b/drivers/net/wireless/ath/ath5k/attach.c @@ -27,7 +27,8 @@ #include "debug.h" /** - * ath5k_hw_post() - Power On Self Test helper function + * ath5k_hw_post - Power On Self Test helper function + * * @ah: The &struct ath5k_hw */ static int ath5k_hw_post(struct ath5k_hw *ah) @@ -91,7 +92,8 @@ static int ath5k_hw_post(struct ath5k_hw *ah) } /** - * ath5k_hw_init() - Check if hw is supported and init the needed structs + * ath5k_hw_init - Check if hw is supported and init the needed structs + * * @ah: The &struct ath5k_hw associated with the device * * Check if the device is supported, perform a POST and initialize the needed @@ -296,7 +298,7 @@ int ath5k_hw_init(struct ath5k_hw *ah) /* Reset SERDES to load new settings */ ath5k_hw_reg_write(ah, 0x00000000, AR5K_PCIE_SERDES_RESET); - usleep_range(1000, 1500); + mdelay(1); } /* Get misc capabilities */ @@ -306,6 +308,11 @@ int ath5k_hw_init(struct ath5k_hw *ah) goto err; } + if (test_bit(ATH_STAT_2G_DISABLED, ah->status)) { + __clear_bit(AR5K_MODE_11B, ah->ah_capabilities.cap_mode); + __clear_bit(AR5K_MODE_11G, ah->ah_capabilities.cap_mode); + } + /* Crypto settings */ common->keymax = (ah->ah_version == AR5K_AR5210 ? AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211); @@ -342,7 +349,8 @@ int ath5k_hw_init(struct ath5k_hw *ah) } /** - * ath5k_hw_deinit() - Free the &struct ath5k_hw + * ath5k_hw_deinit - Free the ath5k_hw struct + * * @ah: The &struct ath5k_hw */ void ath5k_hw_deinit(struct ath5k_hw *ah) diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c old mode 100644 new mode 100755 index c7d1437c..362ed14d --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -68,23 +68,18 @@ #define CREATE_TRACE_POINTS #include "trace.h" -bool ath5k_modparam_nohwcrypt; +int ath5k_modparam_nohwcrypt; module_param_named(nohwcrypt, ath5k_modparam_nohwcrypt, bool, S_IRUGO); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); -static bool modparam_all_channels; +static int modparam_all_channels; module_param_named(all_channels, modparam_all_channels, bool, S_IRUGO); MODULE_PARM_DESC(all_channels, "Expose all channels the device can use."); -static bool modparam_fastchanswitch; +static int modparam_fastchanswitch; module_param_named(fastchanswitch, modparam_fastchanswitch, bool, S_IRUGO); MODULE_PARM_DESC(fastchanswitch, "Enable fast channel switching for AR2413/AR5413 radios."); -static bool ath5k_modparam_no_hw_rfkill_switch; -module_param_named(no_hw_rfkill_switch, ath5k_modparam_no_hw_rfkill_switch, - bool, S_IRUGO); -MODULE_PARM_DESC(no_hw_rfkill_switch, "Ignore the GPIO RFKill switch state"); - /* Module info */ MODULE_AUTHOR("Jiri Slaby"); @@ -188,6 +183,7 @@ static const struct ieee80211_rate ath5k_rates[] = { { .bitrate = 540, .hw_value = ATH5K_RATE_CODE_54M, .flags = 0 }, + /* XR missing */ }; static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp) @@ -725,25 +721,22 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, if (ret) goto err_unmap; - /* Set up MRR descriptor */ - if (ah->ah_capabilities.cap_has_mrr_support) { - memset(mrr_rate, 0, sizeof(mrr_rate)); - memset(mrr_tries, 0, sizeof(mrr_tries)); - for (i = 0; i < 3; i++) { - rate = ieee80211_get_alt_retry_rate(ah->hw, info, i); - if (!rate) - break; - - mrr_rate[i] = rate->hw_value; - mrr_tries[i] = info->control.rates[i + 1].count; - } + memset(mrr_rate, 0, sizeof(mrr_rate)); + memset(mrr_tries, 0, sizeof(mrr_tries)); + for (i = 0; i < 3; i++) { + rate = ieee80211_get_alt_retry_rate(ah->hw, info, i); + if (!rate) + break; - ath5k_hw_setup_mrr_tx_desc(ah, ds, - mrr_rate[0], mrr_tries[0], - mrr_rate[1], mrr_tries[1], - mrr_rate[2], mrr_tries[2]); + mrr_rate[i] = rate->hw_value; + mrr_tries[i] = info->control.rates[i + 1].count; } + ath5k_hw_setup_mrr_tx_desc(ah, ds, + mrr_rate[0], mrr_tries[0], + mrr_rate[1], mrr_tries[1], + mrr_rate[2], mrr_tries[2]); + ds->ds_link = 0; ds->ds_data = bf->skbaddr; @@ -1696,7 +1689,7 @@ ath5k_tasklet_tx(unsigned long data) struct ath5k_hw *ah = (void *)data; for (i = 0; i < AR5K_NUM_TX_QUEUES; i++) - if (ah->txqs[i].setup && (ah->ah_txq_isr_txok_all & BIT(i))) + if (ah->txqs[i].setup && (ah->ah_txq_isr & BIT(i))) ath5k_tx_processq(ah, &ah->txqs[i]); ah->tx_pending = false; @@ -1867,8 +1860,7 @@ ath5k_beacon_send(struct ath5k_hw *ah) ah->bmisscount = 0; } - if ((ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs + - ah->num_mesh_vifs > 1) || + if ((ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs > 1) || ah->opmode == NL80211_IFTYPE_MESH_POINT) { u64 tsf = ath5k_hw_get_tsf64(ah); u32 tsftu = TSF_TO_TU(tsf); @@ -1953,8 +1945,7 @@ ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf) u64 hw_tsf; intval = ah->bintval & AR5K_BEACON_PERIOD; - if (ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs - + ah->num_mesh_vifs > 1) { + if (ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs > 1) { intval /= ATH_BCBUF; /* staggered multi-bss beacons */ if (intval < 15) ATH5K_WARN(ah, "intval %u is too low, min 15\n", @@ -2014,7 +2005,7 @@ ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf) ah->nexttbtt = nexttbtt; intval |= AR5K_BEACON_ENA; - ath5k_hw_init_beacon_timers(ah, nexttbtt, intval); + ath5k_hw_init_beacon(ah, nexttbtt, intval); /* * debugging output last in order to preserve the time critical aspect @@ -2121,29 +2112,16 @@ static void ath5k_intr_calibration_poll(struct ath5k_hw *ah) { if (time_is_before_eq_jiffies(ah->ah_cal_next_ani) && - !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL) && - !(ah->ah_cal_mask & AR5K_CALIBRATION_SHORT)) { - - /* Run ANI only when calibration is not active */ - + !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) { + /* run ANI only when full calibration is not active */ ah->ah_cal_next_ani = jiffies + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI); tasklet_schedule(&ah->ani_tasklet); - } else if (time_is_before_eq_jiffies(ah->ah_cal_next_short) && - !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL) && - !(ah->ah_cal_mask & AR5K_CALIBRATION_SHORT)) { - - /* Run calibration only when another calibration - * is not running. - * - * Note: This is for both full/short calibration, - * if it's time for a full one, ath5k_calibrate_work will deal - * with it. */ - - ah->ah_cal_next_short = jiffies + - msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_SHORT); - ieee80211_queue_work(ah->hw, &ah->calib_work); + } else if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) { + ah->ah_cal_next_full = jiffies + + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL); + tasklet_schedule(&ah->calib); } /* we could use SWI to generate enough interrupts to meet our * calibration interval requirements, if necessary: @@ -2171,110 +2149,69 @@ ath5k_intr(int irq, void *dev_id) enum ath5k_int status; unsigned int counter = 1000; - - /* - * If hw is not ready (or detached) and we get an - * interrupt, or if we have no interrupts pending - * (that means it's not for us) skip it. - * - * NOTE: Group 0/1 PCI interface registers are not - * supported on WiSOCs, so we can't check for pending - * interrupts (ISR belongs to another register group - * so we are ok). - */ if (unlikely(test_bit(ATH_STAT_INVALID, ah->status) || - ((ath5k_get_bus_type(ah) != ATH_AHB) && - !ath5k_hw_is_intr_pending(ah)))) + ((ath5k_get_bus_type(ah) != ATH_AHB) && + !ath5k_hw_is_intr_pending(ah)))) return IRQ_NONE; - /** Main loop **/ do { - ath5k_hw_get_isr(ah, &status); /* NB: clears IRQ too */ - + ath5k_hw_get_isr(ah, &status); /* NB: clears IRQ too */ ATH5K_DBG(ah, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n", status, ah->imask); - - /* - * Fatal hw error -> Log and reset - * - * Fatal errors are unrecoverable so we have to - * reset the card. These errors include bus and - * dma errors. - */ if (unlikely(status & AR5K_INT_FATAL)) { - + /* + * Fatal errors are unrecoverable. + * Typically these are caused by DMA errors. + */ ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "fatal int, resetting\n"); ieee80211_queue_work(ah->hw, &ah->reset_work); - - /* - * RX Overrun -> Count and reset if needed - * - * Receive buffers are full. Either the bus is busy or - * the CPU is not fast enough to process all received - * frames. - */ } else if (unlikely(status & AR5K_INT_RXORN)) { - /* + * Receive buffers are full. Either the bus is busy or + * the CPU is not fast enough to process all received + * frames. * Older chipsets need a reset to come out of this * condition, but we treat it as RX for newer chips. - * We don't know exactly which versions need a reset + * We don't know exactly which versions need a reset - * this guess is copied from the HAL. */ ah->stats.rxorn_intr++; - if (ah->ah_mac_srev < AR5K_SREV_AR5212) { ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "rx overrun, resetting\n"); ieee80211_queue_work(ah->hw, &ah->reset_work); } else ath5k_schedule_rx(ah); - } else { - - /* Software Beacon Alert -> Schedule beacon tasklet */ if (status & AR5K_INT_SWBA) tasklet_hi_schedule(&ah->beacontq); - /* - * No more RX descriptors -> Just count - * - * NB: the hardware should re-read the link when - * RXE bit is written, but it doesn't work at - * least on older hardware revs. - */ - if (status & AR5K_INT_RXEOL) + if (status & AR5K_INT_RXEOL) { + /* + * NB: the hardware should re-read the link when + * RXE bit is written, but it doesn't work at + * least on older hardware revs. + */ ah->stats.rxeol_intr++; - - - /* TX Underrun -> Bump tx trigger level */ - if (status & AR5K_INT_TXURN) + } + if (status & AR5K_INT_TXURN) { + /* bump tx trigger level */ ath5k_hw_update_tx_triglevel(ah, true); - - /* RX -> Schedule rx tasklet */ + } if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR)) ath5k_schedule_rx(ah); - - /* TX -> Schedule tx tasklet */ - if (status & (AR5K_INT_TXOK - | AR5K_INT_TXDESC - | AR5K_INT_TXERR - | AR5K_INT_TXEOL)) + if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC + | AR5K_INT_TXERR | AR5K_INT_TXEOL)) ath5k_schedule_tx(ah); - - /* Missed beacon -> TODO - if (status & AR5K_INT_BMISS) - */ - - /* MIB event -> Update counters and notify ANI */ + if (status & AR5K_INT_BMISS) { + /* TODO */ + } if (status & AR5K_INT_MIB) { ah->stats.mib_intr++; ath5k_hw_update_mib_counters(ah); ath5k_ani_mib_intr(ah); } - - /* GPIO -> Notify RFKill layer */ if (status & AR5K_INT_GPIO) tasklet_schedule(&ah->rf_kill.toggleq); @@ -2285,19 +2222,12 @@ ath5k_intr(int irq, void *dev_id) } while (ath5k_hw_is_intr_pending(ah) && --counter > 0); - /* - * Until we handle rx/tx interrupts mask them on IMR - * - * NOTE: ah->(rx/tx)_pending are set when scheduling the tasklets - * and unset after we 've handled the interrupts. - */ if (ah->rx_pending || ah->tx_pending) ath5k_set_current_imask(ah); if (unlikely(!counter)) ATH5K_WARN(ah, "too many interrupts, giving up for now\n"); - /* Fire up calibration poll */ ath5k_intr_calibration_poll(ah); return IRQ_HANDLED; @@ -2308,48 +2238,41 @@ ath5k_intr(int irq, void *dev_id) * for temperature/environment changes. */ static void -ath5k_calibrate_work(struct work_struct *work) +ath5k_tasklet_calibrate(unsigned long data) { - struct ath5k_hw *ah = container_of(work, struct ath5k_hw, - calib_work); - - /* Should we run a full calibration ? */ - if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) { - - ah->ah_cal_next_full = jiffies + - msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL); - ah->ah_cal_mask |= AR5K_CALIBRATION_FULL; - - ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, - "running full calibration\n"); - - if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) { - /* - * Rfgain is out of bounds, reset the chip - * to load new gain values. - */ - ATH5K_DBG(ah, ATH5K_DEBUG_RESET, - "got new rfgain, resetting\n"); - ieee80211_queue_work(ah->hw, &ah->reset_work); - } - } else - ah->ah_cal_mask |= AR5K_CALIBRATION_SHORT; + struct ath5k_hw *ah = (void *)data; + /* Only full calibration for now */ + ah->ah_cal_mask |= AR5K_CALIBRATION_FULL; ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n", ieee80211_frequency_to_channel(ah->curchan->center_freq), ah->curchan->hw_value); + if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) { + /* + * Rfgain is out of bounds, reset the chip + * to load new gain values. + */ + ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "calibration, resetting\n"); + ieee80211_queue_work(ah->hw, &ah->reset_work); + } if (ath5k_hw_phy_calibrate(ah, ah->curchan)) ATH5K_ERR(ah, "calibration of channel %u failed\n", ieee80211_frequency_to_channel( ah->curchan->center_freq)); - /* Clear calibration flags */ - if (ah->ah_cal_mask & AR5K_CALIBRATION_FULL) - ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL; - else if (ah->ah_cal_mask & AR5K_CALIBRATION_SHORT) - ah->ah_cal_mask &= ~AR5K_CALIBRATION_SHORT; + /* Noise floor calibration interrupts rx/tx path while I/Q calibration + * doesn't. + * TODO: We should stop TX here, so that it doesn't interfere. + * Note that stopping the queues is not enough to stop TX! */ + if (time_is_before_eq_jiffies(ah->ah_cal_next_nf)) { + ah->ah_cal_next_nf = jiffies + + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_NF); + ath5k_hw_update_noise_floor(ah); + } + + ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL; } @@ -2434,9 +2357,6 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT); - /* SW support for IBSS_RSN is provided by mac80211 */ - hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - /* both antennas can be configured as RX or TX */ hw->wiphy->available_antennas_tx = 0x3; hw->wiphy->available_antennas_rx = 0x3; @@ -2487,8 +2407,8 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) if (ret) goto err_irq; - /* Set up multi-rate retry capabilities */ - if (ah->ah_capabilities.cap_has_mrr_support) { + /* set up multi-rate retry capabilities */ + if (ah->ah_version == AR5K_AR5212) { hw->max_rates = 4; hw->max_rate_tries = max(AR5K_INIT_RETRY_SHORT, AR5K_INIT_RETRY_LONG); @@ -2624,22 +2544,15 @@ int ath5k_start(struct ieee80211_hw *hw) * and then setup of the interrupt mask. */ ah->curchan = ah->hw->conf.channel; - ah->imask = AR5K_INT_RXOK - | AR5K_INT_RXERR - | AR5K_INT_RXEOL - | AR5K_INT_RXORN - | AR5K_INT_TXDESC - | AR5K_INT_TXEOL - | AR5K_INT_FATAL - | AR5K_INT_GLOBAL - | AR5K_INT_MIB; + ah->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL | + AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL | + AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB; ret = ath5k_reset(ah, NULL, false); if (ret) goto done; - if (!ath5k_modparam_no_hw_rfkill_switch) - ath5k_rfkill_hw_start(ah); + ath5k_rfkill_hw_start(ah); /* * Reset the key cache since some parts do not reset the @@ -2672,6 +2585,7 @@ static void ath5k_stop_tasklets(struct ath5k_hw *ah) ah->tx_pending = false; tasklet_kill(&ah->rxtq); tasklet_kill(&ah->txtq); + tasklet_kill(&ah->calib); tasklet_kill(&ah->beacontq); tasklet_kill(&ah->ani_tasklet); } @@ -2723,8 +2637,7 @@ void ath5k_stop(struct ieee80211_hw *hw) cancel_delayed_work_sync(&ah->tx_complete_work); - if (!ath5k_modparam_no_hw_rfkill_switch) - ath5k_rfkill_hw_stop(ah); + ath5k_rfkill_hw_stop(ah); } /* @@ -2776,24 +2689,9 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan, ath5k_ani_init(ah, ani_mode); - /* - * Set calibration intervals - * - * Note: We don't need to run calibration imediately - * since some initial calibration is done on reset - * even for fast channel switching. Also on scanning - * this will get set again and again and it won't get - * executed unless we connect somewhere and spend some - * time on the channel (that's what calibration needs - * anyway to be accurate). - */ - ah->ah_cal_next_full = jiffies + - msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL); - ah->ah_cal_next_ani = jiffies + - msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI); - ah->ah_cal_next_short = jiffies + - msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_SHORT); - + ah->ah_cal_next_full = jiffies + msecs_to_jiffies(100); + ah->ah_cal_next_ani = jiffies; + ah->ah_cal_next_nf = jiffies; ewma_init(&ah->ah_beacon_rssi_avg, 1024, 8); /* clear survey data and cycle counters */ @@ -2846,6 +2744,20 @@ ath5k_init(struct ieee80211_hw *hw) int ret; + /* + * Check if the MAC has multi-rate retry support. + * We do this by trying to setup a fake extended + * descriptor. MACs that don't have support will + * return false w/o doing anything. MACs that do + * support it will return true w/o doing anything. + */ + ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0); + + if (ret < 0) + goto err; + if (ret > 0) + __set_bit(ATH_STAT_MRRETRY, ah->status); + /* * Collect the channel list. The 802.11 layer * is responsible for filtering this list based @@ -2929,11 +2841,11 @@ ath5k_init(struct ieee80211_hw *hw) tasklet_init(&ah->rxtq, ath5k_tasklet_rx, (unsigned long)ah); tasklet_init(&ah->txtq, ath5k_tasklet_tx, (unsigned long)ah); + tasklet_init(&ah->calib, ath5k_tasklet_calibrate, (unsigned long)ah); tasklet_init(&ah->beacontq, ath5k_tasklet_beacon, (unsigned long)ah); tasklet_init(&ah->ani_tasklet, ath5k_tasklet_ani, (unsigned long)ah); INIT_WORK(&ah->reset_work, ath5k_reset_work); - INIT_WORK(&ah->calib_work, ath5k_calibrate_work); INIT_DELAYED_WORK(&ah->tx_complete_work, ath5k_tx_complete_poll_work); ret = ath5k_hw_common(ah)->bus_ops->eeprom_read_mac(ah, mac); diff --git a/drivers/net/wireless/ath/ath5k/caps.c b/drivers/net/wireless/ath/ath5k/caps.c index 994169ad..810fba96 100644 --- a/drivers/net/wireless/ath/ath5k/caps.c +++ b/drivers/net/wireless/ath/ath5k/caps.c @@ -85,19 +85,12 @@ int ath5k_hw_set_capabilities(struct ath5k_hw *ah) caps->cap_range.range_2ghz_min = 2412; caps->cap_range.range_2ghz_max = 2732; - /* Override 2GHz modes on SoCs that need it - * NOTE: cap_needs_2GHz_ovr gets set from - * ath_ahb_probe */ - if (!caps->cap_needs_2GHz_ovr) { - if (AR5K_EEPROM_HDR_11B(ee_header)) - __set_bit(AR5K_MODE_11B, - caps->cap_mode); - - if (AR5K_EEPROM_HDR_11G(ee_header) && - ah->ah_version != AR5K_AR5211) - __set_bit(AR5K_MODE_11G, - caps->cap_mode); - } + if (AR5K_EEPROM_HDR_11B(ee_header)) + __set_bit(AR5K_MODE_11B, caps->cap_mode); + + if (AR5K_EEPROM_HDR_11G(ee_header) && + ah->ah_version != AR5K_AR5211) + __set_bit(AR5K_MODE_11G, caps->cap_mode); } } @@ -110,18 +103,12 @@ int ath5k_hw_set_capabilities(struct ath5k_hw *ah) else caps->cap_queues.q_tx_num = AR5K_NUM_TX_QUEUES; - /* Newer hardware has PHY error counters */ + /* newer hardware has PHY error counters */ if (ah->ah_mac_srev >= AR5K_SREV_AR5213A) caps->cap_has_phyerr_counters = true; else caps->cap_has_phyerr_counters = false; - /* MACs since AR5212 have MRR support */ - if (ah->ah_version == AR5K_AR5212) - caps->cap_has_mrr_support = true; - else - caps->cap_has_mrr_support = false; - return 0; } diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index e5e8f45d..8c5ce8b0 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -71,6 +71,13 @@ static unsigned int ath5k_debug; module_param_named(debug, ath5k_debug, uint, 0); +static int ath5k_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + + /* debugfs: registers */ struct reg { @@ -258,7 +265,7 @@ static ssize_t write_file_beacon(struct file *file, static const struct file_operations fops_beacon = { .read = read_file_beacon, .write = write_file_beacon, - .open = simple_open, + .open = ath5k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -278,7 +285,7 @@ static ssize_t write_file_reset(struct file *file, static const struct file_operations fops_reset = { .write = write_file_reset, - .open = simple_open, + .open = ath5k_debugfs_open, .owner = THIS_MODULE, .llseek = noop_llseek, }; @@ -358,7 +365,7 @@ static ssize_t write_file_debug(struct file *file, static const struct file_operations fops_debug = { .read = read_file_debug, .write = write_file_debug, - .open = simple_open, + .open = ath5k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -470,7 +477,7 @@ static ssize_t write_file_antenna(struct file *file, static const struct file_operations fops_antenna = { .read = read_file_antenna, .write = write_file_antenna, - .open = simple_open, + .open = ath5k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -525,7 +532,7 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf, static const struct file_operations fops_misc = { .read = read_file_misc, - .open = simple_open, + .open = ath5k_debugfs_open, .owner = THIS_MODULE, }; @@ -640,7 +647,7 @@ static ssize_t write_file_frameerrors(struct file *file, static const struct file_operations fops_frameerrors = { .read = read_file_frameerrors, .write = write_file_frameerrors, - .open = simple_open, + .open = ath5k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -803,7 +810,7 @@ static ssize_t write_file_ani(struct file *file, static const struct file_operations fops_ani = { .read = read_file_ani, .write = write_file_ani, - .open = simple_open, + .open = ath5k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -874,7 +881,7 @@ static ssize_t write_file_queue(struct file *file, static const struct file_operations fops_queue = { .read = read_file_queue, .write = write_file_queue, - .open = simple_open, + .open = ath5k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; diff --git a/drivers/net/wireless/ath/ath5k/desc.c b/drivers/net/wireless/ath/ath5k/desc.c index f8bfa3ac..7e88dda8 100644 --- a/drivers/net/wireless/ath/ath5k/desc.c +++ b/drivers/net/wireless/ath/ath5k/desc.c @@ -26,61 +26,20 @@ #include "debug.h" -/** - * DOC: Hardware descriptor functions - * - * Here we handle the processing of the low-level hw descriptors - * that hw reads and writes via DMA for each TX and RX attempt (that means - * we can also have descriptors for failed TX/RX tries). We have two kind of - * descriptors for RX and TX, control descriptors tell the hw how to send or - * receive a packet where to read/write it from/to etc and status descriptors - * that contain information about how the packet was sent or received (errors - * included). - * - * Descriptor format is not exactly the same for each MAC chip version so we - * have function pointers on &struct ath5k_hw we initialize at runtime based on - * the chip used. - */ - - /************************\ * TX Control descriptors * \************************/ -/** - * ath5k_hw_setup_2word_tx_desc() - Initialize a 2-word tx control descriptor - * @ah: The &struct ath5k_hw - * @desc: The &struct ath5k_desc - * @pkt_len: Frame length in bytes - * @hdr_len: Header length in bytes (only used on AR5210) - * @padsize: Any padding we've added to the frame length - * @type: One of enum ath5k_pkt_type - * @tx_power: Tx power in 0.5dB steps - * @tx_rate0: HW idx for transmission rate - * @tx_tries0: Max number of retransmissions - * @key_index: Index on key table to use for encryption - * @antenna_mode: Which antenna to use (0 for auto) - * @flags: One of AR5K_TXDESC_* flags (desc.h) - * @rtscts_rate: HW idx for RTS/CTS transmission rate - * @rtscts_duration: What to put on duration field on the header of RTS/CTS - * - * Internal function to initialize a 2-Word TX control descriptor - * found on AR5210 and AR5211 MACs chips. - * - * Returns 0 on success or -EINVAL on false input +/* + * Initialize the 2-word tx control descriptor on 5210/5211 */ static int -ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, - struct ath5k_desc *desc, - unsigned int pkt_len, unsigned int hdr_len, - int padsize, - enum ath5k_pkt_type type, - unsigned int tx_power, - unsigned int tx_rate0, unsigned int tx_tries0, - unsigned int key_index, - unsigned int antenna_mode, - unsigned int flags, - unsigned int rtscts_rate, unsigned int rtscts_duration) +ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, + unsigned int pkt_len, unsigned int hdr_len, int padsize, + enum ath5k_pkt_type type, + unsigned int tx_power, unsigned int tx_rate0, unsigned int tx_tries0, + unsigned int key_index, unsigned int antenna_mode, unsigned int flags, + unsigned int rtscts_rate, unsigned int rtscts_duration) { u32 frame_type; struct ath5k_hw_2w_tx_ctl *tx_ctl; @@ -213,40 +172,17 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, return 0; } -/** - * ath5k_hw_setup_4word_tx_desc() - Initialize a 4-word tx control descriptor - * @ah: The &struct ath5k_hw - * @desc: The &struct ath5k_desc - * @pkt_len: Frame length in bytes - * @hdr_len: Header length in bytes (only used on AR5210) - * @padsize: Any padding we've added to the frame length - * @type: One of enum ath5k_pkt_type - * @tx_power: Tx power in 0.5dB steps - * @tx_rate0: HW idx for transmission rate - * @tx_tries0: Max number of retransmissions - * @key_index: Index on key table to use for encryption - * @antenna_mode: Which antenna to use (0 for auto) - * @flags: One of AR5K_TXDESC_* flags (desc.h) - * @rtscts_rate: HW idx for RTS/CTS transmission rate - * @rtscts_duration: What to put on duration field on the header of RTS/CTS - * - * Internal function to initialize a 4-Word TX control descriptor - * found on AR5212 and later MACs chips. - * - * Returns 0 on success or -EINVAL on false input +/* + * Initialize the 4-word tx control descriptor on 5212 */ -static int -ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah, - struct ath5k_desc *desc, - unsigned int pkt_len, unsigned int hdr_len, - int padsize, - enum ath5k_pkt_type type, - unsigned int tx_power, - unsigned int tx_rate0, unsigned int tx_tries0, - unsigned int key_index, - unsigned int antenna_mode, - unsigned int flags, - unsigned int rtscts_rate, unsigned int rtscts_duration) +static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah, + struct ath5k_desc *desc, unsigned int pkt_len, unsigned int hdr_len, + int padsize, + enum ath5k_pkt_type type, unsigned int tx_power, unsigned int tx_rate0, + unsigned int tx_tries0, unsigned int key_index, + unsigned int antenna_mode, unsigned int flags, + unsigned int rtscts_rate, + unsigned int rtscts_duration) { struct ath5k_hw_4w_tx_ctl *tx_ctl; unsigned int frame_len; @@ -356,29 +292,13 @@ ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah, return 0; } -/** - * ath5k_hw_setup_mrr_tx_desc() - Initialize an MRR tx control descriptor - * @ah: The &struct ath5k_hw - * @desc: The &struct ath5k_desc - * @tx_rate1: HW idx for rate used on transmission series 1 - * @tx_tries1: Max number of retransmissions for transmission series 1 - * @tx_rate2: HW idx for rate used on transmission series 2 - * @tx_tries2: Max number of retransmissions for transmission series 2 - * @tx_rate3: HW idx for rate used on transmission series 3 - * @tx_tries3: Max number of retransmissions for transmission series 3 - * - * Multi rate retry (MRR) tx control descriptors are available only on AR5212 - * MACs, they are part of the normal 4-word tx control descriptor (see above) - * but we handle them through a separate function for better abstraction. - * - * Returns 0 on success or -EINVAL on invalid input +/* + * Initialize a 4-word multi rate retry tx control descriptor on 5212 */ int -ath5k_hw_setup_mrr_tx_desc(struct ath5k_hw *ah, - struct ath5k_desc *desc, - u_int tx_rate1, u_int tx_tries1, - u_int tx_rate2, u_int tx_tries2, - u_int tx_rate3, u_int tx_tries3) +ath5k_hw_setup_mrr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, + unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2, + u_int tx_tries2, unsigned int tx_rate3, u_int tx_tries3) { struct ath5k_hw_4w_tx_ctl *tx_ctl; @@ -430,16 +350,11 @@ ath5k_hw_setup_mrr_tx_desc(struct ath5k_hw *ah, * TX Status descriptors * \***********************/ -/** - * ath5k_hw_proc_2word_tx_status() - Process a tx status descriptor on 5210/1 - * @ah: The &struct ath5k_hw - * @desc: The &struct ath5k_desc - * @ts: The &struct ath5k_tx_status +/* + * Process the tx status descriptor on 5210/5211 */ -static int -ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah, - struct ath5k_desc *desc, - struct ath5k_tx_status *ts) +static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah, + struct ath5k_desc *desc, struct ath5k_tx_status *ts) { struct ath5k_hw_2w_tx_ctl *tx_ctl; struct ath5k_hw_tx_status *tx_status; @@ -484,16 +399,11 @@ ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah, return 0; } -/** - * ath5k_hw_proc_4word_tx_status() - Process a tx status descriptor on 5212 - * @ah: The &struct ath5k_hw - * @desc: The &struct ath5k_desc - * @ts: The &struct ath5k_tx_status +/* + * Process a tx status descriptor on 5212 */ -static int -ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah, - struct ath5k_desc *desc, - struct ath5k_tx_status *ts) +static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah, + struct ath5k_desc *desc, struct ath5k_tx_status *ts) { struct ath5k_hw_4w_tx_ctl *tx_ctl; struct ath5k_hw_tx_status *tx_status; @@ -550,17 +460,11 @@ ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah, * RX Descriptors * \****************/ -/** - * ath5k_hw_setup_rx_desc() - Initialize an rx control descriptor - * @ah: The &struct ath5k_hw - * @desc: The &struct ath5k_desc - * @size: RX buffer length in bytes - * @flags: One of AR5K_RXDESC_* flags +/* + * Initialize an rx control descriptor */ -int -ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, - struct ath5k_desc *desc, - u32 size, unsigned int flags) +int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, + u32 size, unsigned int flags) { struct ath5k_hw_rx_ctl *rx_ctl; @@ -587,22 +491,11 @@ ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, return 0; } -/** - * ath5k_hw_proc_5210_rx_status() - Process the rx status descriptor on 5210/1 - * @ah: The &struct ath5k_hw - * @desc: The &struct ath5k_desc - * @rs: The &struct ath5k_rx_status - * - * Internal function used to process an RX status descriptor - * on AR5210/5211 MAC. - * - * Returns 0 on success or -EINPROGRESS in case we haven't received the who;e - * frame yet. +/* + * Process the rx status descriptor on 5210/5211 */ -static int -ath5k_hw_proc_5210_rx_status(struct ath5k_hw *ah, - struct ath5k_desc *desc, - struct ath5k_rx_status *rs) +static int ath5k_hw_proc_5210_rx_status(struct ath5k_hw *ah, + struct ath5k_desc *desc, struct ath5k_rx_status *rs) { struct ath5k_hw_rx_status *rx_status; @@ -681,22 +574,12 @@ ath5k_hw_proc_5210_rx_status(struct ath5k_hw *ah, return 0; } -/** - * ath5k_hw_proc_5212_rx_status() - Process the rx status descriptor on 5212 - * @ah: The &struct ath5k_hw - * @desc: The &struct ath5k_desc - * @rs: The &struct ath5k_rx_status - * - * Internal function used to process an RX status descriptor - * on AR5212 and later MAC. - * - * Returns 0 on success or -EINPROGRESS in case we haven't received the who;e - * frame yet. +/* + * Process the rx status descriptor on 5212 */ -static int -ath5k_hw_proc_5212_rx_status(struct ath5k_hw *ah, - struct ath5k_desc *desc, - struct ath5k_rx_status *rs) +static int ath5k_hw_proc_5212_rx_status(struct ath5k_hw *ah, + struct ath5k_desc *desc, + struct ath5k_rx_status *rs) { struct ath5k_hw_rx_status *rx_status; u32 rxstat0, rxstat1; @@ -763,16 +646,10 @@ ath5k_hw_proc_5212_rx_status(struct ath5k_hw *ah, * Attach * \********/ -/** - * ath5k_hw_init_desc_functions() - Init function pointers inside ah - * @ah: The &struct ath5k_hw - * - * Maps the internal descriptor functions to the function pointers on ah, used - * from above. This is used as an abstraction layer to handle the various chips - * the same way. +/* + * Init function pointers inside ath5k_hw struct */ -int -ath5k_hw_init_desc_functions(struct ath5k_hw *ah) +int ath5k_hw_init_desc_functions(struct ath5k_hw *ah) { if (ah->ah_version == AR5K_AR5212) { ah->ah_setup_tx_desc = ath5k_hw_setup_4word_tx_desc; diff --git a/drivers/net/wireless/ath/ath5k/desc.h b/drivers/net/wireless/ath/ath5k/desc.h index 8d6c01a4..cfd529b5 100644 --- a/drivers/net/wireless/ath/ath5k/desc.h +++ b/drivers/net/wireless/ath/ath5k/desc.h @@ -20,30 +20,25 @@ * RX/TX descriptor structures */ -/** - * struct ath5k_hw_rx_ctl - Common hardware RX control descriptor - * @rx_control_0: RX control word 0 - * @rx_control_1: RX control word 1 +/* + * Common hardware RX control descriptor */ struct ath5k_hw_rx_ctl { - u32 rx_control_0; - u32 rx_control_1; + u32 rx_control_0; /* RX control word 0 */ + u32 rx_control_1; /* RX control word 1 */ } __packed __aligned(4); /* RX control word 1 fields/flags */ #define AR5K_DESC_RX_CTL1_BUF_LEN 0x00000fff /* data buffer length */ #define AR5K_DESC_RX_CTL1_INTREQ 0x00002000 /* RX interrupt request */ -/** - * struct ath5k_hw_rx_status - Common hardware RX status descriptor - * @rx_status_0: RX status word 0 - * @rx_status_1: RX status word 1 - * +/* + * Common hardware RX status descriptor * 5210, 5211 and 5212 differ only in the fields and flags defined below */ struct ath5k_hw_rx_status { - u32 rx_status_0; - u32 rx_status_1; + u32 rx_status_0; /* RX status word 0 */ + u32 rx_status_1; /* RX status word 1 */ } __packed __aligned(4); /* 5210/5211 */ @@ -103,36 +98,17 @@ struct ath5k_hw_rx_status { /** * enum ath5k_phy_error_code - PHY Error codes - * @AR5K_RX_PHY_ERROR_UNDERRUN: Transmit underrun, [5210] No error - * @AR5K_RX_PHY_ERROR_TIMING: Timing error - * @AR5K_RX_PHY_ERROR_PARITY: Illegal parity - * @AR5K_RX_PHY_ERROR_RATE: Illegal rate - * @AR5K_RX_PHY_ERROR_LENGTH: Illegal length - * @AR5K_RX_PHY_ERROR_RADAR: Radar detect, [5210] 64 QAM rate - * @AR5K_RX_PHY_ERROR_SERVICE: Illegal service - * @AR5K_RX_PHY_ERROR_TOR: Transmit override receive - * @AR5K_RX_PHY_ERROR_OFDM_TIMING: OFDM Timing error [5212+] - * @AR5K_RX_PHY_ERROR_OFDM_SIGNAL_PARITY: OFDM Signal parity error [5212+] - * @AR5K_RX_PHY_ERROR_OFDM_RATE_ILLEGAL: OFDM Illegal rate [5212+] - * @AR5K_RX_PHY_ERROR_OFDM_LENGTH_ILLEGAL: OFDM Illegal length [5212+] - * @AR5K_RX_PHY_ERROR_OFDM_POWER_DROP: OFDM Power drop [5212+] - * @AR5K_RX_PHY_ERROR_OFDM_SERVICE: OFDM Service (?) [5212+] - * @AR5K_RX_PHY_ERROR_OFDM_RESTART: OFDM Restart (?) [5212+] - * @AR5K_RX_PHY_ERROR_CCK_TIMING: CCK Timing error [5212+] - * @AR5K_RX_PHY_ERROR_CCK_HEADER_CRC: Header CRC error [5212+] - * @AR5K_RX_PHY_ERROR_CCK_RATE_ILLEGAL: Illegal rate [5212+] - * @AR5K_RX_PHY_ERROR_CCK_SERVICE: CCK Service (?) [5212+] - * @AR5K_RX_PHY_ERROR_CCK_RESTART: CCK Restart (?) [5212+] */ enum ath5k_phy_error_code { - AR5K_RX_PHY_ERROR_UNDERRUN = 0, - AR5K_RX_PHY_ERROR_TIMING = 1, - AR5K_RX_PHY_ERROR_PARITY = 2, - AR5K_RX_PHY_ERROR_RATE = 3, - AR5K_RX_PHY_ERROR_LENGTH = 4, - AR5K_RX_PHY_ERROR_RADAR = 5, - AR5K_RX_PHY_ERROR_SERVICE = 6, - AR5K_RX_PHY_ERROR_TOR = 7, + AR5K_RX_PHY_ERROR_UNDERRUN = 0, /* Transmit underrun, [5210] No error */ + AR5K_RX_PHY_ERROR_TIMING = 1, /* Timing error */ + AR5K_RX_PHY_ERROR_PARITY = 2, /* Illegal parity */ + AR5K_RX_PHY_ERROR_RATE = 3, /* Illegal rate */ + AR5K_RX_PHY_ERROR_LENGTH = 4, /* Illegal length */ + AR5K_RX_PHY_ERROR_RADAR = 5, /* Radar detect, [5210] 64 QAM rate */ + AR5K_RX_PHY_ERROR_SERVICE = 6, /* Illegal service */ + AR5K_RX_PHY_ERROR_TOR = 7, /* Transmit override receive */ + /* these are specific to the 5212 */ AR5K_RX_PHY_ERROR_OFDM_TIMING = 17, AR5K_RX_PHY_ERROR_OFDM_SIGNAL_PARITY = 18, AR5K_RX_PHY_ERROR_OFDM_RATE_ILLEGAL = 19, @@ -147,14 +123,12 @@ enum ath5k_phy_error_code { AR5K_RX_PHY_ERROR_CCK_RESTART = 31, }; -/** - * struct ath5k_hw_2w_tx_ctl - 5210/5211 hardware 2-word TX control descriptor - * @tx_control_0: TX control word 0 - * @tx_control_1: TX control word 1 +/* + * 5210/5211 hardware 2-word TX control descriptor */ struct ath5k_hw_2w_tx_ctl { - u32 tx_control_0; - u32 tx_control_1; + u32 tx_control_0; /* TX control word 0 */ + u32 tx_control_1; /* TX control word 1 */ } __packed __aligned(4); /* TX control word 0 fields/flags */ @@ -203,18 +177,14 @@ struct ath5k_hw_2w_tx_ctl { #define AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS 4 #define AR5K_AR5211_TX_DESC_FRAME_TYPE_PRESP 4 -/** - * struct ath5k_hw_4w_tx_ctl - 5212 hardware 4-word TX control descriptor - * @tx_control_0: TX control word 0 - * @tx_control_1: TX control word 1 - * @tx_control_2: TX control word 2 - * @tx_control_3: TX control word 3 +/* + * 5212 hardware 4-word TX control descriptor */ struct ath5k_hw_4w_tx_ctl { - u32 tx_control_0; - u32 tx_control_1; - u32 tx_control_2; - u32 tx_control_3; + u32 tx_control_0; /* TX control word 0 */ + u32 tx_control_1; /* TX control word 1 */ + u32 tx_control_2; /* TX control word 2 */ + u32 tx_control_3; /* TX control word 3 */ } __packed __aligned(4); /* TX control word 0 fields/flags */ @@ -268,14 +238,12 @@ struct ath5k_hw_4w_tx_ctl { #define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE 0x01f00000 /* RTS or CTS rate */ #define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE_S 20 -/** - * struct ath5k_hw_tx_status - Common TX status descriptor - * @tx_status_0: TX status word 0 - * @tx_status_1: TX status word 1 +/* + * Common TX status descriptor */ struct ath5k_hw_tx_status { - u32 tx_status_0; - u32 tx_status_1; + u32 tx_status_0; /* TX status word 0 */ + u32 tx_status_1; /* TX status word 1 */ } __packed __aligned(4); /* TX status word 0 fields/flags */ @@ -308,47 +276,37 @@ struct ath5k_hw_tx_status { #define AR5K_DESC_TX_STATUS1_COMP_SUCCESS_5212 0x00800000 /* [5212] compression status */ #define AR5K_DESC_TX_STATUS1_XMIT_ANTENNA_5212 0x01000000 /* [5212] transmit antenna */ -/** - * struct ath5k_hw_5210_tx_desc - 5210/5211 hardware TX descriptor - * @tx_ctl: The &struct ath5k_hw_2w_tx_ctl - * @tx_stat: The &struct ath5k_hw_tx_status +/* + * 5210/5211 hardware TX descriptor */ struct ath5k_hw_5210_tx_desc { struct ath5k_hw_2w_tx_ctl tx_ctl; struct ath5k_hw_tx_status tx_stat; } __packed __aligned(4); -/** - * struct ath5k_hw_5212_tx_desc - 5212 hardware TX descriptor - * @tx_ctl: The &struct ath5k_hw_4w_tx_ctl - * @tx_stat: The &struct ath5k_hw_tx_status +/* + * 5212 hardware TX descriptor */ struct ath5k_hw_5212_tx_desc { struct ath5k_hw_4w_tx_ctl tx_ctl; struct ath5k_hw_tx_status tx_stat; } __packed __aligned(4); -/** - * struct ath5k_hw_all_rx_desc - Common hardware RX descriptor - * @rx_ctl: The &struct ath5k_hw_rx_ctl - * @rx_stat: The &struct ath5k_hw_rx_status +/* + * Common hardware RX descriptor */ struct ath5k_hw_all_rx_desc { struct ath5k_hw_rx_ctl rx_ctl; struct ath5k_hw_rx_status rx_stat; } __packed __aligned(4); -/** - * struct ath5k_desc - Atheros hardware DMA descriptor - * @ds_link: Physical address of the next descriptor - * @ds_data: Physical address of data buffer (skb) - * @ud: Union containing hw_5xxx_tx_desc structs and hw_all_rx_desc - * +/* + * Atheros hardware DMA descriptor * This is read and written to by the hardware */ struct ath5k_desc { - u32 ds_link; - u32 ds_data; + u32 ds_link; /* physical address of the next descriptor */ + u32 ds_data; /* physical address of data buffer (skb) */ union { struct ath5k_hw_5210_tx_desc ds_tx5210; diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c index 5cc9aa81..2481f9c7 100644 --- a/drivers/net/wireless/ath/ath5k/dma.c +++ b/drivers/net/wireless/ath/ath5k/dma.c @@ -20,13 +20,16 @@ * DMA and interrupt masking functions * \*************************************/ -/** - * DOC: DMA and interrupt masking functions +/* + * dma.c - DMA and interrupt masking functions * * Here we setup descriptor pointers (rxdp/txdp) start/stop dma engine and * handle queue setup for 5210 chipset (rest are handled on qcu.c). * Also we setup interrupt mask register (IMR) and read the various interrupt * status registers (ISR). + * + * TODO: Handle SISR on 5211+ and introduce a function to return the queue + * number that resulted the interrupt. */ #include "ath5k.h" @@ -39,22 +42,22 @@ \*********/ /** - * ath5k_hw_start_rx_dma() - Start DMA receive + * ath5k_hw_start_rx_dma - Start DMA receive + * * @ah: The &struct ath5k_hw */ -void -ath5k_hw_start_rx_dma(struct ath5k_hw *ah) +void ath5k_hw_start_rx_dma(struct ath5k_hw *ah) { ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR); ath5k_hw_reg_read(ah, AR5K_CR); } /** - * ath5k_hw_stop_rx_dma() - Stop DMA receive + * ath5k_hw_stop_rx_dma - Stop DMA receive + * * @ah: The &struct ath5k_hw */ -static int -ath5k_hw_stop_rx_dma(struct ath5k_hw *ah) +static int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah) { unsigned int i; @@ -76,24 +79,24 @@ ath5k_hw_stop_rx_dma(struct ath5k_hw *ah) } /** - * ath5k_hw_get_rxdp() - Get RX Descriptor's address + * ath5k_hw_get_rxdp - Get RX Descriptor's address + * * @ah: The &struct ath5k_hw */ -u32 -ath5k_hw_get_rxdp(struct ath5k_hw *ah) +u32 ath5k_hw_get_rxdp(struct ath5k_hw *ah) { return ath5k_hw_reg_read(ah, AR5K_RXDP); } /** - * ath5k_hw_set_rxdp() - Set RX Descriptor's address + * ath5k_hw_set_rxdp - Set RX Descriptor's address + * * @ah: The &struct ath5k_hw * @phys_addr: RX descriptor address * * Returns -EIO if rx is active */ -int -ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr) +int ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr) { if (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) { ATH5K_DBG(ah, ATH5K_DEBUG_DMA, @@ -111,7 +114,8 @@ ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr) \**********/ /** - * ath5k_hw_start_tx_dma() - Start DMA transmit for a specific queue + * ath5k_hw_start_tx_dma - Start DMA transmit for a specific queue + * * @ah: The &struct ath5k_hw * @queue: The hw queue number * @@ -124,8 +128,7 @@ ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr) * NOTE: Must be called after setting up tx control descriptor for that * queue (see below). */ -int -ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue) +int ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue) { u32 tx_queue; @@ -174,16 +177,17 @@ ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue) } /** - * ath5k_hw_stop_tx_dma() - Stop DMA transmit on a specific queue + * ath5k_hw_stop_tx_dma - Stop DMA transmit on a specific queue + * * @ah: The &struct ath5k_hw * @queue: The hw queue number * * Stop DMA transmit on a specific hw queue and drain queue so we don't * have any pending frames. Returns -EBUSY if we still have pending frames, * -EINVAL if queue number is out of range or inactive. + * */ -static int -ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue) +static int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue) { unsigned int i = 40; u32 tx_queue, pending; @@ -316,14 +320,14 @@ ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue) } /** - * ath5k_hw_stop_beacon_queue() - Stop beacon queue - * @ah: The &struct ath5k_hw - * @queue: The queue number + * ath5k_hw_stop_beacon_queue - Stop beacon queue + * + * @ah The &struct ath5k_hw + * @queue The queue number * * Returns -EIO if queue didn't stop */ -int -ath5k_hw_stop_beacon_queue(struct ath5k_hw *ah, unsigned int queue) +int ath5k_hw_stop_beacon_queue(struct ath5k_hw *ah, unsigned int queue) { int ret; ret = ath5k_hw_stop_tx_dma(ah, queue); @@ -336,7 +340,8 @@ ath5k_hw_stop_beacon_queue(struct ath5k_hw *ah, unsigned int queue) } /** - * ath5k_hw_get_txdp() - Get TX Descriptor's address for a specific queue + * ath5k_hw_get_txdp - Get TX Descriptor's address for a specific queue + * * @ah: The &struct ath5k_hw * @queue: The hw queue number * @@ -347,8 +352,7 @@ ath5k_hw_stop_beacon_queue(struct ath5k_hw *ah, unsigned int queue) * * XXX: Is TXDP read and clear ? */ -u32 -ath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue) +u32 ath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue) { u16 tx_reg; @@ -378,10 +382,10 @@ ath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue) } /** - * ath5k_hw_set_txdp() - Set TX Descriptor's address for a specific queue + * ath5k_hw_set_txdp - Set TX Descriptor's address for a specific queue + * * @ah: The &struct ath5k_hw * @queue: The hw queue number - * @phys_addr: The physical address * * Set TX descriptor's address for a specific queue. For 5210 we ignore * the queue number and we use tx queue type since we only have 2 queues @@ -390,8 +394,7 @@ ath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue) * Returns -EINVAL if queue type is invalid for 5210 and -EIO if queue is still * active. */ -int -ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr) +int ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr) { u16 tx_reg; @@ -432,7 +435,8 @@ ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr) } /** - * ath5k_hw_update_tx_triglevel() - Update tx trigger level + * ath5k_hw_update_tx_triglevel - Update tx trigger level + * * @ah: The &struct ath5k_hw * @increase: Flag to force increase of trigger level * @@ -440,15 +444,15 @@ ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr) * buffer (aka FIFO threshold) that is used to indicate when PCU flushes * the buffer and transmits its data. Lowering this results sending small * frames more quickly but can lead to tx underruns, raising it a lot can - * result other problems. Right now we start with the lowest possible - * (64Bytes) and if we get tx underrun we increase it using the increase - * flag. Returns -EIO if we have reached maximum/minimum. + * result other problems (i think bmiss is related). Right now we start with + * the lowest possible (64Bytes) and if we get tx underrun we increase it using + * the increase flag. Returns -EIO if we have reached maximum/minimum. * * XXX: Link this with tx DMA size ? - * XXX2: Use it to save interrupts ? + * XXX: Use it to save interrupts ? + * TODO: Needs testing, i think it's related to bmiss... */ -int -ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase) +int ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase) { u32 trigger_level, imr; int ret = -EIO; @@ -494,20 +498,21 @@ ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase) \*******************/ /** - * ath5k_hw_is_intr_pending() - Check if we have pending interrupts + * ath5k_hw_is_intr_pending - Check if we have pending interrupts + * * @ah: The &struct ath5k_hw * * Check if we have pending interrupts to process. Returns 1 if we * have pending interrupts and 0 if we haven't. */ -bool -ath5k_hw_is_intr_pending(struct ath5k_hw *ah) +bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah) { return ath5k_hw_reg_read(ah, AR5K_INTPEND) == 1 ? 1 : 0; } /** - * ath5k_hw_get_isr() - Get interrupt status + * ath5k_hw_get_isr - Get interrupt status + * * @ah: The @struct ath5k_hw * @interrupt_mask: Driver's interrupt mask used to filter out * interrupts in sw. @@ -518,162 +523,62 @@ ath5k_hw_is_intr_pending(struct ath5k_hw *ah) * being mapped on some standard non hw-specific positions * (check out &ath5k_int). * - * NOTE: We do write-to-clear, so the active PISR/SISR bits at the time this - * function gets called are cleared on return. + * NOTE: We use read-and-clear register, so after this function is called ISR + * is zeroed. */ -int -ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) +int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) { - u32 data = 0; + u32 data; /* - * Read interrupt status from Primary Interrupt - * Register. - * - * Note: PISR/SISR Not available on 5210 + * Read interrupt status from the Interrupt Status register + * on 5210 */ if (ah->ah_version == AR5K_AR5210) { - u32 isr = 0; - isr = ath5k_hw_reg_read(ah, AR5K_ISR); - if (unlikely(isr == AR5K_INT_NOCARD)) { - *interrupt_mask = isr; + data = ath5k_hw_reg_read(ah, AR5K_ISR); + if (unlikely(data == AR5K_INT_NOCARD)) { + *interrupt_mask = data; return -ENODEV; } - - /* - * Filter out the non-common bits from the interrupt - * status. - */ - *interrupt_mask = (isr & AR5K_INT_COMMON) & ah->ah_imr; - - /* Hanlde INT_FATAL */ - if (unlikely(isr & (AR5K_ISR_SSERR | AR5K_ISR_MCABT - | AR5K_ISR_DPERR))) - *interrupt_mask |= AR5K_INT_FATAL; - - /* - * XXX: BMISS interrupts may occur after association. - * I found this on 5210 code but it needs testing. If this is - * true we should disable them before assoc and re-enable them - * after a successful assoc + some jiffies. - interrupt_mask &= ~AR5K_INT_BMISS; - */ - - data = isr; } else { - u32 pisr = 0; - u32 pisr_clear = 0; - u32 sisr0 = 0; - u32 sisr1 = 0; - u32 sisr2 = 0; - u32 sisr3 = 0; - u32 sisr4 = 0; - - /* Read PISR and SISRs... */ - pisr = ath5k_hw_reg_read(ah, AR5K_PISR); - if (unlikely(pisr == AR5K_INT_NOCARD)) { - *interrupt_mask = pisr; - return -ENODEV; - } - - sisr0 = ath5k_hw_reg_read(ah, AR5K_SISR0); - sisr1 = ath5k_hw_reg_read(ah, AR5K_SISR1); - sisr2 = ath5k_hw_reg_read(ah, AR5K_SISR2); - sisr3 = ath5k_hw_reg_read(ah, AR5K_SISR3); - sisr4 = ath5k_hw_reg_read(ah, AR5K_SISR4); - /* - * PISR holds the logical OR of interrupt bits - * from SISR registers: - * - * TXOK and TXDESC -> Logical OR of TXOK and TXDESC - * per-queue bits on SISR0 - * - * TXERR and TXEOL -> Logical OR of TXERR and TXEOL - * per-queue bits on SISR1 - * - * TXURN -> Logical OR of TXURN per-queue bits on SISR2 + * Read interrupt status from Interrupt + * Status Register shadow copy (Read And Clear) * - * HIUERR -> Logical OR of MCABT, SSERR and DPER bits on SISR2 - * - * BCNMISC -> Logical OR of TIM, CAB_END, DTIM_SYNC - * BCN_TIMEOUT, CAB_TIMEOUT and DTIM - * (and TSFOOR ?) bits on SISR2 - * - * QCBRORN and QCBRURN -> Logical OR of QCBRORN and - * QCBRURN per-queue bits on SISR3 - * QTRIG -> Logical OR of QTRIG per-queue bits on SISR4 - * - * If we clean these bits on PISR we 'll also clear all - * related bits from SISRs, e.g. if we write the TXOK bit on - * PISR we 'll clean all TXOK bits from SISR0 so if a new TXOK - * interrupt got fired for another queue while we were reading - * the interrupt registers and we write back the TXOK bit on - * PISR we 'll lose it. So make sure that we don't write back - * on PISR any bits that come from SISRs. Clearing them from - * SISRs will also clear PISR so no need to worry here. - */ - - pisr_clear = pisr & ~AR5K_ISR_BITS_FROM_SISRS; - - /* - * Write to clear them... - * Note: This means that each bit we write back - * to the registers will get cleared, leaving the - * rest unaffected. So this won't affect new interrupts - * we didn't catch while reading/processing, we 'll get - * them next time get_isr gets called. - */ - ath5k_hw_reg_write(ah, sisr0, AR5K_SISR0); - ath5k_hw_reg_write(ah, sisr1, AR5K_SISR1); - ath5k_hw_reg_write(ah, sisr2, AR5K_SISR2); - ath5k_hw_reg_write(ah, sisr3, AR5K_SISR3); - ath5k_hw_reg_write(ah, sisr4, AR5K_SISR4); - ath5k_hw_reg_write(ah, pisr_clear, AR5K_PISR); - /* Flush previous write */ - ath5k_hw_reg_read(ah, AR5K_PISR); - - /* - * Filter out the non-common bits from the interrupt - * status. + * Note: PISR/SISR Not available on 5210 */ - *interrupt_mask = (pisr & AR5K_INT_COMMON) & ah->ah_imr; - - - /* We treat TXOK,TXDESC, TXERR and TXEOL - * the same way (schedule the tx tasklet) - * so we track them all together per queue */ - if (pisr & AR5K_ISR_TXOK) - ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr0, - AR5K_SISR0_QCU_TXOK); + data = ath5k_hw_reg_read(ah, AR5K_RAC_PISR); + if (unlikely(data == AR5K_INT_NOCARD)) { + *interrupt_mask = data; + return -ENODEV; + } + } - if (pisr & AR5K_ISR_TXDESC) - ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr0, - AR5K_SISR0_QCU_TXDESC); + /* + * Get abstract interrupt mask (driver-compatible) + */ + *interrupt_mask = (data & AR5K_INT_COMMON) & ah->ah_imr; - if (pisr & AR5K_ISR_TXERR) - ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1, - AR5K_SISR1_QCU_TXERR); + if (ah->ah_version != AR5K_AR5210) { + u32 sisr2 = ath5k_hw_reg_read(ah, AR5K_RAC_SISR2); - if (pisr & AR5K_ISR_TXEOL) - ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1, - AR5K_SISR1_QCU_TXEOL); + /*HIU = Host Interface Unit (PCI etc)*/ + if (unlikely(data & (AR5K_ISR_HIUERR))) + *interrupt_mask |= AR5K_INT_FATAL; - /* Currently this is not much usefull since we treat - * all queues the same way if we get a TXURN (update - * tx trigger level) but we might need it later on*/ - if (pisr & AR5K_ISR_TXURN) - ah->ah_txq_isr_txurn |= AR5K_REG_MS(sisr2, - AR5K_SISR2_QCU_TXURN); + /*Beacon Not Ready*/ + if (unlikely(data & (AR5K_ISR_BNR))) + *interrupt_mask |= AR5K_INT_BNR; - /* Misc Beacon related interrupts */ + if (unlikely(sisr2 & (AR5K_SISR2_SSERR | + AR5K_SISR2_DPERR | + AR5K_SISR2_MCABT))) + *interrupt_mask |= AR5K_INT_FATAL; - /* For AR5211 */ - if (pisr & AR5K_ISR_TIM) + if (data & AR5K_ISR_TIM) *interrupt_mask |= AR5K_INT_TIM; - /* For AR5212+ */ - if (pisr & AR5K_ISR_BCNMISC) { + if (data & AR5K_ISR_BCNMISC) { if (sisr2 & AR5K_SISR2_TIM) *interrupt_mask |= AR5K_INT_TIM; if (sisr2 & AR5K_SISR2_DTIM) @@ -686,39 +591,63 @@ ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) *interrupt_mask |= AR5K_INT_CAB_TIMEOUT; } - /* Below interrupts are unlikely to happen */ - - /* HIU = Host Interface Unit (PCI etc) - * Can be one of MCABT, SSERR, DPERR from SISR2 */ - if (unlikely(pisr & (AR5K_ISR_HIUERR))) - *interrupt_mask |= AR5K_INT_FATAL; - - /*Beacon Not Ready*/ - if (unlikely(pisr & (AR5K_ISR_BNR))) - *interrupt_mask |= AR5K_INT_BNR; - - /* A queue got CBR overrun */ - if (unlikely(pisr & (AR5K_ISR_QCBRORN))) { + if (data & AR5K_ISR_RXDOPPLER) + *interrupt_mask |= AR5K_INT_RX_DOPPLER; + if (data & AR5K_ISR_QCBRORN) { *interrupt_mask |= AR5K_INT_QCBRORN; - ah->ah_txq_isr_qcborn |= AR5K_REG_MS(sisr3, - AR5K_SISR3_QCBRORN); + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR3), + AR5K_SISR3_QCBRORN); } - - /* A queue got CBR underrun */ - if (unlikely(pisr & (AR5K_ISR_QCBRURN))) { + if (data & AR5K_ISR_QCBRURN) { *interrupt_mask |= AR5K_INT_QCBRURN; - ah->ah_txq_isr_qcburn |= AR5K_REG_MS(sisr3, - AR5K_SISR3_QCBRURN); + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR3), + AR5K_SISR3_QCBRURN); } - - /* A queue got triggered */ - if (unlikely(pisr & (AR5K_ISR_QTRIG))) { + if (data & AR5K_ISR_QTRIG) { *interrupt_mask |= AR5K_INT_QTRIG; - ah->ah_txq_isr_qtrig |= AR5K_REG_MS(sisr4, - AR5K_SISR4_QTRIG); + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR4), + AR5K_SISR4_QTRIG); } - data = pisr; + if (data & AR5K_ISR_TXOK) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR0), + AR5K_SISR0_QCU_TXOK); + + if (data & AR5K_ISR_TXDESC) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR0), + AR5K_SISR0_QCU_TXDESC); + + if (data & AR5K_ISR_TXERR) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR1), + AR5K_SISR1_QCU_TXERR); + + if (data & AR5K_ISR_TXEOL) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR1), + AR5K_SISR1_QCU_TXEOL); + + if (data & AR5K_ISR_TXURN) + ah->ah_txq_isr |= AR5K_REG_MS( + ath5k_hw_reg_read(ah, AR5K_RAC_SISR2), + AR5K_SISR2_QCU_TXURN); + } else { + if (unlikely(data & (AR5K_ISR_SSERR | AR5K_ISR_MCABT + | AR5K_ISR_HIUERR | AR5K_ISR_DPERR))) + *interrupt_mask |= AR5K_INT_FATAL; + + /* + * XXX: BMISS interrupts may occur after association. + * I found this on 5210 code but it needs testing. If this is + * true we should disable them before assoc and re-enable them + * after a successful assoc + some jiffies. + interrupt_mask &= ~AR5K_INT_BMISS; + */ } /* @@ -732,7 +661,8 @@ ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) } /** - * ath5k_hw_set_imr() - Set interrupt mask + * ath5k_hw_set_imr - Set interrupt mask + * * @ah: The &struct ath5k_hw * @new_mask: The new interrupt mask to be set * @@ -740,8 +670,7 @@ ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) * ath5k_int bits to hw-specific bits to remove abstraction and writing * Interrupt Mask Register. */ -enum ath5k_int -ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) +enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) { enum ath5k_int old_mask, int_mask; @@ -768,14 +697,16 @@ ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) u32 simr2 = ath5k_hw_reg_read(ah, AR5K_SIMR2) & AR5K_SIMR2_QCU_TXURN; - /* Fatal interrupt abstraction for 5211+ */ if (new_mask & AR5K_INT_FATAL) { int_mask |= AR5K_IMR_HIUERR; simr2 |= (AR5K_SIMR2_MCABT | AR5K_SIMR2_SSERR | AR5K_SIMR2_DPERR); } - /* Misc beacon related interrupts */ + /*Beacon Not Ready*/ + if (new_mask & AR5K_INT_BNR) + int_mask |= AR5K_INT_BNR; + if (new_mask & AR5K_INT_TIM) int_mask |= AR5K_IMR_TIM; @@ -790,9 +721,8 @@ ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) if (new_mask & AR5K_INT_CAB_TIMEOUT) simr2 |= AR5K_SISR2_CAB_TIMEOUT; - /*Beacon Not Ready*/ - if (new_mask & AR5K_INT_BNR) - int_mask |= AR5K_INT_BNR; + if (new_mask & AR5K_INT_RX_DOPPLER) + int_mask |= AR5K_IMR_RXDOPPLER; /* Note: Per queue interrupt masks * are set via ath5k_hw_reset_tx_queue() (qcu.c) */ @@ -800,12 +730,10 @@ ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2); } else { - /* Fatal interrupt abstraction for 5210 */ if (new_mask & AR5K_INT_FATAL) int_mask |= (AR5K_IMR_SSERR | AR5K_IMR_MCABT | AR5K_IMR_HIUERR | AR5K_IMR_DPERR); - /* Only common interrupts left for 5210 (no SIMRs) */ ath5k_hw_reg_write(ah, int_mask, AR5K_IMR); } @@ -832,7 +760,8 @@ ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) \********************/ /** - * ath5k_hw_dma_init() - Initialize DMA unit + * ath5k_hw_dma_init - Initialize DMA unit + * * @ah: The &struct ath5k_hw * * Set DMA size and pre-enable interrupts @@ -841,8 +770,7 @@ ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) * * XXX: Save/restore RXDP/TXDP registers ? */ -void -ath5k_hw_dma_init(struct ath5k_hw *ah) +void ath5k_hw_dma_init(struct ath5k_hw *ah) { /* * Set Rx/Tx DMA Configuration @@ -871,7 +799,8 @@ ath5k_hw_dma_init(struct ath5k_hw *ah) } /** - * ath5k_hw_dma_stop() - stop DMA unit + * ath5k_hw_dma_stop - stop DMA unit + * * @ah: The &struct ath5k_hw * * Stop tx/rx DMA and interrupts. Returns @@ -881,8 +810,7 @@ ath5k_hw_dma_init(struct ath5k_hw *ah) * stuck frames on tx queues, only a reset * can fix that. */ -int -ath5k_hw_dma_stop(struct ath5k_hw *ah) +int ath5k_hw_dma_stop(struct ath5k_hw *ah) { int i, qmax, err; err = 0; diff --git a/drivers/net/wireless/ath/ath5k/gpio.c b/drivers/net/wireless/ath/ath5k/gpio.c index 73d3dd8a..85929781 100644 --- a/drivers/net/wireless/ath/ath5k/gpio.c +++ b/drivers/net/wireless/ath/ath5k/gpio.c @@ -24,33 +24,10 @@ #include "reg.h" #include "debug.h" - -/** - * DOC: GPIO/LED functions - * - * Here we control the 6 bidirectional GPIO pins provided by the hw. - * We can set a GPIO pin to be an input or an output pin on GPIO control - * register and then read or set its status from GPIO data input/output - * registers. - * - * We also control the two LED pins provided by the hw, LED_0 is our - * "power" LED and LED_1 is our "network activity" LED but many scenarios - * are available from hw. Vendors might also provide LEDs connected to the - * GPIO pins, we handle them through the LED subsystem on led.c - */ - - -/** - * ath5k_hw_set_ledstate() - Set led state - * @ah: The &struct ath5k_hw - * @state: One of AR5K_LED_* - * - * Used to set the LED blinking state. This only - * works for the LED connected to the LED_0, LED_1 pins, - * not the GPIO based. +/* + * Set led state */ -void -ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state) +void ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state) { u32 led; /*5210 has different led mode handling*/ @@ -97,13 +74,10 @@ ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state) AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led_5210); } -/** - * ath5k_hw_set_gpio_input() - Set GPIO inputs - * @ah: The &struct ath5k_hw - * @gpio: GPIO pin to set as input +/* + * Set GPIO inputs */ -int -ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio) +int ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio) { if (gpio >= AR5K_NUM_GPIO) return -EINVAL; @@ -115,13 +89,10 @@ ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio) return 0; } -/** - * ath5k_hw_set_gpio_output() - Set GPIO outputs - * @ah: The &struct ath5k_hw - * @gpio: The GPIO pin to set as output +/* + * Set GPIO outputs */ -int -ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio) +int ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio) { if (gpio >= AR5K_NUM_GPIO) return -EINVAL; @@ -133,13 +104,10 @@ ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio) return 0; } -/** - * ath5k_hw_get_gpio() - Get GPIO state - * @ah: The &struct ath5k_hw - * @gpio: The GPIO pin to read +/* + * Get GPIO state */ -u32 -ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio) +u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio) { if (gpio >= AR5K_NUM_GPIO) return 0xffffffff; @@ -149,14 +117,10 @@ ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio) 0x1; } -/** - * ath5k_hw_set_gpio() - Set GPIO state - * @ah: The &struct ath5k_hw - * @gpio: The GPIO pin to set - * @val: Value to set (boolean) +/* + * Set GPIO state */ -int -ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val) +int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val) { u32 data; @@ -174,19 +138,10 @@ ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val) return 0; } -/** - * ath5k_hw_set_gpio_intr() - Initialize the GPIO interrupt (RFKill switch) - * @ah: The &struct ath5k_hw - * @gpio: The GPIO pin to use - * @interrupt_level: True to generate interrupt on active pin (high) - * - * This function is used to set up the GPIO interrupt for the hw RFKill switch. - * That switch is connected to a GPIO pin and it's number is stored on EEPROM. - * It can either open or close the circuit to indicate that we should disable - * RF/Wireless to save power (we also get that from EEPROM). +/* + * Initialize the GPIO interrupt (RFKill switch) */ -void -ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio, +void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio, u32 interrupt_level) { u32 data; diff --git a/drivers/net/wireless/ath/ath5k/initvals.c b/drivers/net/wireless/ath/ath5k/initvals.c index a1ea78e0..1ffecc0f 100644 --- a/drivers/net/wireless/ath/ath5k/initvals.c +++ b/drivers/net/wireless/ath/ath5k/initvals.c @@ -23,27 +23,24 @@ #include "reg.h" #include "debug.h" -/** - * struct ath5k_ini - Mode-independent initial register writes - * @ini_register: Register address - * @ini_value: Default value - * @ini_mode: 0 to write 1 to read (and clear) +/* + * Mode-independent initial register writes */ + struct ath5k_ini { u16 ini_register; u32 ini_value; enum { AR5K_INI_WRITE = 0, /* Default */ - AR5K_INI_READ = 1, + AR5K_INI_READ = 1, /* Cleared on read */ } ini_mode; }; -/** - * struct ath5k_ini_mode - Mode specific initial register values - * @mode_register: Register address - * @mode_value: Set of values for each enum ath5k_driver_mode +/* + * Mode specific initial register values */ + struct ath5k_ini_mode { u16 mode_register; u32 mode_value[3]; @@ -389,10 +386,11 @@ static const struct ath5k_ini ar5211_ini[] = { /* Initial mode-specific settings for AR5211 * 5211 supports OFDM-only g (draft g) but we - * need to test it ! */ + * need to test it ! + */ static const struct ath5k_ini_mode ar5211_ini_mode[] = { { AR5K_TXCFG, - /* A B G */ + /* A/XR B G */ { 0x00000015, 0x0000001d, 0x00000015 } }, { AR5K_QUEUE_DFS_LOCAL_IFS(0), { 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } }, @@ -462,7 +460,7 @@ static const struct ath5k_ini_mode ar5211_ini_mode[] = { { 0x00000010, 0x00000010, 0x00000010 } }, }; -/* Initial register settings for AR5212 and newer chips */ +/* Initial register settings for AR5212 */ static const struct ath5k_ini ar5212_ini_common_start[] = { { AR5K_RXDP, 0x00000000 }, { AR5K_RXCFG, 0x00000005 }, @@ -726,8 +724,7 @@ static const struct ath5k_ini_mode ar5212_ini_mode_start[] = { { 0x00000000, 0x00000000, 0x00000108 } }, }; -/* Initial mode-specific settings for AR5212 + RF5111 - * (Written after ar5212_ini) */ +/* Initial mode-specific settings for AR5212 + RF5111 (Written after ar5212_ini) */ static const struct ath5k_ini_mode rf5111_ini_mode_end[] = { { AR5K_TXCFG, /* A/XR B G */ @@ -760,7 +757,6 @@ static const struct ath5k_ini_mode rf5111_ini_mode_end[] = { { 0x1883800a, 0x1873800a, 0x1883800a } }, }; -/* Common for all modes */ static const struct ath5k_ini rf5111_ini_common_end[] = { { AR5K_DCU_FP, 0x00000000 }, { AR5K_PHY_AGC, 0x00000000 }, @@ -778,9 +774,7 @@ static const struct ath5k_ini rf5111_ini_common_end[] = { { 0xa23c, 0x13c889af }, }; - -/* Initial mode-specific settings for AR5212 + RF5112 - * (Written after ar5212_ini) */ +/* Initial mode-specific settings for AR5212 + RF5112 (Written after ar5212_ini) */ static const struct ath5k_ini_mode rf5112_ini_mode_end[] = { { AR5K_TXCFG, /* A/XR B G */ @@ -831,9 +825,7 @@ static const struct ath5k_ini rf5112_ini_common_end[] = { { 0xa23c, 0x13c889af }, }; - -/* Initial mode-specific settings for RF5413/5414 - * (Written after ar5212_ini) */ +/* Initial mode-specific settings for RF5413/5414 (Written after ar5212_ini) */ static const struct ath5k_ini_mode rf5413_ini_mode_end[] = { { AR5K_TXCFG, /* A/XR B G */ @@ -971,8 +963,7 @@ static const struct ath5k_ini rf5413_ini_common_end[] = { { 0xa384, 0xf3307ff0 }, }; -/* Initial mode-specific settings for RF2413/2414 - * (Written after ar5212_ini) */ +/* Initial mode-specific settings for RF2413/2414 (Written after ar5212_ini) */ /* XXX: a mode ? */ static const struct ath5k_ini_mode rf2413_ini_mode_end[] = { { AR5K_TXCFG, @@ -1094,8 +1085,7 @@ static const struct ath5k_ini rf2413_ini_common_end[] = { { 0xa384, 0xf3307ff0 }, }; -/* Initial mode-specific settings for RF2425 - * (Written after ar5212_ini) */ +/* Initial mode-specific settings for RF2425 (Written after ar5212_ini) */ /* XXX: a mode ? */ static const struct ath5k_ini_mode rf2425_ini_mode_end[] = { { AR5K_TXCFG, @@ -1367,15 +1357,10 @@ static const struct ath5k_ini rf5112_ini_bbgain[] = { }; -/** - * ath5k_hw_ini_registers() - Write initial register dump common for all modes - * @ah: The &struct ath5k_hw - * @size: Dump size - * @ini_regs: The array of &struct ath5k_ini - * @skip_pcu: Skip PCU registers +/* + * Write initial register dump */ -static void -ath5k_hw_ini_registers(struct ath5k_hw *ah, unsigned int size, +static void ath5k_hw_ini_registers(struct ath5k_hw *ah, unsigned int size, const struct ath5k_ini *ini_regs, bool skip_pcu) { unsigned int i; @@ -1403,15 +1388,7 @@ ath5k_hw_ini_registers(struct ath5k_hw *ah, unsigned int size, } } -/** - * ath5k_hw_ini_mode_registers() - Write initial mode-specific register dump - * @ah: The &struct ath5k_hw - * @size: Dump size - * @ini_mode: The array of &struct ath5k_ini_mode - * @mode: One of enum ath5k_driver_mode - */ -static void -ath5k_hw_ini_mode_registers(struct ath5k_hw *ah, +static void ath5k_hw_ini_mode_registers(struct ath5k_hw *ah, unsigned int size, const struct ath5k_ini_mode *ini_mode, u8 mode) { @@ -1425,17 +1402,7 @@ ath5k_hw_ini_mode_registers(struct ath5k_hw *ah, } -/** - * ath5k_hw_write_initvals() - Write initial chip-specific register dump - * @ah: The &struct ath5k_hw - * @mode: One of enum ath5k_driver_mode - * @skip_pcu: Skip PCU registers - * - * Write initial chip-specific register dump, to get the chipset on a - * clean and ready-to-work state after warm reset. - */ -int -ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool skip_pcu) +int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool skip_pcu) { /* * Write initial register settings diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c old mode 100644 new mode 100755 index 5d1be4da..82a4065a --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -134,8 +134,6 @@ ath5k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) ah->num_ap_vifs++; else if (avf->opmode == NL80211_IFTYPE_ADHOC) ah->num_adhoc_vifs++; - else if (avf->opmode == NL80211_IFTYPE_MESH_POINT) - ah->num_mesh_vifs++; } /* Any MAC address is fine, all others are included through the @@ -177,8 +175,6 @@ ath5k_remove_interface(struct ieee80211_hw *hw, ah->num_ap_vifs--; else if (avf->opmode == NL80211_IFTYPE_ADHOC) ah->num_adhoc_vifs--; - else if (avf->opmode == NL80211_IFTYPE_MESH_POINT) - ah->num_mesh_vifs--; ath5k_update_bssid_mask_and_opmode(ah, NULL); mutex_unlock(&ah->lock); @@ -316,20 +312,42 @@ ath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr_list *mc_list) +#else + int mc_count, struct dev_addr_list *ha) +#endif { u32 mfilt[2], val; u8 pos; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; +#else + int i; +#endif mfilt[0] = 0; mfilt[1] = 1; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) netdev_hw_addr_list_for_each(ha, mc_list) { +#else + for (i = 0; i < mc_count; i++) { + if (!ha) + break; +#endif /* calculate XOR of eight 6-bit values */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) val = get_unaligned_le32(ha->addr + 0); +#else + val = get_unaligned_le32(ha->dmi_addr + 0); +#endif pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) val = get_unaligned_le32(ha->addr + 3); +#else + val = get_unaligned_le32(ha->dmi_addr + 3); +#endif pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; pos &= 0x3f; mfilt[pos / 32] |= (1 << (pos % 32)); @@ -338,6 +356,9 @@ ath5k_prepare_multicast(struct ieee80211_hw *hw, * need to inform below not to reset the mcast */ /* ath5k_hw_set_mcast_filterindex(ah, * ha->addr[5]); */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) + ha = ha->next; +#endif } return ((u64)(mfilt[1]) << 32) | mfilt[0]; @@ -487,14 +508,6 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (ath5k_modparam_nohwcrypt) return -EOPNOTSUPP; - if (vif->type == NL80211_IFTYPE_ADHOC && - (key->cipher == WLAN_CIPHER_SUITE_TKIP || - key->cipher == WLAN_CIPHER_SUITE_CCMP) && - !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { - /* don't program group keys when using IBSS_RSN */ - return -EOPNOTSUPP; - } - switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: diff --git a/drivers/net/wireless/ath/ath5k/pci.c b/drivers/net/wireless/ath/ath5k/pci.c index 849fa060..f90980f0 100644 --- a/drivers/net/wireless/ath/ath5k/pci.c +++ b/drivers/net/wireless/ath/ath5k/pci.c @@ -98,7 +98,7 @@ ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data) 0xffff); return true; } - usleep_range(15, 20); + udelay(15); } return false; @@ -323,6 +323,9 @@ static int ath5k_pci_resume(struct device *dev) return 0; } +compat_pci_suspend(ath5k_pci_suspend) +compat_pci_resume(ath5k_pci_resume) + static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume); #define ATH5K_PM_OPS (&ath5k_pm_ops) #else @@ -334,7 +337,12 @@ static struct pci_driver ath5k_pci_driver = { .id_table = ath5k_pci_id_table, .probe = ath5k_pci_probe, .remove = __devexit_p(ath5k_pci_remove), +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = ATH5K_PM_OPS, +#elif defined(CONFIG_PM_SLEEP) + .suspend = ath5k_pci_suspend_compat, + .resume = ath5k_pci_resume_compat, +#endif }; /* diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index cebfd6fd..a7eafa3e 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -30,47 +30,11 @@ #include "reg.h" #include "debug.h" -/** - * DOC: Protocol Control Unit (PCU) functions - * - * Protocol control unit is responsible to maintain various protocol - * properties before a frame is send and after a frame is received to/from - * baseband. To be more specific, PCU handles: - * - * - Buffering of RX and TX frames (after QCU/DCUs) - * - * - Encrypting and decrypting (using the built-in engine) - * - * - Generating ACKs, RTS/CTS frames - * - * - Maintaining TSF - * - * - FCS - * - * - Updating beacon data (with TSF etc) - * - * - Generating virtual CCA - * - * - RX/Multicast filtering - * - * - BSSID filtering - * - * - Various statistics - * - * -Different operating modes: AP, STA, IBSS - * - * Note: Most of these functions can be tweaked/bypassed so you can do - * them on sw above for debugging or research. For more infos check out PCU - * registers on reg.h. - */ - -/** - * DOC: ACK rates - * +/* * AR5212+ can use higher rates for ack transmission * based on current tx rate instead of the base rate. * It does this to better utilize channel usage. - * There is a mapping between G rates (that cover both + * This is a mapping between G rates (that cover both * CCK and OFDM) and ack rates that we use when setting * rate -> duration table. This mapping is hw-based so * don't change anything. @@ -99,18 +63,17 @@ static const unsigned int ack_rates_high[] = \*******************/ /** - * ath5k_hw_get_frame_duration() - Get tx time of a frame + * ath5k_hw_get_frame_duration - Get tx time of a frame + * * @ah: The &struct ath5k_hw * @len: Frame's length in bytes * @rate: The @struct ieee80211_rate - * @shortpre: Indicate short preample * * Calculate tx duration of a frame given it's rate and length * It extends ieee80211_generic_frame_duration for non standard * bwmodes. */ -int -ath5k_hw_get_frame_duration(struct ath5k_hw *ah, +int ath5k_hw_get_frame_duration(struct ath5k_hw *ah, int len, struct ieee80211_rate *rate, bool shortpre) { int sifs, preamble, plcp_bits, sym_time; @@ -166,11 +129,11 @@ ath5k_hw_get_frame_duration(struct ath5k_hw *ah, } /** - * ath5k_hw_get_default_slottime() - Get the default slot time for current mode + * ath5k_hw_get_default_slottime - Get the default slot time for current mode + * * @ah: The &struct ath5k_hw */ -unsigned int -ath5k_hw_get_default_slottime(struct ath5k_hw *ah) +unsigned int ath5k_hw_get_default_slottime(struct ath5k_hw *ah) { struct ieee80211_channel *channel = ah->ah_current_channel; unsigned int slot_time; @@ -197,11 +160,11 @@ ath5k_hw_get_default_slottime(struct ath5k_hw *ah) } /** - * ath5k_hw_get_default_sifs() - Get the default SIFS for current mode + * ath5k_hw_get_default_sifs - Get the default SIFS for current mode + * * @ah: The &struct ath5k_hw */ -unsigned int -ath5k_hw_get_default_sifs(struct ath5k_hw *ah) +unsigned int ath5k_hw_get_default_sifs(struct ath5k_hw *ah) { struct ieee80211_channel *channel = ah->ah_current_channel; unsigned int sifs; @@ -228,17 +191,17 @@ ath5k_hw_get_default_sifs(struct ath5k_hw *ah) } /** - * ath5k_hw_update_mib_counters() - Update MIB counters (mac layer statistics) + * ath5k_hw_update_mib_counters - Update MIB counters (mac layer statistics) + * * @ah: The &struct ath5k_hw * * Reads MIB counters from PCU and updates sw statistics. Is called after a * MIB interrupt, because one of these counters might have reached their maximum * and triggered the MIB interrupt, to let us read and clear the counter. * - * NOTE: Is called in interrupt context! + * Is called in interrupt context! */ -void -ath5k_hw_update_mib_counters(struct ath5k_hw *ah) +void ath5k_hw_update_mib_counters(struct ath5k_hw *ah) { struct ath5k_statistics *stats = &ah->stats; @@ -256,8 +219,10 @@ ath5k_hw_update_mib_counters(struct ath5k_hw *ah) \******************/ /** - * ath5k_hw_write_rate_duration() - Fill rate code to duration table - * @ah: The &struct ath5k_hw + * ath5k_hw_write_rate_duration - fill rate code to duration table + * + * @ah: the &struct ath5k_hw + * @mode: one of enum ath5k_driver_mode * * Write the rate code to duration table upon hw reset. This is a helper for * ath5k_hw_pcu_init(). It seems all this is doing is setting an ACK timeout on @@ -271,8 +236,7 @@ ath5k_hw_update_mib_counters(struct ath5k_hw *ah) * that include all OFDM and CCK rates. * */ -static inline void -ath5k_hw_write_rate_duration(struct ath5k_hw *ah) +static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah) { struct ieee80211_rate *rate; unsigned int i; @@ -316,12 +280,12 @@ ath5k_hw_write_rate_duration(struct ath5k_hw *ah) } /** - * ath5k_hw_set_ack_timeout() - Set ACK timeout on PCU + * ath5k_hw_set_ack_timeout - Set ACK timeout on PCU + * * @ah: The &struct ath5k_hw * @timeout: Timeout in usec */ -static int -ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout) +static int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout) { if (ath5k_hw_clocktoh(ah, AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK)) <= timeout) @@ -334,12 +298,12 @@ ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout) } /** - * ath5k_hw_set_cts_timeout() - Set CTS timeout on PCU + * ath5k_hw_set_cts_timeout - Set CTS timeout on PCU + * * @ah: The &struct ath5k_hw * @timeout: Timeout in usec */ -static int -ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout) +static int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout) { if (ath5k_hw_clocktoh(ah, AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS)) <= timeout) @@ -357,14 +321,14 @@ ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout) \*******************/ /** - * ath5k_hw_set_lladdr() - Set station id + * ath5k_hw_set_lladdr - Set station id + * * @ah: The &struct ath5k_hw - * @mac: The card's mac address (array of octets) + * @mac: The card's mac address * * Set station id on hw using the provided mac address */ -int -ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) +int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) { struct ath_common *common = ath5k_hw_common(ah); u32 low_id, high_id; @@ -385,14 +349,14 @@ ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) } /** - * ath5k_hw_set_bssid() - Set current BSSID on hw + * ath5k_hw_set_bssid - Set current BSSID on hw + * * @ah: The &struct ath5k_hw * * Sets the current BSSID and BSSID mask we have from the * common struct into the hardware */ -void -ath5k_hw_set_bssid(struct ath5k_hw *ah) +void ath5k_hw_set_bssid(struct ath5k_hw *ah) { struct ath_common *common = ath5k_hw_common(ah); u16 tim_offset = 0; @@ -425,23 +389,7 @@ ath5k_hw_set_bssid(struct ath5k_hw *ah) ath5k_hw_enable_pspoll(ah, NULL, 0); } -/** - * ath5k_hw_set_bssid_mask() - Filter out bssids we listen - * @ah: The &struct ath5k_hw - * @mask: The BSSID mask to set (array of octets) - * - * BSSID masking is a method used by AR5212 and newer hardware to inform PCU - * which bits of the interface's MAC address should be looked at when trying - * to decide which packets to ACK. In station mode and AP mode with a single - * BSS every bit matters since we lock to only one BSS. In AP mode with - * multiple BSSes (virtual interfaces) not every bit matters because hw must - * accept frames for all BSSes and so we tweak some bits of our mac address - * in order to have multiple BSSes. - * - * For more information check out ../hw.c of the common ath module. - */ -void -ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) +void ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) { struct ath_common *common = ath5k_hw_common(ah); @@ -452,21 +400,18 @@ ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) ath_hw_setbssidmask(common); } -/** - * ath5k_hw_set_mcast_filter() - Set multicast filter - * @ah: The &struct ath5k_hw - * @filter0: Lower 32bits of muticast filter - * @filter1: Higher 16bits of multicast filter +/* + * Set multicast filter */ -void -ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1) +void ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1) { ath5k_hw_reg_write(ah, filter0, AR5K_MCAST_FILTER0); ath5k_hw_reg_write(ah, filter1, AR5K_MCAST_FILTER1); } /** - * ath5k_hw_get_rx_filter() - Get current rx filter + * ath5k_hw_get_rx_filter - Get current rx filter + * * @ah: The &struct ath5k_hw * * Returns the RX filter by reading rx filter and @@ -475,8 +420,7 @@ ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1) * and pass to the driver. For a list of frame types * check out reg.h. */ -u32 -ath5k_hw_get_rx_filter(struct ath5k_hw *ah) +u32 ath5k_hw_get_rx_filter(struct ath5k_hw *ah) { u32 data, filter = 0; @@ -496,7 +440,8 @@ ath5k_hw_get_rx_filter(struct ath5k_hw *ah) } /** - * ath5k_hw_set_rx_filter() - Set rx filter + * ath5k_hw_set_rx_filter - Set rx filter + * * @ah: The &struct ath5k_hw * @filter: RX filter mask (see reg.h) * @@ -504,8 +449,7 @@ ath5k_hw_get_rx_filter(struct ath5k_hw *ah) * register on 5212 and newer chips so that we have proper PHY * error reporting. */ -void -ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter) +void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter) { u32 data = 0; @@ -549,13 +493,13 @@ ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter) #define ATH5K_MAX_TSF_READ 10 /** - * ath5k_hw_get_tsf64() - Get the full 64bit TSF + * ath5k_hw_get_tsf64 - Get the full 64bit TSF + * * @ah: The &struct ath5k_hw * * Returns the current TSF */ -u64 -ath5k_hw_get_tsf64(struct ath5k_hw *ah) +u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah) { u32 tsf_lower, tsf_upper1, tsf_upper2; int i; @@ -592,30 +536,28 @@ ath5k_hw_get_tsf64(struct ath5k_hw *ah) return ((u64)tsf_upper1 << 32) | tsf_lower; } -#undef ATH5K_MAX_TSF_READ - /** - * ath5k_hw_set_tsf64() - Set a new 64bit TSF + * ath5k_hw_set_tsf64 - Set a new 64bit TSF + * * @ah: The &struct ath5k_hw * @tsf64: The new 64bit TSF * * Sets the new TSF */ -void -ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64) +void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64) { ath5k_hw_reg_write(ah, tsf64 & 0xffffffff, AR5K_TSF_L32); ath5k_hw_reg_write(ah, (tsf64 >> 32) & 0xffffffff, AR5K_TSF_U32); } /** - * ath5k_hw_reset_tsf() - Force a TSF reset + * ath5k_hw_reset_tsf - Force a TSF reset + * * @ah: The &struct ath5k_hw * * Forces a TSF reset on PCU */ -void -ath5k_hw_reset_tsf(struct ath5k_hw *ah) +void ath5k_hw_reset_tsf(struct ath5k_hw *ah) { u32 val; @@ -631,17 +573,10 @@ ath5k_hw_reset_tsf(struct ath5k_hw *ah) ath5k_hw_reg_write(ah, val, AR5K_BEACON); } -/** - * ath5k_hw_init_beacon_timers() - Initialize beacon timers - * @ah: The &struct ath5k_hw - * @next_beacon: Next TBTT - * @interval: Current beacon interval - * - * This function is used to initialize beacon timers based on current - * operation mode and settings. +/* + * Initialize beacon timers */ -void -ath5k_hw_init_beacon_timers(struct ath5k_hw *ah, u32 next_beacon, u32 interval) +void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) { u32 timer1, timer2, timer3; @@ -720,7 +655,8 @@ ath5k_hw_init_beacon_timers(struct ath5k_hw *ah, u32 next_beacon, u32 interval) } /** - * ath5k_check_timer_win() - Check if timer B is timer A + window + * ath5k_check_timer_win - Check if timer B is timer A + window + * * @a: timer a (before b) * @b: timer b (after a) * @window: difference between a and b @@ -750,11 +686,12 @@ ath5k_check_timer_win(int a, int b, int window, int intval) } /** - * ath5k_hw_check_beacon_timers() - Check if the beacon timers are correct + * ath5k_hw_check_beacon_timers - Check if the beacon timers are correct + * * @ah: The &struct ath5k_hw * @intval: beacon interval * - * This is a workaround for IBSS mode + * This is a workaround for IBSS mode: * * The need for this function arises from the fact that we have 4 separate * HW timer registers (TIMER0 - TIMER3), which are closely related to the @@ -809,14 +746,14 @@ ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval) } /** - * ath5k_hw_set_coverage_class() - Set IEEE 802.11 coverage class + * ath5k_hw_set_coverage_class - Set IEEE 802.11 coverage class + * * @ah: The &struct ath5k_hw * @coverage_class: IEEE 802.11 coverage class number * * Sets IFS intervals and ACK/CTS timeouts for given coverage class. */ -void -ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class) +void ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class) { /* As defined by IEEE 802.11-2007 17.3.8.6 */ int slot_time = ath5k_hw_get_default_slottime(ah) + 3 * coverage_class; @@ -835,7 +772,8 @@ ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class) \***************************/ /** - * ath5k_hw_start_rx_pcu() - Start RX engine + * ath5k_hw_start_rx_pcu - Start RX engine + * * @ah: The &struct ath5k_hw * * Starts RX engine on PCU so that hw can process RXed frames @@ -843,33 +781,32 @@ ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class) * * NOTE: RX DMA should be already enabled using ath5k_hw_start_rx_dma */ -void -ath5k_hw_start_rx_pcu(struct ath5k_hw *ah) +void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah) { AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX); } /** - * at5k_hw_stop_rx_pcu() - Stop RX engine + * at5k_hw_stop_rx_pcu - Stop RX engine + * * @ah: The &struct ath5k_hw * * Stops RX engine on PCU */ -void -ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah) +void ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah) { AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX); } /** - * ath5k_hw_set_opmode() - Set PCU operating mode + * ath5k_hw_set_opmode - Set PCU operating mode + * * @ah: The &struct ath5k_hw - * @op_mode: One of enum nl80211_iftype + * @op_mode: &enum nl80211_iftype operating mode * * Configure PCU for the various operating modes (AP/STA etc) */ -int -ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype op_mode) +int ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype op_mode) { struct ath_common *common = ath5k_hw_common(ah); u32 pcu_reg, beacon_reg, low_id, high_id; @@ -936,17 +873,8 @@ ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype op_mode) return 0; } -/** - * ath5k_hw_pcu_init() - Initialize PCU - * @ah: The &struct ath5k_hw - * @op_mode: One of enum nl80211_iftype - * @mode: One of enum ath5k_driver_mode - * - * This function is used to initialize PCU by setting current - * operation mode and various other settings. - */ -void -ath5k_hw_pcu_init(struct ath5k_hw *ah, enum nl80211_iftype op_mode) +void ath5k_hw_pcu_init(struct ath5k_hw *ah, enum nl80211_iftype op_mode, + u8 mode) { /* Set bssid and bssid mask */ ath5k_hw_set_bssid(ah); diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index 3a284548..01cb72de 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -1,4 +1,6 @@ /* + * PHY functions + * * Copyright (c) 2004-2007 Reyk Floeter * Copyright (c) 2006-2009 Nick Kossifidis * Copyright (c) 2007-2008 Jiri Slaby @@ -18,10 +20,6 @@ * */ -/***********************\ -* PHY related functions * -\***********************/ - #include #include #include @@ -33,53 +31,14 @@ #include "../regd.h" -/** - * DOC: PHY related functions - * - * Here we handle the low-level functions related to baseband - * and analog frontend (RF) parts. This is by far the most complex - * part of the hw code so make sure you know what you are doing. - * - * Here is a list of what this is all about: - * - * - Channel setting/switching - * - * - Automatic Gain Control (AGC) calibration - * - * - Noise Floor calibration - * - * - I/Q imbalance calibration (QAM correction) - * - * - Calibration due to thermal changes (gain_F) - * - * - Spur noise mitigation - * - * - RF/PHY initialization for the various operating modes and bwmodes - * - * - Antenna control - * - * - TX power control per channel/rate/packet type - * - * Also have in mind we never got documentation for most of these - * functions, what we have comes mostly from Atheros's code, reverse - * engineering and patent docs/presentations etc. - */ - - /******************\ * Helper functions * \******************/ -/** - * ath5k_hw_radio_revision() - Get the PHY Chip revision - * @ah: The &struct ath5k_hw - * @band: One of enum ieee80211_band - * - * Returns the revision number of a 2GHz, 5GHz or single chip - * radio. +/* + * Get the PHY Chip revision */ -u16 -ath5k_hw_radio_revision(struct ath5k_hw *ah, enum ieee80211_band band) +u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, enum ieee80211_band band) { unsigned int i; u32 srev; @@ -99,7 +58,7 @@ ath5k_hw_radio_revision(struct ath5k_hw *ah, enum ieee80211_band band) return 0; } - usleep_range(2000, 2500); + mdelay(2); /* ...wait until PHY is ready and read the selected radio revision */ ath5k_hw_reg_write(ah, 0x00001c16, AR5K_PHY(0x34)); @@ -122,16 +81,10 @@ ath5k_hw_radio_revision(struct ath5k_hw *ah, enum ieee80211_band band) return ret; } -/** - * ath5k_channel_ok() - Check if a channel is supported by the hw - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * - * Note: We don't do any regulatory domain checks here, it's just - * a sanity check. +/* + * Check if a channel is supported */ -bool -ath5k_channel_ok(struct ath5k_hw *ah, struct ieee80211_channel *channel) +bool ath5k_channel_ok(struct ath5k_hw *ah, struct ieee80211_channel *channel) { u16 freq = channel->center_freq; @@ -148,13 +101,7 @@ ath5k_channel_ok(struct ath5k_hw *ah, struct ieee80211_channel *channel) return false; } -/** - * ath5k_hw_chan_has_spur_noise() - Check if channel is sensitive to spur noise - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - */ -bool -ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, +bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, struct ieee80211_channel *channel) { u8 refclk_freq; @@ -175,20 +122,11 @@ ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, return false; } -/** - * ath5k_hw_rfb_op() - Perform an operation on the given RF Buffer - * @ah: The &struct ath5k_hw - * @rf_regs: The struct ath5k_rf_reg - * @val: New value - * @reg_id: RF register ID - * @set: Indicate we need to swap data - * - * This is an internal function used to modify RF Banks before - * writing them to AR5K_RF_BUFFER. Check out rfbuffer.h for more - * infos. +/* + * Used to modify RF Banks before writing them to AR5K_RF_BUFFER */ -static unsigned int -ath5k_hw_rfb_op(struct ath5k_hw *ah, const struct ath5k_rf_reg *rf_regs, +static unsigned int ath5k_hw_rfb_op(struct ath5k_hw *ah, + const struct ath5k_rf_reg *rf_regs, u32 val, u8 reg_id, bool set) { const struct ath5k_rf_reg *rfreg = NULL; @@ -266,7 +204,8 @@ ath5k_hw_rfb_op(struct ath5k_hw *ah, const struct ath5k_rf_reg *rf_regs, } /** - * ath5k_hw_write_ofdm_timings() - set OFDM timings on AR5212 + * ath5k_hw_write_ofdm_timings - set OFDM timings on AR5212 + * * @ah: the &struct ath5k_hw * @channel: the currently set channel upon reset * @@ -277,11 +216,10 @@ ath5k_hw_rfb_op(struct ath5k_hw *ah, const struct ath5k_rf_reg *rf_regs, * mantissa and provide these values on hw. * * For more infos i think this patent is related - * "http://www.freepatentsonline.com/7184495.html" + * http://www.freepatentsonline.com/7184495.html */ -static inline int -ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah, - struct ieee80211_channel *channel) +static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah, + struct ieee80211_channel *channel) { /* Get exponent and mantissa and set it */ u32 coef_scaled, coef_exp, coef_man, @@ -340,10 +278,6 @@ ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah, return 0; } -/** - * ath5k_hw_phy_disable() - Disable PHY - * @ah: The &struct ath5k_hw - */ int ath5k_hw_phy_disable(struct ath5k_hw *ah) { /*Just a try M.F.*/ @@ -352,13 +286,10 @@ int ath5k_hw_phy_disable(struct ath5k_hw *ah) return 0; } -/** - * ath5k_hw_wait_for_synth() - Wait for synth to settle - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel +/* + * Wait for synth to settle */ -static void -ath5k_hw_wait_for_synth(struct ath5k_hw *ah, +static void ath5k_hw_wait_for_synth(struct ath5k_hw *ah, struct ieee80211_channel *channel) { /* @@ -377,9 +308,9 @@ ath5k_hw_wait_for_synth(struct ath5k_hw *ah, delay = delay << 2; /* XXX: /2 on turbo ? Let's be safe * for now */ - usleep_range(100 + delay, 100 + (2 * delay)); + udelay(100 + delay); } else { - usleep_range(1000, 1500); + mdelay(1); } } @@ -388,9 +319,7 @@ ath5k_hw_wait_for_synth(struct ath5k_hw *ah, * RF Gain optimization * \**********************/ -/** - * DOC: RF Gain optimization - * +/* * This code is used to optimize RF gain on different environments * (temperature mostly) based on feedback from a power detector. * @@ -399,22 +328,22 @@ ath5k_hw_wait_for_synth(struct ath5k_hw *ah, * no gain optimization ladder-. * * For more infos check out this patent doc - * "http://www.freepatentsonline.com/7400691.html" + * http://www.freepatentsonline.com/7400691.html * * This paper describes power drops as seen on the receiver due to * probe packets - * "http://www.cnri.dit.ie/publications/ICT08%20-%20Practical%20Issues - * %20of%20Power%20Control.pdf" + * http://www.cnri.dit.ie/publications/ICT08%20-%20Practical%20Issues + * %20of%20Power%20Control.pdf * * And this is the MadWiFi bug entry related to the above - * "http://madwifi-project.org/ticket/1659" + * http://madwifi-project.org/ticket/1659 * with various measurements and diagrams + * + * TODO: Deal with power drops due to probes by setting an appropriate + * tx power on the probe packets ! Make this part of the calibration process. */ -/** - * ath5k_hw_rfgain_opt_init() - Initialize ah_gain during attach - * @ah: The &struct ath5k_hw - */ +/* Initialize ah_gain during attach */ int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah) { /* Initialize the gain optimization values */ @@ -438,21 +367,17 @@ int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah) return 0; } -/** - * ath5k_hw_request_rfgain_probe() - Request a PAPD probe packet - * @ah: The &struct ath5k_hw - * - * Schedules a gain probe check on the next transmitted packet. +/* Schedule a gain probe check on the next transmitted packet. * That means our next packet is going to be sent with lower * tx power and a Peak to Average Power Detector (PAPD) will try * to measure the gain. * - * TODO: Force a tx packet (bypassing PCU arbitrator etc) + * XXX: How about forcing a tx packet (bypassing PCU arbitrator etc) * just after we enable the probe so that we don't mess with - * standard traffic. + * standard traffic ? Maybe it's time to use sw interrupts and + * a probe tasklet !!! */ -static void -ath5k_hw_request_rfgain_probe(struct ath5k_hw *ah) +static void ath5k_hw_request_rfgain_probe(struct ath5k_hw *ah) { /* Skip if gain calibration is inactive or @@ -470,15 +395,9 @@ ath5k_hw_request_rfgain_probe(struct ath5k_hw *ah) } -/** - * ath5k_hw_rf_gainf_corr() - Calculate Gain_F measurement correction - * @ah: The &struct ath5k_hw - * - * Calculate Gain_F measurement correction - * based on the current step for RF5112 rev. 2 - */ -static u32 -ath5k_hw_rf_gainf_corr(struct ath5k_hw *ah) +/* Calculate gain_F measurement correction + * based on the current step for RF5112 rev. 2 */ +static u32 ath5k_hw_rf_gainf_corr(struct ath5k_hw *ah) { u32 mix, step; u32 *rf; @@ -531,19 +450,11 @@ ath5k_hw_rf_gainf_corr(struct ath5k_hw *ah) return ah->ah_gain.g_f_corr; } -/** - * ath5k_hw_rf_check_gainf_readback() - Validate Gain_F feedback from detector - * @ah: The &struct ath5k_hw - * - * Check if current gain_F measurement is in the range of our +/* Check if current gain_F measurement is in the range of our * power detector windows. If we get a measurement outside range * we know it's not accurate (detectors can't measure anything outside - * their detection window) so we must ignore it. - * - * Returns true if readback was O.K. or false on failure - */ -static bool -ath5k_hw_rf_check_gainf_readback(struct ath5k_hw *ah) + * their detection window) so we must ignore it */ +static bool ath5k_hw_rf_check_gainf_readback(struct ath5k_hw *ah) { const struct ath5k_rf_reg *rf_regs; u32 step, mix_ovr, level[4]; @@ -595,15 +506,9 @@ ath5k_hw_rf_check_gainf_readback(struct ath5k_hw *ah) ah->ah_gain.g_current <= level[3]); } -/** - * ath5k_hw_rf_gainf_adjust() - Perform Gain_F adjustment - * @ah: The &struct ath5k_hw - * - * Choose the right target gain based on current gain - * and RF gain optimization ladder - */ -static s8 -ath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah) +/* Perform gain_F adjustment by choosing the right set + * of parameters from RF gain optimization ladder */ +static s8 ath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah) { const struct ath5k_gain_opt *go; const struct ath5k_gain_opt_step *g_step; @@ -667,18 +572,13 @@ ath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah) return ret; } -/** - * ath5k_hw_gainf_calibrate() - Do a gain_F calibration - * @ah: The &struct ath5k_hw - * - * Main callback for thermal RF gain calibration engine +/* Main callback for thermal RF gain calibration engine * Check for a new gain reading and schedule an adjustment * if needed. * - * Returns one of enum ath5k_rfgain codes - */ -enum ath5k_rfgain -ath5k_hw_gainf_calibrate(struct ath5k_hw *ah) + * TODO: Use sw interrupt to schedule reset if gain_F needs + * adjustment */ +enum ath5k_rfgain ath5k_hw_gainf_calibrate(struct ath5k_hw *ah) { u32 data, type; struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; @@ -738,18 +638,10 @@ ath5k_hw_gainf_calibrate(struct ath5k_hw *ah) return ah->ah_gain.g_state; } -/** - * ath5k_hw_rfgain_init() - Write initial RF gain settings to hw - * @ah: The &struct ath5k_hw - * @band: One of enum ieee80211_band - * - * Write initial RF gain table to set the RF sensitivity. - * - * NOTE: This one works on all RF chips and has nothing to do - * with Gain_F calibration - */ -static int -ath5k_hw_rfgain_init(struct ath5k_hw *ah, enum ieee80211_band band) +/* Write initial RF gain table to set the RF sensitivity + * this one works on all RF chips and has nothing to do + * with gain_F calibration */ +static int ath5k_hw_rfgain_init(struct ath5k_hw *ah, enum ieee80211_band band) { const struct ath5k_ini_rfgain *ath5k_rfg; unsigned int i, size, index; @@ -796,23 +688,16 @@ ath5k_hw_rfgain_init(struct ath5k_hw *ah, enum ieee80211_band band) } + /********************\ * RF Registers setup * \********************/ -/** - * ath5k_hw_rfregs_init() - Initialize RF register settings - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * @mode: One of enum ath5k_driver_mode - * - * Setup RF registers by writing RF buffer on hw. For - * more infos on this, check out rfbuffer.h +/* + * Setup RF registers by writing RF buffer on hw */ -static int -ath5k_hw_rfregs_init(struct ath5k_hw *ah, - struct ieee80211_channel *channel, - unsigned int mode) +static int ath5k_hw_rfregs_init(struct ath5k_hw *ah, + struct ieee80211_channel *channel, unsigned int mode) { const struct ath5k_rf_reg *rf_regs; const struct ath5k_ini_rfbuffer *ini_rfb; @@ -1170,18 +1055,19 @@ ath5k_hw_rfregs_init(struct ath5k_hw *ah, PHY/RF channel functions \**************************/ -/** - * ath5k_hw_rf5110_chan2athchan() - Convert channel freq on RF5110 - * @channel: The &struct ieee80211_channel - * - * Map channel frequency to IEEE channel number and convert it - * to an internal channel value used by the RF5110 chipset. +/* + * Conversion needed for RF5110 */ -static u32 -ath5k_hw_rf5110_chan2athchan(struct ieee80211_channel *channel) +static u32 ath5k_hw_rf5110_chan2athchan(struct ieee80211_channel *channel) { u32 athchan; + /* + * Convert IEEE channel/MHz to an internal channel value used + * by the AR5210 chipset. This has not been verified with + * newer chipsets like the AR5212A who have a completely + * different RF/PHY part. + */ athchan = (ath5k_hw_bitswap( (ieee80211_frequency_to_channel( channel->center_freq) - 24) / 2, 5) @@ -1189,13 +1075,10 @@ ath5k_hw_rf5110_chan2athchan(struct ieee80211_channel *channel) return athchan; } -/** - * ath5k_hw_rf5110_channel() - Set channel frequency on RF5110 - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel +/* + * Set channel on RF5110 */ -static int -ath5k_hw_rf5110_channel(struct ath5k_hw *ah, +static int ath5k_hw_rf5110_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) { u32 data; @@ -1206,23 +1089,15 @@ ath5k_hw_rf5110_channel(struct ath5k_hw *ah, data = ath5k_hw_rf5110_chan2athchan(channel); ath5k_hw_reg_write(ah, data, AR5K_RF_BUFFER); ath5k_hw_reg_write(ah, 0, AR5K_RF_BUFFER_CONTROL_0); - usleep_range(1000, 1500); + mdelay(1); return 0; } -/** - * ath5k_hw_rf5111_chan2athchan() - Handle 2GHz channels on RF5111/2111 - * @ieee: IEEE channel number - * @athchan: The &struct ath5k_athchan_2ghz - * - * In order to enable the RF2111 frequency converter on RF5111/2111 setups - * we need to add some offsets and extra flags to the data values we pass - * on to the PHY. So for every 2GHz channel this function gets called - * to do the conversion. +/* + * Conversion needed for 5111 */ -static int -ath5k_hw_rf5111_chan2athchan(unsigned int ieee, +static int ath5k_hw_rf5111_chan2athchan(unsigned int ieee, struct ath5k_athchan_2ghz *athchan) { int channel; @@ -1248,13 +1123,10 @@ ath5k_hw_rf5111_chan2athchan(unsigned int ieee, return 0; } -/** - * ath5k_hw_rf5111_channel() - Set channel frequency on RF5111/2111 - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel +/* + * Set channel on 5111 */ -static int -ath5k_hw_rf5111_channel(struct ath5k_hw *ah, +static int ath5k_hw_rf5111_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) { struct ath5k_athchan_2ghz ath5k_channel_2ghz; @@ -1299,20 +1171,10 @@ ath5k_hw_rf5111_channel(struct ath5k_hw *ah, return 0; } -/** - * ath5k_hw_rf5112_channel() - Set channel frequency on 5112 and newer - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * - * On RF5112/2112 and newer we don't need to do any conversion. - * We pass the frequency value after a few modifications to the - * chip directly. - * - * NOTE: Make sure channel frequency given is within our range or else - * we might damage the chip ! Use ath5k_channel_ok before calling this one. +/* + * Set channel on 5112 and newer */ -static int -ath5k_hw_rf5112_channel(struct ath5k_hw *ah, +static int ath5k_hw_rf5112_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) { u32 data, data0, data1, data2; @@ -1321,37 +1183,17 @@ ath5k_hw_rf5112_channel(struct ath5k_hw *ah, data = data0 = data1 = data2 = 0; c = channel->center_freq; - /* My guess based on code: - * 2GHz RF has 2 synth modes, one with a Local Oscillator - * at 2224Hz and one with a LO at 2192Hz. IF is 1520Hz - * (3040/2). data0 is used to set the PLL divider and data1 - * selects synth mode. */ if (c < 4800) { - /* Channel 14 and all frequencies with 2Hz spacing - * below/above (non-standard channels) */ if (!((c - 2224) % 5)) { - /* Same as (c - 2224) / 5 */ data0 = ((2 * (c - 704)) - 3040) / 10; data1 = 1; - /* Channel 1 and all frequencies with 5Hz spacing - * below/above (standard channels without channel 14) */ } else if (!((c - 2192) % 5)) { - /* Same as (c - 2192) / 5 */ data0 = ((2 * (c - 672)) - 3040) / 10; data1 = 0; } else return -EINVAL; data0 = ath5k_hw_bitswap((data0 << 2) & 0xff, 8); - /* This is more complex, we have a single synthesizer with - * 4 reference clock settings (?) based on frequency spacing - * and set using data2. LO is at 4800Hz and data0 is again used - * to set some divider. - * - * NOTE: There is an old atheros presentation at Stanford - * that mentions a method called dual direct conversion - * with 1GHz sliding IF for RF5110. Maybe that's what we - * have here, or an updated version. */ } else if ((c % 5) != 2 || c > 5435) { if (!(c % 20) && c >= 5120) { data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8); @@ -1377,16 +1219,10 @@ ath5k_hw_rf5112_channel(struct ath5k_hw *ah, return 0; } -/** - * ath5k_hw_rf2425_channel() - Set channel frequency on RF2425 - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * - * AR2425/2417 have a different 2GHz RF so code changes - * a little bit from RF5112. +/* + * Set the channel on the RF2425 */ -static int -ath5k_hw_rf2425_channel(struct ath5k_hw *ah, +static int ath5k_hw_rf2425_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) { u32 data, data0, data2; @@ -1422,16 +1258,10 @@ ath5k_hw_rf2425_channel(struct ath5k_hw *ah, return 0; } -/** - * ath5k_hw_channel() - Set a channel on the radio chip - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * - * This is the main function called to set a channel on the - * radio chip based on the radio chip version. +/* + * Set a channel on the radio chip */ -static int -ath5k_hw_channel(struct ath5k_hw *ah, +static int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) { int ret; @@ -1483,46 +1313,11 @@ ath5k_hw_channel(struct ath5k_hw *ah, return 0; } - /*****************\ PHY calibration \*****************/ -/** - * DOC: PHY Calibration routines - * - * Noise floor calibration: When we tell the hardware to - * perform a noise floor calibration by setting the - * AR5K_PHY_AGCCTL_NF bit on AR5K_PHY_AGCCTL, it will periodically - * sample-and-hold the minimum noise level seen at the antennas. - * This value is then stored in a ring buffer of recently measured - * noise floor values so we have a moving window of the last few - * samples. The median of the values in the history is then loaded - * into the hardware for its own use for RSSI and CCA measurements. - * This type of calibration doesn't interfere with traffic. - * - * AGC calibration: When we tell the hardware to perform - * an AGC (Automatic Gain Control) calibration by setting the - * AR5K_PHY_AGCCTL_CAL, hw disconnects the antennas and does - * a calibration on the DC offsets of ADCs. During this period - * rx/tx gets disabled so we have to deal with it on the driver - * part. - * - * I/Q calibration: When we tell the hardware to perform - * an I/Q calibration, it tries to correct I/Q imbalance and - * fix QAM constellation by sampling data from rxed frames. - * It doesn't interfere with traffic. - * - * For more infos on AGC and I/Q calibration check out patent doc - * #03/094463. - */ - -/** - * ath5k_hw_read_measured_noise_floor() - Read measured NF from hw - * @ah: The &struct ath5k_hw - */ -static s32 -ath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah) +static s32 ath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah) { s32 val; @@ -1530,12 +1325,7 @@ ath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah) return sign_extend32(AR5K_REG_MS(val, AR5K_PHY_NF_MINCCA_PWR), 8); } -/** - * ath5k_hw_init_nfcal_hist() - Initialize NF calibration history buffer - * @ah: The &struct ath5k_hw - */ -void -ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah) +void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah) { int i; @@ -1544,11 +1334,6 @@ ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah) ah->ah_nfcal_hist.nfval[i] = AR5K_TUNE_CCA_MAX_GOOD_VALUE; } -/** - * ath5k_hw_update_nfcal_hist() - Update NF calibration history buffer - * @ah: The &struct ath5k_hw - * @noise_floor: The NF we got from hw - */ static void ath5k_hw_update_nfcal_hist(struct ath5k_hw *ah, s16 noise_floor) { struct ath5k_nfcal_hist *hist = &ah->ah_nfcal_hist; @@ -1556,12 +1341,7 @@ static void ath5k_hw_update_nfcal_hist(struct ath5k_hw *ah, s16 noise_floor) hist->nfval[hist->index] = noise_floor; } -/** - * ath5k_hw_get_median_noise_floor() - Get median NF from history buffer - * @ah: The &struct ath5k_hw - */ -static s16 -ath5k_hw_get_median_noise_floor(struct ath5k_hw *ah) +static s16 ath5k_hw_get_median_noise_floor(struct ath5k_hw *ah) { s16 sort[ATH5K_NF_CAL_HIST_MAX]; s16 tmp; @@ -1584,16 +1364,18 @@ ath5k_hw_get_median_noise_floor(struct ath5k_hw *ah) return sort[(ATH5K_NF_CAL_HIST_MAX - 1) / 2]; } -/** - * ath5k_hw_update_noise_floor() - Update NF on hardware - * @ah: The &struct ath5k_hw +/* + * When we tell the hardware to perform a noise floor calibration + * by setting the AR5K_PHY_AGCCTL_NF bit, it will periodically + * sample-and-hold the minimum noise level seen at the antennas. + * This value is then stored in a ring buffer of recently measured + * noise floor values so we have a moving window of the last few + * samples. * - * This is the main function we call to perform a NF calibration, - * it reads NF from hardware, calculates the median and updates - * NF on hw. + * The median of the values in the history is then loaded into the + * hardware for its own use for RSSI and CCA measurements. */ -void -ath5k_hw_update_noise_floor(struct ath5k_hw *ah) +void ath5k_hw_update_noise_floor(struct ath5k_hw *ah) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; u32 val; @@ -1608,8 +1390,6 @@ ath5k_hw_update_noise_floor(struct ath5k_hw *ah) return; } - ah->ah_cal_mask |= AR5K_CALIBRATION_NF; - ee_mode = ath5k_eeprom_mode_from_channel(ah->ah_current_channel); /* completed NF calibration, test threshold */ @@ -1654,29 +1434,20 @@ ath5k_hw_update_noise_floor(struct ath5k_hw *ah) ah->ah_noise_floor = nf; - ah->ah_cal_mask &= ~AR5K_CALIBRATION_NF; - ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "noise floor calibrated: %d\n", nf); } -/** - * ath5k_hw_rf5110_calibrate() - Perform a PHY calibration on RF5110 - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * - * Do a complete PHY calibration (AGC + NF + I/Q) on RF5110 +/* + * Perform a PHY calibration on RF5110 + * -Fix BPSK/QAM Constellation (I/Q correction) */ -static int -ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, +static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel) { u32 phy_sig, phy_agc, phy_sat, beacon; int ret; - if (!(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) - return 0; - /* * Disable beacons and RX/TX queues, wait */ @@ -1685,7 +1456,7 @@ ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, beacon = ath5k_hw_reg_read(ah, AR5K_BEACON_5210); ath5k_hw_reg_write(ah, beacon & ~AR5K_BEACON_ENABLE, AR5K_BEACON_5210); - usleep_range(2000, 2500); + mdelay(2); /* * Set the channel (with AGC turned off) @@ -1698,7 +1469,7 @@ ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, * Activate PHY and wait */ ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT); - usleep_range(1000, 1500); + mdelay(1); AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); @@ -1735,7 +1506,7 @@ ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, ath5k_hw_reg_write(ah, AR5K_PHY_RFSTG_DISABLE, AR5K_PHY_RFSTG); AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); - usleep_range(1000, 1500); + mdelay(1); /* * Enable calibration and wait until completion @@ -1766,9 +1537,8 @@ ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, return 0; } -/** - * ath5k_hw_rf511x_iq_calibrate() - Perform I/Q calibration on RF5111 and newer - * @ah: The &struct ath5k_hw +/* + * Perform I/Q calibration on RF5111/5112 and newer chips */ static int ath5k_hw_rf511x_iq_calibrate(struct ath5k_hw *ah) @@ -1777,19 +1547,12 @@ ath5k_hw_rf511x_iq_calibrate(struct ath5k_hw *ah) s32 iq_corr, i_coff, i_coffd, q_coff, q_coffd; int i; - /* Skip if I/Q calibration is not needed or if it's still running */ - if (!ah->ah_iq_cal_needed) - return -EINVAL; - else if (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN) { - ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE, - "I/Q calibration still running"); - return -EBUSY; - } + if (!ah->ah_calibration || + ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN) + return 0; /* Calibration has finished, get the results and re-run */ - - /* Work around for empty results which can apparently happen on 5212: - * Read registers up to 10 times until we get both i_pr and q_pwr */ + /* work around empty results which can apparently happen on 5212 */ for (i = 0; i <= 10; i++) { iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR); i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I); @@ -1807,13 +1570,9 @@ ath5k_hw_rf511x_iq_calibrate(struct ath5k_hw *ah) else q_coffd = q_pwr >> 7; - /* In case i_coffd became zero, cancel calibration - * not only it's too small, it'll also result a divide - * by zero later on. */ + /* protect against divide by 0 and loss of sign bits */ if (i_coffd == 0 || q_coffd < 2) - return -ECANCELED; - - /* Protect against loss of sign bits */ + return 0; i_coff = (-iq_corr) / i_coffd; i_coff = clamp(i_coff, -32, 31); /* signed 6 bit */ @@ -1842,17 +1601,10 @@ ath5k_hw_rf511x_iq_calibrate(struct ath5k_hw *ah) return 0; } -/** - * ath5k_hw_phy_calibrate() - Perform a PHY calibration - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * - * The main function we call from above to perform - * a short or full PHY calibration based on RF chip - * and current channel +/* + * Perform a PHY calibration */ -int -ath5k_hw_phy_calibrate(struct ath5k_hw *ah, +int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel) { int ret; @@ -1861,28 +1613,11 @@ ath5k_hw_phy_calibrate(struct ath5k_hw *ah, return ath5k_hw_rf5110_calibrate(ah, channel); ret = ath5k_hw_rf511x_iq_calibrate(ah); - if (ret) { - ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE, - "No I/Q correction performed (%uMHz)\n", - channel->center_freq); - /* Happens all the time if there is not much - * traffic, consider it normal behaviour. */ - ret = 0; - } - - /* On full calibration request a PAPD probe for - * gainf calibration if needed */ - if ((ah->ah_cal_mask & AR5K_CALIBRATION_FULL) && - (ah->ah_radio == AR5K_RF5111 || - ah->ah_radio == AR5K_RF5112) && - channel->hw_value != AR5K_MODE_11B) + if ((ah->ah_radio == AR5K_RF5111 || ah->ah_radio == AR5K_RF5112) && + (channel->hw_value != AR5K_MODE_11B)) ath5k_hw_request_rfgain_probe(ah); - /* Update noise floor */ - if (!(ah->ah_cal_mask & AR5K_CALIBRATION_NF)) - ath5k_hw_update_noise_floor(ah); - return ret; } @@ -1891,16 +1626,6 @@ ath5k_hw_phy_calibrate(struct ath5k_hw *ah, * Spur mitigation functions * \***************************/ -/** - * ath5k_hw_set_spur_mitigation_filter() - Configure SPUR filter - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * - * This function gets called during PHY initialization to - * configure the spur filter for the given channel. Spur is noise - * generated due to "reflection" effects, for more information on this - * method check out patent US7643810 - */ static void ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah, struct ieee80211_channel *channel) @@ -2140,73 +1865,15 @@ ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah, * Antenna control * \*****************/ -/** - * DOC: Antenna control - * - * Hw supports up to 14 antennas ! I haven't found any card that implements - * that. The maximum number of antennas I've seen is up to 4 (2 for 2GHz and 2 - * for 5GHz). Antenna 1 (MAIN) should be omnidirectional, 2 (AUX) - * omnidirectional or sectorial and antennas 3-14 sectorial (or directional). - * - * We can have a single antenna for RX and multiple antennas for TX. - * RX antenna is our "default" antenna (usually antenna 1) set on - * DEFAULT_ANTENNA register and TX antenna is set on each TX control descriptor - * (0 for automatic selection, 1 - 14 antenna number). - * - * We can let hw do all the work doing fast antenna diversity for both - * tx and rx or we can do things manually. Here are the options we have - * (all are bits of STA_ID1 register): - * - * AR5K_STA_ID1_DEFAULT_ANTENNA -> When 0 is set as the TX antenna on TX - * control descriptor, use the default antenna to transmit or else use the last - * antenna on which we received an ACK. - * - * AR5K_STA_ID1_DESC_ANTENNA -> Update default antenna after each TX frame to - * the antenna on which we got the ACK for that frame. - * - * AR5K_STA_ID1_RTS_DEF_ANTENNA -> Use default antenna for RTS or else use the - * one on the TX descriptor. - * - * AR5K_STA_ID1_SELFGEN_DEF_ANT -> Use default antenna for self generated frames - * (ACKs etc), or else use current antenna (the one we just used for TX). - * - * Using the above we support the following scenarios: - * - * AR5K_ANTMODE_DEFAULT -> Hw handles antenna diversity etc automatically - * - * AR5K_ANTMODE_FIXED_A -> Only antenna A (MAIN) is present - * - * AR5K_ANTMODE_FIXED_B -> Only antenna B (AUX) is present - * - * AR5K_ANTMODE_SINGLE_AP -> Sta locked on a single ap - * - * AR5K_ANTMODE_SECTOR_AP -> AP with tx antenna set on tx desc - * - * AR5K_ANTMODE_SECTOR_STA -> STA with tx antenna set on tx desc - * - * AR5K_ANTMODE_DEBUG Debug mode -A -> Rx, B-> Tx- - * - * Also note that when setting antenna to F on tx descriptor card inverts - * current tx antenna. - */ - -/** - * ath5k_hw_set_def_antenna() - Set default rx antenna on AR5211/5212 and newer - * @ah: The &struct ath5k_hw - * @ant: Antenna number - */ -static void +static void /*TODO:Boundary check*/ ath5k_hw_set_def_antenna(struct ath5k_hw *ah, u8 ant) { if (ah->ah_version != AR5K_AR5210) ath5k_hw_reg_write(ah, ant & 0x7, AR5K_DEFAULT_ANTENNA); } -/** - * ath5k_hw_set_fast_div() - Enable/disable fast rx antenna diversity - * @ah: The &struct ath5k_hw - * @ee_mode: One of enum ath5k_driver_mode - * @enable: True to enable, false to disable +/* + * Enable/disable fast rx antenna diversity */ static void ath5k_hw_set_fast_div(struct ath5k_hw *ah, u8 ee_mode, bool enable) @@ -2246,14 +1913,6 @@ ath5k_hw_set_fast_div(struct ath5k_hw *ah, u8 ee_mode, bool enable) } } -/** - * ath5k_hw_set_antenna_switch() - Set up antenna switch table - * @ah: The &struct ath5k_hw - * @ee_mode: One of enum ath5k_driver_mode - * - * Switch table comes from EEPROM and includes information on controlling - * the 2 antenna RX attenuators - */ void ath5k_hw_set_antenna_switch(struct ath5k_hw *ah, u8 ee_mode) { @@ -2285,10 +1944,8 @@ ath5k_hw_set_antenna_switch(struct ath5k_hw *ah, u8 ee_mode) AR5K_PHY_ANT_SWITCH_TABLE_1); } -/** - * ath5k_hw_set_antenna_mode() - Set antenna operating mode - * @ah: The &struct ath5k_hw - * @ant_mode: One of enum ath5k_ant_mode +/* + * Set antenna operating mode */ void ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode) @@ -2411,13 +2068,8 @@ ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode) * Helper functions */ -/** - * ath5k_get_interpolated_value() - Get interpolated Y val between two points - * @target: X value of the middle point - * @x_left: X value of the left point - * @x_right: X value of the right point - * @y_left: Y value of the left point - * @y_right: Y value of the right point +/* + * Do linear interpolation between two given (x, y) points */ static s16 ath5k_get_interpolated_value(s16 target, s16 x_left, s16 x_right, @@ -2444,18 +2096,13 @@ ath5k_get_interpolated_value(s16 target, s16 x_left, s16 x_right, return result; } -/** - * ath5k_get_linear_pcdac_min() - Find vertical boundary (min pwr) for the - * linear PCDAC curve - * @stepL: Left array with y values (pcdac steps) - * @stepR: Right array with y values (pcdac steps) - * @pwrL: Left array with x values (power steps) - * @pwrR: Right array with x values (power steps) +/* + * Find vertical boundary (min pwr) for the linear PCDAC curve. * * Since we have the top of the curve and we draw the line below * until we reach 1 (1 pcdac step) we need to know which point - * (x value) that is so that we don't go below x axis and have negative - * pcdac values when creating the curve, or fill the table with zeros. + * (x value) that is so that we don't go below y axis and have negative + * pcdac values when creating the curve, or fill the table with zeroes. */ static s16 ath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR, @@ -2501,16 +2148,7 @@ ath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR, return max(min_pwrL, min_pwrR); } -/** - * ath5k_create_power_curve() - Create a Power to PDADC or PCDAC curve - * @pmin: Minimum power value (xmin) - * @pmax: Maximum power value (xmax) - * @pwr: Array of power steps (x values) - * @vpd: Array of matching PCDAC/PDADC steps (y values) - * @num_points: Number of provided points - * @vpd_table: Array to fill with the full PCDAC/PDADC values (y values) - * @type: One of enum ath5k_powertable_type (eeprom.h) - * +/* * Interpolate (pwr,vpd) points to create a Power to PDADC or a * Power to PCDAC curve. * @@ -2568,14 +2206,7 @@ ath5k_create_power_curve(s16 pmin, s16 pmax, } } -/** - * ath5k_get_chan_pcal_surrounding_piers() - Get surrounding calibration piers - * for a given channel. - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * @pcinfo_l: The &struct ath5k_chan_pcal_info to put the left cal. pier - * @pcinfo_r: The &struct ath5k_chan_pcal_info to put the right cal. pier - * +/* * Get the surrounding per-channel power calibration piers * for a given frequency so that we can interpolate between * them and come up with an appropriate dataset for our current @@ -2658,17 +2289,11 @@ ath5k_get_chan_pcal_surrounding_piers(struct ath5k_hw *ah, *pcinfo_r = &pcinfo[idx_r]; } -/** - * ath5k_get_rate_pcal_data() - Get the interpolated per-rate power - * calibration data - * @ah: The &struct ath5k_hw *ah, - * @channel: The &struct ieee80211_channel - * @rates: The &struct ath5k_rate_pcal_info to fill - * +/* * Get the surrounding per-rate power calibration data * for a given frequency and interpolate between power * values to set max target power supported by hw for - * each rate on this frequency. + * each rate. */ static void ath5k_get_rate_pcal_data(struct ath5k_hw *ah, @@ -2756,11 +2381,7 @@ ath5k_get_rate_pcal_data(struct ath5k_hw *ah, rpinfo[idx_r].target_power_54); } -/** - * ath5k_get_max_ctl_power() - Get max edge power for a given frequency - * @ah: the &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * +/* * Get the max edge power for this channel if * we have such data from EEPROM's Conformance Test * Limits (CTL), and limit max power if needed. @@ -2840,39 +2461,8 @@ ath5k_get_max_ctl_power(struct ath5k_hw *ah, * Power to PCDAC table functions */ -/** - * DOC: Power to PCDAC table functions - * - * For RF5111 we have an XPD -eXternal Power Detector- curve - * for each calibrated channel. Each curve has 0,5dB Power steps - * on x axis and PCDAC steps (offsets) on y axis and looks like an - * exponential function. To recreate the curve we read 11 points - * from eeprom (eeprom.c) and interpolate here. - * - * For RF5112 we have 4 XPD -eXternal Power Detector- curves - * for each calibrated channel on 0, -6, -12 and -18dBm but we only - * use the higher (3) and the lower (0) curves. Each curve again has 0.5dB - * power steps on x axis and PCDAC steps on y axis and looks like a - * linear function. To recreate the curve and pass the power values - * on hw, we get 4 points for xpd 0 (lower gain -> max power) - * and 3 points for xpd 3 (higher gain -> lower power) from eeprom (eeprom.c) - * and interpolate here. - * - * For a given channel we get the calibrated points (piers) for it or - * -if we don't have calibration data for this specific channel- from the - * available surrounding channels we have calibration data for, after we do a - * linear interpolation between them. Then since we have our calibrated points - * for this channel, we do again a linear interpolation between them to get the - * whole curve. - * - * We finally write the Y values of the curve(s) (the PCDAC values) on hw - */ - -/** - * ath5k_fill_pwr_to_pcdac_table() - Fill Power to PCDAC table on RF5111 - * @ah: The &struct ath5k_hw - * @table_min: Minimum power (x min) - * @table_max: Maximum power (x max) +/* + * Fill Power to PCDAC table on RF5111 * * No further processing is needed for RF5111, the only thing we have to * do is fill the values below and above calibration range since eeprom data @@ -2913,14 +2503,10 @@ ath5k_fill_pwr_to_pcdac_table(struct ath5k_hw *ah, s16* table_min, } -/** - * ath5k_combine_linear_pcdac_curves() - Combine available PCDAC Curves - * @ah: The &struct ath5k_hw - * @table_min: Minimum power (x min) - * @table_max: Maximum power (x max) - * @pdcurves: Number of pd curves +/* + * Combine available XPD Curves and fill Linear Power to PCDAC table + * on RF5112 * - * Combine available XPD Curves and fill Linear Power to PCDAC table on RF5112 * RFX112 can have up to 2 curves (one for low txpower range and one for * higher txpower range). We need to put them both on pcdac_out and place * them in the correct location. In case we only have one curve available @@ -3022,10 +2608,7 @@ ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min, } } -/** - * ath5k_write_pcdac_table() - Write the PCDAC values on hw - * @ah: The &struct ath5k_hw - */ +/* Write PCDAC values on hw */ static void ath5k_write_pcdac_table(struct ath5k_hw *ah) { @@ -3048,32 +2631,9 @@ ath5k_write_pcdac_table(struct ath5k_hw *ah) * Power to PDADC table functions */ -/** - * DOC: Power to PDADC table functions - * - * For RF2413 and later we have a Power to PDADC table (Power Detector) - * instead of a PCDAC (Power Control) and 4 pd gain curves for each - * calibrated channel. Each curve has power on x axis in 0.5 db steps and - * PDADC steps on y axis and looks like an exponential function like the - * RF5111 curve. - * - * To recreate the curves we read the points from eeprom (eeprom.c) - * and interpolate here. Note that in most cases only 2 (higher and lower) - * curves are used (like RF5112) but vendors have the opportunity to include - * all 4 curves on eeprom. The final curve (higher power) has an extra - * point for better accuracy like RF5112. - * - * The process is similar to what we do above for RF5111/5112 - */ - -/** - * ath5k_combine_pwr_to_pdadc_curves() - Combine the various PDADC curves - * @ah: The &struct ath5k_hw - * @pwr_min: Minimum power (x min) - * @pwr_max: Maximum power (x max) - * @pdcurves: Number of available curves +/* + * Set the gain boundaries and create final Power to PDADC table * - * Combine the various pd curves and create the final Power to PDADC table * We can have up to 4 pd curves, we need to do a similar process * as we do for RF5112. This time we don't have an edge_flag but we * set the gain boundaries on a separate register. @@ -3197,11 +2757,7 @@ ath5k_combine_pwr_to_pdadc_curves(struct ath5k_hw *ah, } -/** - * ath5k_write_pwr_to_pdadc_table() - Write the PDADC values on hw - * @ah: The &struct ath5k_hw - * @ee_mode: One of enum ath5k_driver_mode - */ +/* Write PDADC values on hw */ static void ath5k_write_pwr_to_pdadc_table(struct ath5k_hw *ah, u8 ee_mode) { @@ -3258,13 +2814,7 @@ ath5k_write_pwr_to_pdadc_table(struct ath5k_hw *ah, u8 ee_mode) * Common code for PCDAC/PDADC tables */ -/** - * ath5k_setup_channel_powertable() - Set up power table for this channel - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * @ee_mode: One of enum ath5k_driver_mode - * @type: One of enum ath5k_powertable_type (eeprom.h) - * +/* * This is the main function that uses all of the above * to set PCDAC/PDADC table on hw for the current channel. * This table is used for tx power calibration on the baseband, @@ -3462,12 +3012,7 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah, return 0; } -/** - * ath5k_write_channel_powertable() - Set power table for current channel on hw - * @ah: The &struct ath5k_hw - * @ee_mode: One of enum ath5k_driver_mode - * @type: One of enum ath5k_powertable_type (eeprom.h) - */ +/* Write power table for current channel to hw */ static void ath5k_write_channel_powertable(struct ath5k_hw *ah, u8 ee_mode, u8 type) { @@ -3477,36 +3022,28 @@ ath5k_write_channel_powertable(struct ath5k_hw *ah, u8 ee_mode, u8 type) ath5k_write_pcdac_table(ah); } - -/** - * DOC: Per-rate tx power setting +/* + * Per-rate tx power setting * - * This is the code that sets the desired tx power limit (below + * This is the code that sets the desired tx power (below * maximum) on hw for each rate (we also have TPC that sets - * power per packet type). We do that by providing an index on the - * PCDAC/PDADC table we set up above, for each rate. + * power per packet). We do that by providing an index on the + * PCDAC/PDADC table we set up. + */ + +/* + * Set rate power table * * For now we only limit txpower based on maximum tx power - * supported by hw (what's inside rate_info) + conformance test - * limits. We need to limit this even more, based on regulatory domain - * etc to be safe. Normally this is done from above so we don't care - * here, all we care is that the tx power we set will be O.K. - * for the hw (e.g. won't create noise on PA etc). + * supported by hw (what's inside rate_info). We need to limit + * this even more, based on regulatory domain etc. * - * Rate power table contains indices to PCDAC/PDADC table (0.5dB steps - - * x values) and is indexed as follows: + * Rate power table contains indices to PCDAC/PDADC table (0.5dB steps) + * and is indexed as follows: * rates[0] - rates[7] -> OFDM rates * rates[8] - rates[14] -> CCK rates * rates[15] -> XR rates (they all have the same power) */ - -/** - * ath5k_setup_rate_powertable() - Set up rate power table for a given tx power - * @ah: The &struct ath5k_hw - * @max_pwr: The maximum tx power requested in 0.5dB steps - * @rate_info: The &struct ath5k_rate_pcal_info to fill - * @ee_mode: One of enum ath5k_driver_mode - */ static void ath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr, struct ath5k_rate_pcal_info *rate_info, @@ -3577,14 +3114,8 @@ ath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr, } -/** - * ath5k_hw_txpower() - Set transmission power limit for a given channel - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * @txpower: Requested tx power in 0.5dB steps - * - * Combines all of the above to set the requested tx power limit - * on hw. +/* + * Set transmission power */ static int ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, @@ -3702,16 +3233,7 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, return 0; } -/** - * ath5k_hw_set_txpower_limit() - Set txpower limit for the current channel - * @ah: The &struct ath5k_hw - * @txpower: The requested tx power limit in 0.5dB steps - * - * This function provides access to ath5k_hw_txpower to the driver in - * case user or an application changes it while PHY is running. - */ -int -ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower) +int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower) { ATH5K_DBG(ah, ATH5K_DEBUG_TXPOWER, "changing txpower to %d\n", txpower); @@ -3719,26 +3241,11 @@ ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower) return ath5k_hw_txpower(ah, ah->ah_current_channel, txpower); } - /*************\ Init function \*************/ -/** - * ath5k_hw_phy_init() - Initialize PHY - * @ah: The &struct ath5k_hw - * @channel: The @struct ieee80211_channel - * @mode: One of enum ath5k_driver_mode - * @fast: Try a fast channel switch instead - * - * This is the main function used during reset to initialize PHY - * or do a fast channel change if possible. - * - * NOTE: Do not call this one from the driver, it assumes PHY is in a - * warm reset state ! - */ -int -ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel, +int ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel, u8 mode, bool fast) { struct ieee80211_channel *curr_channel; @@ -3848,7 +3355,7 @@ ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel, if (ret) return ret; - usleep_range(1000, 1500); + mdelay(1); /* * Write RF buffer @@ -3869,10 +3376,10 @@ ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel, } } else if (ah->ah_version == AR5K_AR5210) { - usleep_range(1000, 1500); + mdelay(1); /* Disable phy and wait */ ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT); - usleep_range(1000, 1500); + mdelay(1); } /* Set channel on PHY */ @@ -3898,7 +3405,7 @@ ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel, for (i = 0; i <= 20; i++) { if (!(ath5k_hw_reg_read(ah, AR5K_PHY_ADC_TEST) & 0x10)) break; - usleep_range(200, 250); + udelay(200); } ath5k_hw_reg_write(ah, phy_tst1, AR5K_PHY_TST1); @@ -3926,9 +3433,9 @@ ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel, /* At the same time start I/Q calibration for QAM constellation * -no need for CCK- */ - ah->ah_iq_cal_needed = false; + ah->ah_calibration = false; if (!(mode == AR5K_MODE_11B)) { - ah->ah_iq_cal_needed = true; + ah->ah_calibration = true; AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15); AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c index 30b50f93..ff3d348a 100644 --- a/drivers/net/wireless/ath/ath5k/qcu.c +++ b/drivers/net/wireless/ath/ath5k/qcu.c @@ -17,48 +17,23 @@ */ /********************************************\ -Queue Control Unit, DCF Control Unit Functions +Queue Control Unit, DFS Control Unit Functions \********************************************/ #include "ath5k.h" #include "reg.h" #include "debug.h" -#include - -/** - * DOC: Queue Control Unit (QCU)/DCF Control Unit (DCU) functions - * - * Here we setup parameters for the 12 available TX queues. Note that - * on the various registers we can usually only map the first 10 of them so - * basically we have 10 queues to play with. Each queue has a matching - * QCU that controls when the queue will get triggered and multiple QCUs - * can be mapped to a single DCU that controls the various DFS parameters - * for the various queues. In our setup we have a 1:1 mapping between QCUs - * and DCUs allowing us to have different DFS settings for each queue. - * - * When a frame goes into a TX queue, QCU decides when it'll trigger a - * transmission based on various criteria (such as how many data we have inside - * it's buffer or -if it's a beacon queue- if it's time to fire up the queue - * based on TSF etc), DCU adds backoff, IFSes etc and then a scheduler - * (arbitrator) decides the priority of each QCU based on it's configuration - * (e.g. beacons are always transmitted when they leave DCU bypassing all other - * frames from other queues waiting to be transmitted). After a frame leaves - * the DCU it goes to PCU for further processing and then to PHY for - * the actual transmission. - */ /******************\ * Helper functions * \******************/ -/** - * ath5k_hw_num_tx_pending() - Get number of pending frames for a given queue - * @ah: The &struct ath5k_hw - * @queue: One of enum ath5k_tx_queue_id +/* + * Get number of pending frames + * for a specific queue [5211+] */ -u32 -ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue) +u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue) { u32 pending; AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); @@ -83,13 +58,10 @@ ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue) return pending; } -/** - * ath5k_hw_release_tx_queue() - Set a transmit queue inactive - * @ah: The &struct ath5k_hw - * @queue: One of enum ath5k_tx_queue_id +/* + * Set a transmit queue inactive */ -void -ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue) +void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue) { if (WARN_ON(queue >= ah->ah_capabilities.cap_queues.q_tx_num)) return; @@ -100,56 +72,34 @@ ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue) AR5K_Q_DISABLE_BITS(ah->ah_txq_status, queue); } -/** - * ath5k_cw_validate() - Make sure the given cw is valid - * @cw_req: The contention window value to check - * +/* * Make sure cw is a power of 2 minus 1 and smaller than 1024 */ -static u16 -ath5k_cw_validate(u16 cw_req) +static u16 ath5k_cw_validate(u16 cw_req) { + u32 cw = 1; cw_req = min(cw_req, (u16)1023); - /* Check if cw_req + 1 a power of 2 */ - if (is_power_of_2(cw_req + 1)) - return cw_req; + while (cw < cw_req) + cw = (cw << 1) | 1; - /* Check if cw_req is a power of 2 */ - if (is_power_of_2(cw_req)) - return cw_req - 1; - - /* If none of the above is correct - * find the closest power of 2 */ - cw_req = (u16) roundup_pow_of_two(cw_req) - 1; - - return cw_req; + return cw; } -/** - * ath5k_hw_get_tx_queueprops() - Get properties for a transmit queue - * @ah: The &struct ath5k_hw - * @queue: One of enum ath5k_tx_queue_id - * @queue_info: The &struct ath5k_txq_info to fill +/* + * Get properties for a transmit queue */ -int -ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, +int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, struct ath5k_txq_info *queue_info) { memcpy(queue_info, &ah->ah_txq[queue], sizeof(struct ath5k_txq_info)); return 0; } -/** - * ath5k_hw_set_tx_queueprops() - Set properties for a transmit queue - * @ah: The &struct ath5k_hw - * @queue: One of enum ath5k_tx_queue_id - * @qinfo: The &struct ath5k_txq_info to use - * - * Returns 0 on success or -EIO if queue is inactive +/* + * Set properties for a transmit queue */ -int -ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue, +int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue, const struct ath5k_txq_info *qinfo) { struct ath5k_txq_info *qi; @@ -189,16 +139,10 @@ ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue, return 0; } -/** - * ath5k_hw_setup_tx_queue() - Initialize a transmit queue - * @ah: The &struct ath5k_hw - * @queue_type: One of enum ath5k_tx_queue - * @queue_info: The &struct ath5k_txq_info to use - * - * Returns 0 on success, -EINVAL on invalid arguments +/* + * Initialize a transmit queue */ -int -ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, +int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, struct ath5k_txq_info *queue_info) { unsigned int queue; @@ -223,13 +167,7 @@ ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, } else { switch (queue_type) { case AR5K_TX_QUEUE_DATA: - for (queue = AR5K_TX_QUEUE_ID_DATA_MIN; - ah->ah_txq[queue].tqi_type != - AR5K_TX_QUEUE_INACTIVE; queue++) { - - if (queue > AR5K_TX_QUEUE_ID_DATA_MAX) - return -EINVAL; - } + queue = queue_info->tqi_subtype; break; case AR5K_TX_QUEUE_UAPSD: queue = AR5K_TX_QUEUE_ID_UAPSD; @@ -273,16 +211,10 @@ ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, * Single QCU/DCU initialization * \*******************************/ -/** - * ath5k_hw_set_tx_retry_limits() - Set tx retry limits on DCU - * @ah: The &struct ath5k_hw - * @queue: One of enum ath5k_tx_queue_id - * - * This function is used when initializing a queue, to set - * retry limits based on ah->ah_retry_* and the chipset used. +/* + * Set tx retry limits on DCU */ -void -ath5k_hw_set_tx_retry_limits(struct ath5k_hw *ah, +void ath5k_hw_set_tx_retry_limits(struct ath5k_hw *ah, unsigned int queue) { /* Single data queue on AR5210 */ @@ -317,15 +249,15 @@ ath5k_hw_set_tx_retry_limits(struct ath5k_hw *ah, } /** - * ath5k_hw_reset_tx_queue() - Initialize a single hw queue - * @ah: The &struct ath5k_hw - * @queue: One of enum ath5k_tx_queue_id + * ath5k_hw_reset_tx_queue - Initialize a single hw queue + * + * @ah The &struct ath5k_hw + * @queue The hw queue number * - * Set DCF properties for the given transmit queue on DCU + * Set DFS properties for the given transmit queue on DCU * and configures all queue-specific parameters. */ -int -ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) +int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) { struct ath5k_txq_info *tq = &ah->ah_txq[queue]; @@ -553,9 +485,10 @@ ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) \**************************/ /** - * ath5k_hw_set_ifs_intervals() - Set global inter-frame spaces on DCU - * @ah: The &struct ath5k_hw - * @slot_time: Slot time in us + * ath5k_hw_set_ifs_intervals - Set global inter-frame spaces on DCU + * + * @ah The &struct ath5k_hw + * @slot_time Slot time in us * * Sets the global IFS intervals on DCU (also works on AR5210) for * the given slot time and the current bwmode. @@ -658,15 +591,7 @@ int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time) } -/** - * ath5k_hw_init_queues() - Initialize tx queues - * @ah: The &struct ath5k_hw - * - * Initializes all tx queues based on information on - * ah->ah_txq* set by the driver - */ -int -ath5k_hw_init_queues(struct ath5k_hw *ah) +int ath5k_hw_init_queues(struct ath5k_hw *ah) { int i, ret; diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h index 0ea1608b..f5c10000 100644 --- a/drivers/net/wireless/ath/ath5k/reg.h +++ b/drivers/net/wireless/ath/ath5k/reg.h @@ -280,10 +280,6 @@ * 5211/5212 we have one primary and 4 secondary registers. * So we have AR5K_ISR for 5210 and AR5K_PISR /SISRx for 5211/5212. * Most of these bits are common for all chipsets. - * - * NOTE: On 5211+ TXOK, TXDESC, TXERR, TXEOL and TXURN contain - * the logical OR from per-queue interrupt bits found on SISR registers - * (see below). */ #define AR5K_ISR 0x001c /* Register Address [5210] */ #define AR5K_PISR 0x0080 /* Register Address [5211+] */ @@ -296,10 +292,7 @@ #define AR5K_ISR_TXOK 0x00000040 /* Frame successfully transmitted */ #define AR5K_ISR_TXDESC 0x00000080 /* TX descriptor request */ #define AR5K_ISR_TXERR 0x00000100 /* Transmit error */ -#define AR5K_ISR_TXNOFRM 0x00000200 /* No frame transmitted (transmit timeout) - * NOTE: We don't have per-queue info for this - * one, but we can enable it per-queue through - * TXNOFRM_QCU field on TXNOFRM register */ +#define AR5K_ISR_TXNOFRM 0x00000200 /* No frame transmitted (transmit timeout) */ #define AR5K_ISR_TXEOL 0x00000400 /* Empty TX descriptor */ #define AR5K_ISR_TXURN 0x00000800 /* Transmit FIFO underrun */ #define AR5K_ISR_MIB 0x00001000 /* Update MIB counters */ @@ -309,29 +302,21 @@ #define AR5K_ISR_SWBA 0x00010000 /* Software beacon alert */ #define AR5K_ISR_BRSSI 0x00020000 /* Beacon rssi below threshold (?) */ #define AR5K_ISR_BMISS 0x00040000 /* Beacon missed */ -#define AR5K_ISR_HIUERR 0x00080000 /* Host Interface Unit error [5211+] - * 'or' of MCABT, SSERR, DPERR from SISR2 */ +#define AR5K_ISR_HIUERR 0x00080000 /* Host Interface Unit error [5211+] */ #define AR5K_ISR_BNR 0x00100000 /* Beacon not ready [5211+] */ #define AR5K_ISR_MCABT 0x00100000 /* Master Cycle Abort [5210] */ #define AR5K_ISR_RXCHIRP 0x00200000 /* CHIRP Received [5212+] */ #define AR5K_ISR_SSERR 0x00200000 /* Signaled System Error [5210] */ -#define AR5K_ISR_DPERR 0x00400000 /* Bus parity error [5210] */ +#define AR5K_ISR_DPERR 0x00400000 /* Det par Error (?) [5210] */ #define AR5K_ISR_RXDOPPLER 0x00400000 /* Doppler chirp received [5212+] */ #define AR5K_ISR_TIM 0x00800000 /* [5211+] */ -#define AR5K_ISR_BCNMISC 0x00800000 /* Misc beacon related interrupt - * 'or' of TIM, CAB_END, DTIM_SYNC, BCN_TIMEOUT, - * CAB_TIMEOUT and DTIM bits from SISR2 [5212+] */ +#define AR5K_ISR_BCNMISC 0x00800000 /* 'or' of TIM, CAB_END, DTIM_SYNC, BCN_TIMEOUT, + CAB_TIMEOUT and DTIM bits from SISR2 [5212+] */ #define AR5K_ISR_GPIO 0x01000000 /* GPIO (rf kill) */ #define AR5K_ISR_QCBRORN 0x02000000 /* QCU CBR overrun [5211+] */ #define AR5K_ISR_QCBRURN 0x04000000 /* QCU CBR underrun [5211+] */ #define AR5K_ISR_QTRIG 0x08000000 /* QCU scheduling trigger [5211+] */ -#define AR5K_ISR_BITS_FROM_SISRS (AR5K_ISR_TXOK | AR5K_ISR_TXDESC |\ - AR5K_ISR_TXERR | AR5K_ISR_TXEOL |\ - AR5K_ISR_TXURN | AR5K_ISR_HIUERR |\ - AR5K_ISR_BCNMISC | AR5K_ISR_QCBRORN |\ - AR5K_ISR_QCBRURN | AR5K_ISR_QTRIG) - /* * Secondary status registers [5211+] (0 - 4) * @@ -362,7 +347,7 @@ #define AR5K_SISR2_BCN_TIMEOUT 0x08000000 /* Beacon Timeout [5212+] */ #define AR5K_SISR2_CAB_TIMEOUT 0x10000000 /* CAB Timeout [5212+] */ #define AR5K_SISR2_DTIM 0x20000000 /* [5212+] */ -#define AR5K_SISR2_TSFOOR 0x80000000 /* TSF Out of range */ +#define AR5K_SISR2_TSFOOR 0x80000000 /* TSF OOR (?) */ #define AR5K_SISR3 0x0090 /* Register Address [5211+] */ #define AR5K_SISR3_QCBRORN 0x000003ff /* Mask for QCBRORN */ diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index 200f165c..2abac257 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -19,9 +19,9 @@ * */ -/****************************\ - Reset function and helpers -\****************************/ +/*****************************\ + Reset functions and helpers +\*****************************/ #include @@ -33,36 +33,14 @@ #include "debug.h" -/** - * DOC: Reset function and helpers - * - * Here we implement the main reset routine, used to bring the card - * to a working state and ready to receive. We also handle routines - * that don't fit on other places such as clock, sleep and power control - */ - - /******************\ * Helper functions * \******************/ -/** - * ath5k_hw_register_timeout() - Poll a register for a flag/field change - * @ah: The &struct ath5k_hw - * @reg: The register to read - * @flag: The flag/field to check on the register - * @val: The field value we expect (if we check a field) - * @is_set: Instead of checking if the flag got cleared, check if it got set - * - * Some registers contain flags that indicate that an operation is - * running. We use this function to poll these registers and check - * if these flags get cleared. We also use it to poll a register - * field (containing multiple flags) until it gets a specific value. - * - * Returns -EAGAIN if we exceeded AR5K_TUNE_REGISTER_TIMEOUT * 15us or 0 +/* + * Check if a register write has been completed */ -int -ath5k_hw_register_timeout(struct ath5k_hw *ah, u32 reg, u32 flag, u32 val, +int ath5k_hw_register_timeout(struct ath5k_hw *ah, u32 reg, u32 flag, u32 val, bool is_set) { int i; @@ -86,48 +64,35 @@ ath5k_hw_register_timeout(struct ath5k_hw *ah, u32 reg, u32 flag, u32 val, \*************************/ /** - * ath5k_hw_htoclock() - Translate usec to hw clock units + * ath5k_hw_htoclock - Translate usec to hw clock units + * * @ah: The &struct ath5k_hw * @usec: value in microseconds - * - * Translate usecs to hw clock units based on the current - * hw clock rate. - * - * Returns number of clock units */ -unsigned int -ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec) +unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec) { struct ath_common *common = ath5k_hw_common(ah); return usec * common->clockrate; } /** - * ath5k_hw_clocktoh() - Translate hw clock units to usec - * @ah: The &struct ath5k_hw + * ath5k_hw_clocktoh - Translate hw clock units to usec * @clock: value in hw clock units - * - * Translate hw clock units to usecs based on the current - * hw clock rate. - * - * Returns number of usecs */ -unsigned int -ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock) +unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock) { struct ath_common *common = ath5k_hw_common(ah); return clock / common->clockrate; } /** - * ath5k_hw_init_core_clock() - Initialize core clock - * @ah: The &struct ath5k_hw + * ath5k_hw_init_core_clock - Initialize core clock + * + * @ah The &struct ath5k_hw * - * Initialize core clock parameters (usec, usec32, latencies etc), - * based on current bwmode and chipset properties. + * Initialize core clock parameters (usec, usec32, latencies etc). */ -static void -ath5k_hw_init_core_clock(struct ath5k_hw *ah) +static void ath5k_hw_init_core_clock(struct ath5k_hw *ah) { struct ieee80211_channel *channel = ah->ah_current_channel; struct ath_common *common = ath5k_hw_common(ah); @@ -262,21 +227,16 @@ ath5k_hw_init_core_clock(struct ath5k_hw *ah) } } -/** - * ath5k_hw_set_sleep_clock() - Setup sleep clock operation - * @ah: The &struct ath5k_hw - * @enable: Enable sleep clock operation (false to disable) - * +/* * If there is an external 32KHz crystal available, use it * as ref. clock instead of 32/40MHz clock and baseband clocks * to save power during sleep or restore normal 32/40MHz * operation. * - * NOTE: When operating on 32KHz certain PHY registers (27 - 31, - * 123 - 127) require delay on access. + * XXX: When operating on 32KHz certain PHY registers (27 - 31, + * 123 - 127) require delay on access. */ -static void -ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable) +static void ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; u32 scal, spending, sclock; @@ -380,19 +340,10 @@ ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable) * Reset/Sleep control * \*********************/ -/** - * ath5k_hw_nic_reset() - Reset the various chipset units - * @ah: The &struct ath5k_hw - * @val: Mask to indicate what units to reset - * - * To reset the various chipset units we need to write - * the mask to AR5K_RESET_CTL and poll the register until - * all flags are cleared. - * - * Returns 0 if we are O.K. or -EAGAIN (from athk5_hw_register_timeout) +/* + * Reset chipset */ -static int -ath5k_hw_nic_reset(struct ath5k_hw *ah, u32 val) +static int ath5k_hw_nic_reset(struct ath5k_hw *ah, u32 val) { int ret; u32 mask = val ? val : ~0U; @@ -406,7 +357,7 @@ ath5k_hw_nic_reset(struct ath5k_hw *ah, u32 val) ath5k_hw_reg_write(ah, val, AR5K_RESET_CTL); /* Wait at least 128 PCI clocks */ - usleep_range(15, 20); + udelay(15); if (ah->ah_version == AR5K_AR5210) { val &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA @@ -431,17 +382,12 @@ ath5k_hw_nic_reset(struct ath5k_hw *ah, u32 val) return ret; } -/** - * ath5k_hw_wisoc_reset() - Reset AHB chipset - * @ah: The &struct ath5k_hw - * @flags: Mask to indicate what units to reset - * - * Same as ath5k_hw_nic_reset but for AHB based devices - * - * Returns 0 if we are O.K. or -EAGAIN (from athk5_hw_register_timeout) +/* + * Reset AHB chipset + * AR5K_RESET_CTL_PCU flag resets WMAC + * AR5K_RESET_CTL_BASEBAND flag resets WBB */ -static int -ath5k_hw_wisoc_reset(struct ath5k_hw *ah, u32 flags) +static int ath5k_hw_wisoc_reset(struct ath5k_hw *ah, u32 flags) { u32 mask = flags ? flags : ~0U; u32 __iomem *reg; @@ -473,14 +419,14 @@ ath5k_hw_wisoc_reset(struct ath5k_hw *ah, u32 flags) } /* Put BB/MAC into reset */ - regval = ioread32(reg); - iowrite32(regval | val, reg); - regval = ioread32(reg); - usleep_range(100, 150); + regval = __raw_readl(reg); + __raw_writel(regval | val, reg); + regval = __raw_readl(reg); + udelay(100); /* Bring BB/MAC out of reset */ - iowrite32(regval & ~val, reg); - regval = ioread32(reg); + __raw_writel(regval & ~val, reg); + regval = __raw_readl(reg); /* * Reset configuration register (for hw byte-swap). Note that this @@ -493,23 +439,11 @@ ath5k_hw_wisoc_reset(struct ath5k_hw *ah, u32 flags) return 0; } -/** - * ath5k_hw_set_power_mode() - Set power mode - * @ah: The &struct ath5k_hw - * @mode: One of enum ath5k_power_mode - * @set_chip: Set to true to write sleep control register - * @sleep_duration: How much time the device is allowed to sleep - * when sleep logic is enabled (in 128 microsecond increments). - * - * This function is used to configure sleep policy and allowed - * sleep modes. For more information check out the sleep control - * register on reg.h and STA_ID1. - * - * Returns 0 on success, -EIO if chip didn't wake up or -EINVAL if an invalid - * mode is requested. + +/* + * Sleep control */ -static int -ath5k_hw_set_power_mode(struct ath5k_hw *ah, enum ath5k_power_mode mode, +static int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, bool set_chip, u16 sleep_duration) { unsigned int i; @@ -559,7 +493,7 @@ ath5k_hw_set_power_mode(struct ath5k_hw *ah, enum ath5k_power_mode mode, ath5k_hw_reg_write(ah, data | AR5K_SLEEP_CTL_SLE_WAKE, AR5K_SLEEP_CTL); - usleep_range(15, 20); + udelay(15); for (i = 200; i > 0; i--) { /* Check if the chip did wake up */ @@ -568,7 +502,7 @@ ath5k_hw_set_power_mode(struct ath5k_hw *ah, enum ath5k_power_mode mode, break; /* Wait a bit and retry */ - usleep_range(50, 75); + udelay(50); ath5k_hw_reg_write(ah, data | AR5K_SLEEP_CTL_SLE_WAKE, AR5K_SLEEP_CTL); } @@ -589,20 +523,17 @@ ath5k_hw_set_power_mode(struct ath5k_hw *ah, enum ath5k_power_mode mode, return 0; } -/** - * ath5k_hw_on_hold() - Put device on hold - * @ah: The &struct ath5k_hw +/* + * Put device on hold * - * Put MAC and Baseband on warm reset and keep that state - * (don't clean sleep control register). After this MAC - * and Baseband are disabled and a full reset is needed - * to come back. This way we save as much power as possible + * Put MAC and Baseband on warm reset and + * keep that state (don't clean sleep control + * register). After this MAC and Baseband are + * disabled and a full reset is needed to come + * back. This way we save as much power as possible * without putting the card on full sleep. - * - * Returns 0 on success or -EIO on error */ -int -ath5k_hw_on_hold(struct ath5k_hw *ah) +int ath5k_hw_on_hold(struct ath5k_hw *ah) { struct pci_dev *pdev = ah->pdev; u32 bus_flags; @@ -612,7 +543,7 @@ ath5k_hw_on_hold(struct ath5k_hw *ah) return 0; /* Make sure device is awake */ - ret = ath5k_hw_set_power_mode(ah, AR5K_PM_AWAKE, true, 0); + ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); if (ret) { ATH5K_ERR(ah, "failed to wakeup the MAC Chip\n"); return ret; @@ -632,7 +563,7 @@ ath5k_hw_on_hold(struct ath5k_hw *ah) ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA | AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI); - usleep_range(2000, 2500); + mdelay(2); } else { ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND | bus_flags); @@ -644,7 +575,7 @@ ath5k_hw_on_hold(struct ath5k_hw *ah) } /* ...wakeup again!*/ - ret = ath5k_hw_set_power_mode(ah, AR5K_PM_AWAKE, true, 0); + ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); if (ret) { ATH5K_ERR(ah, "failed to put device on hold\n"); return ret; @@ -653,18 +584,11 @@ ath5k_hw_on_hold(struct ath5k_hw *ah) return ret; } -/** - * ath5k_hw_nic_wakeup() - Force card out of sleep - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * +/* * Bring up MAC + PHY Chips and program PLL - * NOTE: Channel is NULL for the initial wakeup. - * - * Returns 0 on success, -EIO on hw failure or -EINVAL for false channel infos + * Channel is NULL for the initial wakeup. */ -int -ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel) +int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel) { struct pci_dev *pdev = ah->pdev; u32 turbo, mode, clock, bus_flags; @@ -676,7 +600,7 @@ ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel) if ((ath5k_get_bus_type(ah) != ATH_AHB) || channel) { /* Wakeup the device */ - ret = ath5k_hw_set_power_mode(ah, AR5K_PM_AWAKE, true, 0); + ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); if (ret) { ATH5K_ERR(ah, "failed to wakeup the MAC Chip\n"); return ret; @@ -697,7 +621,7 @@ ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel) ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA | AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI); - usleep_range(2000, 2500); + mdelay(2); } else { if (ath5k_get_bus_type(ah) == ATH_AHB) ret = ath5k_hw_wisoc_reset(ah, AR5K_RESET_CTL_PCU | @@ -713,7 +637,7 @@ ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel) } /* ...wakeup again!...*/ - ret = ath5k_hw_set_power_mode(ah, AR5K_PM_AWAKE, true, 0); + ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); if (ret) { ATH5K_ERR(ah, "failed to resume the MAC Chip\n"); return ret; @@ -815,7 +739,7 @@ ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel) /* ...update PLL if needed */ if (ath5k_hw_reg_read(ah, AR5K_PHY_PLL) != clock) { ath5k_hw_reg_write(ah, clock, AR5K_PHY_PLL); - usleep_range(300, 350); + udelay(300); } /* ...set the PHY operating mode */ @@ -831,19 +755,8 @@ ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel) * Post-initvals register modifications * \**************************************/ -/** - * ath5k_hw_tweak_initval_settings() - Tweak initial settings - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * - * Some settings are not handled on initvals, e.g. bwmode - * settings, some phy settings, workarounds etc that in general - * don't fit anywhere else or are too small to introduce a separate - * function for each one. So we have this function to handle - * them all during reset and complete card's initialization. - */ -static void -ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah, +/* TODO: Half/Quarter rate */ +static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah, struct ieee80211_channel *channel) { if (ah->ah_version == AR5K_AR5212 && @@ -962,16 +875,7 @@ ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah, } } -/** - * ath5k_hw_commit_eeprom_settings() - Commit settings from EEPROM - * @ah: The &struct ath5k_hw - * @channel: The &struct ieee80211_channel - * - * Use settings stored on EEPROM to properly initialize the card - * based on various infos and per-mode calibration data. - */ -static void -ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah, +static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah, struct ieee80211_channel *channel) { struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; @@ -1125,23 +1029,7 @@ ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah, * Main reset function * \*********************/ -/** - * ath5k_hw_reset() - The main reset function - * @ah: The &struct ath5k_hw - * @op_mode: One of enum nl80211_iftype - * @channel: The &struct ieee80211_channel - * @fast: Enable fast channel switching - * @skip_pcu: Skip pcu initialization - * - * This is the function we call each time we want to (re)initialize the - * card and pass new settings to hw. We also call it when hw runs into - * trouble to make it come back to a working state. - * - * Returns 0 on success, -EINVAL on false op_mode or channel infos, or -EIO - * on failure. - */ -int -ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, +int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, struct ieee80211_channel *channel, bool fast, bool skip_pcu) { u32 s_seq[10], s_led[3], tsf_up, tsf_lo; @@ -1159,7 +1047,7 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, */ if (fast && (ah->ah_radio != AR5K_RF2413) && (ah->ah_radio != AR5K_RF5413)) - fast = false; + fast = 0; /* Disable sleep clock operation * to avoid register access delay on certain @@ -1185,7 +1073,7 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, if (ret && fast) { ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "DMA didn't stop, falling back to normal reset\n"); - fast = false; + fast = 0; /* Non fatal, just continue with * normal reset */ ret = 0; @@ -1354,7 +1242,7 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, /* * Initialize PCU */ - ath5k_hw_pcu_init(ah, op_mode); + ath5k_hw_pcu_init(ah, op_mode, mode); /* * Initialize PHY diff --git a/drivers/net/wireless/ath/ath5k/rfbuffer.h b/drivers/net/wireless/ath/ath5k/rfbuffer.h index aed34d99..5d11c23b 100644 --- a/drivers/net/wireless/ath/ath5k/rfbuffer.h +++ b/drivers/net/wireless/ath/ath5k/rfbuffer.h @@ -18,9 +18,7 @@ */ -/** - * DOC: RF Buffer registers - * +/* * There are some special registers on the RF chip * that control various operation settings related mostly to * the analog parts (channel, gain adjustment etc). @@ -46,63 +44,40 @@ */ -/** - * struct ath5k_ini_rfbuffer - Initial RF Buffer settings - * @rfb_bank: RF Bank number - * @rfb_ctrl_register: RF Buffer control register - * @rfb_mode_data: RF Buffer data for each mode - * +/* * Struct to hold default mode specific RF - * register values (RF Banks) for each chip. + * register values (RF Banks) */ struct ath5k_ini_rfbuffer { - u8 rfb_bank; - u16 rfb_ctrl_register; - u32 rfb_mode_data[3]; + u8 rfb_bank; /* RF Bank number */ + u16 rfb_ctrl_register; /* RF Buffer control register */ + u32 rfb_mode_data[3]; /* RF Buffer data for each mode */ }; -/** - * struct ath5k_rfb_field - An RF Buffer field (register/value) - * @len: Field length - * @pos: Offset on the raw packet - * @col: Used for shifting - * +/* * Struct to hold RF Buffer field * infos used to access certain RF * analog registers */ struct ath5k_rfb_field { - u8 len; - u16 pos; - u8 col; + u8 len; /* Field length */ + u16 pos; /* Offset on the raw packet */ + u8 col; /* Column -used for shifting */ }; -/** - * struct ath5k_rf_reg - RF analog register definition - * @bank: RF Buffer Bank number - * @index: Register's index on ath5k_rf_regx_idx - * @field: The &struct ath5k_rfb_field - * - * We use this struct to define the set of RF registers - * on each chip that we want to tweak. Some RF registers - * are common between different chip versions so this saves - * us space and complexity because we can refer to an rf - * register by it's index no matter what chip we work with - * as long as it has that register. +/* + * RF analog register definition */ struct ath5k_rf_reg { - u8 bank; - u8 index; - struct ath5k_rfb_field field; + u8 bank; /* RF Buffer Bank number */ + u8 index; /* Register's index on rf_regs_idx */ + struct ath5k_rfb_field field; /* RF Buffer field for this register */ }; -/** - * enum ath5k_rf_regs_idx - Map RF registers to indexes - * +/* Map RF registers to indexes * We do this to handle common bits and make our * life easier by using an index for each register - * instead of a full rfb_field - */ + * instead of a full rfb_field */ enum ath5k_rf_regs_idx { /* BANK 2 */ AR5K_RF_TURBO = 0, diff --git a/drivers/net/wireless/ath/ath5k/rfgain.h b/drivers/net/wireless/ath/ath5k/rfgain.h index 4d21df0e..ebfae052 100644 --- a/drivers/net/wireless/ath/ath5k/rfgain.h +++ b/drivers/net/wireless/ath/ath5k/rfgain.h @@ -18,17 +18,13 @@ * */ -/** - * struct ath5k_ini_rfgain - RF Gain table - * @rfg_register: RF Gain register address - * @rfg_value: Register value for 5 and 2GHz - * +/* * Mode-specific RF Gain table (64bytes) for RF5111/5112 * (RF5110 only comes with AR5210 and only supports a/turbo a mode so initial * RF Gain values are included in AR5K_AR5210_INI) */ struct ath5k_ini_rfgain { - u16 rfg_register; + u16 rfg_register; /* RF Gain register address */ u32 rfg_value[2]; /* [freq (see below)] */ }; @@ -459,31 +455,18 @@ static const struct ath5k_ini_rfgain rfgain_2425[] = { #define AR5K_GAIN_CHECK_ADJUST(_g) \ ((_g)->g_current <= (_g)->g_low || (_g)->g_current >= (_g)->g_high) -/** - * struct ath5k_gain_opt_step - An RF gain optimization step - * @gos_param: Set of parameters - * @gos_gain: Gain - */ struct ath5k_gain_opt_step { s8 gos_param[AR5K_GAIN_CRN_MAX_FIX_BITS]; s8 gos_gain; }; -/** - * struct ath5k_gain_opt - RF Gain optimization ladder - * @go_default: The default step - * @go_steps_count: How many optimization steps - * @go_step: Array of &struct ath5k_gain_opt_step - */ struct ath5k_gain_opt { u8 go_default; u8 go_steps_count; const struct ath5k_gain_opt_step go_step[AR5K_GAIN_STEP_COUNT]; }; - /* - * RF5111 * Parameters on gos_param: * 1) Tx clip PHY register * 2) PWD 90 RF register @@ -507,7 +490,6 @@ static const struct ath5k_gain_opt rfgain_opt_5111 = { }; /* - * RF5112 * Parameters on gos_param: * 1) Mixgain ovr RF register * 2) PWD 138 RF register diff --git a/drivers/net/wireless/ath/ath5k/trace.h b/drivers/net/wireless/ath/ath5k/trace.h index 00f01581..39f002ed 100644 --- a/drivers/net/wireless/ath/ath5k/trace.h +++ b/drivers/net/wireless/ath/ath5k/trace.h @@ -3,8 +3,7 @@ #include - -#if !defined(CONFIG_ATH5K_TRACER) || defined(__CHECKER__) +#ifndef CONFIG_ATH5K_TRACER #undef TRACE_EVENT #define TRACE_EVENT(name, proto, ...) \ static inline void trace_ ## name(proto) {} @@ -94,7 +93,7 @@ TRACE_EVENT(ath5k_tx_complete, #endif /* __TRACE_ATH5K_H */ -#if defined(CONFIG_ATH5K_TRACER) && !defined(__CHECKER__) +#ifdef CONFIG_ATH5K_TRACER #undef TRACE_INCLUDE_PATH #define TRACE_INCLUDE_PATH ../../drivers/net/wireless/ath/ath5k diff --git a/drivers/net/wireless/ath/ath6kl/Kconfig b/drivers/net/wireless/ath/ath6kl/Kconfig index d755a5e7..c930b5a8 100644 --- a/drivers/net/wireless/ath/ath6kl/Kconfig +++ b/drivers/net/wireless/ath/ath6kl/Kconfig @@ -13,6 +13,12 @@ config ATH6KL_SDIO Please note that AR6002 and AR6001 are not supported by this driver. +config WIFI_MAC_FROM_NV + tristate "Atheros ath6kl softmac" + depends on ATH6KL + ---help--- + Softmac. + config ATH6KL_USB tristate "Atheros ath6kl USB support" depends on ATH6KL @@ -30,3 +36,11 @@ config ATH6KL_DEBUG depends on ATH6KL ---help--- Enables debug support + +config ATH6KL_REGDOMAIN + bool "Atheros ath6kl regdomain support" + depends on ATH6KL + ---help--- + Enabling this makes it possible to change the regdomain in + the firmware. This can be only enabled if regulatory requirements + are taken into account. diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile index 85746c3e..bab4f310 100644 --- a/drivers/net/wireless/ath/ath6kl/Makefile +++ b/drivers/net/wireless/ath/ath6kl/Makefile @@ -22,21 +22,44 @@ # Author(s): ="Atheros" #------------------------------------------------------------------------------ -obj-$(CONFIG_ATH6KL) += ath6kl_core.o -ath6kl_core-y += debug.o -ath6kl_core-y += hif.o -ath6kl_core-y += htc.o -ath6kl_core-y += bmi.o -ath6kl_core-y += cfg80211.o -ath6kl_core-y += init.o -ath6kl_core-y += main.o -ath6kl_core-y += txrx.o -ath6kl_core-y += wmi.o -ath6kl_core-y += core.o -ath6kl_core-$(CONFIG_NL80211_TESTMODE) += testmode.o - -obj-$(CONFIG_ATH6KL_SDIO) += ath6kl_sdio.o +obj-$(CONFIG_ATH6KL_SDIO) := ath6kl_sdio.o +ath6kl_sdio-y += debug.o +ath6kl_sdio-y += debugfs_pri.o +ath6kl_sdio-y += hif.o +ath6kl_sdio-y += htc.o +ath6kl_sdio-y += bmi.o +ath6kl_sdio-y += cfg80211.o +ath6kl_sdio-y += cfg80211_btcoex.o +ath6kl_sdio-y += pm.o +ath6kl_sdio-y += init.o +ath6kl_sdio-y += main.o +ath6kl_sdio-y += txrx.o +ath6kl_sdio-y += wmi.o +ath6kl_sdio-y += wmi_btcoex.o ath6kl_sdio-y += sdio.o +ath6kl_sdio-y += platform.o +ath6kl_sdio-y += softmac.o +ath6kl_sdio-y += regd.o +ath6kl_sdio-y += wmiconfig.o +ath6kl_sdio-y += recovery.o +ath6kl_sdio-$(CONFIG_NL80211_TESTMODE) += testmode.o obj-$(CONFIG_ATH6KL_USB) += ath6kl_usb.o +ath6kl_usb-y += debug.o +ath6kl_usb-y += hif.o +ath6kl_usb-y += htc.o +ath6kl_usb-y += bmi.o +ath6kl_usb-y += cfg80211.o +ath6kl_usb-y += cfg80211_btcoex.o +ath6kl_usb-y += pm.o +ath6kl_usb-y += init.o +ath6kl_usb-y += main.o +ath6kl_usb-y += txrx.o +ath6kl_usb-y += wmi.o +ath6kl_usb-y += wmi_btcoex.o ath6kl_usb-y += usb.o +ath6kl_usb-y += wmiconfig.o +ath6kl_usb-$(CONFIG_NL80211_TESTMODE) += testmode.o + +ccflags-y += -D__CHECK_ENDIAN__ +ccflags-y += -DSS_3RD_INTF diff --git a/drivers/net/wireless/ath/ath6kl/ath.h b/drivers/net/wireless/ath/ath6kl/ath.h new file mode 100644 index 00000000..4596c33a --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/ath.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2008-2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef ATH_H +#define ATH_H + +#include +#include +#include +#include + +/* + * The key cache is used for h/w cipher state and also for + * tracking station state such as the current tx antenna. + * We also setup a mapping table between key cache slot indices + * and station state to short-circuit node lookups on rx. + * Different parts have different size key caches. We handle + * up to ATH_KEYMAX entries (could dynamically allocate state). + */ +#define ATH_KEYMAX 128 /* max key cache size we handle */ + +static const u8 ath_bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +struct ath_ani { + bool caldone; + unsigned int longcal_timer; + unsigned int shortcal_timer; + unsigned int resetcal_timer; + unsigned int checkani_timer; + struct timer_list timer; +}; + +struct ath_cycle_counters { + u32 cycles; + u32 rx_busy; + u32 rx_frame; + u32 tx_frame; +}; + +enum ath_device_state { + ATH_HW_UNAVAILABLE, + ATH_HW_INITIALIZED, +}; + +enum ath_bus_type { + ATH_PCI, + ATH_AHB, + ATH_USB, +}; + +struct reg_dmn_pair_mapping { + u16 regDmnEnum; + u16 reg_5ghz_ctl; + u16 reg_2ghz_ctl; +}; + +struct ath_regulatory { + char alpha2[2]; + u16 country_code; + u16 max_power_level; + u16 current_rd; + int16_t power_limit; + struct reg_dmn_pair_mapping *regpair; +}; + +enum ath_crypt_caps { + ATH_CRYPT_CAP_CIPHER_AESCCM = BIT(0), + ATH_CRYPT_CAP_MIC_COMBINED = BIT(1), +}; + +struct ath_keyval { + u8 kv_type; + u8 kv_pad; + u16 kv_len; + u8 kv_val[16]; /* TK */ + u8 kv_mic[8]; /* Michael MIC key */ + u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware + * supports both MIC keys in the same key cache entry; + * in that case, kv_mic is the RX key) */ +}; + +enum ath_cipher { + ATH_CIPHER_WEP = 0, + ATH_CIPHER_AES_OCB = 1, + ATH_CIPHER_AES_CCM = 2, + ATH_CIPHER_CKIP = 3, + ATH_CIPHER_TKIP = 4, + ATH_CIPHER_CLR = 5, + ATH_CIPHER_MIC = 127 +}; + +/** + * struct ath_ops - Register read/write operations + * + * @read: Register read + * @multi_read: Multiple register read + * @write: Register write + * @enable_write_buffer: Enable multiple register writes + * @write_flush: flush buffered register writes and disable buffering + */ +struct ath_ops { + unsigned int (*read)(void *, u32 reg_offset); + void (*multi_read)(void *, u32 *addr, u32 *val, u16 count); + void (*write)(void *, u32 val, u32 reg_offset); + void (*enable_write_buffer)(void *); + void (*write_flush) (void *); + u32 (*rmw)(void *, u32 reg_offset, u32 set, u32 clr); +}; + +struct ath_common; +struct ath_bus_ops; + +struct ath_common { + void *ah; + void *priv; + struct ieee80211_hw *hw; + int debug_mask; + enum ath_device_state state; + + struct ath_ani ani; + + u16 cachelsz; + u16 curaid; + u8 macaddr[ETH_ALEN]; + u8 curbssid[ETH_ALEN]; + u8 bssidmask[ETH_ALEN]; + + u32 rx_bufsize; + + u32 keymax; + DECLARE_BITMAP(keymap, ATH_KEYMAX); + DECLARE_BITMAP(tkip_keymap, ATH_KEYMAX); + enum ath_crypt_caps crypt_caps; + + unsigned int clockrate; + + spinlock_t cc_lock; + struct ath_cycle_counters cc_ani; + struct ath_cycle_counters cc_survey; + + struct ath_regulatory regulatory; + const struct ath_ops *ops; + const struct ath_bus_ops *bus_ops; + + bool btcoex_enabled; + bool disable_ani; +}; + +struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, + u32 len, + gfp_t gfp_mask); + +void ath_hw_setbssidmask(struct ath_common *common); +void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key); +int ath_key_config(struct ath_common *common, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +bool ath_hw_keyreset(struct ath_common *common, u16 entry); +void ath_hw_cycle_counters_update(struct ath_common *common); +int32_t ath_hw_get_listen_time(struct ath_common *common); + +extern __printf(2, 3) void ath_printk(const char *level, const char *fmt, ...); + +#define _ath_printk(level, common, fmt, ...) \ +do { \ + __always_unused struct ath_common *unused = common; \ + ath_printk(level, fmt, ##__VA_ARGS__); \ +} while (0) + +#define ath_emerg(common, fmt, ...) \ + _ath_printk(KERN_EMERG, common, fmt, ##__VA_ARGS__) +#define ath_alert(common, fmt, ...) \ + _ath_printk(KERN_ALERT, common, fmt, ##__VA_ARGS__) +#define ath_crit(common, fmt, ...) \ + _ath_printk(KERN_CRIT, common, fmt, ##__VA_ARGS__) +#define ath_err(common, fmt, ...) \ + _ath_printk(KERN_ERR, common, fmt, ##__VA_ARGS__) +#define ath_warn(common, fmt, ...) \ + _ath_printk(KERN_WARNING, common, fmt, ##__VA_ARGS__) +#define ath_notice(common, fmt, ...) \ + _ath_printk(KERN_NOTICE, common, fmt, ##__VA_ARGS__) +#define ath_info(common, fmt, ...) \ + _ath_printk(KERN_INFO, common, fmt, ##__VA_ARGS__) + +/** + * enum ath_debug_level - atheros wireless debug level + * + * @ATH_DBG_RESET: reset processing + * @ATH_DBG_QUEUE: hardware queue management + * @ATH_DBG_EEPROM: eeprom processing + * @ATH_DBG_CALIBRATE: periodic calibration + * @ATH_DBG_INTERRUPT: interrupt processing + * @ATH_DBG_REGULATORY: regulatory processing + * @ATH_DBG_ANI: adaptive noise immunitive processing + * @ATH_DBG_XMIT: basic xmit operation + * @ATH_DBG_BEACON: beacon handling + * @ATH_DBG_CONFIG: configuration of the hardware + * @ATH_DBG_FATAL: fatal errors, this is the default, DBG_DEFAULT + * @ATH_DBG_PS: power save processing + * @ATH_DBG_HWTIMER: hardware timer handling + * @ATH_DBG_BTCOEX: bluetooth coexistance + * @ATH_DBG_BSTUCK: stuck beacons + * @ATH_DBG_ANY: enable all debugging + * + * The debug level is used to control the amount and type of debugging output + * we want to see. Each driver has its own method for enabling debugging and + * modifying debug level states -- but this is typically done through a + * module parameter 'debug' along with a respective 'debug' debugfs file + * entry. + */ +enum ATH_DEBUG { + ATH_DBG_RESET = 0x00000001, + ATH_DBG_QUEUE = 0x00000002, + ATH_DBG_EEPROM = 0x00000004, + ATH_DBG_CALIBRATE = 0x00000008, + ATH_DBG_INTERRUPT = 0x00000010, + ATH_DBG_REGULATORY = 0x00000020, + ATH_DBG_ANI = 0x00000040, + ATH_DBG_XMIT = 0x00000080, + ATH_DBG_BEACON = 0x00000100, + ATH_DBG_CONFIG = 0x00000200, + ATH_DBG_FATAL = 0x00000400, + ATH_DBG_PS = 0x00000800, + ATH_DBG_HWTIMER = 0x00001000, + ATH_DBG_BTCOEX = 0x00002000, + ATH_DBG_WMI = 0x00004000, + ATH_DBG_BSTUCK = 0x00008000, + ATH_DBG_MCI = 0x00010000, + ATH_DBG_ANY = 0xffffffff +}; + +#define ATH_DBG_DEFAULT (ATH_DBG_FATAL) + +#ifdef CONFIG_ATH_DEBUG + +#define ath_dbg(common, dbg_mask, fmt, ...) \ +do { \ + if ((common)->debug_mask & dbg_mask) \ + _ath_printk(KERN_DEBUG, common, fmt, ##__VA_ARGS__); \ +} while (0) + +#define ATH_DBG_WARN(foo, arg...) WARN(foo, arg) +#define ATH_DBG_WARN_ON_ONCE(foo) WARN_ON_ONCE(foo) + +#else + +static inline __attribute__ ((format (printf, 3, 4))) +void ath_dbg(struct ath_common *common, enum ATH_DEBUG dbg_mask, + const char *fmt, ...) +{ +} +#define ATH_DBG_WARN(foo, arg...) do {} while (0) +#define ATH_DBG_WARN_ON_ONCE(foo) ({ \ + int __ret_warn_once = !!(foo); \ + unlikely(__ret_warn_once); \ +}) + +#endif /* CONFIG_ATH_DEBUG */ + +/** Returns string describing opmode, or NULL if unknown mode. */ +#ifdef CONFIG_ATH_DEBUG +const char *ath_opmode_to_string(enum nl80211_iftype opmode); +#else +static inline const char *ath_opmode_to_string(enum nl80211_iftype opmode) +{ + return "UNKNOWN"; +} +#endif + +#endif /* ATH_H */ diff --git a/drivers/net/wireless/ath/ath6kl/bmi.c b/drivers/net/wireless/ath/ath6kl/bmi.c index 334dbd83..05d1d894 100644 --- a/drivers/net/wireless/ath/ath6kl/bmi.c +++ b/drivers/net/wireless/ath/ath6kl/bmi.c @@ -106,7 +106,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar, } ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n", - targ_info->version, targ_info->type); + targ_info->version, targ_info->type); return 0; } @@ -193,7 +193,7 @@ int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header); ath6kl_dbg(ATH6KL_DBG_BMI, - "bmi write memory: addr: 0x%x, len: %d\n", addr, len); + "bmi write memory: addr: 0x%x, len: %d\n", addr, len); len_remain = len; while (len_remain) { @@ -435,7 +435,7 @@ int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len) memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); offset += sizeof(tx_len); memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain], - tx_len); + tx_len); offset += tx_len; ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); diff --git a/drivers/net/wireless/ath/ath6kl/bmi.h b/drivers/net/wireless/ath/ath6kl/bmi.h index 18fdd69e..114f7b26 100644 --- a/drivers/net/wireless/ath/ath6kl/bmi.h +++ b/drivers/net/wireless/ath/ath6kl/bmi.h @@ -223,29 +223,6 @@ struct ath6kl_bmi_target_info { __le32 type; /* target type */ } __packed; -#define ath6kl_bmi_write_hi32(ar, item, val) \ - ({ \ - u32 addr; \ - __le32 v; \ - \ - addr = ath6kl_get_hi_item_addr(ar, HI_ITEM(item)); \ - v = cpu_to_le32(val); \ - ath6kl_bmi_write(ar, addr, (u8 *) &v, sizeof(v)); \ - }) - -#define ath6kl_bmi_read_hi32(ar, item, val) \ - ({ \ - u32 addr, *check_type = val; \ - __le32 tmp; \ - int ret; \ - \ - (void) (check_type == val); \ - addr = ath6kl_get_hi_item_addr(ar, HI_ITEM(item)); \ - ret = ath6kl_bmi_read(ar, addr, (u8 *) &tmp, 4); \ - *val = le32_to_cpu(tmp); \ - ret; \ - }) - int ath6kl_bmi_init(struct ath6kl *ar); void ath6kl_bmi_cleanup(struct ath6kl *ar); void ath6kl_bmi_reset(struct ath6kl *ar); diff --git a/drivers/net/wireless/ath/ath6kl/btc.h b/drivers/net/wireless/ath/ath6kl/btc.h new file mode 100644 index 00000000..71075a8e --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/btc.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef BTC_H +#define BTC_H +#include + +#define EVENT_ID_LEN 2 + +void cfg80211_send_genevent_to_app(struct net_device *dev, + u16 event_id, + u8 *datap, int len) +{ + char *buf; + u16 size; + union iwreq_data wrqu; + + size = len + EVENT_ID_LEN; + + if (size > IW_GENERIC_IE_MAX) + return; + + buf = kmalloc(size, GFP_ATOMIC); + if (buf == NULL) + return; + + memset(buf, 0, size); + memcpy(buf, &event_id, EVENT_ID_LEN); + memcpy(buf + EVENT_ID_LEN, datap, len); + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = size; + wireless_send_event(dev, IWEVGENIE, &wrqu, buf); + kfree(buf); +} + +void cfg80211_send_event_to_app(struct net_device *dev, + u16 event_id, + u8 *datap, int len) +{ + char *buf; + u16 size; + union iwreq_data wrqu; + + size = len + EVENT_ID_LEN; + + if (size > IW_CUSTOM_MAX) + return; + + buf = kmalloc(size, GFP_ATOMIC); + if (buf == NULL) + return; + + memset(buf, 0, size); + memcpy(buf, &event_id, EVENT_ID_LEN); + memcpy(buf + EVENT_ID_LEN, datap, len); + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = size; + wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); + kfree(buf); +} +#endif diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 00d38952..aaa62fb2 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -17,13 +17,18 @@ #include #include -#include #include "core.h" #include "cfg80211.h" #include "debug.h" #include "hif-ops.h" #include "testmode.h" +#include "cfg80211_btcoex.h" +#include "pm.h" + +static unsigned int ath6kl_p2p = 1; + +module_param(ath6kl_p2p, uint, 0644); #define RATETAB_ENT(_rate, _rateid, _flags) { \ .bitrate = (_rate), \ @@ -49,6 +54,13 @@ .max_power = 30, \ } +#define DEFAULT_BG_SCAN_PERIOD 60 + +struct ath6kl_cfg80211_match_probe_ssid { + struct cfg80211_ssid ssid; + u8 flag; +}; + static struct ieee80211_rate ath6kl_rates[] = { RATETAB_ENT(10, 0x1, 0), RATETAB_ENT(20, 0x2, 0), @@ -69,7 +81,8 @@ static struct ieee80211_rate ath6kl_rates[] = { #define ath6kl_g_rates (ath6kl_rates + 0) #define ath6kl_g_rates_size 12 -#define ath6kl_g_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \ +#define ath6kl_g_htcap IEEE80211_HT_CAP_SGI_20 +#define ath6kl_a_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \ IEEE80211_HT_CAP_SGI_20 | \ IEEE80211_HT_CAP_SGI_40) @@ -126,26 +139,28 @@ static struct ieee80211_supported_band ath6kl_band_5ghz = { .channels = ath6kl_5ghz_a_channels, .n_bitrates = ath6kl_a_rates_size, .bitrates = ath6kl_a_rates, - .ht_cap.cap = ath6kl_g_htcap, + .ht_cap.cap = ath6kl_a_htcap, .ht_cap.ht_supported = true, }; #define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */ +#define SSCAN_WAKELOCK_IN_SECOND 35 + /* returns true if scheduled scan was stopped */ static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif) { struct ath6kl *ar = vif->ar; - if (ar->state != ATH6KL_STATE_SCHED_SCAN) + if (!test_and_clear_bit(SCHED_SCANNING, &vif->flags)) return false; del_timer_sync(&vif->sched_scan_timer); - ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_HOST_MODE_AWAKE); + if (ar->state == ATH6KL_STATE_RECOVERY) + return true; - ar->state = ATH6KL_STATE_ON; + ath6kl_wmi_enable_sched_scan_cmd(ar->wmi, vif->fw_vif_idx, false); return true; } @@ -203,7 +218,7 @@ static int ath6kl_set_auth_type(struct ath6kl_vif *vif, break; default: - ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type); + ath6kl_err("%s: 0x%x not spported\n", __func__, auth_type); return -ENOTSUPP; } @@ -272,7 +287,7 @@ static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt) } } -static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif) +bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif) { struct ath6kl *ar = vif->ar; @@ -390,7 +405,7 @@ static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type, return false; if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) && - ar->num_vif)) + ar->num_vif)) return false; if (type == NL80211_IFTYPE_STATION || @@ -416,12 +431,6 @@ static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type, return false; } -static bool ath6kl_is_tx_pending(struct ath6kl *ar) -{ - return ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0; -} - - static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { @@ -466,8 +475,8 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, * sleep until the command queue drains */ wait_event_interruptible_timeout(ar->event_wq, - ath6kl_is_tx_pending(ar), - WMI_TIMEOUT); + ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0, + WMI_TIMEOUT); if (signal_pending(current)) { ath6kl_err("cmd queue drain timeout\n"); up(&ar->sem); @@ -562,7 +571,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, if (!ar->usr_bss_filter) { clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, - ALL_BSS_FILTER, 0) != 0) { + ALL_BSS_FILTER, 0) != 0) { ath6kl_err("couldn't set bss filtering\n"); up(&ar->sem); return -EIO; @@ -571,6 +580,9 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, vif->nw_type = vif->next_mode; + /* enable enhanced bmiss detection if applicable */ + ath6kl_cfg80211_sta_bmiss_enhance(vif, true); + if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) nw_subtype = SUBTYPE_P2PCLIENT; @@ -586,8 +598,8 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, vif->reconnect_flag = 0; if (vif->nw_type == INFRA_NETWORK) { - interval = max_t(u16, vif->listen_intvl_t, - ATH6KL_MAX_WOW_LISTEN_INTL); + interval = max(vif->listen_intvl_t, + (u16) ATH6KL_MAX_WOW_LISTEN_INTL); status = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, interval, 0); @@ -607,6 +619,17 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, vif->req_bssid, vif->ch_hint, ar->connect_ctrl_flags, nw_subtype); + /* disable background scan if period is 0 */ + if (sme->bg_scan_period == 0) + sme->bg_scan_period = 0xffff; + + /* configure default value if not specified */ + if (sme->bg_scan_period == -1) + sme->bg_scan_period = DEFAULT_BG_SCAN_PERIOD; + + ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0, + sme->bg_scan_period, 0, 0, 0, 3, 0, 0, 0); + up(&ar->sem); if (status == -EINVAL) { @@ -620,8 +643,8 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, } if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) && - ((vif->auth_mode == WPA_PSK_AUTH) || - (vif->auth_mode == WPA2_PSK_AUTH))) { + ((vif->auth_mode == WPA_PSK_AUTH) + || (vif->auth_mode == WPA2_PSK_AUTH))) { mod_timer(&vif->disconnect_timer, jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL)); } @@ -836,34 +859,110 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, } } + clear_bit(CONNECT_PEND, &vif->flags); + + if (vif->sme_state == SME_CONNECTING) { + cfg80211_connect_result(vif->ndev, + bssid, NULL, 0, + NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + } else if (vif->sme_state == SME_CONNECTED) { + cfg80211_disconnected(vif->ndev, proto_reason, + NULL, 0, GFP_KERNEL); + } + + vif->sme_state = SME_DISCONNECTED; + /* * Send a disconnect command to target when a disconnect event is * received with reason code other than 3 (DISCONNECT_CMD - disconnect * request from host) to make the firmware stop trying to connect even * after giving disconnect event. There will be one more disconnect * event for this disconnect command with reason code DISCONNECT_CMD - * which will be notified to cfg80211. + * which won't be notified to cfg80211. */ - - if (reason != DISCONNECT_CMD) { + if (reason != DISCONNECT_CMD) ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx); - return; +} + +static int ath6kl_set_probed_ssids(struct ath6kl *ar, + struct ath6kl_vif *vif, + struct cfg80211_ssid *ssids, int n_ssids, + struct cfg80211_match_set *match_set, + int n_match_ssid) +{ + u8 i, j, index_to_add, ssid_found = false; + struct ath6kl_cfg80211_match_probe_ssid ssid_list[MAX_PROBED_SSIDS]; + + memset(ssid_list, 0, sizeof(ssid_list)); + + if (n_ssids > MAX_PROBED_SSIDS || + n_match_ssid > MAX_PROBED_SSIDS) + return -EINVAL; + + for (i = 0; i < n_ssids; i++) { + memcpy(ssid_list[i].ssid.ssid, + ssids[i].ssid, + ssids[i].ssid_len); + ssid_list[i].ssid.ssid_len = ssids[i].ssid_len; + + if (ssids[i].ssid_len) + ssid_list[i].flag = SPECIFIC_SSID_FLAG; + else + ssid_list[i].flag = ANY_SSID_FLAG; + + if (n_match_ssid == 0) + ssid_list[i].flag |= MATCH_SSID_FLAG; } - clear_bit(CONNECT_PEND, &vif->flags); + index_to_add = i; - if (vif->sme_state == SME_CONNECTING) { - cfg80211_connect_result(vif->ndev, - bssid, NULL, 0, - NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); - } else if (vif->sme_state == SME_CONNECTED) { - cfg80211_disconnected(vif->ndev, reason, - NULL, 0, GFP_KERNEL); + for (i = 0; i < n_match_ssid; i++) { + ssid_found = false; + + for (j = 0; j < n_ssids; j++) { + if ((match_set[i].ssid.ssid_len == + ssid_list[j].ssid.ssid_len) && + (!memcmp(ssid_list[j].ssid.ssid, + match_set[i].ssid.ssid, + match_set[i].ssid.ssid_len))) { + ssid_list[j].flag |= MATCH_SSID_FLAG; + ssid_found = true; + break; + } + } + + if (ssid_found) + continue; + + if (index_to_add >= MAX_PROBED_SSIDS) + continue; + + ssid_list[index_to_add].ssid.ssid_len = + match_set[i].ssid.ssid_len; + memcpy(ssid_list[index_to_add].ssid.ssid, + match_set[i].ssid.ssid, + match_set[i].ssid.ssid_len); + ssid_list[index_to_add].flag |= MATCH_SSID_FLAG; + index_to_add++; } - vif->sme_state = SME_DISCONNECTED; + for (i = 0; i < index_to_add; i++) { + ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i, + ssid_list[i].flag, + ssid_list[i].ssid.ssid_len, + ssid_list[i].ssid.ssid); + + } + + /* Make sure no old entries are left behind */ + for (i = index_to_add; i < MAX_PROBED_SSIDS; i++) { + ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i, + DISABLE_SSID_FLAG, 0, NULL); + } + + return 0; } static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, @@ -883,28 +982,18 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, if (!ar->usr_bss_filter) { clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); - ret = ath6kl_wmi_bssfilter_cmd( - ar->wmi, vif->fw_vif_idx, - (test_bit(CONNECTED, &vif->flags) ? - ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0); + ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, + ALL_BSS_FILTER, 0); if (ret) { ath6kl_err("couldn't set bss filtering\n"); return ret; } } - if (request->n_ssids && request->ssids[0].ssid_len) { - u8 i; - - if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1)) - request->n_ssids = MAX_PROBED_SSID_INDEX - 1; - - for (i = 0; i < request->n_ssids; i++) - ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, - i + 1, SPECIFIC_SSID_FLAG, - request->ssids[i].ssid_len, - request->ssids[i].ssid); - } + ret = ath6kl_set_probed_ssids(ar, vif, request->ssids, + request->n_ssids, NULL, 0); + if (ret < 0) + return ret; /* this also clears IE in fw if it's not set */ ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, @@ -941,32 +1030,20 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, if (test_bit(CONNECTED, &vif->flags)) force_fg_scan = 1; - if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, - ar->fw_capabilities)) { - /* - * If capable of doing P2P mgmt operations using - * station interface, send additional information like - * supported rates to advertise and xmit rates for - * probe requests - */ - ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx, - WMI_LONG_SCAN, force_fg_scan, - false, 0, - ATH6KL_FG_SCAN_INTERVAL, - n_channels, channels, - request->no_cck, - request->rates); - } else { - ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, - WMI_LONG_SCAN, force_fg_scan, - false, 0, - ATH6KL_FG_SCAN_INTERVAL, - n_channels, channels); + vif->scan_req = request; + + ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx, + WMI_LONG_SCAN, force_fg_scan, + false, 0, + ATH6KL_FG_SCAN_INTERVAL, + n_channels, channels, + request->no_cck, + request->rates); + + if (ret) { + ath6kl_err("failed to start scan: %d\n", ret); + vif->scan_req = NULL; } - if (ret) - ath6kl_err("wmi_startscan_cmd failed\n"); - else - vif->scan_req = request; kfree(channels); @@ -1000,6 +1077,20 @@ void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted) vif->scan_req = NULL; } +void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, + enum wmi_phy_mode mode) +{ + enum nl80211_channel_type type; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "channel switch notify nw_type %d freq %d mode %d\n", + vif->nw_type, freq, mode); + + type = (mode == WMI_11G_HT20) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT; + + cfg80211_ch_switch_notify(vif->ndev, freq, type); +} + static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_index, bool pairwise, const u8 *mac_addr, @@ -1008,7 +1099,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, struct ath6kl *ar = ath6kl_priv(ndev); struct ath6kl_vif *vif = netdev_priv(ndev); struct ath6kl_key *key = NULL; - int seq_len; + int seq_len = 0; u8 key_usage; u8 key_type; @@ -1074,9 +1165,9 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, return -ENOTSUPP; } - if (((vif->auth_mode == WPA_PSK_AUTH) || - (vif->auth_mode == WPA2_PSK_AUTH)) && - (key_usage & GROUP_USAGE)) + if (((vif->auth_mode == WPA_PSK_AUTH) + || (vif->auth_mode == WPA2_PSK_AUTH)) + && (key_usage & GROUP_USAGE)) del_timer(&vif->disconnect_timer); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, @@ -1291,6 +1382,7 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, { struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); struct ath6kl_vif *vif; + u8 ath6kl_dbm; int dbm = MBM_TO_DBM(mbm); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__, @@ -1307,7 +1399,7 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, case NL80211_TX_POWER_AUTOMATIC: return 0; case NL80211_TX_POWER_LIMITED: - ar->tx_pwr = dbm; + ar->tx_pwr = ath6kl_dbm = dbm; break; default: ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n", @@ -1315,7 +1407,7 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, return -EOPNOTSUPP; } - ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, dbm); + ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, ath6kl_dbm); return 0; } @@ -1376,7 +1468,7 @@ static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy, } if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx, - mode.pwr_mode) != 0) { + mode.pwr_mode) != 0) { ath6kl_err("wmi_powermode_cmd failed\n"); return -EIO; } @@ -1413,6 +1505,15 @@ static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy, return ndev; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) +struct net_device *ath6kl_cfg80211_add_p2p0_iface(struct ath6kl *ar) +{ + return ath6kl_cfg80211_add_iface(ar->wiphy, "p2p0", + NL80211_IFTYPE_P2P_CLIENT, + NULL, NULL); +} +#endif + static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy, struct net_device *ndev) { @@ -1425,7 +1526,7 @@ static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy, ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag)); - ath6kl_cfg80211_vif_cleanup(vif); + ath6kl_deinit_if_data(vif); return 0; } @@ -1436,9 +1537,41 @@ static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, struct vif_params *params) { struct ath6kl_vif *vif = netdev_priv(ndev); + int i; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type); + /* + * Don't bring up p2p on an interface which is not initialized + * for p2p operation where fw does not have capability to switch + * dynamically between non-p2p and p2p type interface. + */ + if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, + vif->ar->fw_capabilities) && + (type == NL80211_IFTYPE_P2P_CLIENT || + type == NL80211_IFTYPE_P2P_GO)) { + if (vif->ar->vif_max == 1) { + if (vif->fw_vif_idx != 0) + return -EINVAL; + else + goto set_iface_type; + } + + for (i = vif->ar->max_norm_iface; i < vif->ar->vif_max; i++) { + if (i == vif->fw_vif_idx) + break; + } + + if (i == vif->ar->vif_max) { + ath6kl_err("Invalid interface to bring up P2P\n"); + return -EINVAL; + } + } + + /* need to clean up enhanced bmiss detection fw state */ + ath6kl_cfg80211_sta_bmiss_enhance(vif, false); + +set_iface_type: switch (type) { case NL80211_IFTYPE_STATION: vif->next_mode = INFRA_NETWORK; @@ -1887,8 +2020,10 @@ static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif) static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif) { struct net_device *ndev = vif->ndev; - static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 }; - static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 }; + static const u8 discvr_lmnr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 }; + static const u8 discvr_lmnr_mask[] = { 0xff, 0xff, 0xff, 0xf8 }; + static const u8 discvr_ssdp_pattern[] = { 0xef, 0xff, 0xff, 0xfa }; + static const u8 discvr_ssdp_mask[] = { 0xff, 0xff, 0xff, 0xff }; u8 discvr_offset = 38; u8 mac_mask[ETH_ALEN]; int ret; @@ -1912,11 +2047,18 @@ static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif) (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) { ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, WOW_LIST_ID, - sizeof(discvr_pattern), discvr_offset, - discvr_pattern, discvr_mask); + sizeof(discvr_lmnr_pattern), discvr_offset, + discvr_lmnr_pattern, discvr_lmnr_mask); + if (ret) { + ath6kl_err("failed to add WOW mDNS/LMNR pattern\n"); + return ret; + } + ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, + vif->fw_vif_idx, WOW_LIST_ID, + sizeof(discvr_ssdp_pattern), discvr_offset, + discvr_ssdp_pattern, discvr_ssdp_mask); if (ret) { - ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR " - "pattern\n"); + ath6kl_err("failed to add WOW SSDP pattern\n"); return ret; } } @@ -1924,29 +2066,72 @@ static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif) return 0; } -static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +static int is_hsleep_mode_procsed(struct ath6kl_vif *vif) +{ + return test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); +} + +static bool is_ctrl_ep_empty(struct ath6kl *ar) +{ + return !ar->tx_pending[ar->ctrl_ep]; +} + +static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif) { - struct in_device *in_dev; - struct in_ifaddr *ifa; - struct ath6kl_vif *vif; int ret, left; - u32 filter = 0; - u16 i, bmiss_time; - u8 index = 0; - __be32 ips[MAX_IP_ADDRS]; - vif = ath6kl_vif_first(ar); - if (!vif) - return -EIO; + clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); - if (!ath6kl_cfg80211_ready(vif)) - return -EIO; + ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_HOST_MODE_ASLEEP); + if (ret) + return ret; - if (!test_bit(CONNECTED, &vif->flags)) - return -ENOTCONN; + left = wait_event_interruptible_timeout(ar->event_wq, + is_hsleep_mode_procsed(vif), + WMI_TIMEOUT); + if (left == 0) { + ath6kl_warn("timeout, didn't get host sleep cmd processed event\n"); + ret = -ETIMEDOUT; + } else if (left < 0) { + ath6kl_warn("error while waiting for host sleep cmd processed event %d\n", + left); + ret = left; + } - if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST)) - return -EINVAL; + if (ar->tx_pending[ar->ctrl_ep]) { + left = wait_event_interruptible_timeout(ar->event_wq, + is_ctrl_ep_empty(ar), + WMI_TIMEOUT); + if (left == 0) { + ath6kl_warn("clear wmi ctrl data timeout\n"); + ret = -ETIMEDOUT; + } else if (left < 0) { + ath6kl_warn("clear wmi ctrl data failed: %d\n", left); + ret = left; + } + } + + return ret; +} + +static int ath6kl_wow_suspend_vif(struct ath6kl_vif *vif, + struct cfg80211_wowlan *wow, u32 *filter) +{ + struct ath6kl *ar = vif->ar; + struct in_device *in_dev; + struct in_ifaddr *ifa; + int ret; + u16 i, bmiss_time; + __be32 ips[MAX_IP_ADDRS]; + u8 index = 0; + + if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags)) { + ret = ath6kl_wmi_mcast_filter_cmd(ar->wmi, + vif->fw_vif_idx, false); + if (ret) + return ret; + } /* Clear existing WOW patterns */ for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++) @@ -1959,7 +2144,7 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) * the user. */ if (wow) - ret = ath6kl_wow_usr(ar, vif, wow, &filter); + ret = ath6kl_wow_usr(ar, vif, wow, filter); else if (vif->nw_type == AP_NETWORK) ret = ath6kl_wow_ap(ar, vif); else @@ -1994,12 +2179,10 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) return ret; } - ar->state = ATH6KL_STATE_SUSPENDING; - /* Setup own IP addr for ARP agent. */ in_dev = __in_dev_get_rtnl(vif->ndev); if (!in_dev) - goto skip_arp; + return 0; ifa = in_dev->ifa_list; memset(&ips, 0, sizeof(ips)); @@ -2022,68 +2205,71 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) return ret; } -skip_arp: - ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_WOW_MODE_ENABLE, - filter, - WOW_HOST_REQ_DELAY); - if (ret) - return ret; + return ret; +} - clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); +static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +{ + struct ath6kl_vif *first_vif, *vif; + int ret = 0; + u32 filter = 0; + bool connected = false, sscan = false; - ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_HOST_MODE_ASLEEP); - if (ret) - return ret; + /* enter / leave wow suspend on first vif always */ + first_vif = ath6kl_vif_first(ar); + if (WARN_ON(unlikely(!first_vif)) || + !ath6kl_cfg80211_ready(first_vif)) + return -EIO; - left = wait_event_interruptible_timeout(ar->event_wq, - test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags), - WMI_TIMEOUT); - if (left == 0) { - ath6kl_warn("timeout, didn't get host sleep cmd " - "processed event\n"); - ret = -ETIMEDOUT; - } else if (left < 0) { - ath6kl_warn("error while waiting for host sleep cmd " - "processed event %d\n", left); - ret = left; - } + if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST)) + return -EINVAL; - if (ar->tx_pending[ar->ctrl_ep]) { - left = wait_event_interruptible_timeout(ar->event_wq, - ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT); - if (left == 0) { - ath6kl_warn("clear wmi ctrl data timeout\n"); - ret = -ETIMEDOUT; - } else if (left < 0) { - ath6kl_warn("clear wmi ctrl data failed: %d\n", left); - ret = left; + /* install filters for each connected vif */ + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + if (!test_bit(CONNECTED, &vif->flags) || + !ath6kl_cfg80211_ready(vif)) { + /* if a sscan is going on, do not clear + WOW_FILTER_SSID later */ + if (test_bit(SCHED_SCANNING, &vif->flags)) { + filter |= WOW_FILTER_SSID; + sscan = true; + } + continue; } - } - return ret; -} + connected = true; -static int ath6kl_wow_resume(struct ath6kl *ar) -{ - struct ath6kl_vif *vif; - int ret; + spin_unlock_bh(&ar->list_lock); + ret = ath6kl_wow_suspend_vif(vif, wow, &filter); + spin_lock_bh(&ar->list_lock); + if (ret) + break; + } + spin_unlock_bh(&ar->list_lock); - vif = ath6kl_vif_first(ar); - if (!vif) - return -EIO; + /* to enable pno, goes into wow while sscan */ + if (!connected && !sscan) + return -ENOTCONN; + else if (ret) + return ret; - ar->state = ATH6KL_STATE_RESUMING; + ar->state = ATH6KL_STATE_SUSPENDING; - ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_HOST_MODE_AWAKE); - if (ret) { - ath6kl_warn("Failed to configure host sleep mode for " - "wow resume: %d\n", ret); - ar->state = ATH6KL_STATE_WOW; + ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, first_vif->fw_vif_idx, + ATH6KL_WOW_MODE_ENABLE, + filter, + WOW_HOST_REQ_DELAY); + if (ret) return ret; - } + + return ath6kl_cfg80211_host_sleep(ar, first_vif); +} + +static int ath6kl_wow_resume_vif(struct ath6kl_vif *vif) +{ + struct ath6kl *ar = vif->ar; + int ret; if (vif->nw_type != AP_NETWORK) { ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, @@ -2101,23 +2287,166 @@ static int ath6kl_wow_resume(struct ath6kl *ar) if (ret) return ret; } - - ar->state = ATH6KL_STATE_ON; + if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags)) { + ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, + vif->fw_vif_idx, true); + if (ret) + return ret; + } netif_wake_queue(vif->ndev); return 0; } -int ath6kl_cfg80211_suspend(struct ath6kl *ar, - enum ath6kl_cfg_suspend_mode mode, - struct cfg80211_wowlan *wow) +static int ath6kl_wow_resume(struct ath6kl *ar) { - enum ath6kl_state prev_state; + struct ath6kl_vif *vif, *vif_p; int ret; + bool sscan = false; - switch (mode) { - case ATH6KL_CFG_SUSPEND_WOW: + vif = ath6kl_vif_first(ar); + if (WARN_ON(unlikely(!vif)) || + !ath6kl_cfg80211_ready(vif)) + return -EIO; + +#ifdef CONFIG_HAS_WAKELOCK + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif_p, &ar->vif_list, list) { + if (test_bit(SCHED_SCANNING, &vif_p->flags)) { + sscan = true; + break; + } + } + spin_unlock_bh(&ar->list_lock); + + if (sscan) { + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sched scan %ds(%d jiffies) wake lock\n", + SSCAN_WAKELOCK_IN_SECOND, SSCAN_WAKELOCK_IN_SECOND*HZ); + wake_lock_timeout(&ar->wake_lock, + SSCAN_WAKELOCK_IN_SECOND * HZ); + } else { + wake_lock_timeout(&ar->wake_lock, 5); + } +#endif + + ar->state = ATH6KL_STATE_RESUMING; + + ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_HOST_MODE_AWAKE); + if (ret) { + ath6kl_warn("Failed to configure host sleep mode for " + "wow resume: %d\n", ret); + goto cleanup; + } + + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + if (!test_bit(CONNECTED, &vif->flags) || + !ath6kl_cfg80211_ready(vif)) + continue; + spin_unlock_bh(&ar->list_lock); + ret = ath6kl_wow_resume_vif(vif); + spin_lock_bh(&ar->list_lock); + if (ret) + break; + } + spin_unlock_bh(&ar->list_lock); + + if (ret) + goto cleanup; + + ar->state = ATH6KL_STATE_ON; + return 0; + +cleanup: + ar->state = ATH6KL_STATE_WOW; + return ret; +} + +static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar) +{ + struct ath6kl_vif *vif; + int ret; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + if (!test_bit(WMI_READY, &ar->flag)) { + ath6kl_err("deepsleep failed as wmi is not ready\n"); + return -EIO; + } + + ath6kl_cfg80211_stop_all(ar); + + /* Save the current power mode before enabling power save */ + ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; + + ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER); + if (ret) + return ret; + + /* Disable WOW mode */ + ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_WOW_MODE_DISABLE, + 0, 0); + if (ret) + return ret; + + /* Flush all non control pkts in TX path */ + ath6kl_tx_data_cleanup(ar); + + ret = ath6kl_cfg80211_host_sleep(ar, vif); + if (ret) + return ret; + + return 0; +} + +static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar) +{ + struct ath6kl_vif *vif; + int ret; + + vif = ath6kl_vif_first(ar); + + if (!vif) + return -EIO; + + if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) { + ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, + ar->wmi->saved_pwr_mode); + if (ret) + return ret; + } + + ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_HOST_MODE_AWAKE); + if (ret) + return ret; + + ar->state = ATH6KL_STATE_ON; + + /* Reset scan parameter to default values */ + ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, + 0, 0, 0, 0, 0, 0, 3, 0, 0, 0); + if (ret) + return ret; + + return 0; +} + +int ath6kl_cfg80211_suspend(struct ath6kl *ar, + enum ath6kl_cfg_suspend_mode mode, + struct cfg80211_wowlan *wow) +{ + struct ath6kl_vif *vif; + enum ath6kl_state prev_state; + int ret; + + switch (mode) { + case ATH6KL_CFG_SUSPEND_WOW: ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n"); @@ -2125,7 +2454,6 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, ath6kl_tx_data_cleanup(ar); prev_state = ar->state; - ret = ath6kl_wow_suspend(ar, wow); if (ret) { ar->state = prev_state; @@ -2137,15 +2465,12 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, case ATH6KL_CFG_SUSPEND_DEEPSLEEP: - ath6kl_cfg80211_stop_all(ar); + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n"); - /* save the current power mode before enabling power save */ - ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; - - ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER); + ret = ath6kl_cfg80211_deepsleep_suspend(ar); if (ret) { - ath6kl_warn("wmi powermode command failed during suspend: %d\n", - ret); + ath6kl_err("deepsleep suspend failed: %d\n", ret); + return ret; } ar->state = ATH6KL_STATE_DEEPSLEEP; @@ -2174,20 +2499,15 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, break; - case ATH6KL_CFG_SUSPEND_SCHED_SCAN: - /* - * Nothing needed for schedule scan, firmware is already in - * wow mode and sleeping most of the time. - */ - break; - default: break; } + list_for_each_entry(vif, &ar->vif_list, list) + ath6kl_cfg80211_scan_complete_event(vif, true); + return 0; } -EXPORT_SYMBOL(ath6kl_cfg80211_suspend); int ath6kl_cfg80211_resume(struct ath6kl *ar) { @@ -2206,17 +2526,13 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar) break; case ATH6KL_STATE_DEEPSLEEP: - if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) { - ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, - ar->wmi->saved_pwr_mode); - if (ret) { - ath6kl_warn("wmi powermode command failed during resume: %d\n", - ret); - } - } - - ar->state = ATH6KL_STATE_ON; + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n"); + ret = ath6kl_cfg80211_deepsleep_resume(ar); + if (ret) { + ath6kl_warn("deep sleep resume failed: %d\n", ret); + return ret; + } break; case ATH6KL_STATE_CUTPOWER: @@ -2229,18 +2545,14 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar) } break; - case ATH6KL_STATE_SCHED_SCAN: - break; - default: break; } return 0; } -EXPORT_SYMBOL(ath6kl_cfg80211_resume); -#ifdef CONFIG_PM +#if defined(CONFIG_PM) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) /* hif layer decides what suspend mode to use */ static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy, @@ -2248,14 +2560,23 @@ static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy, { struct ath6kl *ar = wiphy_priv(wiphy); + ath6kl_recovery_suspend(ar); + return ath6kl_hif_suspend(ar, wow); } static int __ath6kl_cfg80211_resume(struct wiphy *wiphy) { struct ath6kl *ar = wiphy_priv(wiphy); + int err; + + err = ath6kl_hif_resume(ar); + if (err) + return err; - return ath6kl_hif_resume(ar); + ath6kl_recovery_resume(ar); + + return 0; } /* @@ -2274,22 +2595,47 @@ static int __ath6kl_cfg80211_resume(struct wiphy *wiphy) * * ath6kl_check_wow_status() is called from ath6kl_rx(). */ -void ath6kl_check_wow_status(struct ath6kl *ar) +void ath6kl_check_wow_status(struct ath6kl *ar, struct sk_buff *skb, + bool is_event_pkt) { if (ar->state == ATH6KL_STATE_SUSPENDING) return; if (ar->state == ATH6KL_STATE_WOW) ath6kl_cfg80211_resume(ar); + else + ath6kl_config_suspend_wake_lock(ar, skb, is_event_pkt); } #else -void ath6kl_check_wow_status(struct ath6kl *ar) +void ath6kl_check_wow_status(struct ath6kl *ar, struct sk_buff *skb, + bool is_event_pkt) { } #endif +static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band, + bool ht_enable) +{ + struct ath6kl_htcap *htcap = &vif->htcap[band]; + + if (htcap->ht_enable == ht_enable) + return 0; + + if (ht_enable) { + /* Set default ht capabilities */ + htcap->ht_enable = true; + htcap->cap_info = (band == IEEE80211_BAND_2GHZ) ? + ath6kl_g_htcap : ath6kl_a_htcap; + htcap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K; + } else /* Disable ht */ + memset(htcap, 0, sizeof(*htcap)); + + return ath6kl_wmi_set_htcap_cmd(vif->ar->wmi, vif->fw_vif_idx, + band, htcap); +} + static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) @@ -2313,6 +2659,8 @@ static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n", __func__, chan->center_freq, chan->hw_value); vif->next_chan = chan->center_freq; + vif->next_ch_type = channel_type; + vif->next_ch_band = chan->band; return 0; } @@ -2360,11 +2708,31 @@ static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif, return ret; } -static int ath6kl_set_ies(struct ath6kl_vif *vif, - struct cfg80211_beacon_data *info) +static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *info, bool add) { - struct ath6kl *ar = vif->ar; + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + struct ieee80211_mgmt *mgmt; + enum wmi_phy_mode phy_mode; + bool hidden = false; + u8 *ies; + int ies_len; + struct wmi_connect_cmd p; int res; + int i, ret; + u8 *rsn_ie; + int rsn_ie_len; + u16 cnt; + u16 rsn_capb; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (vif->next_mode != AP_NETWORK) + return -EOPNOTSUPP; /* this also clears IE in fw if it's not set */ res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, @@ -2388,31 +2756,8 @@ static int ath6kl_set_ies(struct ath6kl_vif *vif, if (res) return res; - return 0; -} - -static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_ap_settings *info) -{ - struct ath6kl *ar = ath6kl_priv(dev); - struct ath6kl_vif *vif = netdev_priv(dev); - struct ieee80211_mgmt *mgmt; - bool hidden = false; - u8 *ies; - int ies_len; - struct wmi_connect_cmd p; - int res; - int i, ret; - - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__); - - if (!ath6kl_cfg80211_ready(vif)) - return -EIO; - - if (vif->next_mode != AP_NETWORK) - return -EOPNOTSUPP; - - res = ath6kl_set_ies(vif, &info->beacon); + if (!add) + return 0; ar->ap_mode_bkey.valid = false; @@ -2421,13 +2766,13 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, * info->dtim_period */ - if (info->beacon.head == NULL) + if (info->head == NULL) return -EINVAL; - mgmt = (struct ieee80211_mgmt *) info->beacon.head; + mgmt = (struct ieee80211_mgmt *) info->head; ies = mgmt->u.beacon.variable; - if (ies > info->beacon.head + info->beacon.head_len) + if (ies > info->head + info->head_len) return -EINVAL; - ies_len = info->beacon.head + info->beacon.head_len - ies; + ies_len = info->head + info->head_len - ies; if (info->ssid == NULL) return -EINVAL; @@ -2515,8 +2860,61 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, p.ssid_len = vif->ssid_len; memcpy(p.ssid, vif->ssid, vif->ssid_len); p.dot11_auth_mode = vif->dot11_auth_mode; + + if (info->auto_channel_select) { + ar->want_ch_switch |= 1 << vif->fw_vif_idx; + vif->next_chan = info->auto_channel_select - 1; + } + p.ch = cpu_to_le16(vif->next_chan); + /* + * Get the PTKSA replay counter in the RSN IE. Supplicant + * will use the RSN IE in M3 message and firmware has to + * advertise the same in beacon/probe response. Send + * the complete RSN IE capability field to firmware + */ + if ((p.auth_mode & (WPA2_AUTH | WPA2_PSK_AUTH)) && (info->tail) && + test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, + ar->fw_capabilities)) { + rsn_capb = 0; + rsn_ie = (u8 *)cfg80211_find_ie(WLAN_EID_RSN, info->tail, + info->tail_len); + if (rsn_ie) { + rsn_ie_len = *(rsn_ie + 1); + rsn_ie += 2; /* skip element id and length */ + do { + /* skip version, group cipher */ + if (rsn_ie_len < 6) + break; + rsn_ie += 6; + rsn_ie_len -= 6; + /* skip pairwise cipher suite */ + if (rsn_ie_len < 2) + break; + cnt = *((u16 *)rsn_ie); + rsn_ie += (2 + cnt * 4); + rsn_ie_len -= (2 + cnt * 4); + /* skip akm suite */ + if (rsn_ie_len < 2) + break; + cnt = *((u16 *)rsn_ie); + rsn_ie += (2 + cnt * 4); + rsn_ie_len -= (2 + cnt * 4); + if (rsn_ie_len < 2) + break; + rsn_capb = *((u16 *)rsn_ie); + } while (0); + rsn_capb = cpu_to_le16(rsn_capb); + res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx, + WLAN_EID_RSN, WMI_RSN_IE_CAPB, + (const u8 *)&rsn_capb, sizeof(rsn_capb)); + vif->rsn_capab = rsn_capb; + if (res < 0) + return res; + } + } + /* Enable uAPSD support by default */ res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true); if (res < 0) @@ -2532,6 +2930,39 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, p.nw_subtype = SUBTYPE_NONE; } + if (ath6kl_set_htcap(vif, vif->next_ch_band, + vif->next_ch_type != NL80211_CHAN_NO_HT)) + return -EIO; + + if (test_bit(ATH6KL_FW_CAPABILITY_MAC_ACL, ar->fw_capabilities)) { + res = ath6kl_wmi_set_acl_policy(ar->wmi, vif->fw_vif_idx, + info->acl_mac); + if (res < 0) + return res; + } + + if (!info->sta_cap_req) + goto ap_commit; + + switch (info->sta_cap_req) { + case NL80211_STA_CAP_REQ_11BONLY: + phy_mode = WMI_11B_MODE; + break; + case NL80211_STA_CAP_REQ_11GONLY: + phy_mode = WMI_11GONLY_MODE; + break; + default: + ath6kl_err("STA capability requirement %d is not supported in AP mode\n", + info->sta_cap_req); + return -ENOTSUPP; + } + + res = ath6kl_wmi_set_ch_params(ar->wmi, vif->fw_vif_idx, phy_mode); + if (res) + return res; + +ap_commit: + memcpy(&vif->profile, &p, sizeof(p)); res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p); if (res < 0) return res; @@ -2539,21 +2970,43 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, return 0; } -static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_beacon_data *beacon) +void ath6kl_cfg80211_sta_bmiss_enhance(struct ath6kl_vif *vif, bool enable) { - struct ath6kl_vif *vif = netdev_priv(dev); + int err; - if (!ath6kl_cfg80211_ready(vif)) - return -EIO; + if (!test_bit(ATH6KL_FW_CAPABILITY_BMISS_ENHANCE, + vif->ar->fw_capabilities)) + return; - if (vif->next_mode != AP_NETWORK) - return -EOPNOTSUPP; + if (WARN_ON(!test_bit(WMI_READY, &vif->ar->flag))) + return; - return ath6kl_set_ies(vif, beacon); + if (vif->nw_type != INFRA_NETWORK) + return; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s fw bmiss enhance\n", + enable ? "enable" : "disable"); + + err = ath6kl_wmi_sta_bmiss_enhance_cmd(vif->ar->wmi, + vif->fw_vif_idx, enable); + if (err) + ath6kl_err("failed to %s enhanced bmiss detection: %d\n", + enable ? "enable" : "disable", err); } -static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev) +static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *info) +{ + return ath6kl_ap_beacon(wiphy, dev, info, true); +} + +static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *info) +{ + return ath6kl_ap_beacon(wiphy, dev, info, false); +} + +static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); @@ -2566,6 +3019,13 @@ static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev) ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx); clear_bit(CONNECTED, &vif->flags); + /* Restore ht setting in firmware */ + if (ath6kl_set_htcap(vif, IEEE80211_BAND_2GHZ, true)) + return -EIO; + + if (ath6kl_set_htcap(vif, IEEE80211_BAND_5GHZ, true)) + return -EIO; + return 0; } @@ -2612,7 +3072,10 @@ static int ath6kl_remain_on_channel(struct wiphy *wiphy, struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); u32 id; - +#ifdef CONFIG_HAS_WAKELOCK + ath6kl_p2p_release_wakelock(ar); + ath6kl_p2p_acquire_wakelock(ar , duration + 100); +#endif /* TODO: if already pending or ongoing remain-on-channel, * return -EBUSY */ id = ++vif->last_roc_id; @@ -2747,6 +3210,21 @@ static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif, return false; } +/* Check if SSID length is greater than DIRECT- */ +static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + mgmt = (const struct ieee80211_mgmt *) buf; + + /* variable[1] contains the SSID tag length */ + if (buf + len >= &mgmt->u.probe_resp.variable[1] && + (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) { + return true; + } + + return false; +} + static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, @@ -2761,11 +3239,11 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, bool more_data, queued; mgmt = (const struct ieee80211_mgmt *) buf; - if (buf + len >= mgmt->u.probe_resp.variable && - vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) && - ieee80211_is_probe_resp(mgmt->frame_control)) { + if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) && + ieee80211_is_probe_resp(mgmt->frame_control) && + ath6kl_is_p2p_go_ssid(buf, len)) { /* - * Send Probe Response frame in AP mode using a separate WMI + * Send Probe Response frame in GO mode using a separate WMI * command to allow the target to fill in the generic IEs. */ *cookie = 0; /* TX status not supported */ @@ -2824,8 +3302,7 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); u16 interval; - int ret; - u8 i; + int ret, rssi_thold; if (ar->state != ATH6KL_STATE_ON) return -EIO; @@ -2833,10 +3310,42 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, if (vif->sme_state != SME_DISCONNECTED) return -EBUSY; - for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) { - ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, - i, DISABLE_SSID_FLAG, - 0, NULL); + ath6kl_cfg80211_scan_complete_event(vif, true); + + ret = ath6kl_set_probed_ssids(ar, vif, request->ssids, + request->n_ssids, + request->match_sets, + request->n_match_sets); + if (ret < 0) + return ret; + + if (!request->n_match_sets) { + ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, + ALL_BSS_FILTER, 0); + if (ret < 0) + return ret; + } else { + ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, + MATCHED_SSID_FILTER, 0); + if (ret < 0) + return ret; + } + + if (test_bit(ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD, + ar->fw_capabilities)) { + if (request->rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF) + rssi_thold = 0; + else if (request->rssi_thold < -127) + rssi_thold = -127; + else + rssi_thold = request->rssi_thold; + + ret = ath6kl_wmi_set_rssi_filter_cmd(ar->wmi, vif->fw_vif_idx, + rssi_thold); + if (ret) { + ath6kl_err("failed to set RSSI threshold for scan\n"); + return ret; + } } /* fw uses seconds, also make sure that it's >0 */ @@ -2844,25 +3353,7 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, interval, interval, - 10, 0, 0, 0, 3, 0, 0, 0); - - if (request->n_ssids && request->ssids[0].ssid_len) { - for (i = 0; i < request->n_ssids; i++) { - ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, - i, SPECIFIC_SSID_FLAG, - request->ssids[i].ssid_len, - request->ssids[i].ssid); - } - } - - ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_WOW_MODE_ENABLE, - WOW_FILTER_SSID, - WOW_HOST_REQ_DELAY); - if (ret) { - ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret); - return ret; - } + vif->bg_scan_period, 0, 0, 0, 3, 0, 0, 0); /* this also clears IE in fw if it's not set */ ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, @@ -2874,17 +3365,13 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, return ret; } - ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_HOST_MODE_ASLEEP); - if (ret) { - ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n", - ret); + ret = ath6kl_wmi_enable_sched_scan_cmd(ar->wmi, vif->fw_vif_idx, true); + if (ret) return ret; - } - ar->state = ATH6KL_STATE_SCHED_SCAN; + set_bit(SCHED_SCANNING, &vif->flags); - return ret; + return 0; } static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy, @@ -2901,6 +3388,77 @@ static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy, return 0; } +static int ath6kl_cfg80211_set_bitrate(struct wiphy *wiphy, + struct net_device *dev, + const u8 *addr, + const struct cfg80211_bitrate_mask *mask) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + + return ath6kl_wmi_set_bitrate_mask(ar->wmi, vif->fw_vif_idx, + mask); +} + +static int ath6kl_set_mac_acl(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_acl_params *acl_info) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + int i, err; + static const u8 zero_mac[ETH_ALEN] = { 0 }; + + /* Reset the acl list */ + err = ath6kl_wmi_set_acl_list(ar->wmi, vif->fw_vif_idx, 0, zero_mac, + acl_info->acl_policy, true); + if (err) + return err; + + for (i = 0; i < acl_info->n_acl_entries; i++) { + err = ath6kl_wmi_set_acl_list(ar->wmi, vif->fw_vif_idx, i, + acl_info->mac_addrs[i].addr, + acl_info->acl_policy, false); + if (err) + return err; + } + + /* + * Notify fw of the state that the host is done with setting + * the acl list. This is done with a special configuration + * where the index of the mac address is passed as 0xff and + * the mac address is zero mac. Firmware would carry out + * certain operations based on the newly set acl list after + * this notification. + */ + err = ath6kl_wmi_set_acl_list(ar->wmi, vif->fw_vif_idx, + MAC_ACL_INDEX_EOL, zero_mac, + acl_info->acl_policy, false); + + return err; +} + +static int ath6kl_cfg80211_set_txe_config(struct wiphy *wiphy, + struct net_device *dev, + u32 rate, u32 pkts, u32 intvl) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + + if (vif->nw_type != INFRA_NETWORK || + !test_bit(ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, ar->fw_capabilities)) + return -EOPNOTSUPP; + + if (vif->sme_state != SME_CONNECTED) + return -ENOTCONN; + + /* save this since the firmware won't report the interval */ + vif->txe_intvl = intvl; + + return ath6kl_wmi_set_txe_notify(ar->wmi, vif->fw_vif_idx, + rate, pkts, intvl); +} + static const struct ieee80211_txrx_stypes ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_STATION] = { @@ -2951,22 +3509,26 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .del_pmksa = ath6kl_del_pmksa, .flush_pmksa = ath6kl_flush_pmksa, CFG80211_TESTMODE_CMD(ath6kl_tm_cmd) -#ifdef CONFIG_PM +#if defined(CONFIG_PM) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) .suspend = __ath6kl_cfg80211_suspend, .resume = __ath6kl_cfg80211_resume, #endif .set_channel = ath6kl_set_channel, - .start_ap = ath6kl_start_ap, - .change_beacon = ath6kl_change_beacon, - .stop_ap = ath6kl_stop_ap, + .add_beacon = ath6kl_add_beacon, + .set_beacon = ath6kl_set_beacon, + .del_beacon = ath6kl_del_beacon, .del_station = ath6kl_del_station, .change_station = ath6kl_change_station, .remain_on_channel = ath6kl_remain_on_channel, .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel, .mgmt_tx = ath6kl_mgmt_tx, .mgmt_frame_register = ath6kl_mgmt_frame_register, + .notify_btcoex = ath6kl_notify_btcoex, .sched_scan_start = ath6kl_cfg80211_sscan_start, .sched_scan_stop = ath6kl_cfg80211_sscan_stop, + .set_bitrate_mask = ath6kl_cfg80211_set_bitrate, + .set_mac_acl = ath6kl_set_mac_acl, + .set_cqm_txe_config = ath6kl_cfg80211_set_txe_config, }; void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) @@ -2987,16 +3549,22 @@ void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) break; } - if (test_bit(CONNECTED, &vif->flags) || - test_bit(CONNECT_PEND, &vif->flags)) + if (vif->ar->state != ATH6KL_STATE_RECOVERY && + (test_bit(CONNECTED, &vif->flags) || + test_bit(CONNECT_PEND, &vif->flags))) ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx); vif->sme_state = SME_DISCONNECTED; clear_bit(CONNECTED, &vif->flags); clear_bit(CONNECT_PEND, &vif->flags); + /* Stop netdev queues, needed during recovery */ + netif_stop_queue(vif->ndev); + netif_carrier_off(vif->ndev); + /* disable scanning */ - if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF, + if (vif->ar->state != ATH6KL_STATE_RECOVERY && + ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0) ath6kl_warn("failed to disable scan during stop\n"); @@ -3008,7 +3576,7 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar) struct ath6kl_vif *vif; vif = ath6kl_vif_first(ar); - if (!vif) { + if (!vif && ar->state != ATH6KL_STATE_RECOVERY) { /* save the current power mode before enabling power save */ ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; @@ -3027,7 +3595,200 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar) ath6kl_cfg80211_stop(vif); } -static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif) +#ifdef CONFIG_ATH6KL_REGDOMAIN +static int ath6kl_cfg80211_reg_notify(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ath6kl *ar = wiphy_priv(wiphy); + u32 rates[IEEE80211_NUM_BANDS]; + int ret, i; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "cfg reg_notify %c%c%s%s initiator %d\n", + request->alpha2[0], request->alpha2[1], + request->intersect ? " intersect" : "", + request->processed ? " processed" : "", + request->initiator); + + ret = ath6kl_wmi_set_regdomain_cmd(ar->wmi, request->alpha2); + if (ret) { + ath6kl_err("failed to set regdomain: %d\n", ret); + return ret; + } + + /* + * Firmware will apply the regdomain change only after a scan is + * issued and it will send a WMI_REGDOMAIN_EVENTID when it has been + * changed. + */ + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + if (wiphy->bands[i]) + rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; + + + ret = ath6kl_wmi_beginscan_cmd(ar->wmi, 0, WMI_LONG_SCAN, false, + false, 0, ATH6KL_FG_SCAN_INTERVAL, + 0, NULL, false, rates); + if (ret) { + ath6kl_err("failed to start scan for a regdomain change: %d\n", + ret); + return ret; + } + + return 0; +} +#endif + +struct ath6kl *ath6kl_core_alloc(struct device *dev) +{ + struct ath6kl *ar; + struct wiphy *wiphy; + u8 ctr; + + /* create a new wiphy for use with cfg80211 */ + wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl)); + + if (!wiphy) { + ath6kl_err("couldn't allocate wiphy device\n"); + return NULL; + } + + ar = wiphy_priv(wiphy); + ar->p2p = !!ath6kl_p2p; + ar->wiphy = wiphy; + ar->dev = dev; + + ar->vif_max = 1; + + ar->max_norm_iface = 1; + + spin_lock_init(&ar->lock); + spin_lock_init(&ar->mcastpsq_lock); + spin_lock_init(&ar->list_lock); + + init_waitqueue_head(&ar->event_wq); + sema_init(&ar->sem, 1); + + INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue); + INIT_LIST_HEAD(&ar->vif_list); + + clear_bit(WMI_ENABLED, &ar->flag); + clear_bit(SKIP_SCAN, &ar->flag); + clear_bit(DESTROY_IN_PROGRESS, &ar->flag); + + ar->tx_pwr = 0; + ar->intra_bss = 1; + ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD; + + ar->state = ATH6KL_STATE_OFF; + + memset((u8 *)ar->sta_list, 0, + AP_MAX_NUM_STA * sizeof(struct ath6kl_sta)); + + /* Init the PS queues */ + for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) { + spin_lock_init(&ar->sta_list[ctr].psq_lock); + skb_queue_head_init(&ar->sta_list[ctr].psq); + skb_queue_head_init(&ar->sta_list[ctr].apsdq); + ar->sta_list[ctr].mgmt_psq_len = 0; + INIT_LIST_HEAD(&ar->sta_list[ctr].mgmt_psq); + ar->sta_list[ctr].aggr_conn = + kzalloc(sizeof(struct aggr_info_conn), GFP_KERNEL); + if (!ar->sta_list[ctr].aggr_conn) { + ath6kl_err("Failed to allocate memory for sta aggregation information\n"); + ath6kl_core_cleanup(ar); + return NULL; + } + } + + skb_queue_head_init(&ar->mcastpsq); + + memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3); + + return ar; +} + +int ath6kl_register_ieee80211_hw(struct ath6kl *ar) +{ + struct wiphy *wiphy = ar->wiphy; + int ret; + + wiphy->mgmt_stypes = ath6kl_mgmt_stypes; + + wiphy->max_remain_on_channel_duration = 5000; + + /* set device pointer for wiphy */ + set_wiphy_dev(wiphy, ar->dev); + + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP); + if (ar->p2p) { + wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT); + } + +#ifdef CONFIG_ATH6KL_REGDOMAIN + /* FIXME: add firmware capability */ + wiphy->reg_notifier = ath6kl_cfg80211_reg_notify; +#endif + + /* max num of ssids that can be probed during scanning */ + wiphy->max_scan_ssids = MAX_PROBED_SSIDS; + + /* max num of ssids that can be matched after scan */ + if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST, + ar->fw_capabilities)) + wiphy->max_match_sets = MAX_PROBED_SSIDS; + + wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */ + if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) { + ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff; + ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; + ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff; + ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff; + } else { + ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff; + ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; + } + + wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz; + wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz; + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + wiphy->cipher_suites = cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + + wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_GTK_REKEY_FAILURE | + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_4WAY_HANDSHAKE; + wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST; + wiphy->wowlan.pattern_min_len = 1; + wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE; + + wiphy->max_sched_scan_ssids = MAX_PROBED_SSIDS; + + ath6kl_setup_android_resource(ar); + + if (test_bit(ATH6KL_FW_CAPABILITY_MAC_ACL, ar->fw_capabilities)) { + ar->wiphy->features |= NL80211_FEATURE_MAC_ACL; + ar->wiphy->max_acl_mac_addrs = MAX_ACL_MAC_ADDRS; + } + + ret = wiphy_register(wiphy); + if (ret < 0) { + ath6kl_err("couldn't register wiphy device\n"); + return ret; + } + + return 0; +} + +static int ath6kl_init_if_data(struct ath6kl_vif *vif) { vif->aggr_cntxt = aggr_init(vif); if (!vif->aggr_cntxt) { @@ -3048,7 +3809,7 @@ static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif) return 0; } -void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif) +void ath6kl_deinit_if_data(struct ath6kl_vif *vif) { struct ath6kl *ar = vif->ar; struct ath6kl_mc_filter *mc_filter, *tmp; @@ -3090,21 +3851,29 @@ struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, vif->wdev.netdev = ndev; vif->wdev.iftype = type; vif->fw_vif_idx = fw_vif_idx; - vif->nw_type = nw_type; - vif->next_mode = nw_type; + vif->nw_type = vif->next_mode = nw_type; + vif->scan_ctrl_flag = 0; vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL; vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME; + vif->bg_scan_period = 0; + vif->htcap[IEEE80211_BAND_2GHZ].ht_enable = true; + vif->htcap[IEEE80211_BAND_5GHZ].ht_enable = true; memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN); - if (fw_vif_idx != 0) + if (fw_vif_idx != 0) { ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) | 0x2; + if (test_bit(ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR, + ar->fw_capabilities)) + ndev->dev_addr[4] ^= 0x80; + } init_netdev(ndev); ath6kl_init_control_info(vif); - if (ath6kl_cfg80211_vif_init(vif)) + /* TODO: Pass interface specific pointer instead of ar */ + if (ath6kl_init_if_data(vif)) goto err; if (register_netdevice(ndev)) @@ -3131,107 +3900,15 @@ struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, return NULL; } -int ath6kl_cfg80211_init(struct ath6kl *ar) -{ - struct wiphy *wiphy = ar->wiphy; - int ret; - - wiphy->mgmt_stypes = ath6kl_mgmt_stypes; - - wiphy->max_remain_on_channel_duration = 5000; - - /* set device pointer for wiphy */ - set_wiphy_dev(wiphy, ar->dev); - - wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP); - if (ar->p2p) { - wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_CLIENT); - } - - /* max num of ssids that can be probed during scanning */ - wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX; - wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */ - wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz; - wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz; - wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; - - wiphy->cipher_suites = cipher_suites; - wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); - - wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | - WIPHY_WOWLAN_DISCONNECT | - WIPHY_WOWLAN_GTK_REKEY_FAILURE | - WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | - WIPHY_WOWLAN_EAP_IDENTITY_REQ | - WIPHY_WOWLAN_4WAY_HANDSHAKE; - wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST; - wiphy->wowlan.pattern_min_len = 1; - wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE; - - wiphy->max_sched_scan_ssids = 10; - - ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM | - WIPHY_FLAG_HAVE_AP_SME | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; - - if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities)) - ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; - - ar->wiphy->probe_resp_offload = - NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | - NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | - NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P | - NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U; - - ret = wiphy_register(wiphy); - if (ret < 0) { - ath6kl_err("couldn't register wiphy device\n"); - return ret; - } - - ar->wiphy_registered = true; - - return 0; -} - -void ath6kl_cfg80211_cleanup(struct ath6kl *ar) -{ - wiphy_unregister(ar->wiphy); - - ar->wiphy_registered = false; -} - -struct ath6kl *ath6kl_cfg80211_create(void) -{ - struct ath6kl *ar; - struct wiphy *wiphy; - - /* create a new wiphy for use with cfg80211 */ - wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl)); - - if (!wiphy) { - ath6kl_err("couldn't allocate wiphy device\n"); - return NULL; - } - - ar = wiphy_priv(wiphy); - ar->wiphy = wiphy; - - return ar; -} - -/* Note: ar variable must not be accessed after calling this! */ -void ath6kl_cfg80211_destroy(struct ath6kl *ar) +void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar) { int i; + ath6kl_cleanup_android_resource(ar); + for (i = 0; i < AP_MAX_NUM_STA; i++) kfree(ar->sta_list[i].aggr_conn); + wiphy_unregister(ar->wiphy); wiphy_free(ar->wiphy); } - diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h index c5def436..dce4756d 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.h +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h @@ -22,12 +22,16 @@ enum ath6kl_cfg_suspend_mode { ATH6KL_CFG_SUSPEND_DEEPSLEEP, ATH6KL_CFG_SUSPEND_CUTPOWER, ATH6KL_CFG_SUSPEND_WOW, - ATH6KL_CFG_SUSPEND_SCHED_SCAN, }; struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, enum nl80211_iftype type, u8 fw_vif_idx, u8 nw_type); +int ath6kl_register_ieee80211_hw(struct ath6kl *ar); +struct ath6kl *ath6kl_core_alloc(struct device *dev); +void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar); +void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, + enum wmi_phy_mode mode); void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted); void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, @@ -50,15 +54,15 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, int ath6kl_cfg80211_resume(struct ath6kl *ar); -void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif); - void ath6kl_cfg80211_stop(struct ath6kl_vif *vif); void ath6kl_cfg80211_stop_all(struct ath6kl *ar); +bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif); -int ath6kl_cfg80211_init(struct ath6kl *ar); -void ath6kl_cfg80211_cleanup(struct ath6kl *ar); +/* TODO: remove this once ath6kl_vif_cleanup() is moved to cfg80211.c */ +void ath6kl_cfg80211_sta_bmiss_enhance(struct ath6kl_vif *vif, bool enable); -struct ath6kl *ath6kl_cfg80211_create(void); -void ath6kl_cfg80211_destroy(struct ath6kl *ar); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) +struct net_device *ath6kl_cfg80211_add_p2p0_iface(struct ath6kl *ar); +#endif #endif /* ATH6KL_CFG80211_H */ diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211_btcoex.c b/drivers/net/wireless/ath/ath6kl/cfg80211_btcoex.c new file mode 100644 index 00000000..2d0b02e4 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/cfg80211_btcoex.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#ifdef CONFIG_HAS_WAKELOCK +#include +#endif + +#include "core.h" +#include "cfg80211.h" +#include "debug.h" +#include "hif-ops.h" +#include "testmode.h" +#include "cfg80211_btcoex.h" + +bool ath6kl_btcoex_cfg80211_ready(struct ath6kl_vif *vif) +{ + struct ath6kl *ar = vif->ar; + + if (!test_bit(WMI_READY, &ar->flag)) { + ath6kl_err("wmi is not ready\n"); + return false; + } + + if (!test_bit(WLAN_ENABLED, &vif->flags)) { + ath6kl_err("wlan disabled\n"); + return false; + } + + return true; +} + +int ath6kl_notify_btcoex(struct wiphy *wiphy, u8 *buf, + int len) +{ + struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); + struct ath6kl_vif *vif; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "BT coex wmi command:%p\n", buf); + + if (!ath6kl_btcoex_cfg80211_ready(vif)) + return -EIO; + + return ath6kl_wmi_send_btcoex_cmd(ar->wmi, buf, + len); +} diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211_btcoex.h b/drivers/net/wireless/ath/ath6kl/cfg80211_btcoex.h new file mode 100644 index 00000000..365d6e37 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/cfg80211_btcoex.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +int ath6kl_notify_btcoex(struct wiphy *wiphy, u8 *buf, + int len); diff --git a/drivers/net/wireless/ath/ath6kl/common.h b/drivers/net/wireless/ath/ath6kl/common.h index a60e78c0..f78c59c8 100644 --- a/drivers/net/wireless/ath/ath6kl/common.h +++ b/drivers/net/wireless/ath/ath6kl/common.h @@ -77,8 +77,12 @@ enum crypto_type { struct htc_endpoint_credit_dist; struct ath6kl; +struct ath6kl_htcap; enum htc_credit_dist_reason; struct ath6kl_htc_credit_info; +struct ath6kl *ath6kl_core_alloc(struct device *sdev); +int ath6kl_core_init(struct ath6kl *ar); +void ath6kl_core_cleanup(struct ath6kl *ar); struct sk_buff *ath6kl_buf_alloc(int size); #endif /* COMMON_H */ diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c deleted file mode 100644 index 45e641f3..00000000 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (c) 2004-2011 Atheros Communications Inc. - * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "core.h" - -#include -#include -#include - -#include "debug.h" -#include "hif-ops.h" -#include "cfg80211.h" - -unsigned int debug_mask; -static unsigned int suspend_mode; -static unsigned int wow_mode; -static unsigned int uart_debug; -static unsigned int ath6kl_p2p; -static unsigned int testmode; - -module_param(debug_mask, uint, 0644); -module_param(suspend_mode, uint, 0644); -module_param(wow_mode, uint, 0644); -module_param(uart_debug, uint, 0644); -module_param(ath6kl_p2p, uint, 0644); -module_param(testmode, uint, 0644); - -int ath6kl_core_init(struct ath6kl *ar) -{ - struct ath6kl_bmi_target_info targ_info; - struct net_device *ndev; - int ret = 0, i; - - ar->ath6kl_wq = create_singlethread_workqueue("ath6kl"); - if (!ar->ath6kl_wq) - return -ENOMEM; - - ret = ath6kl_bmi_init(ar); - if (ret) - goto err_wq; - - /* - * Turn on power to get hardware (target) version and leave power - * on delibrately as we will boot the hardware anyway within few - * seconds. - */ - ret = ath6kl_hif_power_on(ar); - if (ret) - goto err_bmi_cleanup; - - ret = ath6kl_bmi_get_target_info(ar, &targ_info); - if (ret) - goto err_power_off; - - ar->version.target_ver = le32_to_cpu(targ_info.version); - ar->target_type = le32_to_cpu(targ_info.type); - ar->wiphy->hw_version = le32_to_cpu(targ_info.version); - - ret = ath6kl_init_hw_params(ar); - if (ret) - goto err_power_off; - - ar->htc_target = ath6kl_htc_create(ar); - - if (!ar->htc_target) { - ret = -ENOMEM; - goto err_power_off; - } - - ar->testmode = testmode; - - ret = ath6kl_init_fetch_firmwares(ar); - if (ret) - goto err_htc_cleanup; - - /* FIXME: we should free all firmwares in the error cases below */ - - /* Indicate that WMI is enabled (although not ready yet) */ - set_bit(WMI_ENABLED, &ar->flag); - ar->wmi = ath6kl_wmi_init(ar); - if (!ar->wmi) { - ath6kl_err("failed to initialize wmi\n"); - ret = -EIO; - goto err_htc_cleanup; - } - - ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi); - - /* setup access class priority mappings */ - ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest */ - ar->ac_stream_pri_map[WMM_AC_BE] = 1; - ar->ac_stream_pri_map[WMM_AC_VI] = 2; - ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */ - - /* allocate some buffers that handle larger AMSDU frames */ - ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS); - - ath6kl_cookie_init(ar); - - ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | - ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; - - if (suspend_mode && - suspend_mode >= WLAN_POWER_STATE_CUT_PWR && - suspend_mode <= WLAN_POWER_STATE_WOW) - ar->suspend_mode = suspend_mode; - else - ar->suspend_mode = 0; - - if (suspend_mode == WLAN_POWER_STATE_WOW && - (wow_mode == WLAN_POWER_STATE_CUT_PWR || - wow_mode == WLAN_POWER_STATE_DEEP_SLEEP)) - ar->wow_suspend_mode = wow_mode; - else - ar->wow_suspend_mode = 0; - - if (uart_debug) - ar->conf_flags |= ATH6KL_CONF_UART_DEBUG; - - set_bit(FIRST_BOOT, &ar->flag); - - ath6kl_debug_init(ar); - - ret = ath6kl_init_hw_start(ar); - if (ret) { - ath6kl_err("Failed to start hardware: %d\n", ret); - goto err_rxbuf_cleanup; - } - - /* give our connected endpoints some buffers */ - ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep); - ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]); - - ret = ath6kl_cfg80211_init(ar); - if (ret) - goto err_rxbuf_cleanup; - - ret = ath6kl_debug_init_fs(ar); - if (ret) { - wiphy_unregister(ar->wiphy); - goto err_rxbuf_cleanup; - } - - for (i = 0; i < ar->vif_max; i++) - ar->avail_idx_map |= BIT(i); - - rtnl_lock(); - - /* Add an initial station interface */ - ndev = ath6kl_interface_add(ar, "wlan%d", NL80211_IFTYPE_STATION, 0, - INFRA_NETWORK); - - rtnl_unlock(); - - if (!ndev) { - ath6kl_err("Failed to instantiate a network device\n"); - ret = -ENOMEM; - wiphy_unregister(ar->wiphy); - goto err_rxbuf_cleanup; - } - - ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", - __func__, ndev->name, ndev, ar); - - return ret; - -err_rxbuf_cleanup: - ath6kl_debug_cleanup(ar); - ath6kl_htc_flush_rx_buf(ar->htc_target); - ath6kl_cleanup_amsdu_rxbufs(ar); - ath6kl_wmi_shutdown(ar->wmi); - clear_bit(WMI_ENABLED, &ar->flag); - ar->wmi = NULL; -err_htc_cleanup: - ath6kl_htc_cleanup(ar->htc_target); -err_power_off: - ath6kl_hif_power_off(ar); -err_bmi_cleanup: - ath6kl_bmi_cleanup(ar); -err_wq: - destroy_workqueue(ar->ath6kl_wq); - - return ret; -} -EXPORT_SYMBOL(ath6kl_core_init); - -struct ath6kl *ath6kl_core_create(struct device *dev) -{ - struct ath6kl *ar; - u8 ctr; - - ar = ath6kl_cfg80211_create(); - if (!ar) - return NULL; - - ar->p2p = !!ath6kl_p2p; - ar->dev = dev; - - ar->vif_max = 1; - - ar->max_norm_iface = 1; - - spin_lock_init(&ar->lock); - spin_lock_init(&ar->mcastpsq_lock); - spin_lock_init(&ar->list_lock); - - init_waitqueue_head(&ar->event_wq); - sema_init(&ar->sem, 1); - - INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue); - INIT_LIST_HEAD(&ar->vif_list); - - clear_bit(WMI_ENABLED, &ar->flag); - clear_bit(SKIP_SCAN, &ar->flag); - clear_bit(DESTROY_IN_PROGRESS, &ar->flag); - - ar->tx_pwr = 0; - ar->intra_bss = 1; - ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD; - - ar->state = ATH6KL_STATE_OFF; - - memset((u8 *)ar->sta_list, 0, - AP_MAX_NUM_STA * sizeof(struct ath6kl_sta)); - - /* Init the PS queues */ - for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) { - spin_lock_init(&ar->sta_list[ctr].psq_lock); - skb_queue_head_init(&ar->sta_list[ctr].psq); - skb_queue_head_init(&ar->sta_list[ctr].apsdq); - ar->sta_list[ctr].mgmt_psq_len = 0; - INIT_LIST_HEAD(&ar->sta_list[ctr].mgmt_psq); - ar->sta_list[ctr].aggr_conn = - kzalloc(sizeof(struct aggr_info_conn), GFP_KERNEL); - if (!ar->sta_list[ctr].aggr_conn) { - ath6kl_err("Failed to allocate memory for sta aggregation information\n"); - ath6kl_core_destroy(ar); - return NULL; - } - } - - skb_queue_head_init(&ar->mcastpsq); - - memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3); - - return ar; -} -EXPORT_SYMBOL(ath6kl_core_create); - -void ath6kl_core_cleanup(struct ath6kl *ar) -{ - ath6kl_hif_power_off(ar); - - destroy_workqueue(ar->ath6kl_wq); - - if (ar->htc_target) - ath6kl_htc_cleanup(ar->htc_target); - - ath6kl_cookie_cleanup(ar); - - ath6kl_cleanup_amsdu_rxbufs(ar); - - ath6kl_bmi_cleanup(ar); - - ath6kl_debug_cleanup(ar); - - kfree(ar->fw_board); - kfree(ar->fw_otp); - kfree(ar->fw); - kfree(ar->fw_patch); - kfree(ar->fw_testscript); - - ath6kl_cfg80211_cleanup(ar); -} -EXPORT_SYMBOL(ath6kl_core_cleanup); - -void ath6kl_core_destroy(struct ath6kl *ar) -{ - ath6kl_cfg80211_destroy(ar); -} -EXPORT_SYMBOL(ath6kl_core_destroy); - -MODULE_AUTHOR("Qualcomm Atheros"); -MODULE_DESCRIPTION("Core module for AR600x SDIO and USB devices."); -MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index f1dd8906..5e3da091 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -24,10 +24,18 @@ #include #include #include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#ifdef CONFIG_HAS_WAKELOCK +#include +#endif #include "htc.h" #include "wmi.h" #include "bmi.h" #include "target.h" +#include "wmi_btcoex.h" #define MAX_ATH6KL 1 #define ATH6KL_MAX_RX_BUFFERS 16 @@ -54,13 +62,20 @@ /* MAX_HI_COOKIE_NUM are reserved for high priority traffic */ #define MAX_DEF_COOKIE_NUM 180 -#define MAX_HI_COOKIE_NUM 18 /* 10% of MAX_COOKIE_NUM */ +/* Reserved for data sync cmd to avoid htc dead lock */ +#define DATA_SYNC_RESERVED 10 +/* 10% of MAX_COOKIE_NUM(18) + DATA_SYNC_RESERVED(10) */ +#define MAX_HI_COOKIE_NUM 28 #define MAX_COOKIE_NUM (MAX_DEF_COOKIE_NUM + MAX_HI_COOKIE_NUM) +#define WMI_MAX_COOKIE_NUM 80 #define MAX_DEFAULT_SEND_QUEUE_DEPTH (MAX_DEF_COOKIE_NUM / WMM_NUM_AC) #define DISCON_TIMER_INTVAL 10000 /* in msec */ +/* Channel dwell time in fg scan */ +#define ATH6KL_FG_SCAN_INTERVAL 50 /* in ms */ + /* Channel dwell time in fg scan */ #define ATH6KL_FG_SCAN_INTERVAL 50 /* in ms */ @@ -91,6 +106,57 @@ enum ath6kl_fw_capability { */ ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, + /* + * Firmware has support to cleanup inactive stations + * in AP mode. + */ + ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, + + /* Firmware has support to override rsn cap of rsn ie */ + ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, + + /* + * Multicast support in WOW and host awake mode. + * Allow all multicast in host awake mode. + * Apply multicast filter in WOW mode. + */ + ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, + + /* Firmware supports enhanced bmiss detection */ + ATH6KL_FW_CAPABILITY_BMISS_ENHANCE, + + /* + * FW supports matching of ssid in schedule scan + */ + ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST, + + /* Firmware supports filtering BSS results by RSSI */ + ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD, + + /* FW sets mac_addr[4] ^= 0x80 for newly created interfaces */ + ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR, + + /* Firmware supports TX error rate notification */ + ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, + + /* supports WMI_SET_REGDOMAIN_CMDID command */ + ATH6KL_FW_CAPABILITY_REGDOMAIN, + + /* Firmware supports sched scan decoupled from host sleep */ + ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, + + /* + * Firmware capability for hang detection through heart beat + * challenge messages. + */ + ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, + + /* + * Firmware supports mac address based ACL with + * white/black list + */ + ATH6KL_FW_CAPABILITY_MAC_ACL, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; @@ -103,8 +169,13 @@ struct ath6kl_fw_ie { u8 data[0]; }; +enum ath6kl_hw_flags { + ATH6KL_HW_FLAG_64BIT_RATES = BIT(0), +}; + #define ATH6KL_FW_API2_FILE "fw-2.bin" #define ATH6KL_FW_API3_FILE "fw-3.bin" +#define ATH6KL_FW_API4_FILE "fw-4.bin" /* AR6003 1.0 definitions */ #define AR6003_HW_1_0_VERSION 0x300002ba @@ -179,7 +250,7 @@ struct ath6kl_fw_ie { #define AGGR_NUM_OF_FREE_NETBUFS 16 -#define AGGR_RX_TIMEOUT 400 /* in ms */ +#define AGGR_RX_TIMEOUT 100 /* in ms */ #define WMI_TIMEOUT (2 * HZ) @@ -205,6 +276,8 @@ struct ath6kl_fw_ie { #define ATH6KL_CONF_ENABLE_TX_BURST BIT(3) #define ATH6KL_CONF_UART_DEBUG BIT(4) +#define P2P_WILDCARD_SSID_LEN 7 /* DIRECT- */ + enum wlan_low_pwr_state { WLAN_POWER_STATE_ON, WLAN_POWER_STATE_CUT_PWR, @@ -233,12 +306,6 @@ struct rxtid { u32 hold_q_sz; struct skb_hold_q *hold_q; struct sk_buff_head q; - - /* - * FIXME: No clue what this should protect. Apparently it should - * protect some of the fields above but they are also accessed - * without taking the lock. - */ spinlock_t lock; }; @@ -317,10 +384,7 @@ struct ath6kl_sta { u8 auth; u8 wpa_ie[ATH6KL_MAX_IE]; struct sk_buff_head psq; - - /* protects psq, mgmt_psq, apsdq, and mgmt_psq_len fields */ spinlock_t psq_lock; - struct list_head mgmt_psq; size_t mgmt_psq_len; u8 apsd_info; @@ -342,6 +406,40 @@ struct ath6kl_bmi { u32 max_cmd_size; }; +/* Same as struct target_stats, expect it trackes only few + stats in u32 variable. */ +struct target_stats_dup { + u32 tx_retry_cnt[4]; + u32 tx_mult_retry_cnt[4]; + u32 tx_pkt_per_ac[4]; + u32 rx_pkt; + u32 rx_dupl_frame; + u32 tx_fail_cnt[4]; + u32 tx_rts_fail_cnt; + u32 tx_err; + u32 tx_rts_success_cnt; + u32 rx_err; + u32 tx_byte; +}; + +enum ath6kl_wlan_stats { + WLAN_STATS_INVALID, + WLAN_STATS_RETRY_CNT, + WLAN_STATS_MUL_RETRY_CNT, + WLAN_STATS_TX_FRM_CNT, + WLAN_STATS_RX_FRM_CNT, + WLAN_STATS_FRM_DUP_CNT, + WLAN_STATS_FAIL_CNT, + WLAN_STATS_RTS_FAIL_CNT, + WLAN_STATS_ACK_FAIL_CNT, + WLAN_STATS_RTS_SUC_CNT, + WLAN_STATS_RX_DISCARD_CNT, + WLAN_STATS_RX_ERROR_CNT, + WLAN_STATS_TX_BYTE_CNT, +}; + +#define ATH6KL_PRI_IOCTL_REPLY_BUF_MAX 1024 + struct target_stats { u64 tx_pkt; u64 tx_byte; @@ -449,6 +547,16 @@ struct ath6kl_req_key { u8 key_len; }; +/* + * Bluetooth WiFi co-ex information. + * This structure keeps track of the Bluetooth related status. + * This involves the Bluetooth ACL link role, Bluetooth remote lmp version. + */ +struct ath6kl_btcoex { + u32 acl_role; /* Master/slave role of Bluetooth ACL link */ + u32 remote_lmp_ver; /* LMP version of the remote device. */ + u32 bt_vendor; /* Keeps track of the Bluetooth chip vendor */ +}; enum ath6kl_hif_type { ATH6KL_HIF_TYPE_SDIO, ATH6KL_HIF_TYPE_USB, @@ -461,6 +569,12 @@ struct ath6kl_mc_filter { char hw_addr[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE]; }; +struct ath6kl_htcap { + bool ht_enable; + u8 ampdu_factor; + unsigned short cap_info; +}; + /* * Driver's maximum limit, note that some firmwares support only one vif * and the runtime (current) limit must be checked from ar->vif_max. @@ -480,6 +594,9 @@ enum ath6kl_vif_state { WLAN_ENABLED, STATS_UPDATE_PEND, HOST_SLEEP_MODE_CMD_PROCESSED, + NETDEV_MCAST_ALL_ON, + NETDEV_MCAST_ALL_OFF, + SCHED_SCANNING, }; struct ath6kl_vif { @@ -509,6 +626,7 @@ struct ath6kl_vif { struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1]; struct ath6kl_key keys[WMI_MAX_KEY_INDEX + 1]; struct aggr_info *aggr_cntxt; + struct ath6kl_htcap htcap[IEEE80211_NUM_BANDS]; struct timer_list disconnect_timer; struct timer_list sched_scan_timer; @@ -521,12 +639,20 @@ struct ath6kl_vif { u32 send_action_id; bool probe_req_report; u16 next_chan; + enum nl80211_channel_type next_ch_type; + enum ieee80211_band next_ch_band; u16 assoc_bss_beacon_int; + u8 scan_ctrl_flag; u16 listen_intvl_t; u16 bmiss_time_t; + u32 txe_intvl; + u16 bg_scan_period; u8 assoc_bss_dtim_period; struct net_device_stats net_stats; struct target_stats target_stats; + struct target_stats_dup target_stats_dup; + struct wmi_connect_cmd profile; + u16 rsn_capab; struct list_head mc_filter; }; @@ -536,6 +662,8 @@ struct ath6kl_vif { #define ATH6KL_SCHED_SCAN_RESULT_DELAY 5000 /* ms */ +#define ATH6KL_PRIV_GET_WLAN_STATS (SIOCIWFIRSTPRIV + 21) + /* Flag info */ enum ath6kl_dev_state { WMI_ENABLED, @@ -556,7 +684,16 @@ enum ath6kl_state { ATH6KL_STATE_DEEPSLEEP, ATH6KL_STATE_CUTPOWER, ATH6KL_STATE_WOW, - ATH6KL_STATE_SCHED_SCAN, + ATH6KL_STATE_RECOVERY, +}; + +/* Fw error recovery */ +#define ATH6KL_HB_RESP_MISS_THRES 5 + +enum ath6kl_fw_err { + ATH6KL_FW_ASSERT, + ATH6KL_FW_HB_RESP_FAILURE, + ATH6KL_FW_EP_FULL, }; struct ath6kl { @@ -564,7 +701,6 @@ struct ath6kl { struct wiphy *wiphy; enum ath6kl_state state; - unsigned int testmode; struct ath6kl_bmi bmi; const struct ath6kl_hif_ops *hif_ops; @@ -581,13 +717,7 @@ struct ath6kl { unsigned int vif_max; u8 max_norm_iface; u8 avail_idx_map; - - /* - * Protects at least amsdu_rx_buffer_queue, ath6kl_alloc_cookie() - * calls, tx_pending and total_tx_data_pend. - */ spinlock_t lock; - struct semaphore sem; u8 lrssi_roam_threshold; struct ath6kl_version version; @@ -600,6 +730,8 @@ struct ath6kl { u8 next_ep_id; struct ath6kl_cookie *cookie_list; u32 cookie_count; + struct ath6kl_cookie *wmi_cookie_list; + u32 wmi_cookie_count; enum htc_endpoint_id ac2ep_map[WMM_NUM_AC]; bool ac_stream_active[WMM_NUM_AC]; u8 ac_stream_pri_map[WMM_NUM_AC]; @@ -614,13 +746,9 @@ struct ath6kl { u8 sta_list_index; struct ath6kl_req_key ap_mode_bkey; struct sk_buff_head mcastpsq; - - /* - * FIXME: protects access to mcastpsq but is actually useless as - * all skbe_queue_*() functions provide serialisation themselves - */ + u32 want_ch_switch; + u16 last_ch; spinlock_t mcastpsq_lock; - u8 intra_bss; struct wmi_ap_mode_stat ap_stats; u8 ap_country_code[3]; @@ -646,6 +774,7 @@ struct ath6kl { u32 refclk_hz; u32 uarttx_pin; u32 testscript_addr; + u32 flags; struct ath6kl_hw_fw { const char *dir; @@ -668,6 +797,7 @@ struct ath6kl { struct ath6kl_mbox_info mbox_info; struct ath6kl_cookie cookie_mem[MAX_COOKIE_NUM]; + struct ath6kl_cookie wmi_cookie_mem[WMI_MAX_COOKIE_NUM]; unsigned long flag; u8 *fw_board; @@ -694,7 +824,18 @@ struct ath6kl { bool p2p; - bool wiphy_registered; + struct ath6kl_btcoex btcoex_info; + + struct ath6kl_fw_recovery { + bool enable; + struct work_struct recovery_work; + unsigned long err_reason; + unsigned long hb_poll; + struct timer_list hb_timer; + u32 seq_num; + bool hb_pending; + u8 hb_misscnt; + } fw_recovery; #ifdef CONFIG_ATH6KL_DEBUG struct { @@ -719,6 +860,17 @@ struct ath6kl { u8 disc_timeout; } debug; #endif /* CONFIG_ATH6KL_DEBUG */ + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; + bool screen_off; +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock wake_lock; + struct wake_lock p2p_wake_lock; +#endif /* CONFIG_HAS_WAKELOCK */ + }; static inline struct ath6kl *ath6kl_priv(struct net_device *dev) @@ -746,7 +898,8 @@ void init_netdev(struct net_device *dev); void ath6kl_cookie_init(struct ath6kl *ar); void ath6kl_cookie_cleanup(struct ath6kl *ar); void ath6kl_rx(struct htc_target *target, struct htc_packet *packet); -void ath6kl_tx_complete(void *context, struct list_head *packet_queue); +void ath6kl_tx_complete(struct htc_target *context, + struct list_head *packet_queue); enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, struct htc_packet *packet); void ath6kl_stop_txrx(struct ath6kl *ar); @@ -759,8 +912,9 @@ int ath6kl_read_fwlogs(struct ath6kl *ar); void ath6kl_init_profile_info(struct ath6kl_vif *vif); void ath6kl_tx_data_cleanup(struct ath6kl *ar); -struct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar); -void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie); +struct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar, bool isctrl); +void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie, + bool ctrl_ep); int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev); struct aggr_info *aggr_init(struct ath6kl_vif *vif); @@ -775,10 +929,10 @@ struct htc_packet *ath6kl_alloc_amsdu_rxbuf(struct htc_target *target, void aggr_module_destroy(struct aggr_info *aggr_info); void aggr_reset_state(struct aggr_info_conn *aggr_conn); -struct ath6kl_sta *ath6kl_find_sta(struct ath6kl_vif *vif, u8 *node_addr); +struct ath6kl_sta *ath6kl_find_sta(struct ath6kl_vif *vif, u8 * node_addr); struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl *ar, u8 aid); -void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver); +void ath6kl_ready_event(void *devt, u8 * datap, u32 sw_ver, u32 abi_ver); int ath6kl_control_tx(void *devt, struct sk_buff *skb, enum htc_endpoint_id eid); void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, @@ -812,18 +966,27 @@ void ath6kl_wakeup_event(void *dev); void ath6kl_reset_device(struct ath6kl *ar, u32 target_type, bool wait_fot_compltn, bool cold_reset); void ath6kl_init_control_info(struct ath6kl_vif *vif); +void ath6kl_deinit_if_data(struct ath6kl_vif *vif); +void ath6kl_core_free(struct ath6kl *ar); struct ath6kl_vif *ath6kl_vif_first(struct ath6kl *ar); void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready); int ath6kl_init_hw_start(struct ath6kl *ar); int ath6kl_init_hw_stop(struct ath6kl *ar); -int ath6kl_init_fetch_firmwares(struct ath6kl *ar); -int ath6kl_init_hw_params(struct ath6kl *ar); - -void ath6kl_check_wow_status(struct ath6kl *ar); - -struct ath6kl *ath6kl_core_create(struct device *dev); -int ath6kl_core_init(struct ath6kl *ar); -void ath6kl_core_cleanup(struct ath6kl *ar); -void ath6kl_core_destroy(struct ath6kl *ar); - +void ath6kl_check_wow_status(struct ath6kl *ar, struct sk_buff *skb, + bool is_event_pkt); +void ath6kl_sdio_init_platform(void); +void ath6kl_sdio_exit_platform(void); +void ath6kl_mangle_mac_address(struct ath6kl *ar, u8 locally_administered_bit); + +int ath6kl_wait_for_init_comp(void); +void ath6kl_notify_init_done(void); + +/* Fw error recovery */ +void ath6kl_init_hw_restart(struct ath6kl *ar); +void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason); +void ath6kl_recovery_hb_event(struct ath6kl *ar, u32 cookie); +void ath6kl_recovery_init(struct ath6kl *ar); +void ath6kl_recovery_cleanup(struct ath6kl *ar); +void ath6kl_recovery_suspend(struct ath6kl *ar); +void ath6kl_recovery_resume(struct ath6kl *ar); #endif /* CORE_H */ diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index d01403a2..b7e4ad1b 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -23,6 +23,7 @@ #include #include "debug.h" +#include "debugfs_pri.h" #include "target.h" struct ath6kl_fwlog_slot { @@ -39,57 +40,31 @@ struct ath6kl_fwlog_slot { int ath6kl_printk(const char *level, const char *fmt, ...) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) struct va_format vaf; +#endif va_list args; int rtn; va_start(args, fmt); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) vaf.fmt = fmt; vaf.va = &args; rtn = printk("%sath6kl: %pV", level, &vaf); +#else + printk("%sath6kl: ", level); + rtn = vprintk(fmt, args); +#endif va_end(args); return rtn; } -EXPORT_SYMBOL(ath6kl_printk); #ifdef CONFIG_ATH6KL_DEBUG -void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - if (!(debug_mask & mask)) - return; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - ath6kl_printk(KERN_DEBUG, "%pV", &vaf); - - va_end(args); -} -EXPORT_SYMBOL(ath6kl_dbg); - -void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, - const char *msg, const char *prefix, - const void *buf, size_t len) -{ - if (debug_mask & mask) { - if (msg) - ath6kl_dbg(mask, "%s\n", msg); - - print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len); - } -} -EXPORT_SYMBOL(ath6kl_dbg_dump); - #define REG_OUTPUT_LEN_PER_LINE 25 #define REGTYPE_STR_LEN 100 @@ -115,63 +90,63 @@ void ath6kl_dump_registers(struct ath6kl_device *dev, struct ath6kl_irq_enable_reg *irq_enable_reg) { - ath6kl_dbg(ATH6KL_DBG_IRQ, ("<------- Register Table -------->\n")); + ath6kl_dbg(ATH6KL_DBG_ANY, ("<------- Register Table -------->\n")); if (irq_proc_reg != NULL) { - ath6kl_dbg(ATH6KL_DBG_IRQ, - "Host Int status: 0x%x\n", - irq_proc_reg->host_int_status); - ath6kl_dbg(ATH6KL_DBG_IRQ, + ath6kl_dbg(ATH6KL_DBG_ANY, + "Host Int status: 0x%x\n", + irq_proc_reg->host_int_status); + ath6kl_dbg(ATH6KL_DBG_ANY, "CPU Int status: 0x%x\n", - irq_proc_reg->cpu_int_status); - ath6kl_dbg(ATH6KL_DBG_IRQ, + irq_proc_reg->cpu_int_status); + ath6kl_dbg(ATH6KL_DBG_ANY, "Error Int status: 0x%x\n", - irq_proc_reg->error_int_status); - ath6kl_dbg(ATH6KL_DBG_IRQ, + irq_proc_reg->error_int_status); + ath6kl_dbg(ATH6KL_DBG_ANY, "Counter Int status: 0x%x\n", - irq_proc_reg->counter_int_status); - ath6kl_dbg(ATH6KL_DBG_IRQ, + irq_proc_reg->counter_int_status); + ath6kl_dbg(ATH6KL_DBG_ANY, "Mbox Frame: 0x%x\n", - irq_proc_reg->mbox_frame); - ath6kl_dbg(ATH6KL_DBG_IRQ, + irq_proc_reg->mbox_frame); + ath6kl_dbg(ATH6KL_DBG_ANY, "Rx Lookahead Valid: 0x%x\n", - irq_proc_reg->rx_lkahd_valid); - ath6kl_dbg(ATH6KL_DBG_IRQ, + irq_proc_reg->rx_lkahd_valid); + ath6kl_dbg(ATH6KL_DBG_ANY, "Rx Lookahead 0: 0x%x\n", - irq_proc_reg->rx_lkahd[0]); - ath6kl_dbg(ATH6KL_DBG_IRQ, + irq_proc_reg->rx_lkahd[0]); + ath6kl_dbg(ATH6KL_DBG_ANY, "Rx Lookahead 1: 0x%x\n", - irq_proc_reg->rx_lkahd[1]); + irq_proc_reg->rx_lkahd[1]); if (dev->ar->mbox_info.gmbox_addr != 0) { /* * If the target supports GMBOX hardware, dump some * additional state. */ - ath6kl_dbg(ATH6KL_DBG_IRQ, - "GMBOX Host Int status 2: 0x%x\n", - irq_proc_reg->host_int_status2); - ath6kl_dbg(ATH6KL_DBG_IRQ, - "GMBOX RX Avail: 0x%x\n", - irq_proc_reg->gmbox_rx_avail); - ath6kl_dbg(ATH6KL_DBG_IRQ, - "GMBOX lookahead alias 0: 0x%x\n", - irq_proc_reg->rx_gmbox_lkahd_alias[0]); - ath6kl_dbg(ATH6KL_DBG_IRQ, - "GMBOX lookahead alias 1: 0x%x\n", - irq_proc_reg->rx_gmbox_lkahd_alias[1]); + ath6kl_dbg(ATH6KL_DBG_ANY, + "GMBOX Host Int status 2: 0x%x\n", + irq_proc_reg->host_int_status2); + ath6kl_dbg(ATH6KL_DBG_ANY, + "GMBOX RX Avail: 0x%x\n", + irq_proc_reg->gmbox_rx_avail); + ath6kl_dbg(ATH6KL_DBG_ANY, + "GMBOX lookahead alias 0: 0x%x\n", + irq_proc_reg->rx_gmbox_lkahd_alias[0]); + ath6kl_dbg(ATH6KL_DBG_ANY, + "GMBOX lookahead alias 1: 0x%x\n", + irq_proc_reg->rx_gmbox_lkahd_alias[1]); } } if (irq_enable_reg != NULL) { - ath6kl_dbg(ATH6KL_DBG_IRQ, - "Int status Enable: 0x%x\n", - irq_enable_reg->int_status_en); - ath6kl_dbg(ATH6KL_DBG_IRQ, "Counter Int status Enable: 0x%x\n", - irq_enable_reg->cntr_int_status_en); + ath6kl_dbg(ATH6KL_DBG_ANY, + "Int status Enable: 0x%x\n", + irq_enable_reg->int_status_en); + ath6kl_dbg(ATH6KL_DBG_ANY, "Counter Int status Enable: 0x%x\n", + irq_enable_reg->cntr_int_status_en); } - ath6kl_dbg(ATH6KL_DBG_IRQ, "<------------------------------->\n"); + ath6kl_dbg(ATH6KL_DBG_ANY, "<------------------------------->\n"); } static void dump_cred_dist(struct htc_endpoint_credit_dist *ep_dist) @@ -208,6 +183,9 @@ void dump_cred_dist_stats(struct htc_target *target) { struct htc_endpoint_credit_dist *ep_list; + if (!AR_DBG_LVL_CHECK(ATH6KL_DBG_CREDIT)) + return; + list_for_each_entry(ep_list, &target->cred_dist_list, list) dump_cred_dist(ep_list); @@ -217,6 +195,12 @@ void dump_cred_dist_stats(struct htc_target *target) target->credit_info->cur_free_credits); } +static int ath6kl_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war) { switch (war) { @@ -257,7 +241,7 @@ static ssize_t read_file_war_stats(struct file *file, char __user *user_buf, static const struct file_operations fops_war_stats = { .read = read_file_war_stats, - .open = simple_open, + .open = ath6kl_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -401,8 +385,10 @@ static ssize_t ath6kl_fwlog_block_read(struct file *file, ret = wait_for_completion_interruptible( &ar->debug.fwlog_completion); - if (ret == -ERESTARTSYS) + if (ret == -ERESTARTSYS) { + vfree(buf); return ret; + } spin_lock(&ar->debug.fwlog_queue.lock); } @@ -482,7 +468,7 @@ static ssize_t ath6kl_fwlog_mask_write(struct file *file, } static const struct file_operations fops_fwlog_mask = { - .open = simple_open, + .open = ath6kl_debugfs_open, .read = ath6kl_fwlog_mask_read, .write = ath6kl_fwlog_mask_write, .owner = THIS_MODULE, @@ -616,6 +602,12 @@ static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, "Num disconnects", tgt_stats->cs_discon_cnt); len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", "Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi); + len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", + "ARP pkt received", tgt_stats->arp_received); + len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", + "ARP pkt matched", tgt_stats->arp_matched); + len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", + "ARP pkt replied", tgt_stats->arp_replied); if (len > buf_len) len = buf_len; @@ -628,7 +620,7 @@ static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, static const struct file_operations fops_tgt_stats = { .read = read_file_tgt_stats, - .open = simple_open, + .open = ath6kl_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -693,7 +685,7 @@ static ssize_t read_file_credit_dist_stats(struct file *file, static const struct file_operations fops_credit_dist_stats = { .read = read_file_credit_dist_stats, - .open = simple_open, + .open = ath6kl_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -734,13 +726,9 @@ static ssize_t ath6kl_endpoint_stats_read(struct file *file, return -ENOMEM; #define EPSTAT(name) \ - do { \ - len = print_endpoint_stat(target, buf, buf_len, len, \ - offsetof(struct htc_endpoint_stats, \ - name), \ - #name); \ - } while (0) - + len = print_endpoint_stat(target, buf, buf_len, len, \ + offsetof(struct htc_endpoint_stats, name), \ + #name) EPSTAT(cred_low_indicate); EPSTAT(tx_issued); EPSTAT(tx_pkt_bundled); @@ -796,7 +784,7 @@ static ssize_t ath6kl_endpoint_stats_write(struct file *file, } static const struct file_operations fops_endpoint_stats = { - .open = simple_open, + .open = ath6kl_debugfs_open, .read = ath6kl_endpoint_stats_read, .write = ath6kl_endpoint_stats_write, .owner = THIS_MODULE, @@ -850,9 +838,17 @@ static ssize_t ath6kl_regread_write(struct file *file, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; + u8 buf[50]; + unsigned int len; unsigned long reg_addr; - if (kstrtoul_from_user(user_buf, count, 0, ®_addr)) + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + + if (strict_strtoul(buf, 0, ®_addr)) return -EINVAL; if ((reg_addr % 4) != 0) @@ -869,7 +865,7 @@ static ssize_t ath6kl_regread_write(struct file *file, static const struct file_operations fops_diag_reg_read = { .read = ath6kl_regread_read, .write = ath6kl_regread_write, - .open = simple_open, + .open = ath6kl_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -966,8 +962,15 @@ static ssize_t ath6kl_lrssi_roam_write(struct file *file, { struct ath6kl *ar = file->private_data; unsigned long lrssi_roam_threshold; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; - if (kstrtoul_from_user(user_buf, count, 0, &lrssi_roam_threshold)) + buf[len] = '\0'; + if (strict_strtoul(buf, 0, &lrssi_roam_threshold)) return -EINVAL; ar->lrssi_roam_threshold = lrssi_roam_threshold; @@ -993,7 +996,7 @@ static ssize_t ath6kl_lrssi_roam_read(struct file *file, static const struct file_operations fops_lrssi_roam_threshold = { .read = ath6kl_lrssi_roam_read, .write = ath6kl_lrssi_roam_write, - .open = simple_open, + .open = ath6kl_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1055,7 +1058,7 @@ static ssize_t ath6kl_regwrite_write(struct file *file, static const struct file_operations fops_diag_reg_write = { .read = ath6kl_regwrite_read, .write = ath6kl_regwrite_write, - .open = simple_open, + .open = ath6kl_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1160,7 +1163,7 @@ static ssize_t ath6kl_roam_table_read(struct file *file, char __user *user_buf, static const struct file_operations fops_roam_table = { .read = ath6kl_roam_table_read, - .open = simple_open, + .open = ath6kl_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1198,7 +1201,7 @@ static ssize_t ath6kl_force_roam_write(struct file *file, static const struct file_operations fops_force_roam = { .write = ath6kl_force_roam_write, - .open = simple_open, + .open = ath6kl_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1238,7 +1241,7 @@ static ssize_t ath6kl_roam_mode_write(struct file *file, static const struct file_operations fops_roam_mode = { .write = ath6kl_roam_mode_write, - .open = simple_open, + .open = ath6kl_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1280,7 +1283,7 @@ static ssize_t ath6kl_keepalive_write(struct file *file, } static const struct file_operations fops_keepalive = { - .open = simple_open, + .open = ath6kl_debugfs_open, .read = ath6kl_keepalive_read, .write = ath6kl_keepalive_write, .owner = THIS_MODULE, @@ -1325,7 +1328,7 @@ static ssize_t ath6kl_disconnect_timeout_write(struct file *file, } static const struct file_operations fops_disconnect_timeout = { - .open = simple_open, + .open = ath6kl_debugfs_open, .read = ath6kl_disconnect_timeout_read, .write = ath6kl_disconnect_timeout_write, .owner = THIS_MODULE, @@ -1497,7 +1500,7 @@ static ssize_t ath6kl_create_qos_write(struct file *file, return -EINVAL; pstream.medium_time = cpu_to_le32(val32); - pstream.nominal_phy = le32_to_cpu(pstream.min_phy_rate) / 1000000; + pstream.nominal_phy = ((le32_to_cpu(pstream.min_phy_rate) / 1000) / 1000); ath6kl_wmi_create_pstream_cmd(ar->wmi, vif->fw_vif_idx, &pstream); @@ -1506,7 +1509,7 @@ static ssize_t ath6kl_create_qos_write(struct file *file, static const struct file_operations fops_create_qos = { .write = ath6kl_create_qos_write, - .open = simple_open, + .open = ath6kl_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1554,7 +1557,7 @@ static ssize_t ath6kl_delete_qos_write(struct file *file, static const struct file_operations fops_delete_qos = { .write = ath6kl_delete_qos_write, - .open = simple_open, + .open = ath6kl_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1564,10 +1567,15 @@ static ssize_t ath6kl_bgscan_int_write(struct file *file, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; u16 bgscan_int; char buf[32]; ssize_t len; + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; @@ -1579,15 +1587,17 @@ static ssize_t ath6kl_bgscan_int_write(struct file *file, if (bgscan_int == 0) bgscan_int = 0xffff; + vif->bg_scan_period = bgscan_int; + ath6kl_wmi_scanparams_cmd(ar->wmi, 0, 0, 0, bgscan_int, 0, 0, 0, 3, - 0, 0, 0); + vif->scan_ctrl_flag, 0, 0); return count; } static const struct file_operations fops_bgscan_int = { .write = ath6kl_bgscan_int_write, - .open = simple_open, + .open = ath6kl_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1645,7 +1655,7 @@ static ssize_t ath6kl_listen_int_read(struct file *file, static const struct file_operations fops_listen_int = { .read = ath6kl_listen_int_read, .write = ath6kl_listen_int_write, - .open = simple_open, + .open = ath6kl_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1705,12 +1715,12 @@ static ssize_t ath6kl_power_params_write(struct file *file, static const struct file_operations fops_power_params = { .write = ath6kl_power_params_write, - .open = simple_open, + .open = ath6kl_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; -void ath6kl_debug_init(struct ath6kl *ar) +int ath6kl_debug_init(struct ath6kl *ar) { skb_queue_head_init(&ar->debug.fwlog_queue); init_completion(&ar->debug.fwlog_completion); @@ -1720,15 +1730,7 @@ void ath6kl_debug_init(struct ath6kl *ar) * value from the firmware. */ ar->debug.fwlog_mask = 0; -} -/* - * Initialisation needs to happen in two stages as fwlog events can come - * before cfg80211 is initialised, and debugfs depends on cfg80211 - * initialisation. - */ -int ath6kl_debug_init_fs(struct ath6kl *ar) -{ ar->debugfs_phy = debugfs_create_dir("ath6kl", ar->wiphy->debugfsdir); if (!ar->debugfs_phy) @@ -1783,21 +1785,21 @@ int ath6kl_debug_init_fs(struct ath6kl *ar) ar->debugfs_phy, ar, &fops_disconnect_timeout); debugfs_create_file("create_qos", S_IWUSR, ar->debugfs_phy, ar, - &fops_create_qos); + &fops_create_qos); debugfs_create_file("delete_qos", S_IWUSR, ar->debugfs_phy, ar, - &fops_delete_qos); + &fops_delete_qos); debugfs_create_file("bgscan_interval", S_IWUSR, - ar->debugfs_phy, ar, &fops_bgscan_int); + ar->debugfs_phy, ar, &fops_bgscan_int); debugfs_create_file("listen_interval", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, &fops_listen_int); debugfs_create_file("power_params", S_IWUSR, ar->debugfs_phy, ar, - &fops_power_params); + &fops_power_params); - return 0; + return ath6kl_init_debugfs_pri(ar); } void ath6kl_debug_cleanup(struct ath6kl *ar) diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 1803a0ba..65f2a334 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -43,6 +43,8 @@ enum ATH6K_DEBUG_MASK { ATH6KL_DBG_WMI_DUMP = BIT(19), ATH6KL_DBG_SUSPEND = BIT(20), ATH6KL_DBG_USB = BIT(21), + ATH6KL_DBG_RECOVERY = BIT(22), + ATH6KL_DBG_STACK_DUMP = BIT(23), ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ }; @@ -57,16 +59,35 @@ int ath6kl_printk(const char *level, const char *fmt, ...); #define ath6kl_warn(fmt, ...) \ ath6kl_printk(KERN_WARNING, fmt, ##__VA_ARGS__) +#define AR_DBG_LVL_CHECK(mask) (debug_mask & mask) + enum ath6kl_war { ATH6KL_WAR_INVALID_RATE, }; #ifdef CONFIG_ATH6KL_DEBUG +#define ath6kl_dbg(mask, fmt, ...) \ + ({ \ + int rtn; \ + if (debug_mask & mask) \ + rtn = ath6kl_printk(KERN_DEBUG, fmt, ##__VA_ARGS__); \ + else \ + rtn = 0; \ + \ + rtn; \ + }) + +static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, + const char *msg, const char *prefix, + const void *buf, size_t len) +{ + if (debug_mask & mask) { + if (msg) + ath6kl_dbg(mask, "%s\n", msg); -void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...); -void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, - const char *msg, const char *prefix, - const void *buf, size_t len); + print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len); + } +} void ath6kl_dump_registers(struct ath6kl_device *dev, struct ath6kl_irq_proc_registers *irq_proc_reg, @@ -78,8 +99,7 @@ int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf, size_t len); void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive); void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, u8 timeout); -void ath6kl_debug_init(struct ath6kl *ar); -int ath6kl_debug_init_fs(struct ath6kl *ar); +int ath6kl_debug_init(struct ath6kl *ar); void ath6kl_debug_cleanup(struct ath6kl *ar); #else @@ -129,11 +149,7 @@ static inline void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, { } -static inline void ath6kl_debug_init(struct ath6kl *ar) -{ -} - -static inline int ath6kl_debug_init_fs(struct ath6kl *ar) +static inline int ath6kl_debug_init(struct ath6kl *ar) { return 0; } diff --git a/drivers/net/wireless/ath/ath6kl/debugfs_pri.c b/drivers/net/wireless/ath/ath6kl/debugfs_pri.c new file mode 100644 index 00000000..69552a45 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/debugfs_pri.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef CONFIG_ATH6KL_DEBUG + +#include +#include +#include "core.h" +#include "wmi.h" +#include "debug.h" +#include "debugfs_pri.h" + +struct wmi_set_inact_period_cmd { + __le32 inact_period; + u8 num_null_func; +} __packed; + +static inline struct sk_buff *ath6kl_wmi_get_new_buf_pri(u32 size) +{ + struct sk_buff *skb; + + skb = ath6kl_buf_alloc(size); + if (!skb) + return NULL; + + skb_put(skb, size); + if (size) + memset(skb->data, 0, size); + + return skb; +} + +static int ath6kl_wmi_inact_period_cmd(struct wmi *wmi, u32 inact_period, + u8 num_null_func) +{ + struct sk_buff *skb; + struct wmi_set_inact_period_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf_pri(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_inact_period_cmd *) skb->data; + cmd->inact_period = cpu_to_le32(inact_period); + cmd->num_null_func = num_null_func; + + ret = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_AP_CONN_INACT_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} + +static int ath6kl_wmi_set_err_report_bitmask(struct wmi *wmi, u8 if_idx, + u32 mask) +{ + struct sk_buff *skb; + struct wmi_tgt_err_report_mask *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf_pri(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_tgt_err_report_mask *) skb->data; + cmd->mask = cpu_to_le32(mask); + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_TARGET_ERROR_REPORT_BITMASK_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_error_report_event(struct wmi *wmi, u8 *data, int len) +{ + struct wmi_tgt_err_report_evt *report; + + if (len < sizeof(*report)) + return -EINVAL; + + report = (struct wmi_tgt_err_report_evt *) data; + ath6kl_dbg(ATH6KL_DBG_WMI, "Reason for error report: 0x%x\n", + report->err_val); + + return 0; +} + +static int ath6kl_debugfs_open_pri(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t ath6kl_inact_period_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + char buf[32]; + u32 inact_period; + size_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtou32(buf, 0, &inact_period)) + return -EINVAL; + + ret = ath6kl_wmi_inact_period_cmd(ar->wmi, inact_period, 0); + + if (ret) + return ret; + + return count; +} + +static const struct file_operations fops_inact_period = { + .write = ath6kl_inact_period_write, + .open = ath6kl_debugfs_open_pri, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_bmisstime_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u16 bmiss_time; + char buf[32]; + ssize_t len; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtou16(buf, 0, &bmiss_time)) + return -EINVAL; + + vif->bmiss_time_t = bmiss_time; + + /* Enable error report event for bmiss */ + ath6kl_wmi_set_err_report_bitmask(ar->wmi, vif->fw_vif_idx, + ATH6KL_ERR_REPORT_BMISS_MASK); + + ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx, + vif->bmiss_time_t, 0); + return count; +} + +static ssize_t ath6kl_bmisstime_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + char buf[32]; + int len; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + len = scnprintf(buf, sizeof(buf), "%u\n", vif->bmiss_time_t); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_bmisstime = { + .read = ath6kl_bmisstime_read, + .write = ath6kl_bmisstime_write, + .open = ath6kl_debugfs_open_pri, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_max_num_sta_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + u8 val; + + ret = kstrtou8_from_user(user_buf, count, 0, &val); + if (ret) + return ret; + + ret = ath6kl_wmi_ap_set_num_sta(ar->wmi, 0, val); + if (ret) + return ret; + + return count; +} + +static const struct file_operations fops_max_num_sta = { + .open = ath6kl_debugfs_open_pri, + .write = ath6kl_max_num_sta_write, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int ath6kl_init_debugfs_pri(struct ath6kl *ar) +{ + debugfs_create_file("inactivity_period", S_IWUSR, ar->debugfs_phy, ar, + &fops_inact_period); + + debugfs_create_file("bmiss_time", S_IRUSR | S_IWUSR, ar->debugfs_phy, + ar, &fops_bmisstime); + + debugfs_create_file("max_num_sta", S_IWUSR, ar->debugfs_phy, + ar, &fops_max_num_sta); + + return 0; +} + +#endif diff --git a/drivers/net/wireless/ath/ath6kl/debugfs_pri.h b/drivers/net/wireless/ath/ath6kl/debugfs_pri.h new file mode 100644 index 00000000..f29df302 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/debugfs_pri.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DEBUG_PRI_H +#define DEBUG_PRI_H + +#ifdef CONFIG_ATH6KL_DEBUG + +#define ATH6KL_ERR_REPORT_BMISS_MASK BIT(3) + +struct wmi_tgt_err_report_mask { + __le32 mask; +}; + +struct wmi_tgt_err_report_evt { + __le32 err_val; +} __packed; + +int ath6kl_wmi_error_report_event(struct wmi *wmi, u8 *data, int len); +int ath6kl_init_debugfs_pri(struct ath6kl *ar); +#endif +#endif diff --git a/drivers/net/wireless/ath/ath6kl/hif-ops.h b/drivers/net/wireless/ath/ath6kl/hif-ops.h index fd840866..fb2b1967 100644 --- a/drivers/net/wireless/ath/ath6kl/hif-ops.h +++ b/drivers/net/wireless/ath/ath6kl/hif-ops.h @@ -84,6 +84,7 @@ static inline void ath6kl_hif_cleanup_scatter(struct ath6kl *ar) return ar->hif_ops->cleanup_scatter(ar); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static inline int ath6kl_hif_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) { @@ -91,6 +92,7 @@ static inline int ath6kl_hif_suspend(struct ath6kl *ar, return ar->hif_ops->suspend(ar, wow); } +#endif /* * Read from the ATH6KL through its diagnostic window. No cooperation from @@ -122,12 +124,14 @@ static inline int ath6kl_hif_bmi_write(struct ath6kl *ar, u8 *buf, u32 len) return ar->hif_ops->bmi_write(ar, buf, len); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static inline int ath6kl_hif_resume(struct ath6kl *ar) { ath6kl_dbg(ATH6KL_DBG_HIF, "hif resume\n"); return ar->hif_ops->resume(ar); } +#endif static inline int ath6kl_hif_power_on(struct ath6kl *ar) { diff --git a/drivers/net/wireless/ath/ath6kl/hif.c b/drivers/net/wireless/ath/ath6kl/hif.c index 68ed6c26..4ad3a5f0 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.c +++ b/drivers/net/wireless/ath/ath6kl/hif.c @@ -16,8 +16,6 @@ */ #include "hif.h" -#include - #include "core.h" #include "target.h" #include "hif-ops.h" @@ -62,8 +60,6 @@ int ath6kl_hif_rw_comp_handler(void *context, int status) return 0; } -EXPORT_SYMBOL(ath6kl_hif_rw_comp_handler); - #define REG_DUMP_COUNT_AR6003 60 #define REGISTER_DUMP_LEN_MAX 60 @@ -90,7 +86,7 @@ static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar) } ath6kl_dbg(ATH6KL_DBG_IRQ, "register dump data address 0x%x\n", - regdump_addr); + regdump_addr); regdump_addr = TARG_VTOP(ar->target_type, regdump_addr); /* fetch register dump data */ @@ -118,6 +114,93 @@ static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar) } +#define DUMP_MASK_FULL_STACK 0x01 +#define DUMP_MASK_DBGLOG 0x02 + +#define AR6003_HW211_KERNELSTACK_BASE 0x543938 +#define AR6003_HW211_KERNELSTACK_SIZE 2560 +#define MAX_DUMP_BYTE_NUM_ONE_ITERATION 256 +#define DUMP_STACK_OFFSET 0x40 + +#define AR6003_HW211_DBGLOG_ADDR 0x543730 +#define AR6003_HW211_DBGLOG_SIZE 3300 + +static void ath6kl_hif_dump(struct ath6kl *ar, u32 fw_dump_addr, u32 len) +{ + __le32 regdump_val[MAX_DUMP_BYTE_NUM_ONE_ITERATION / 4]; + u32 read_len = 0; + u32 i = 0,count; + int ret; + u32 phy_addr = TARG_VTOP(ar->target_type, fw_dump_addr); + + len = (len + 3) & (~0x3); + fw_dump_addr = (fw_dump_addr + 3) & (~0x3); + + while(len) { + read_len = len; + if(read_len > MAX_DUMP_BYTE_NUM_ONE_ITERATION) + read_len = MAX_DUMP_BYTE_NUM_ONE_ITERATION; + + phy_addr = TARG_VTOP(ar->target_type, fw_dump_addr); + ret = ath6kl_diag_read(ar, phy_addr, (u8 *) ®dump_val[0], read_len); + if (ret) { + ath6kl_warn("failed to get register dump: %d\n", ret); + return; + } + + count = read_len / 4; + for (i = 0; i < count; i += 4) { + ath6kl_info("0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n", + le32_to_cpu(fw_dump_addr + 4 * i), + le32_to_cpu(regdump_val[i]), + le32_to_cpu(regdump_val[i + 1]), + le32_to_cpu(regdump_val[i + 2]), + le32_to_cpu(regdump_val[i + 3])); + } + + len -= read_len; + fw_dump_addr += read_len; + } +} + +static void ath6kl_hif_dump_fw_more(struct ath6kl *ar, u32 mask) +{ + u32 fw_dump_addr, fw_dump_len; + u32 address; + int ret; + + if (ar->target_type != TARGET_TYPE_AR6003 ) { + ath6kl_warn("not support dump stack for type: %x\n", ar->target_type); + return; + } + + if(mask & DUMP_MASK_FULL_STACK) { + if(ar->wiphy->hw_version == AR6003_HW_2_1_1_VERSION) { + fw_dump_addr = AR6003_HW211_KERNELSTACK_BASE - DUMP_STACK_OFFSET; + fw_dump_len = AR6003_HW211_KERNELSTACK_SIZE + DUMP_STACK_OFFSET; + ath6kl_warn("firmware stack:0x%x, len:0x%x\n", + AR6003_HW211_KERNELSTACK_BASE, + AR6003_HW211_KERNELSTACK_SIZE); + ath6kl_hif_dump(ar, fw_dump_addr, fw_dump_len); + } + } + + if(mask & DUMP_MASK_DBGLOG) { + if(ar->wiphy->hw_version == AR6003_HW_2_1_1_VERSION) { + address = TARG_VTOP(ar->target_type, + AR6003_HW211_DBGLOG_ADDR); + ret = ath6kl_diag_read32(ar, address, &fw_dump_addr); + if(!ret && fw_dump_addr) { + fw_dump_len = AR6003_HW211_DBGLOG_SIZE; + ath6kl_warn("fw dblog:0x%x, len:0x%x\n", + fw_dump_addr, + AR6003_HW211_DBGLOG_SIZE); + ath6kl_hif_dump(ar, fw_dump_addr, fw_dump_len); + } + } + } +} + static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev) { u32 dummy; @@ -135,7 +218,10 @@ static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev) ath6kl_warn("Failed to clear debug interrupt: %d\n", ret); ath6kl_hif_dump_fw_crash(dev->ar); + ath6kl_hif_dump_fw_more(dev->ar, DUMP_MASK_FULL_STACK | + DUMP_MASK_DBGLOG); ath6kl_read_fwlogs(dev->ar); + ath6kl_recovery_err_notify(dev->ar, ATH6KL_FW_ASSERT); return ret; } @@ -285,7 +371,7 @@ static int ath6kl_hif_proc_counter_intr(struct ath6kl_device *dev) dev->irq_en_reg.cntr_int_status_en; ath6kl_dbg(ATH6KL_DBG_IRQ, - "valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n", + "valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n", counter_int_status); /* @@ -360,7 +446,7 @@ static int ath6kl_hif_proc_cpu_intr(struct ath6kl_device *dev) } ath6kl_dbg(ATH6KL_DBG_IRQ, - "valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n", + "valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n", cpu_int_status); /* Clear the interrupt */ @@ -435,8 +521,9 @@ static int proc_pending_irqs(struct ath6kl_device *dev, bool *done) if (status) goto out; - ath6kl_dump_registers(dev, &dev->irq_proc_reg, - &dev->irq_en_reg); + if (AR_DBG_LVL_CHECK(ATH6KL_DBG_IRQ)) + ath6kl_dump_registers(dev, &dev->irq_proc_reg, + &dev->irq_en_reg); /* Update only those registers that are enabled */ host_int_status = dev->irq_proc_reg.host_int_status & @@ -566,7 +653,6 @@ int ath6kl_hif_intr_bh_handler(struct ath6kl *ar) return status; } -EXPORT_SYMBOL(ath6kl_hif_intr_bh_handler); static int ath6kl_hif_enable_intrs(struct ath6kl_device *dev) { diff --git a/drivers/net/wireless/ath/ath6kl/hif.h b/drivers/net/wireless/ath/ath6kl/hif.h index 20ed6b73..7f757841 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.h +++ b/drivers/net/wireless/ath/ath6kl/hif.h @@ -197,9 +197,9 @@ struct hif_scatter_req { /* bounce buffer for upper layers to copy to/from */ u8 *virt_dma_buf; - struct hif_scatter_item scat_list[1]; - u32 scat_q_depth; + + struct hif_scatter_item scat_list[1]; }; struct ath6kl_irq_proc_registers { @@ -223,7 +223,6 @@ struct ath6kl_irq_enable_reg { } __packed; struct ath6kl_device { - /* protects irq_proc_reg and irq_en_reg below */ spinlock_t lock; struct ath6kl_irq_proc_registers irq_proc_reg; struct ath6kl_irq_enable_reg irq_en_reg; diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc.c index 4849d99c..35cf01bd 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.c +++ b/drivers/net/wireless/ath/ath6kl/htc.c @@ -75,10 +75,7 @@ static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info, * never goes inactive EVER. */ cur_ep_dist->dist_flags |= HTC_EP_ACTIVE; - } else if (cur_ep_dist->svc_id == WMI_DATA_BK_SVC) - /* this is the lowest priority data endpoint */ - /* FIXME: this looks fishy, check */ - cred_info->lowestpri_ep_dist = cur_ep_dist->list; + } /* * Streams have to be created (explicit | implicit) for all @@ -92,6 +89,13 @@ static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info, */ } + /* + * For ath6kl_credit_seek function, + * it use list_for_each_entry_reverse to walk around the whole ep list. + * Therefore assign this lowestpri_ep_dist after walk around the ep_list + */ + cred_info->lowestpri_ep_dist = cur_ep_dist->list; + WARN_ON(cred_info->cur_free_credits <= 0); list_for_each_entry(cur_ep_dist, ep_list, list) { @@ -172,29 +176,31 @@ static void ath6kl_credit_reduce(struct ath6kl_htc_credit_info *cred_info, static void ath6kl_credit_update(struct ath6kl_htc_credit_info *cred_info, struct list_head *epdist_list) { - struct htc_endpoint_credit_dist *cur_list; + struct htc_endpoint_credit_dist *cur_dist_list; - list_for_each_entry(cur_list, epdist_list, list) { - if (cur_list->endpoint == ENDPOINT_0) + list_for_each_entry(cur_dist_list, epdist_list, list) { + if (cur_dist_list->endpoint == ENDPOINT_0) continue; - if (cur_list->cred_to_dist > 0) { - cur_list->credits += cur_list->cred_to_dist; - cur_list->cred_to_dist = 0; - - if (cur_list->credits > cur_list->cred_assngd) + if (cur_dist_list->cred_to_dist > 0) { + cur_dist_list->credits += + cur_dist_list->cred_to_dist; + cur_dist_list->cred_to_dist = 0; + if (cur_dist_list->credits > + cur_dist_list->cred_assngd) ath6kl_credit_reduce(cred_info, - cur_list, - cur_list->cred_assngd); + cur_dist_list, + cur_dist_list->cred_assngd); - if (cur_list->credits > cur_list->cred_norm) - ath6kl_credit_reduce(cred_info, cur_list, - cur_list->cred_norm); + if (cur_dist_list->credits > + cur_dist_list->cred_norm) + ath6kl_credit_reduce(cred_info, cur_dist_list, + cur_dist_list->cred_norm); - if (!(cur_list->dist_flags & HTC_EP_ACTIVE)) { - if (cur_list->txq_depth == 0) + if (!(cur_dist_list->dist_flags & HTC_EP_ACTIVE)) { + if (cur_dist_list->txq_depth == 0) ath6kl_credit_reduce(cred_info, - cur_list, 0); + cur_dist_list, 0); } } } @@ -432,7 +438,7 @@ static void htc_tx_complete(struct htc_endpoint *endpoint, "htc tx complete ep %d pkts %d\n", endpoint->eid, get_queue_depth(txq)); - ath6kl_tx_complete(endpoint->target->dev->ar, txq); + ath6kl_tx_complete(endpoint->target, txq); } static void htc_tx_comp_handler(struct htc_target *target, @@ -462,8 +468,8 @@ static void htc_async_tx_scat_complete(struct htc_target *target, INIT_LIST_HEAD(&tx_compq); ath6kl_dbg(ATH6KL_DBG_HTC, - "htc tx scat complete len %d entries %d\n", - scat_req->len, scat_req->scat_entries); + "htc tx scat complete len %d entries %d\n", + scat_req->len, scat_req->scat_entries); if (scat_req->status) ath6kl_err("send scatter req failed: %d\n", scat_req->status); @@ -601,8 +607,8 @@ static void ath6kl_htc_tx_pkts_get(struct htc_target *target, list); ath6kl_dbg(ATH6KL_DBG_HTC, - "htc tx got packet 0x%p queue depth %d\n", - packet, get_queue_depth(&endpoint->txq)); + "htc tx got packet 0x%p queue depth %d\n", + packet, get_queue_depth(&endpoint->txq)); len = CALC_TXRX_PADDED_LEN(target, packet->act_len + HTC_HDR_LENGTH); @@ -672,7 +678,6 @@ static int ath6kl_htc_tx_setup_scat_list(struct htc_target *target, struct htc_packet *packet; int i, len, rem_scat, cred_pad; int status = 0; - u8 flags; rem_scat = target->max_tx_bndl_sz; @@ -699,9 +704,9 @@ static int ath6kl_htc_tx_setup_scat_list(struct htc_target *target, scat_req->scat_list[i].packet = packet; /* prepare packet and flag message as part of a send bundle */ - flags = packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE; - ath6kl_htc_tx_prep_pkt(packet, flags, - cred_pad, packet->info.tx.seqno); + ath6kl_htc_tx_prep_pkt(packet, + packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE, + cred_pad, packet->info.tx.seqno); /* Make sure the buffer is 4-byte aligned */ ath6kl_htc_tx_buf_align(&packet->buf, packet->act_len + HTC_HDR_LENGTH); @@ -750,8 +755,8 @@ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint, u32 txb_mask; u8 ac = WMM_NUM_AC; - if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) || - (WMI_CONTROL_SVC != endpoint->svc_id)) + if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) && + (WMI_CONTROL_SVC != endpoint->svc_id)) ac = target->dev->ar->ep2ac_map[endpoint->eid]; while (true) { @@ -768,7 +773,7 @@ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint, if (!scat_req) { /* no scatter resources */ ath6kl_dbg(ATH6KL_DBG_HTC, - "htc tx no more scatter resources\n"); + "htc tx no more scatter resources\n"); break; } @@ -841,6 +846,7 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target, int bundle_sent; int n_pkts_bundle; u8 ac = WMM_NUM_AC; + int status; spin_lock_bh(&target->tx_lock); @@ -858,8 +864,8 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target, */ INIT_LIST_HEAD(&txq); - if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) || - (WMI_CONTROL_SVC != endpoint->svc_id)) + if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) && + (WMI_CONTROL_SVC != endpoint->svc_id)) ac = target->dev->ar->ep2ac_map[endpoint->eid]; while (true) { @@ -902,7 +908,12 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target, ath6kl_htc_tx_prep_pkt(packet, packet->info.tx.flags, 0, packet->info.tx.seqno); - ath6kl_htc_tx_issue(target, packet); + status = ath6kl_htc_tx_issue(target, packet); + + if (packet->completion && status) { + packet->status = status; + packet->completion(packet->context, packet); + } } spin_lock_bh(&target->tx_lock); @@ -917,7 +928,7 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target, */ if (!bundle_sent) { if (!(target->tx_bndl_mask & (1 << ac)) && - (ac < WMM_NUM_AC)) { + (ac < WMM_NUM_AC)) { if (++target->ac_tx_count[ac] >= TX_RESUME_BUNDLE_THRESHOLD) { target->ac_tx_count[ac] = 0; @@ -1041,8 +1052,8 @@ static int htc_setup_tx_complete(struct htc_target *target) memcpy(&setup_comp_ext->flags, &flags, sizeof(setup_comp_ext->flags)); set_htc_pkt_info(send_pkt, NULL, (u8 *) setup_comp_ext, - sizeof(struct htc_setup_comp_ext_msg), - ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); + sizeof(struct htc_setup_comp_ext_msg), + ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); } else { struct htc_setup_comp_msg *setup_comp; @@ -1050,8 +1061,8 @@ static int htc_setup_tx_complete(struct htc_target *target) memset(setup_comp, 0, sizeof(struct htc_setup_comp_msg)); setup_comp->msg_id = cpu_to_le16(HTC_MSG_SETUP_COMPLETE_ID); set_htc_pkt_info(send_pkt, NULL, (u8 *) setup_comp, - sizeof(struct htc_setup_comp_msg), - ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); + sizeof(struct htc_setup_comp_msg), + ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); } /* we want synchronous operation */ @@ -1150,9 +1161,9 @@ void ath6kl_htc_flush_txep(struct htc_target *target, packet->status = -ECANCELED; list_del(&packet->list); ath6kl_dbg(ATH6KL_DBG_HTC, - "htc tx flushing pkt 0x%p len %d ep %d tag 0x%x\n", - packet, packet->act_len, - packet->endpoint, packet->info.tx.tag); + "htc tx flushing pkt 0x%p len %d ep %d tag 0x%x\n", + packet, packet->act_len, + packet->endpoint, packet->info.tx.tag); INIT_LIST_HEAD(&container); list_add_tail(&packet->list, &container); @@ -1552,7 +1563,7 @@ static void htc_ctrl_rx(struct htc_target *context, struct htc_packet *packets) if (packets->act_len > 0) { ath6kl_err("htc_ctrl_rx, got message with len:%zu\n", - packets->act_len + HTC_HDR_LENGTH); + packets->act_len + HTC_HDR_LENGTH); ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx unexpected endpoint 0 message", "", @@ -1671,8 +1682,8 @@ static int htc_parse_trailer(struct htc_target *target, } lk_ahd = (struct htc_lookahead_report *) record_buf; - if ((lk_ahd->pre_valid == ((~lk_ahd->post_valid) & 0xFF)) && - next_lk_ahds) { + if ((lk_ahd->pre_valid == ((~lk_ahd->post_valid) & 0xFF)) + && next_lk_ahds) { ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx lk_ahd found pre_valid 0x%x post_valid 0x%x\n", @@ -1685,7 +1696,8 @@ static int htc_parse_trailer(struct htc_target *target, "htc rx next look ahead", "", next_lk_ahds, 4); - *n_lk_ahds = 1; + if (n_lk_ahds) + *n_lk_ahds = 1; } break; case HTC_RECORD_LOOKAHEAD_BUNDLE: @@ -1710,7 +1722,8 @@ static int htc_parse_trailer(struct htc_target *target, bundle_lkahd_rpt++; } - *n_lk_ahds = i; + if (n_lk_ahds) + *n_lk_ahds = i; } break; default: @@ -1874,13 +1887,50 @@ static int ath6kl_htc_rx_process_hdr(struct htc_target *target, return status; } +static void ath6kl_htc_rx_work(struct work_struct *work) +{ + struct htc_target *target; + struct htc_packet *packet, *tmp_pkt; + struct htc_endpoint *endpoint; + + target = container_of(work, struct htc_target, rx_work); + + spin_lock_bh(&target->rx_comp_lock); + list_for_each_entry_safe(packet, tmp_pkt, &target->rx_bufq, list) { + list_del(&packet->list); + + spin_unlock_bh(&target->rx_comp_lock); + endpoint = &target->endpoint[packet->endpoint]; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc rx complete ep %d packet 0x%p\n", + endpoint->eid, packet); + + endpoint->ep_cb.rx(endpoint->target, packet); + spin_lock_bh(&target->rx_comp_lock); + } + spin_unlock_bh(&target->rx_comp_lock); +} + static void ath6kl_htc_rx_complete(struct htc_endpoint *endpoint, struct htc_packet *packet) { + struct htc_target *target; + + if (endpoint->eid == ENDPOINT_0) { ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx complete ep %d packet 0x%p\n", endpoint->eid, packet); endpoint->ep_cb.rx(endpoint->target, packet); + } else { + target = endpoint->target; + + spin_lock_bh(&target->rx_comp_lock); + list_add_tail(&packet->list, &target->rx_bufq); + spin_unlock_bh(&target->rx_comp_lock); + + queue_work(target->rx_wq, &target->rx_work); + } } static int ath6kl_htc_rx_bundle(struct htc_target *target, @@ -2100,13 +2150,13 @@ static int ath6kl_htc_rx_fetch(struct htc_target *target, list_for_each_entry_safe(packet, tmp_pkt, rx_pktq, list) { list_del(&packet->list); htc_reclaim_rxbuf(target, packet, - &target->endpoint[packet->endpoint]); + &target->endpoint[packet->endpoint]); } list_for_each_entry_safe(packet, tmp_pkt, &tmp_rxq, list) { list_del(&packet->list); htc_reclaim_rxbuf(target, packet, - &target->endpoint[packet->endpoint]); + &target->endpoint[packet->endpoint]); } return status; @@ -2238,11 +2288,11 @@ static struct htc_packet *htc_wait_for_ctrl_msg(struct htc_target *target) u32 look_ahead; if (ath6kl_hif_poll_mboxmsg_rx(target->dev, &look_ahead, - HTC_TARGET_RESPONSE_TIMEOUT)) + HTC_TARGET_RESPONSE_TIMEOUT)) return NULL; ath6kl_dbg(ATH6KL_DBG_HTC, - "htc rx wait ctrl look_ahead 0x%X\n", look_ahead); + "htc rx wait ctrl look_ahead 0x%X\n", look_ahead); htc_hdr = (struct htc_frame_hdr *)&look_ahead; @@ -2307,7 +2357,7 @@ int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, depth = get_queue_depth(pkt_queue); ath6kl_dbg(ATH6KL_DBG_HTC, - "htc rx add multiple ep id %d cnt %d len %d\n", + "htc rx add multiple ep id %d cnt %d len %d\n", first_pkt->endpoint, depth, first_pkt->buf_len); endpoint = &target->endpoint[first_pkt->endpoint]; @@ -2333,8 +2383,8 @@ int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, if (target->rx_st_flags & HTC_RECV_WAIT_BUFFERS) { if (target->ep_waiting == first_pkt->endpoint) { ath6kl_dbg(ATH6KL_DBG_HTC, - "htc rx blocked on ep %d, unblocking\n", - target->ep_waiting); + "htc rx blocked on ep %d, unblocking\n", + target->ep_waiting); target->rx_st_flags &= ~HTC_RECV_WAIT_BUFFERS; target->ep_waiting = ENDPOINT_MAX; rx_unblock = true; @@ -2404,7 +2454,6 @@ int ath6kl_htc_conn_service(struct htc_target *target, enum htc_endpoint_id assigned_ep = ENDPOINT_MAX; unsigned int max_msg_sz = 0; int status = 0; - u16 msg_id; ath6kl_dbg(ATH6KL_DBG_HTC, "htc connect service target 0x%p service id 0x%x\n", @@ -2448,10 +2497,9 @@ int ath6kl_htc_conn_service(struct htc_target *target, } resp_msg = (struct htc_conn_service_resp *)rx_pkt->buf; - msg_id = le16_to_cpu(resp_msg->msg_id); - if ((msg_id != HTC_MSG_CONN_SVC_RESP_ID) || - (rx_pkt->act_len < sizeof(*resp_msg))) { + if ((le16_to_cpu(resp_msg->msg_id) != HTC_MSG_CONN_SVC_RESP_ID) + || (rx_pkt->act_len < sizeof(*resp_msg))) { status = -ENOMEM; goto fail_tx; } @@ -2469,7 +2517,8 @@ int ath6kl_htc_conn_service(struct htc_target *target, max_msg_sz = le16_to_cpu(resp_msg->max_msg_sz); } - if (assigned_ep >= ENDPOINT_MAX || !max_msg_sz) { + if (WARN_ON_ONCE(assigned_ep == ENDPOINT_UNUSED || + assigned_ep >= ENDPOINT_MAX || !max_msg_sz)) { status = -ENOMEM; goto fail_tx; } @@ -2502,6 +2551,9 @@ int ath6kl_htc_conn_service(struct htc_target *target, case WMI_DATA_BK_SVC: endpoint->tx_drop_packet_threshold = MAX_DEF_COOKIE_NUM / 3; break; + case WMI_DATA_VO_SVC: + endpoint->tx_drop_packet_threshold = DATA_SYNC_RESERVED; + break; default: endpoint->tx_drop_packet_threshold = MAX_HI_COOKIE_NUM; break; @@ -2677,8 +2729,8 @@ int ath6kl_htc_wait_target(struct htc_target *target) } ath6kl_dbg(ATH6KL_DBG_BOOT, "htc using protocol %s (%d)\n", - (target->htc_tgt_ver == HTC_VERSION_2P0) ? "2.0" : ">= 2.1", - target->htc_tgt_ver); + (target->htc_tgt_ver == HTC_VERSION_2P0) ? "2.0" : ">= 2.1", + target->htc_tgt_ver); if (target->msg_per_bndl_max > 0) htc_setup_msg_bndl(target); @@ -2834,13 +2886,24 @@ void *ath6kl_htc_create(struct ath6kl *ar) goto err_htc_cleanup; } + target->rx_wq = create_singlethread_workqueue("htc_rx"); + if (!target->rx_wq) { + ath6kl_err("unable to create rx_wq workqueue\n"); + status = -ENOMEM; + goto err_htc_cleanup; + } + spin_lock_init(&target->htc_lock); spin_lock_init(&target->rx_lock); spin_lock_init(&target->tx_lock); + spin_lock_init(&target->rx_comp_lock); INIT_LIST_HEAD(&target->free_ctrl_txbuf); INIT_LIST_HEAD(&target->free_ctrl_rxbuf); INIT_LIST_HEAD(&target->cred_dist_list); + INIT_LIST_HEAD(&target->rx_bufq); + + INIT_WORK(&target->rx_work, ath6kl_htc_rx_work); target->dev->ar = ar; target->dev->htc_cnxt = target; @@ -2867,19 +2930,21 @@ void ath6kl_htc_cleanup(struct htc_target *target) { struct htc_packet *packet, *tmp_packet; + destroy_workqueue(target->rx_wq); + /* FIXME: remove check once USB support is implemented */ if (target->dev->ar->hif_type != ATH6KL_HIF_TYPE_USB) ath6kl_hif_cleanup_scatter(target->dev->ar); list_for_each_entry_safe(packet, tmp_packet, - &target->free_ctrl_txbuf, list) { + &target->free_ctrl_txbuf, list) { list_del(&packet->list); kfree(packet->buf_start); kfree(packet); } list_for_each_entry_safe(packet, tmp_packet, - &target->free_ctrl_rxbuf, list) { + &target->free_ctrl_rxbuf, list) { list_del(&packet->list); kfree(packet->buf_start); kfree(packet); diff --git a/drivers/net/wireless/ath/ath6kl/htc.h b/drivers/net/wireless/ath/ath6kl/htc.h index 5027ccc3..83f5aa5a 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.h +++ b/drivers/net/wireless/ath/ath6kl/htc.h @@ -112,7 +112,7 @@ #define HTC_TARGET_DEBUG_INTR_MASK 0x01 #define HTC_TARGET_CREDIT_INTR_MASK 0xF0 -#define HTC_HOST_MAX_MSG_PER_BUNDLE 8 +#define HTC_HOST_MAX_MSG_PER_BUNDLE 7 #define HTC_MIN_HTC_MSGS_TO_BUNDLE 2 /* packet flags */ @@ -523,16 +523,9 @@ struct htc_target { struct ath6kl_htc_credit_info *credit_info; int tgt_creds; unsigned int tgt_cred_sz; - - /* protects free_ctrl_txbuf and free_ctrl_rxbuf */ spinlock_t htc_lock; - - /* FIXME: does this protext rx_bufq and endpoint structures or what? */ spinlock_t rx_lock; - - /* protects endpoint->txq */ spinlock_t tx_lock; - struct ath6kl_device *dev; u32 htc_flags; u32 rx_st_flags; @@ -557,6 +550,13 @@ struct htc_target { /* counts the number of Tx without bundling continously per AC */ u32 ac_tx_count[WMM_NUM_AC]; + + /* protects rx_compq */ + spinlock_t rx_comp_lock; + + struct workqueue_struct *rx_wq; + struct work_struct rx_work; + struct list_head rx_bufq; }; void *ath6kl_htc_create(struct ath6kl *ar); diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 03cae142..e58c90f0 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -18,15 +18,50 @@ #include #include -#include -#include +//#include #include - +#include #include "core.h" #include "cfg80211.h" #include "target.h" #include "debug.h" #include "hif-ops.h" +#include "regd.h" +#include "pm.h" +/* + open log switches. + ATH6KL_DBG_TRC + ATH6KL_DBG_BOOT + ATH6KL_DBG_SUSPEND + ATH6KL_DBG_WLAN_CFG. (defined in debug.h) +*/ +unsigned int debug_mask = 0x142800; + +#ifdef WIFI_POWER_TUNED +unsigned int needPowerTuned = 1; +#endif + +static unsigned int testmode; +/* Set WOW mode as default suspend mode */ +static unsigned int suspend_mode = 3; +static unsigned int wow_mode; +static unsigned int uart_debug; +static unsigned int ar6k_clock = 19200000; +static unsigned short locally_administered_bit; +static unsigned int heart_beat_poll = 2000; + +module_param(debug_mask, uint, 0644); +module_param(testmode, uint, 0644); +module_param(suspend_mode, uint, 0644); +module_param(wow_mode, uint, 0644); +module_param(uart_debug, uint, 0644); + +#ifdef WIFI_POWER_TUNED +module_param(needPowerTuned, uint, 0644); +#endif + +module_param(ar6k_clock, uint, 0644); +module_param(locally_administered_bit, ushort, 0644); +module_param(heart_beat_poll, uint, 0644); +MODULE_PARM_DESC(heart_beat_poll, "Enable fw error detection periodic" \ + "polling. This also specifies the polling interval in msecs"); static const struct ath6kl_hw hw_list[] = { { @@ -41,6 +76,7 @@ static const struct ath6kl_hw hw_list[] = { /* hw2.0 needs override address hardcoded */ .app_start_override_addr = 0x944C00, + .flags = 0, .fw = { .dir = AR6003_HW_2_0_FW_DIR, @@ -63,6 +99,7 @@ static const struct ath6kl_hw hw_list[] = { .refclk_hz = 26000000, .uarttx_pin = 8, .testscript_addr = 0x57ef74, + .flags = 0, .fw = { .dir = AR6003_HW_2_1_1_FW_DIR, @@ -75,7 +112,7 @@ static const struct ath6kl_hw hw_list[] = { }, .fw_board = AR6003_HW_2_1_1_BOARD_DATA_FILE, - .fw_default_board = AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE, + .fw_default_board = AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE, }, { .id = AR6004_HW_1_0_VERSION, @@ -87,6 +124,7 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x433900, .refclk_hz = 26000000, .uarttx_pin = 11, + .flags = ATH6KL_HW_FLAG_64BIT_RATES, .fw = { .dir = AR6004_HW_1_0_FW_DIR, @@ -106,6 +144,7 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x43d400, .refclk_hz = 40000000, .uarttx_pin = 11, + .flags = ATH6KL_HW_FLAG_64BIT_RATES, .fw = { .dir = AR6004_HW_1_1_FW_DIR, @@ -140,7 +179,7 @@ static const struct ath6kl_hw hw_list[] = { * If the firmware successly roams within the disconnect timeout * it sends a new connect event */ -#define WLAN_CONFIG_DISCONNECT_TIMEOUT 10 +#define WLAN_CONFIG_DISCONNECT_TIMEOUT 2 //10-->2 #define ATH6KL_DATA_OFFSET 64 @@ -150,8 +189,8 @@ struct sk_buff *ath6kl_buf_alloc(int size) u16 reserved; /* Add chacheline space at front and back of buffer */ - reserved = (2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET + - sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES; + reserved = roundup((2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET + + sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES, 4); skb = dev_alloc_skb(size + reserved); if (skb) @@ -297,7 +336,7 @@ static int ath6kl_init_service_ep(struct ath6kl *ar) */ connect.conn_flags |= HTC_CONN_FLGS_REDUCE_CRED_DRIB; connect.conn_flags &= ~HTC_CONN_FLGS_THRESH_MASK; - connect.conn_flags |= HTC_CONN_FLGS_THRESH_LVL_HALF; + connect.conn_flags |= HTC_CONN_FLGS_THRESH_LVL_QUAT; connect.svc_id = WMI_DATA_BE_SVC; @@ -352,7 +391,11 @@ static int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val, blk_size |= ((u32)htc_ctrl_buf) << 16; /* set the host interest area for the block size */ - status = ath6kl_bmi_write_hi32(ar, hi_mbox_io_block_sz, blk_size); + status = ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_mbox_io_block_sz)), + (u8 *)&blk_size, + 4); if (status) { ath6kl_err("bmi_write_memory for IO block size failed\n"); goto out; @@ -364,8 +407,11 @@ static int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val, if (mbox_isr_yield_val) { /* set the host interest area for the mbox ISR yield limit */ - status = ath6kl_bmi_write_hi32(ar, hi_mbox_isr_yield_limit, - mbox_isr_yield_val); + status = ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_mbox_isr_yield_limit)), + (u8 *)&mbox_isr_yield_val, + 4); if (status) { ath6kl_err("bmi_write_memory for yield limit failed\n"); goto out; @@ -378,6 +424,7 @@ static int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val, static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) { + int status = 0; int ret; /* @@ -385,54 +432,43 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) * default values. Required if checksum offload is needed. Set * RxMetaVersion to 2. */ - ret = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, idx, - ar->rx_meta_ver, 0, 0); - if (ret) { - ath6kl_err("unable to set the rx frame format: %d\n", ret); - return ret; + if (ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, idx, + ar->rx_meta_ver, 0, 0)) { + ath6kl_err("unable to set the rx frame format\n"); + status = -EIO; } - if (ar->conf_flags & ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN) { - ret = ath6kl_wmi_pmparams_cmd(ar->wmi, idx, 0, 1, 0, 0, 1, - IGNORE_PS_FAIL_DURING_SCAN); - if (ret) { - ath6kl_err("unable to set power save fail event policy: %d\n", - ret); - return ret; + if (ar->conf_flags & ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN) + if ((ath6kl_wmi_pmparams_cmd(ar->wmi, idx, 0, 1, 0, 0, 1, + IGNORE_POWER_SAVE_FAIL_EVENT_DURING_SCAN)) != 0) { + ath6kl_err("unable to set power save fail event policy\n"); + status = -EIO; } - } - if (!(ar->conf_flags & ATH6KL_CONF_IGNORE_ERP_BARKER)) { - ret = ath6kl_wmi_set_lpreamble_cmd(ar->wmi, idx, 0, - WMI_FOLLOW_BARKER_IN_ERP); - if (ret) { - ath6kl_err("unable to set barker preamble policy: %d\n", - ret); - return ret; + if (!(ar->conf_flags & ATH6KL_CONF_IGNORE_ERP_BARKER)) + if ((ath6kl_wmi_set_lpreamble_cmd(ar->wmi, idx, 0, + WMI_DONOT_IGNORE_BARKER_IN_ERP)) != 0) { + ath6kl_err("unable to set barker preamble policy\n"); + status = -EIO; } - } - ret = ath6kl_wmi_set_keepalive_cmd(ar->wmi, idx, - WLAN_CONFIG_KEEP_ALIVE_INTERVAL); - if (ret) { - ath6kl_err("unable to set keep alive interval: %d\n", ret); - return ret; + if (ath6kl_wmi_set_keepalive_cmd(ar->wmi, idx, + WLAN_CONFIG_KEEP_ALIVE_INTERVAL)) { + ath6kl_err("unable to set keep alive interval\n"); + status = -EIO; } - ret = ath6kl_wmi_disctimeout_cmd(ar->wmi, idx, - WLAN_CONFIG_DISCONNECT_TIMEOUT); - if (ret) { - ath6kl_err("unable to set disconnect timeout: %d\n", ret); - return ret; + if (ath6kl_wmi_disctimeout_cmd(ar->wmi, idx, + WLAN_CONFIG_DISCONNECT_TIMEOUT)) { + ath6kl_err("unable to set disconnect timeout\n"); + status = -EIO; } - if (!(ar->conf_flags & ATH6KL_CONF_ENABLE_TX_BURST)) { - ret = ath6kl_wmi_set_wmm_txop(ar->wmi, idx, WMI_TXOP_DISABLED); - if (ret) { - ath6kl_err("unable to set txop bursting: %d\n", ret); - return ret; + if (!(ar->conf_flags & ATH6KL_CONF_ENABLE_TX_BURST)) + if (ath6kl_wmi_set_wmm_txop(ar->wmi, idx, WMI_TXOP_DISABLED)) { + ath6kl_err("unable to set txop bursting\n"); + status = -EIO; } - } if (ar->p2p && (ar->vif_max == 1 || idx)) { ret = ath6kl_wmi_info_req_cmd(ar->wmi, idx, @@ -443,7 +479,7 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P " "capabilities (%d) - assuming P2P not " "supported\n", ret); - ar->p2p = false; + ar->p2p = 0; } } @@ -456,7 +492,7 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) } } - return ret; + return status; } int ath6kl_configure_target(struct ath6kl *ar) @@ -465,8 +501,9 @@ int ath6kl_configure_target(struct ath6kl *ar) u8 fw_iftype, fw_mode = 0, fw_submode = 0; int i, status; - param = !!(ar->conf_flags & ATH6KL_CONF_UART_DEBUG); - if (ath6kl_bmi_write_hi32(ar, hi_serial_enable, param)) { + param = uart_debug; + if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_serial_enable)), (u8 *)¶m, 4)) { ath6kl_err("bmi_write_memory for uart debug failed\n"); return -EIO; } @@ -485,25 +522,37 @@ int ath6kl_configure_target(struct ath6kl *ar) fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS); /* - * By default, submodes : + * Submodes when fw does not support dynamic interface + * switching: * vif[0] - AP/STA/IBSS * vif[1] - "P2P dev"/"P2P GO"/"P2P Client" * vif[2] - "P2P dev"/"P2P GO"/"P2P Client" + * Otherwise, All the interface are initialized to p2p dev. */ - for (i = 0; i < ar->max_norm_iface; i++) - fw_submode |= HI_OPTION_FW_SUBMODE_NONE << - (i * HI_OPTION_FW_SUBMODE_BITS); + if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, + ar->fw_capabilities)) { + for (i = 0; i < ar->vif_max; i++) + fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << + (i * HI_OPTION_FW_SUBMODE_BITS); + } else { + for (i = 0; i < ar->max_norm_iface; i++) + fw_submode |= HI_OPTION_FW_SUBMODE_NONE << + (i * HI_OPTION_FW_SUBMODE_BITS); - for (i = ar->max_norm_iface; i < ar->vif_max; i++) - fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << - (i * HI_OPTION_FW_SUBMODE_BITS); + for (i = ar->max_norm_iface; i < ar->vif_max; i++) + fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << + (i * HI_OPTION_FW_SUBMODE_BITS); - if (ar->p2p && ar->vif_max == 1) - fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV; + if (ar->p2p && ar->vif_max == 1) + fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV; + } - if (ath6kl_bmi_write_hi32(ar, hi_app_host_interest, - HTC_PROTOCOL_VERSION) != 0) { + param = HTC_PROTOCOL_VERSION; + if (ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_app_host_interest)), + (u8 *)¶m, 4) != 0) { ath6kl_err("bmi_write_memory for htc version failed\n"); return -EIO; } @@ -511,7 +560,10 @@ int ath6kl_configure_target(struct ath6kl *ar) /* set the firmware mode to STA/IBSS/AP */ param = 0; - if (ath6kl_bmi_read_hi32(ar, hi_option_flag, ¶m) != 0) { + if (ath6kl_bmi_read(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_option_flag)), + (u8 *)¶m, 4) != 0) { ath6kl_err("bmi_read_memory for setting fwmode failed\n"); return -EIO; } @@ -523,7 +575,11 @@ int ath6kl_configure_target(struct ath6kl *ar) param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT); param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT); - if (ath6kl_bmi_write_hi32(ar, hi_option_flag, param) != 0) { + if (ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_option_flag)), + (u8 *)¶m, + 4) != 0) { ath6kl_err("bmi_write_memory for setting fwmode failed\n"); return -EIO; } @@ -542,13 +598,16 @@ int ath6kl_configure_target(struct ath6kl *ar) param = ar->hw.board_ext_data_addr; ram_reserved_size = ar->hw.reserved_ram_size; - if (ath6kl_bmi_write_hi32(ar, hi_board_ext_data, param) != 0) { + if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_ext_data)), + (u8 *)¶m, 4) != 0) { ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); return -EIO; } - if (ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, - ram_reserved_size) != 0) { + if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_end_ram_reserve_sz)), + (u8 *)&ram_reserved_size, 4) != 0) { ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n"); return -EIO; } @@ -559,19 +618,59 @@ int ath6kl_configure_target(struct ath6kl *ar) return -EIO; /* Configure GPIO AR600x UART */ - status = ath6kl_bmi_write_hi32(ar, hi_dbg_uart_txpin, - ar->hw.uarttx_pin); + param = ar->hw.uarttx_pin; + status = ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_dbg_uart_txpin)), + (u8 *)¶m, 4); if (status) return status; /* Configure target refclk_hz */ - status = ath6kl_bmi_write_hi32(ar, hi_refclk_hz, ar->hw.refclk_hz); + param = ar->hw.refclk_hz; + status = ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_refclk_hz)), + (u8 *)¶m, 4); if (status) return status; return 0; } +void ath6kl_core_free(struct ath6kl *ar) +{ + wiphy_free(ar->wiphy); +} + +void ath6kl_core_cleanup(struct ath6kl *ar) +{ + ath6kl_hif_power_off(ar); + + ath6kl_recovery_cleanup(ar); + + destroy_workqueue(ar->ath6kl_wq); + + if (ar->htc_target) + ath6kl_htc_cleanup(ar->htc_target); + + ath6kl_cookie_cleanup(ar); + + ath6kl_cleanup_amsdu_rxbufs(ar); + + ath6kl_bmi_cleanup(ar); + + ath6kl_debug_cleanup(ar); + + kfree(ar->fw_board); + kfree(ar->fw_otp); + vfree(ar->fw); + kfree(ar->fw_patch); + kfree(ar->fw_testscript); + + ath6kl_deinit_ieee80211_hw(ar); +} + /* firmware upload */ static int ath6kl_get_fw(struct ath6kl *ar, const char *filename, u8 **fw, size_t *fw_len) @@ -594,6 +693,7 @@ static int ath6kl_get_fw(struct ath6kl *ar, const char *filename, return ret; } +#if 0 #ifdef CONFIG_OF /* * Check the device tree for a board-id and use it to construct @@ -636,7 +736,7 @@ static bool check_device_tree(struct ath6kl *ar) return false; } #endif /* CONFIG_OF */ - +#endif static int ath6kl_fetch_board_file(struct ath6kl *ar) { const char *filename; @@ -656,12 +756,12 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar) /* managed to get proper board file */ return 0; } - +#if 0 if (check_device_tree(ar)) { /* got board file from device tree */ return 0; } - +#endif /* there was no proper board file, try to use default instead */ ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n", filename, ret); @@ -715,12 +815,12 @@ static int ath6kl_fetch_testmode_file(struct ath6kl *ar) char filename[100]; int ret; - if (ar->testmode == 0) + if (testmode == 0) return 0; - ath6kl_dbg(ATH6KL_DBG_BOOT, "testmode %d\n", ar->testmode); + ath6kl_dbg(ATH6KL_DBG_BOOT, "testmode %d\n", testmode); - if (ar->testmode == 2) { + if (testmode == 2) { if (ar->hw.fw.utf == NULL) { ath6kl_warn("testmode 2 not supported\n"); return -EOPNOTSUPP; @@ -743,7 +843,7 @@ static int ath6kl_fetch_testmode_file(struct ath6kl *ar) ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); if (ret) { ath6kl_err("Failed to get testmode %d firmware file %s: %d\n", - ar->testmode, filename, ret); + testmode, filename, ret); return ret; } @@ -805,7 +905,7 @@ static int ath6kl_fetch_testscript_file(struct ath6kl *ar) char filename[100]; int ret; - if (ar->testmode != 2) + if (testmode != 2) return 0; if (ar->fw_testscript != NULL) @@ -815,13 +915,13 @@ static int ath6kl_fetch_testscript_file(struct ath6kl *ar) return 0; snprintf(filename, sizeof(filename), "%s/%s", - ar->hw.fw.dir, ar->hw.fw.testscript); + ar->hw.fw.dir, ar->hw.fw.testscript); ret = ath6kl_get_fw(ar, filename, &ar->fw_testscript, &ar->fw_testscript_len); if (ret) { ath6kl_err("Failed to get testscript file %s: %d\n", - filename, ret); + filename, ret); return ret; } @@ -903,9 +1003,17 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) } switch (ie_id) { + case ATH6KL_FW_IE_FW_VERSION: + strlcpy(ar->wiphy->fw_version, data, + sizeof(ar->wiphy->fw_version)); + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "found fw version %s\n", + ar->wiphy->fw_version); + break; case ATH6KL_FW_IE_OTP_IMAGE: ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie (%zd B)\n", - ie_len); + ie_len); ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL); @@ -918,24 +1026,25 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) break; case ATH6KL_FW_IE_FW_IMAGE: ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie (%zd B)\n", - ie_len); + ie_len); /* in testmode we already might have a fw file */ if (ar->fw != NULL) break; - ar->fw = kmemdup(data, ie_len, GFP_KERNEL); + ar->fw = vmalloc(ie_len); if (ar->fw == NULL) { ret = -ENOMEM; goto out; } + memcpy(ar->fw, data, ie_len); ar->fw_len = ie_len; break; case ATH6KL_FW_IE_PATCH_IMAGE: ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch image ie (%zd B)\n", - ie_len); + ie_len); ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL); @@ -955,9 +1064,6 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) ar->hw.reserved_ram_size); break; case ATH6KL_FW_IE_CAPABILITIES: - if (ie_len < DIV_ROUND_UP(ATH6KL_FW_CAPABILITY_MAX, 8)) - break; - ath6kl_dbg(ATH6KL_DBG_BOOT, "found firmware capabilities ie (%zd B)\n", ie_len); @@ -966,6 +1072,9 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) index = i / 8; bit = i % 8; + if (index == ie_len) + break; + if (data[index] & (1 << bit)) __set_bit(i, ar->fw_capabilities); } @@ -1020,6 +1129,8 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) data += ie_len; }; + __set_bit(ATH6KL_FW_CAPABILITY_MAC_ACL, ar->fw_capabilities); + ret = 0; out: release_firmware(fw); @@ -1027,7 +1138,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) return ret; } -int ath6kl_init_fetch_firmwares(struct ath6kl *ar) +static int ath6kl_fetch_firmwares(struct ath6kl *ar) { int ret; @@ -1039,6 +1150,12 @@ int ath6kl_init_fetch_firmwares(struct ath6kl *ar) if (ret) return ret; + ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API4_FILE); + if (ret == 0) { + ar->fw_api = 4; + goto out; + } + ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API3_FILE); if (ret == 0) { ar->fw_api = 3; @@ -1079,14 +1196,22 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) */ if (ar->hw.board_addr != 0) { board_address = ar->hw.board_addr; - ath6kl_bmi_write_hi32(ar, hi_board_data, - board_address); + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_data)), + (u8 *) &board_address, 4); } else { - ath6kl_bmi_read_hi32(ar, hi_board_data, &board_address); + ath6kl_bmi_read(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_data)), + (u8 *) &board_address, 4); } /* determine where in target ram to write extended board data */ - ath6kl_bmi_read_hi32(ar, hi_board_ext_data, &board_ext_address); + ath6kl_bmi_read(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_ext_data)), + (u8 *) &board_ext_address, 4); if (ar->target_type == TARGET_TYPE_AR6003 && board_ext_address == 0) { @@ -1131,7 +1256,10 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) /* record that extended board data is initialized */ param = (board_ext_data_size << 16) | 1; - ath6kl_bmi_write_hi32(ar, hi_board_ext_data_config, param); + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_ext_data_config)), + (unsigned char *) ¶m, 4); } if (ar->fw_board_len < board_data_size) { @@ -1152,7 +1280,11 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) } /* record the fact that Board Data IS initialized */ - ath6kl_bmi_write_hi32(ar, hi_board_data_initialized, 1); + param = 1; + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_data_initialized)), + (u8 *)¶m, 4); return ret; } @@ -1179,7 +1311,10 @@ static int ath6kl_upload_otp(struct ath6kl *ar) } /* read firmware start address */ - ret = ath6kl_bmi_read_hi32(ar, hi_app_start, &address); + ret = ath6kl_bmi_read(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_app_start)), + (u8 *) &address, sizeof(address)); if (ret) { ath6kl_err("Failed to read hi_app_start: %d\n", ret); @@ -1198,7 +1333,14 @@ static int ath6kl_upload_otp(struct ath6kl *ar) /* execute the OTP code */ ath6kl_dbg(ATH6KL_DBG_BOOT, "executing OTP at 0x%x\n", ar->hw.app_start_override_addr); + +#ifdef CONFIG_MACH_PX + /* SOFTMAC has higher priority than OTP MAC */ + param = 1; +#else param = 0; +#endif + ath6kl_bmi_execute(ar, ar->hw.app_start_override_addr, ¶m); return ret; @@ -1237,7 +1379,7 @@ static int ath6kl_upload_firmware(struct ath6kl *ar) static int ath6kl_upload_patch(struct ath6kl *ar) { - u32 address; + u32 address, param; int ret; if (ar->fw_patch == NULL) @@ -1254,17 +1396,21 @@ static int ath6kl_upload_patch(struct ath6kl *ar) return ret; } - ath6kl_bmi_write_hi32(ar, hi_dset_list_head, address); + param = address; + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_dset_list_head)), + (unsigned char *) ¶m, 4); return 0; } static int ath6kl_upload_testscript(struct ath6kl *ar) { - u32 address; + u32 address, param; int ret; - if (ar->testmode != 2) + if (testmode != 2) return 0; if (ar->fw_testscript == NULL) @@ -1273,7 +1419,7 @@ static int ath6kl_upload_testscript(struct ath6kl *ar) address = ar->hw.testscript_addr; ath6kl_dbg(ATH6KL_DBG_BOOT, "writing testscript to 0x%x (%zd B)\n", - address, ar->fw_testscript_len); + address, ar->fw_testscript_len); ret = ath6kl_bmi_write(ar, address, ar->fw_testscript, ar->fw_testscript_len); @@ -1282,9 +1428,23 @@ static int ath6kl_upload_testscript(struct ath6kl *ar) return ret; } - ath6kl_bmi_write_hi32(ar, hi_ota_testscript, address); - ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, 4096); - ath6kl_bmi_write_hi32(ar, hi_test_apps_related, 1); + param = address; + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_ota_testscript)), + (unsigned char *) ¶m, 4); + + param = 4096; + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_end_ram_reserve_sz)), + (unsigned char *) ¶m, 4); + + param = 1; + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_test_apps_related)), + (unsigned char *) ¶m, 4); return 0; } @@ -1295,7 +1455,7 @@ static int ath6kl_init_upload(struct ath6kl *ar) int status = 0; if (ar->target_type != TARGET_TYPE_AR6003 && - ar->target_type != TARGET_TYPE_AR6004) + ar->target_type != TARGET_TYPE_AR6004) return -EINVAL; /* temporarily disable system sleep */ @@ -1356,6 +1516,12 @@ static int ath6kl_init_upload(struct ath6kl *ar) ar->version.target_ver == AR6003_HW_2_1_1_VERSION) { ath6kl_err("temporary war to avoid sdio crc error\n"); + param = 0x28; + address = GPIO_BASE_ADDRESS + GPIO_PIN9_ADDRESS; + status = ath6kl_bmi_reg_write(ar, address, param); + if (status) + return status; + param = 0x20; address = GPIO_BASE_ADDRESS + GPIO_PIN10_ADDRESS; @@ -1379,6 +1545,8 @@ static int ath6kl_init_upload(struct ath6kl *ar) return status; } + ath6kl_bmi_reg_write(ar, 0x540678, ar6k_clock); + /* write EEPROM data to Target RAM */ status = ath6kl_upload_board_file(ar); if (status) @@ -1410,7 +1578,7 @@ static int ath6kl_init_upload(struct ath6kl *ar) return status; address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS; - param = options | 0x20; + param = options & ~0x20; /* enable ANI */ status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; @@ -1418,9 +1586,9 @@ static int ath6kl_init_upload(struct ath6kl *ar) return status; } -int ath6kl_init_hw_params(struct ath6kl *ar) +static int ath6kl_init_hw_params(struct ath6kl *ar) { - const struct ath6kl_hw *uninitialized_var(hw); + const struct ath6kl_hw *hw; int i; for (i = 0; i < ARRAY_SIZE(hw_list); i++) { @@ -1465,7 +1633,7 @@ static const char *ath6kl_init_get_hif_name(enum ath6kl_hif_type type) return NULL; } -int ath6kl_init_hw_start(struct ath6kl *ar) +static int __ath6kl_init_hw_start(struct ath6kl *ar) { long timeleft; int ret, i; @@ -1484,6 +1652,8 @@ int ath6kl_init_hw_start(struct ath6kl *ar) if (ret) goto err_power_off; + if (ath6kl_set_reg_dmn(ar)) + goto err_power_off; /* Do we need to finish the BMI phase */ /* FIXME: return error from ath6kl_bmi_done() */ if (ath6kl_bmi_done(ar)) { @@ -1497,11 +1667,13 @@ int ath6kl_init_hw_start(struct ath6kl *ar) * size. */ if (ath6kl_htc_wait_target(ar->htc_target)) { + ath6kl_err("htc wait target failed: %d\n", ret); ret = -EIO; goto err_power_off; } if (ath6kl_init_service_ep(ar)) { + ath6kl_err("Endpoint service initilisation failed: %d\n", ret); ret = -EIO; goto err_cleanup_scatter; } @@ -1543,6 +1715,7 @@ int ath6kl_init_hw_start(struct ath6kl *ar) } if (!timeleft || signal_pending(current)) { + clear_bit(WMI_READY, &ar->flag); ath6kl_err("wmi is not ready or wait was interrupted\n"); ret = -EIO; goto err_htc_stop; @@ -1561,8 +1734,6 @@ int ath6kl_init_hw_start(struct ath6kl *ar) goto err_htc_stop; } - ar->state = ATH6KL_STATE_ON; - return 0; err_htc_stop: @@ -1575,7 +1746,18 @@ int ath6kl_init_hw_start(struct ath6kl *ar) return ret; } -int ath6kl_init_hw_stop(struct ath6kl *ar) +int ath6kl_init_hw_start(struct ath6kl *ar) +{ + int err; + + err = __ath6kl_init_hw_start(ar); + if (err) + return err; + ar->state = ATH6KL_STATE_ON; + return 0; +} + +static int __ath6kl_init_hw_stop(struct ath6kl *ar) { int ret; @@ -1591,12 +1773,241 @@ int ath6kl_init_hw_stop(struct ath6kl *ar) if (ret) ath6kl_warn("failed to power off hif: %d\n", ret); - ar->state = ATH6KL_STATE_OFF; + return 0; +} +int ath6kl_init_hw_stop(struct ath6kl *ar) +{ + int err; + + err = __ath6kl_init_hw_stop(ar); + if (err) + return err; + ar->state = ATH6KL_STATE_OFF; return 0; } -/* FIXME: move this to cfg80211.c and rename to ath6kl_cfg80211_vif_stop() */ +int ath6kl_core_init(struct ath6kl *ar) +{ + struct ath6kl_bmi_target_info targ_info; + struct net_device *ndev; + int ret = 0, i; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) + struct net_device *ndev_p2p0; +#endif + ar->ath6kl_wq = create_singlethread_workqueue("ath6kl"); + if (!ar->ath6kl_wq) + return -ENOMEM; + + ret = ath6kl_bmi_init(ar); + if (ret) + goto err_wq; + + /* + * Turn on power to get hardware (target) version and leave power + * on delibrately as we will boot the hardware anyway within few + * seconds. + */ + ret = ath6kl_hif_power_on(ar); + if (ret) + goto err_bmi_cleanup; + + ret = ath6kl_bmi_get_target_info(ar, &targ_info); + if (ret) + goto err_power_off; + + ar->version.target_ver = le32_to_cpu(targ_info.version); + ar->target_type = le32_to_cpu(targ_info.type); + ar->wiphy->hw_version = le32_to_cpu(targ_info.version); + + ret = ath6kl_init_hw_params(ar); + if (ret) + goto err_power_off; + + ar->htc_target = ath6kl_htc_create(ar); + + if (!ar->htc_target) { + ret = -ENOMEM; + goto err_power_off; + } + + ret = ath6kl_fetch_firmwares(ar); + if (ret) + goto err_htc_cleanup; + ath6kl_mangle_mac_address(ar, locally_administered_bit); + + /* FIXME: we should free all firmwares in the error cases below */ + + /* Indicate that WMI is enabled (although not ready yet) */ + set_bit(WMI_ENABLED, &ar->flag); + ar->wmi = ath6kl_wmi_init(ar); + if (!ar->wmi) { + ath6kl_err("failed to initialize wmi\n"); + ret = -EIO; + goto err_htc_cleanup; + } + + ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi); + + ret = ath6kl_register_ieee80211_hw(ar); + if (ret) + goto err_node_cleanup; + + ret = ath6kl_debug_init(ar); + if (ret) { + wiphy_unregister(ar->wiphy); + goto err_node_cleanup; + } + + for (i = 0; i < ar->vif_max; i++) + ar->avail_idx_map |= BIT(i); + + rtnl_lock(); + + /* Add an initial station interface */ + ndev = ath6kl_interface_add(ar, "eth%d", NL80211_IFTYPE_STATION, 0, + INFRA_NETWORK); + + rtnl_unlock(); + + if (!ndev) { + ath6kl_err("Failed to instantiate a network device\n"); + ret = -ENOMEM; + wiphy_unregister(ar->wiphy); + goto err_debug_init; + } + + + ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", + __func__, ndev->name, ndev, ar); + + /* setup access class priority mappings */ + ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest */ + ar->ac_stream_pri_map[WMM_AC_BE] = 1; + ar->ac_stream_pri_map[WMM_AC_VI] = 2; + ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */ + + /* allocate some buffers that handle larger AMSDU frames */ + ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS); + + ath6kl_cookie_init(ar); + + ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | + ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; + + if (suspend_mode && + suspend_mode >= WLAN_POWER_STATE_CUT_PWR && + suspend_mode <= WLAN_POWER_STATE_WOW) + ar->suspend_mode = suspend_mode; + else + ar->suspend_mode = 0; + + if (suspend_mode == WLAN_POWER_STATE_WOW && + (wow_mode == WLAN_POWER_STATE_CUT_PWR || + wow_mode == WLAN_POWER_STATE_DEEP_SLEEP)) + ar->wow_suspend_mode = wow_mode; + else + ar->wow_suspend_mode = 0; + + ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM | + WIPHY_FLAG_HAVE_AP_SME | + WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | + WIPHY_FLAG_SUPPORTS_ACS; + + if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, ar->fw_capabilities)) + ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + + ar->wiphy->probe_resp_offload = + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U; + + set_bit(FIRST_BOOT, &ar->flag); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) + ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM; +#endif + + ret = ath6kl_init_hw_start(ar); + if (ret) { + ath6kl_err("Failed to start hardware: %d\n", ret); + goto err_rxbuf_cleanup; + } + + /* give our connected endpoints some buffers */ + ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep); + ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]); + + /* + * Set mac address which is received in ready event + * FIXME: Move to ath6kl_interface_add() + */ + memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) + rtnl_lock(); + ndev_p2p0 = ath6kl_cfg80211_add_p2p0_iface(ar); + rtnl_unlock(); + + if (!ndev_p2p0) { + ath6kl_err("Failed to create p2p0 iface\n"); + ret = -ENOMEM; + goto err_rxbuf_cleanup; + } +#endif + if (heart_beat_poll && + test_bit(ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, + ar->fw_capabilities)) + ar->fw_recovery.hb_poll = heart_beat_poll; + + ath6kl_recovery_init(ar); + + return ret; + +err_rxbuf_cleanup: + ath6kl_htc_flush_rx_buf(ar->htc_target); + ath6kl_cleanup_amsdu_rxbufs(ar); + rtnl_lock(); + ath6kl_deinit_if_data(netdev_priv(ndev)); + rtnl_unlock(); + wiphy_unregister(ar->wiphy); +err_debug_init: + ath6kl_debug_cleanup(ar); +err_node_cleanup: + ath6kl_cleanup_android_resource(ar); + ath6kl_wmi_shutdown(ar->wmi); + clear_bit(WMI_ENABLED, &ar->flag); + ar->wmi = NULL; +err_htc_cleanup: + ath6kl_htc_cleanup(ar->htc_target); +err_power_off: + ath6kl_hif_power_off(ar); +err_bmi_cleanup: + ath6kl_bmi_cleanup(ar); +err_wq: + destroy_workqueue(ar->ath6kl_wq); + + return ret; +} + +void ath6kl_init_hw_restart(struct ath6kl *ar) +{ + clear_bit(WMI_READY, &ar->flag); + + ath6kl_cfg80211_stop_all(ar); + + if (__ath6kl_init_hw_stop(ar)) { + ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to stop during fw error recovery\n"); + return; + } + + if (__ath6kl_init_hw_start(ar)) { + ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to restart during fw error recovery\n"); + return; + } +} + void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready) { static u8 bcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; @@ -1623,6 +2034,9 @@ void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready) cfg80211_scan_done(vif->scan_req, true); vif->scan_req = NULL; } + + /* need to clean up enhanced bmiss detection fw state */ + ath6kl_cfg80211_sta_bmiss_enhance(vif, false); } void ath6kl_stop_txrx(struct ath6kl *ar) @@ -1646,7 +2060,7 @@ void ath6kl_stop_txrx(struct ath6kl *ar) spin_unlock_bh(&ar->list_lock); ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag)); rtnl_lock(); - ath6kl_cfg80211_vif_cleanup(vif); + ath6kl_deinit_if_data(vif); rtnl_unlock(); spin_lock_bh(&ar->list_lock); } @@ -1654,6 +2068,8 @@ void ath6kl_stop_txrx(struct ath6kl *ar) clear_bit(WMI_READY, &ar->flag); + del_timer_sync(&ar->fw_recovery.hb_timer); + /* * After wmi_shudown all WMI events will be dropped. We * need to cleanup the buffers allocated in AP mode and @@ -1676,11 +2092,8 @@ void ath6kl_stop_txrx(struct ath6kl *ar) * configure NOT to reset the target during a debug session. */ ath6kl_dbg(ATH6KL_DBG_TRC, - "attempting to reset target on instance destroy\n"); + "attempting to reset target on instance destroy\n"); ath6kl_reset_device(ar, ar->target_type, true, true); clear_bit(WLAN_ENABLED, &ar->flag); - - up(&ar->sem); } -EXPORT_SYMBOL(ath6kl_stop_txrx); diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 229e1922..8a223bd9 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -20,6 +20,7 @@ #include "cfg80211.h" #include "target.h" #include "debug.h" +#include "wmiconfig.h" struct ath6kl_sta *ath6kl_find_sta(struct ath6kl_vif *vif, u8 *node_addr) { @@ -147,14 +148,23 @@ enum htc_endpoint_id ath6kl_ac2_endpoint_id(void *devt, u8 ac) return ar->ac2ep_map[ac]; } -struct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar) +struct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar, bool isctrl) { struct ath6kl_cookie *cookie; - cookie = ar->cookie_list; - if (cookie != NULL) { - ar->cookie_list = cookie->arc_list_next; - ar->cookie_count--; + /* If this cookie is for control packet*/ + if (isctrl) { + cookie = ar->wmi_cookie_list; + if (cookie != NULL) { + ar->wmi_cookie_list = cookie->arc_list_next; + ar->wmi_cookie_count--; + } + } else { + cookie = ar->cookie_list; + if (cookie != NULL) { + ar->cookie_list = cookie->arc_list_next; + ar->cookie_count--; + } } return cookie; @@ -164,31 +174,54 @@ void ath6kl_cookie_init(struct ath6kl *ar) { u32 i; + /* Initilize data cookie list */ ar->cookie_list = NULL; ar->cookie_count = 0; memset(ar->cookie_mem, 0, sizeof(ar->cookie_mem)); for (i = 0; i < MAX_COOKIE_NUM; i++) - ath6kl_free_cookie(ar, &ar->cookie_mem[i]); + ath6kl_free_cookie(ar, &ar->cookie_mem[i], false); + + /* Initilize control cookie list */ + ar->wmi_cookie_list = NULL; + ar->wmi_cookie_count = 0; + + memset(ar->wmi_cookie_mem, 0, sizeof(ar->wmi_cookie_mem)); + + for (i = 0; i < WMI_MAX_COOKIE_NUM; i++) + ath6kl_free_cookie(ar, &ar->wmi_cookie_mem[i], true); } void ath6kl_cookie_cleanup(struct ath6kl *ar) { + /* Cleanup the data cookie */ ar->cookie_list = NULL; ar->cookie_count = 0; + + /* Cleanup the control cookie */ + ar->wmi_cookie_list = NULL; + ar->wmi_cookie_count = 0; } -void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie) +void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie, + bool isctrl) { /* Insert first */ if (!ar || !cookie) return; - cookie->arc_list_next = ar->cookie_list; - ar->cookie_list = cookie; - ar->cookie_count++; + /* If it is control cookie */ + if (isctrl) { + cookie->arc_list_next = ar->wmi_cookie_list; + ar->wmi_cookie_list = cookie; + ar->wmi_cookie_count++; + } else { + cookie->arc_list_next = ar->cookie_list; + ar->cookie_list = cookie; + ar->cookie_count++; + } } /* @@ -350,7 +383,7 @@ void ath6kl_reset_device(struct ath6kl *ar, u32 target_type, __le32 data; if (target_type != TARGET_TYPE_AR6003 && - target_type != TARGET_TYPE_AR6004) + target_type != TARGET_TYPE_AR6004) return; data = cold_reset ? cpu_to_le32(RESET_CONTROL_COLD_RST) : @@ -434,11 +467,41 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel) break; } + if (ar->last_ch != channel) + /* we actually don't know the phymode, default to HT20 */ + ath6kl_cfg80211_ch_switch_notify(vif, channel, WMI_11G_HT20); + ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0); set_bit(CONNECTED, &vif->flags); netif_carrier_on(vif->ndev); } +/*add new_sta join uevent SOFTAP=STA_JOIN */ +void ath6kl_uevent_new_sta(struct ath6kl *ar, const u8 *mac_addr) +{ + char event[64]; + char *envp[] = { event, NULL }; + + memset(event, 0, sizeof(event)); + sprintf(event, "SOFTAP=STA_JOIN " "eth0" "0 wl0.1 %02x:%02x:%02x:%02x:%02x:%02x", + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); + ath6kl_err("%s(): send uevent=%s\n",__FUNCTION__, event); + kobject_uevent_env(&(ar->dev->kobj), KOBJ_CHANGE, envp); +} + +/*add new_sta leave uevent SOFTAP=STA_LEAVE */ +void ath6kl_uevent_del_sta(struct ath6kl *ar, const u8 *mac_addr) +{ + char event[64]; + char *envp[] = { event, NULL }; + + memset(event, 0, sizeof(event)); + sprintf(event, "SOFTAP=STA_LEAVE " "eth0" "0 wl0.1 %02x:%02x:%02x:%02x:%02x:%02x", + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); + ath6kl_err("%s(): send uevent=%s\n",__FUNCTION__, event); + kobject_uevent_env(&(ar->dev->kobj), KOBJ_CHANGE, envp); +} + void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr, u8 keymgmt, u8 ucipher, u8 auth, u8 assoc_req_len, u8 *assoc_info, u8 apsd_info) @@ -511,6 +574,8 @@ void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr, cfg80211_new_sta(vif->ndev, mac_addr, &sinfo, GFP_KERNEL); + ath6kl_uevent_new_sta(vif->ar, mac_addr); + netif_wake_queue(vif->ndev); } @@ -544,19 +609,23 @@ void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver) struct ath6kl *ar = devt; memcpy(ar->mac_addr, datap, ETH_ALEN); - ath6kl_dbg(ATH6KL_DBG_TRC, "%s: mac addr = %pM\n", - __func__, ar->mac_addr); + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "ready event mac addr %pM sw_ver 0x%x abi_ver 0x%x\n", + ar->mac_addr, sw_ver, abi_ver); ar->version.wlan_ver = sw_ver; ar->version.abi_ver = abi_ver; - snprintf(ar->wiphy->fw_version, - sizeof(ar->wiphy->fw_version), - "%u.%u.%u.%u", - (ar->version.wlan_ver & 0xf0000000) >> 28, - (ar->version.wlan_ver & 0x0f000000) >> 24, - (ar->version.wlan_ver & 0x00ff0000) >> 16, - (ar->version.wlan_ver & 0x0000ffff)); + if (strlen(ar->wiphy->fw_version) == 0) { + snprintf(ar->wiphy->fw_version, + sizeof(ar->wiphy->fw_version), + "%u.%u.%u.%u", + (ar->version.wlan_ver & 0xf0000000) >> 28, + (ar->version.wlan_ver & 0x0f000000) >> 24, + (ar->version.wlan_ver & 0x00ff0000) >> 16, + (ar->version.wlan_ver & 0x0000ffff)); + } /* indicate to the waiting thread that the ready event was received */ set_bit(WMI_READY, &ar->flag); @@ -582,6 +651,60 @@ void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status) ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "scan complete: %d\n", status); } +static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel) +{ + + struct ath6kl *ar = vif->ar; + + vif->next_chan = channel; + vif->profile.ch = cpu_to_le16(channel); + + switch (vif->nw_type) { + case AP_NETWORK: + /* + * reconfigure any saved RSN IE capabilites in the beacon / + * probe response to stay in sync with the supplicant. + */ + if (vif->rsn_capab && + test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, + ar->fw_capabilities)) + ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx, + WLAN_EID_RSN, WMI_RSN_IE_CAPB, + (const u8 *) &vif->rsn_capab, + sizeof(vif->rsn_capab)); + + return ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, + &vif->profile); + default: + ath6kl_err("won't switch channels nw_type=%d\n", vif->nw_type); + return -ENOTSUPP; + } +} + +static void ath6kl_check_ch_switch(struct ath6kl *ar, u16 channel) +{ + + struct ath6kl_vif *vif; + int res = 0; + + if (!ar->want_ch_switch) + return; + + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) + res = ath6kl_commit_ch_switch(vif, channel); + + /* if channel switch failed, oh well we tried */ + ar->want_ch_switch &= ~(1 << vif->fw_vif_idx); + + if (res) + ath6kl_err("channel switch failed nw_type %d res %d\n", + vif->nw_type, res); + } + spin_unlock_bh(&ar->list_lock); +} + void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, u16 listen_int, u16 beacon_int, enum network_type net_type, u8 beacon_ie_len, @@ -599,9 +722,11 @@ void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, memcpy(vif->bssid, bssid, sizeof(vif->bssid)); vif->bss_ch = channel; - if ((vif->nw_type == INFRA_NETWORK)) + if ((vif->nw_type == INFRA_NETWORK)) { ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, vif->listen_intvl_t, 0); + ath6kl_check_ch_switch(ar, channel); + } netif_wake_queue(vif->ndev); @@ -661,6 +786,7 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len) (struct wmi_target_stats *) ptr; struct ath6kl *ar = vif->ar; struct target_stats *stats = &vif->target_stats; + struct target_stats_dup *stats_dup = &vif->target_stats_dup; struct tkip_ccmp_stats *ccmp_stats; u8 ac; @@ -671,6 +797,7 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len) stats->tx_pkt += le32_to_cpu(tgt_stats->stats.tx.pkt); stats->tx_byte += le32_to_cpu(tgt_stats->stats.tx.byte); + stats_dup->tx_byte += le32_to_cpu(tgt_stats->stats.tx.byte); stats->tx_ucast_pkt += le32_to_cpu(tgt_stats->stats.tx.ucast_pkt); stats->tx_ucast_byte += le32_to_cpu(tgt_stats->stats.tx.ucast_byte); stats->tx_mcast_pkt += le32_to_cpu(tgt_stats->stats.tx.mcast_pkt); @@ -679,22 +806,37 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len) stats->tx_bcast_byte += le32_to_cpu(tgt_stats->stats.tx.bcast_byte); stats->tx_rts_success_cnt += le32_to_cpu(tgt_stats->stats.tx.rts_success_cnt); + stats_dup->tx_rts_success_cnt += + le32_to_cpu(tgt_stats->stats.tx.rts_success_cnt); - for (ac = 0; ac < WMM_NUM_AC; ac++) + for (ac = 0; ac < WMM_NUM_AC; ac++) { stats->tx_pkt_per_ac[ac] += le32_to_cpu(tgt_stats->stats.tx.pkt_per_ac[ac]); + stats_dup->tx_pkt_per_ac[ac] += + le32_to_cpu(tgt_stats->stats.tx.pkt_per_ac[ac]); + stats_dup->tx_retry_cnt[ac] += + le32_to_cpu(tgt_stats->stats.tx.retry_cnt); + stats_dup->tx_fail_cnt[ac] += + le32_to_cpu(tgt_stats->stats.tx.fail_cnt); + stats_dup->tx_mult_retry_cnt[ac] += + le32_to_cpu(tgt_stats->stats.tx.mult_retry_cnt); + } stats->tx_err += le32_to_cpu(tgt_stats->stats.tx.err); + stats_dup->tx_err += le32_to_cpu(tgt_stats->stats.tx.err); stats->tx_fail_cnt += le32_to_cpu(tgt_stats->stats.tx.fail_cnt); stats->tx_retry_cnt += le32_to_cpu(tgt_stats->stats.tx.retry_cnt); stats->tx_mult_retry_cnt += le32_to_cpu(tgt_stats->stats.tx.mult_retry_cnt); stats->tx_rts_fail_cnt += le32_to_cpu(tgt_stats->stats.tx.rts_fail_cnt); + stats_dup->tx_rts_fail_cnt += + le32_to_cpu(tgt_stats->stats.tx.rts_fail_cnt); stats->tx_ucast_rate = ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.tx.ucast_rate)); stats->rx_pkt += le32_to_cpu(tgt_stats->stats.rx.pkt); + stats_dup->rx_pkt += le32_to_cpu(tgt_stats->stats.rx.pkt); stats->rx_byte += le32_to_cpu(tgt_stats->stats.rx.byte); stats->rx_ucast_pkt += le32_to_cpu(tgt_stats->stats.rx.ucast_pkt); stats->rx_ucast_byte += le32_to_cpu(tgt_stats->stats.rx.ucast_byte); @@ -704,11 +846,13 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len) stats->rx_bcast_byte += le32_to_cpu(tgt_stats->stats.rx.bcast_byte); stats->rx_frgment_pkt += le32_to_cpu(tgt_stats->stats.rx.frgment_pkt); stats->rx_err += le32_to_cpu(tgt_stats->stats.rx.err); + stats_dup->rx_err += le32_to_cpu(tgt_stats->stats.rx.err); stats->rx_crc_err += le32_to_cpu(tgt_stats->stats.rx.crc_err); stats->rx_key_cache_miss += le32_to_cpu(tgt_stats->stats.rx.key_cache_miss); stats->rx_decrypt_err += le32_to_cpu(tgt_stats->stats.rx.decrypt_err); stats->rx_dupl_frame += le32_to_cpu(tgt_stats->stats.rx.dupl_frame); + stats_dup->rx_dupl_frame += le32_to_cpu(tgt_stats->stats.rx.dupl_frame); stats->rx_ucast_rate = ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.rx.ucast_rate)); @@ -756,6 +900,12 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len) stats->wow_evt_discarded += le16_to_cpu(tgt_stats->wow_stats.wow_evt_discarded); + stats->arp_received = le32_to_cpu(tgt_stats->arp_stats.arp_received); + stats->arp_replied = le32_to_cpu(tgt_stats->arp_stats.arp_replied); + stats->arp_matched = le32_to_cpu(tgt_stats->arp_stats.arp_matched); + + ath6kl_wmicfg_send_stats(vif, stats); + if (test_bit(STATS_UPDATE_PEND, &vif->flags)) { clear_bit(STATS_UPDATE_PEND, &vif->flags); wake_up(&ar->event_wq); @@ -920,6 +1070,28 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, struct ath6kl *ar = vif->ar; if (vif->nw_type == AP_NETWORK) { + /* disconnect due to other STA vif switching channels */ + if (reason == BSS_DISCONNECTED && + prot_reason_status == WMI_AP_REASON_STA_ROAM) { + ar->want_ch_switch |= 1 << vif->fw_vif_idx; + /* bail back to this channel if STA vif fails connect */ + ar->last_ch = le16_to_cpu(vif->profile.ch); + } + + if (prot_reason_status == WMI_AP_REASON_MAX_STA) { + /* send max client reached notification to user space */ + cfg80211_conn_failed(vif->ndev, bssid, + NL80211_CONN_FAIL_MAX_CLIENTS, + GFP_KERNEL); + } + + if (prot_reason_status == WMI_AP_REASON_ACL) { + /* send blocked client notification to user space */ + cfg80211_conn_failed(vif->ndev, bssid, + NL80211_CONN_FAIL_BLOCKED_CLIENT, + GFP_KERNEL); + } + if (!ath6kl_remove_sta(ar, bssid, prot_reason_status)) return; @@ -938,6 +1110,7 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, if (!is_broadcast_ether_addr(bssid)) { /* send event to application */ cfg80211_del_sta(vif->ndev, bssid, GFP_KERNEL); + ath6kl_uevent_del_sta(vif->ar, bssid); } if (memcmp(vif->ndev->dev_addr, bssid, ETH_ALEN) == 0) { @@ -948,8 +1121,8 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, } ath6kl_cfg80211_disconnect_event(vif, reason, bssid, - assoc_resp_len, assoc_info, - prot_reason_status); + assoc_resp_len, assoc_info, + prot_reason_status); aggr_reset_state(vif->aggr_cntxt->aggr_conn); @@ -969,14 +1142,17 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, } else { set_bit(CONNECT_PEND, &vif->flags); if (((reason == ASSOC_FAILED) && - (prot_reason_status == 0x11)) || - ((reason == ASSOC_FAILED) && (prot_reason_status == 0x0) && - (vif->reconnect_flag == 1))) { + (prot_reason_status == 0x11)) || + ((reason == ASSOC_FAILED) && (prot_reason_status == 0x0) + && (vif->reconnect_flag == 1))) { set_bit(CONNECTED, &vif->flags); return; } } + /* restart disconnected concurrent vifs waiting for new channel */ + ath6kl_check_ch_switch(ar, ar->last_ch); + /* update connect & link status atomically */ spin_lock_bh(&vif->if_lock); clear_bit(CONNECTED, &vif->flags); @@ -1048,8 +1224,12 @@ static struct net_device_stats *ath6kl_get_stats(struct net_device *dev) return &vif->net_stats; } -static int ath6kl_set_features(struct net_device *dev, - netdev_features_t features) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) +static int ath6kl_set_features(struct net_device *dev, netdev_features_t features) +#else +static int ath6kl_set_features(struct net_device *dev, u32 features) +#endif { struct ath6kl_vif *vif = netdev_priv(dev); struct ath6kl *ar = vif->ar; @@ -1080,11 +1260,12 @@ static int ath6kl_set_features(struct net_device *dev, return err; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ static void ath6kl_set_multicast_list(struct net_device *ndev) { struct ath6kl_vif *vif = netdev_priv(ndev); - bool mc_all_on = false, mc_all_off = false; + bool mc_all_on = false; int mc_count = netdev_mc_count(ndev); struct netdev_hw_addr *ha; bool found; @@ -1096,29 +1277,46 @@ static void ath6kl_set_multicast_list(struct net_device *ndev) !test_bit(WLAN_ENABLED, &vif->flags)) return; + /* Enable multicast-all filter. */ mc_all_on = !!(ndev->flags & IFF_PROMISC) || !!(ndev->flags & IFF_ALLMULTI) || !!(mc_count > ATH6K_MAX_MC_FILTERS_PER_LIST); - mc_all_off = !(ndev->flags & IFF_MULTICAST) || mc_count == 0; + if (mc_all_on) + set_bit(NETDEV_MCAST_ALL_ON, &vif->flags); + else + clear_bit(NETDEV_MCAST_ALL_ON, &vif->flags); - if (mc_all_on || mc_all_off) { - /* Enable/disable all multicast */ - ath6kl_dbg(ATH6KL_DBG_TRC, "%s multicast filter\n", - mc_all_on ? "enabling" : "disabling"); - ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx, - mc_all_on); - if (ret) - ath6kl_warn("Failed to %s multicast receive\n", - mc_all_on ? "enable" : "disable"); + mc_all_on = mc_all_on || (vif->ar->state == ATH6KL_STATE_ON); + + if (!(ndev->flags & IFF_MULTICAST)) { + mc_all_on = false; + set_bit(NETDEV_MCAST_ALL_OFF, &vif->flags); + } else { + clear_bit(NETDEV_MCAST_ALL_OFF, &vif->flags); + } + + /* Enable/disable "multicast-all" filter*/ + ath6kl_dbg(ATH6KL_DBG_TRC, "%s multicast-all filter\n", + mc_all_on ? "enabling" : "disabling"); + ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx, + mc_all_on); + + if (ret) { + ath6kl_warn("Failed to %s multicast-all receive\n", + mc_all_on ? "enable" : "disable"); return; } + if (test_bit(NETDEV_MCAST_ALL_ON, &vif->flags)) + return; + + /* Keep the driver and firmware mcast list in sync. */ list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) { found = false; netdev_for_each_mc_addr(ha, ndev) { if (memcmp(ha->addr, mc_filter->hw_addr, - ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) { + ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) { found = true; break; } @@ -1137,7 +1335,7 @@ static void ath6kl_set_multicast_list(struct net_device *ndev) false); if (ret) { ath6kl_warn("Failed to remove multicast filter:%pM\n", - mc_filter->hw_addr); + mc_filter->hw_addr); return; } @@ -1152,7 +1350,7 @@ static void ath6kl_set_multicast_list(struct net_device *ndev) found = false; list_for_each_entry(mc_filter, &vif->mc_filter, list) { if (memcmp(ha->addr, mc_filter->hw_addr, - ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) { + ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) { found = true; break; } @@ -1177,7 +1375,7 @@ static void ath6kl_set_multicast_list(struct net_device *ndev) true); if (ret) { ath6kl_warn("Failed to add multicast filter :%pM\n", - mc_filter->hw_addr); + mc_filter->hw_addr); kfree(mc_filter); goto out; } @@ -1190,18 +1388,146 @@ static void ath6kl_set_multicast_list(struct net_device *ndev) list_splice_tail(&mc_filter_new, &vif->mc_filter); } -static const struct net_device_ops ath6kl_netdev_ops = { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) + +static inline void ath6kl_pack_tlv(char *buf, char type, char size, + char *val, int *tlen) +{ + if (*tlen > ATH6KL_PRI_IOCTL_REPLY_BUF_MAX) + return; + + *buf++ = type; + *buf++ = size; + memcpy(buf, val, size); + *tlen += size + 2; +} + +static int ath6kl_update_stats(struct ath6kl *ar, struct ath6kl_vif *vif, + void __user *user_buf) +{ + struct target_stats_dup *stats; + char *buf; + int tlen = 0; + long left; + size_t ret; + + if (down_interruptible(&ar->sem)) + goto exit; + + set_bit(STATS_UPDATE_PEND, &vif->flags); + + if (ath6kl_wmi_get_stats_cmd(ar->wmi, 0)) { + up(&ar->sem); + goto exit; + } + + left = wait_event_interruptible_timeout(ar->event_wq, + !test_bit(STATS_UPDATE_PEND, + &vif->flags), WMI_TIMEOUT); + up(&ar->sem); + + if (left <= 0) + goto exit; + + stats = &vif->target_stats_dup; + + buf = kzalloc(ATH6KL_PRI_IOCTL_REPLY_BUF_MAX, GFP_KERNEL); + if (!buf) + goto exit; + + ath6kl_pack_tlv(buf + tlen, WLAN_STATS_RETRY_CNT, + sizeof(stats->tx_retry_cnt), + (char *)&stats->tx_retry_cnt, &tlen); + + ath6kl_pack_tlv(buf + tlen, WLAN_STATS_MUL_RETRY_CNT, + sizeof(stats->tx_mult_retry_cnt), + (char *)&stats->tx_mult_retry_cnt, &tlen); + + ath6kl_pack_tlv(buf + tlen, WLAN_STATS_TX_FRM_CNT, + sizeof(stats->tx_pkt_per_ac), + (char *)&stats->tx_pkt_per_ac, &tlen); + + ath6kl_pack_tlv(buf + tlen, WLAN_STATS_RX_FRM_CNT, + sizeof(stats->rx_pkt), + (char *)&stats->rx_pkt, &tlen); + + ath6kl_pack_tlv(buf + tlen, WLAN_STATS_FRM_DUP_CNT, + sizeof(stats->rx_dupl_frame), + (char *)&stats->rx_dupl_frame, &tlen); + + ath6kl_pack_tlv(buf + tlen, WLAN_STATS_FAIL_CNT, + sizeof(stats->tx_fail_cnt), + (char *)&stats->tx_fail_cnt, &tlen); + + ath6kl_pack_tlv(buf + tlen, WLAN_STATS_RTS_FAIL_CNT, + sizeof(stats->tx_rts_fail_cnt), + (char *)&stats->tx_rts_fail_cnt, &tlen); + + ath6kl_pack_tlv(buf + tlen, WLAN_STATS_ACK_FAIL_CNT, + sizeof(stats->tx_err), + (char *)&stats->tx_err, &tlen); + + ath6kl_pack_tlv(buf + tlen, WLAN_STATS_RTS_SUC_CNT, + sizeof(stats->tx_rts_success_cnt), + (char *)&stats->tx_rts_success_cnt, &tlen); + + ath6kl_pack_tlv(buf + tlen, WLAN_STATS_RX_DISCARD_CNT, + sizeof(stats->rx_err), + (char *)&stats->rx_err, &tlen); + + ath6kl_pack_tlv(buf + tlen, WLAN_STATS_RX_ERROR_CNT, + sizeof(stats->rx_err), + (char *)&stats->rx_err, &tlen); + + ath6kl_pack_tlv(buf + tlen, WLAN_STATS_TX_BYTE_CNT, + sizeof(stats->tx_byte), + (char *)&stats->tx_byte, &tlen); + + ret = copy_to_user(user_buf, buf, tlen); + if (ret) + ath6kl_err("failed to copy target stats to user buffer\n"); + + kfree(buf); +exit: + return 0; +} + +static int ath6kl_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct ath6kl_vif *vif = netdev_priv(dev); + struct ath6kl *ar = vif->ar; + + if (!ar || !vif) + goto exit; + + if (!test_bit(WMI_READY, &vif->ar->flag) || + !test_bit(WLAN_ENABLED, &vif->flags)) + goto exit; + + if (cmd == ATH6KL_PRIV_GET_WLAN_STATS) + ath6kl_update_stats(ar, vif, rq->ifr_data); +exit: + return 0; +} +#endif + +static struct net_device_ops ath6kl_netdev_ops = { .ndo_open = ath6kl_open, .ndo_stop = ath6kl_close, .ndo_start_xmit = ath6kl_data_tx, .ndo_get_stats = ath6kl_get_stats, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) .ndo_set_features = ath6kl_set_features, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ .ndo_set_rx_mode = ath6kl_set_multicast_list, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) + .ndo_do_ioctl = ath6kl_ioctl, +#endif }; void init_netdev(struct net_device *dev) { - dev->netdev_ops = &ath6kl_netdev_ops; + netdev_attach_ops(dev, &ath6kl_netdev_ops); dev->destructor = free_netdev; dev->watchdog_timeo = ATH6KL_TX_TIMEOUT; @@ -1210,7 +1536,5 @@ void init_netdev(struct net_device *dev) sizeof(struct wmi_data_hdr) + HTC_HDR_LENGTH + WMI_MAX_TX_META_SZ + ATH6KL_HTC_ALIGN_BYTES; - dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM; - return; } diff --git a/drivers/net/wireless/ath/ath6kl/platform.c b/drivers/net/wireless/ath/ath6kl/platform.c new file mode 100644 index 00000000..dc10ec13 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/platform.c @@ -0,0 +1,206 @@ +#ifdef CONFIG_ARCH_MSM7X27A +#include +#else +#include +#include +#include +#include +#include +/* replace with plaftform specific changes */ +#endif + +#include "core.h" + +/* BeginMMC polling stuff */ +#ifdef CONFIG_ARCH_MSM7X27A +#define MMC_PLATFORM_DEV "msm_sdcc.2" +#else +#define MMC_PLATFORM_DEV "sdcc" +#endif + +/* End MMC polling stuff */ + +#define GET_INODE_FROM_FILEP(filp) \ + ((filp)->f_path.dentry->d_inode) + + + +#define ATH6KL_INIT_TIMEOUT (3 * HZ) + +wait_queue_head_t init_wq; +static atomic_t init_done = ATOMIC_INIT(0); + + +int android_readwrite_file(const char *filename, + char *rbuf, const char *wbuf, size_t length) +{ + int ret = 0; + struct file *filp = (struct file *)-ENOENT; + mm_segment_t oldfs; + oldfs = get_fs(); + set_fs(KERNEL_DS); + + do { + int mode = (wbuf) ? O_RDWR : O_RDONLY; + filp = filp_open(filename, mode, S_IRUSR); + + if (IS_ERR(filp) || !filp->f_op) { + ret = -ENOENT; + break; + } + + if (length == 0) { + /* Read the length of the file only */ + struct inode *inode; + + inode = GET_INODE_FROM_FILEP(filp); + if (!inode) { + printk(KERN_ERR "android_readwrite_file: Error 2\n"); + ret = -ENOENT; + break; + } + ret = i_size_read(inode->i_mapping->host); + break; + } + + if (wbuf) { + ret = filp->f_op->write(filp, wbuf, length, &filp->f_pos); + if (ret < 0) { + printk(KERN_ERR + "android_readwrite_file: Error 3\n"); + break; + } + } else { + ret = filp->f_op->read(filp, rbuf, length, &filp->f_pos); + if (ret < 0) { + printk(KERN_ERR + "android_readwrite_file: Error 4\n"); + break; + } + } + } while (0); + + if (!IS_ERR(filp)) + filp_close(filp, NULL); + + set_fs(oldfs); + printk(KERN_ERR "android_readwrite_file: ret=%d\n", ret); + + return ret; +} + +//extern void ar600x_wlan_power(bool on); + +static int ath6kl_pm_probe(struct platform_device *pdev) +{ +#if 1 + int (*plat_setup_power)(int on); + plat_setup_power = pdev->dev.platform_data; + printk(KERN_ERR "%s () enter and will invoke power-up sequence\n", + __func__); + if (plat_setup_power) + plat_setup_power(1); +#else + /* replace with plaftform specific changes */ +#endif + return 0; +} + +static int ath6kl_pm_remove(struct platform_device *pdev) +{ +#if 1 + int (*plat_setup_power)(int on); + plat_setup_power = pdev->dev.platform_data; + printk(KERN_ERR "%s () enter and will invoke power-down sequence\n", + __func__); + if (plat_setup_power) + plat_setup_power(0); +#else + /* replace with plaftform specific changes */ +#endif + return 0; +} + +static int ath6kl_pm_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static inline void *ar6k_priv(struct net_device *dev) +{ + return wdev_priv(dev->ieee80211_ptr); +} + +static int ath6kl_pm_resume(struct platform_device *pdev) +{ + return 0; +} + + +static struct platform_driver ath6kl_pm_device = { + .probe = ath6kl_pm_probe, + .remove = ath6kl_pm_remove, + .suspend = ath6kl_pm_suspend, + .resume = ath6kl_pm_resume, + .driver = { + .name = "wlan_ar6000_pm_dev", + }, +}; + +void __init ath6kl_sdio_init_platform(void) +{ + char buf[3]; + int length; + + platform_driver_register(&ath6kl_pm_device); + + length = snprintf(buf, sizeof(buf), "%d\n", 1 ? 1 : 0); + android_readwrite_file("/sys/devices/platform/" MMC_PLATFORM_DEV + "/polling", NULL, buf, length); + length = snprintf(buf, sizeof(buf), "%d\n", 0 ? 1 : 0); + android_readwrite_file("/sys/devices/platform/" MMC_PLATFORM_DEV + "/polling", NULL, buf, length); + + mdelay(50); +} + +void __exit ath6kl_sdio_exit_platform(void) +{ + char buf[3]; + int length; + platform_driver_unregister(&ath6kl_pm_device); + + length = snprintf(buf, sizeof(buf), "%d\n", 1 ? 1 : 0); + /* fall back to polling */ + android_readwrite_file("/sys/devices/platform/" MMC_PLATFORM_DEV + "/polling", NULL, buf, length); + length = snprintf(buf, sizeof(buf), "%d\n", 0 ? 1 : 0); + /* fall back to polling */ + android_readwrite_file("/sys/devices/platform/" MMC_PLATFORM_DEV + "/polling", NULL, buf, length); + mdelay(1000); + +} + +int ath6kl_wait_for_init_comp(void) +{ + int left; + + if (atomic_read(&init_done) == 1) + return 0; + + left = wait_event_interruptible_timeout(init_wq, + atomic_read(&init_done) == 1, + ATH6KL_INIT_TIMEOUT); + if (left == 0) + printk(KERN_ERR "timeout while waiting for init operation\n"); + else if (left < 0) + printk(KERN_ERR "wait for init operation failed: %d\n", left); + + return 0; +} +void ath6kl_notify_init_done(void) +{ + atomic_set(&init_done, 1); + wake_up(&init_wq); +} diff --git a/drivers/net/wireless/ath/ath6kl/pm.c b/drivers/net/wireless/ath/ath6kl/pm.c new file mode 100644 index 00000000..6ff5fafc --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/pm.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "core.h" + +static bool ath6kl_parse_event_pkt_for_wake_lock(struct sk_buff *skb) +{ + u16 cmd_id; + bool need_wake = false; + + if (skb->len < sizeof(u16)) + return need_wake; + + cmd_id = *(const u16 *) skb->data; + cmd_id = le16_to_cpu(cmd_id); + + switch (cmd_id) { + case WMI_CONNECT_EVENTID: + need_wake = true; + break; + default: + /* Don't wake lock the system for other events */ + break; + } + + return need_wake; +} + +static bool ath6kl_parse_ip_pkt_for_wake_lock(struct sk_buff *skb) +{ + const u8 ipsec_keepalve[] = { 0x11, 0x94, 0x11, 0x94, 0x00, + 0x09, 0x00, 0x00, 0xff }; + bool need_wake = true; + u16 size; + u8 *udp; + u8 ihl; + + if (skb->len >= 24 && (*((u8 *)skb->data + 23) == 0x11)) { + + udp = (u8 *)skb->data + 14; + ihl = (*udp & 0x0f) * sizeof(u32); + udp += ihl; + size = 14 + ihl + sizeof(ipsec_keepalve); + + if ((skb->len >= size) && + !memcmp(udp, ipsec_keepalve, sizeof(ipsec_keepalve) - 3) && + udp[8] == 0xff) { + /* + * RFC 3948 UDP Encapsulation of IPsec ESP Packets + * src and dest port must be 4500 + * Receivers MUST NOT depend upon the UDP checksum + * being zero + * Sender must use 1 byte payload with 0xff + * Receiver SHOULD ignore a received NAT-keepalive pkt + * + * IPSec over UDP NAT keepalive packet. Just ignore + */ + need_wake = false; + } + } + + return need_wake; +} + +static bool ath6kl_parse_data_pkt_for_wake_lock(struct ath6kl *ar, + struct sk_buff *skb) +{ + struct net_device *ndev; + struct ath6kl_vif *vif; + struct ethhdr *hdr; + bool need_wake = false; + u16 dst_port; + + vif = ath6kl_vif_first(ar); + if (!vif) + return need_wake; + + if (skb->len < sizeof(struct ethhdr)) + return need_wake; + + hdr = (struct ethhdr *) skb->data; + + if (!is_multicast_ether_addr(hdr->h_dest)) { + switch (ntohs(hdr->h_proto)) { + case 0x0800: /* IP */ + need_wake = ath6kl_parse_ip_pkt_for_wake_lock(skb); + break; + case 0x888e: /* EAPOL */ + case 0x88c7: /* RSN_PREAUTH */ + case 0x88b4: /* WAPI */ + need_wake = true; + break; + default: + break; + } + } else if (!is_broadcast_ether_addr(hdr->h_dest)) { + if (skb->len >= 14 + 20) { /* mDNS packets */ + u8 *dst_ipaddr = (u8 *)(skb->data + 14 + 20 - 4); + ndev = vif->ndev; + if (((dst_ipaddr[3] & 0xf8) == 0xf8) && + (vif->nw_type == AP_NETWORK || + (ndev->flags & IFF_ALLMULTI || + ndev->flags & IFF_MULTICAST))) + need_wake = true; + } + } else if (vif->nw_type == AP_NETWORK) { + switch (ntohs(hdr->h_proto)) { + case 0x0800: /* IP */ + if (skb->len >= 14 + 20 + 2) { + dst_port = *(u16 *)(skb->data + 14 + 20); + /* dhcp req */ + need_wake = (ntohs(dst_port) == 0x43); + } + break; + case 0x0806: + need_wake = true; + default: + break; + } + } + + return need_wake; +} + +void ath6kl_config_suspend_wake_lock(struct ath6kl *ar, struct sk_buff *skb, + bool is_event_pkt) +{ + struct ath6kl_vif *vif; +#ifdef CONFIG_HAS_WAKELOCK + unsigned long wl_timeout = 5; +#endif + bool need_wake = false; + + vif = ath6kl_vif_first(ar); + if (!vif) + return; + + if ( +#ifdef CONFIG_HAS_EARLYSUSPEND + ar->screen_off && +#endif + skb && test_bit(CONNECTED, &vif->flags)) { + if (is_event_pkt) { /* Ctrl pkt received */ + need_wake = + ath6kl_parse_event_pkt_for_wake_lock(skb); + if (need_wake) { +#ifdef CONFIG_HAS_WAKELOCK + wl_timeout = 3 * HZ; +#endif + } + } else /* Data pkt received */ + need_wake = ath6kl_parse_data_pkt_for_wake_lock(ar, + skb); + } + + if (need_wake) { +#ifdef CONFIG_HAS_WAKELOCK + /* + * Keep the host wake up if there is any event + * and pkt comming in. + */ + wake_lock_timeout(&ar->wake_lock, wl_timeout); +#endif + } +} +#ifdef CONFIG_HAS_WAKELOCK +void ath6kl_p2p_acquire_wakelock(struct ath6kl *ar, int wl_timeout) +{ + if (!wake_lock_active(&ar->p2p_wake_lock)) + wake_lock_timeout(&ar->p2p_wake_lock, wl_timeout); + return; +} + +void ath6kl_p2p_release_wakelock(struct ath6kl *ar) +{ + if (wake_lock_active(&ar->p2p_wake_lock)) + wake_unlock(&ar->p2p_wake_lock); + return; +} +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND +static void ath6kl_early_suspend(struct early_suspend *handler) +{ + struct ath6kl *ar = container_of(handler, struct ath6kl, early_suspend); + + if (ar) + ar->screen_off = true; +} + +static void ath6kl_late_resume(struct early_suspend *handler) +{ + struct ath6kl *ar = container_of(handler, struct ath6kl, early_suspend); + + if (ar) + ar->screen_off = false; +} +#endif + +void ath6kl_setup_android_resource(struct ath6kl *ar) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + ar->screen_off = false; + ar->early_suspend.suspend = ath6kl_early_suspend; + ar->early_suspend.resume = ath6kl_late_resume; + ar->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; + register_early_suspend(&ar->early_suspend); +#endif +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&ar->wake_lock, WAKE_LOCK_SUSPEND, "ath6kl_suspend_wl"); + wake_lock_init(&ar->p2p_wake_lock, + WAKE_LOCK_SUSPEND, + "ath6kl_p2p_suspend_wl"); +#endif +} + +void ath6kl_cleanup_android_resource(struct ath6kl *ar) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ar->early_suspend); +#endif +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&ar->wake_lock); + wake_lock_destroy(&ar->p2p_wake_lock); +#endif +} diff --git a/drivers/net/wireless/ath/ath6kl/pm.h b/drivers/net/wireless/ath/ath6kl/pm.h new file mode 100644 index 00000000..1ac989bf --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/pm.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +void ath6kl_config_suspend_wake_lock(struct ath6kl *ar, struct sk_buff *skb, + bool is_event_pkt); +void ath6kl_setup_android_resource(struct ath6kl *ar); +void ath6kl_cleanup_android_resource(struct ath6kl *ar); +#ifdef CONFIG_HAS_WAKELOCK +void ath6kl_p2p_acquire_wakelock(struct ath6kl *ar, int wl_timeout); +void ath6kl_p2p_release_wakelock(struct ath6kl *ar); +#endif diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c new file mode 100644 index 00000000..3fe75cb8 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/recovery.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "core.h" +#include "cfg80211.h" +#include "debug.h" + +static void ath6kl_recovery_work(struct work_struct *work) +{ + struct ath6kl *ar = container_of(work, struct ath6kl, + fw_recovery.recovery_work); + + ar->state = ATH6KL_STATE_RECOVERY; + + del_timer_sync(&ar->fw_recovery.hb_timer); + + ath6kl_init_hw_restart(ar); + + ar->state = ATH6KL_STATE_ON; + clear_bit(WMI_CTRL_EP_FULL, &ar->flag); + + ar->fw_recovery.err_reason = 0; + + if (ar->fw_recovery.hb_poll) + mod_timer(&ar->fw_recovery.hb_timer, jiffies + + msecs_to_jiffies(ar->fw_recovery.hb_poll)); +} + +void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason) +{ + ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Fw error detected, reason:%d\n", + reason); + + set_bit(reason, &ar->fw_recovery.err_reason); + + if (ar->fw_recovery.enable && ar->state != ATH6KL_STATE_RECOVERY) + queue_work(ar->ath6kl_wq, &ar->fw_recovery.recovery_work); +} + +void ath6kl_recovery_hb_event(struct ath6kl *ar, u32 cookie) +{ + if (cookie == ar->fw_recovery.seq_num) + ar->fw_recovery.hb_pending = false; +} + +static void ath6kl_recovery_hb_timer(unsigned long data) +{ + struct ath6kl *ar = (struct ath6kl *) data; + int err; + + if (!ar->fw_recovery.enable || + !test_bit(WMI_READY, &ar->flag) || + !ar->fw_recovery.hb_poll) + return; + + if (ar->fw_recovery.hb_pending) + ar->fw_recovery.hb_misscnt++; + else + ar->fw_recovery.hb_misscnt = 0; + + if (ar->fw_recovery.hb_misscnt > ATH6KL_HB_RESP_MISS_THRES) { + ar->fw_recovery.hb_misscnt = 0; + ar->fw_recovery.seq_num = 0; + ar->fw_recovery.hb_pending = false; + ath6kl_recovery_err_notify(ar, ATH6KL_FW_HB_RESP_FAILURE); + return; + } + + ar->fw_recovery.seq_num++; + ar->fw_recovery.hb_pending = true; + + err = ath6kl_wmi_get_challenge_resp_cmd(ar->wmi, + ar->fw_recovery.seq_num, 0); + if (err) + ath6kl_warn("Failed to send hb challenge request, err:%d\n", + err); + + if ((ar->state == ATH6KL_STATE_RECOVERY) || !ar->fw_recovery.enable) + return; + + mod_timer(&ar->fw_recovery.hb_timer, jiffies + + msecs_to_jiffies(ar->fw_recovery.hb_poll)); +} + +void ath6kl_recovery_init(struct ath6kl *ar) +{ + struct ath6kl_fw_recovery *recovery = &ar->fw_recovery; + + recovery->enable = true; + INIT_WORK(&recovery->recovery_work, ath6kl_recovery_work); + recovery->seq_num = 0; + recovery->hb_misscnt = 0; + ar->fw_recovery.hb_pending = false; + ar->fw_recovery.hb_timer.function = ath6kl_recovery_hb_timer; + ar->fw_recovery.hb_timer.data = (unsigned long) ar; + init_timer_deferrable(&ar->fw_recovery.hb_timer); + + if (ar->fw_recovery.hb_poll) + mod_timer(&ar->fw_recovery.hb_timer, jiffies + + msecs_to_jiffies(ar->fw_recovery.hb_poll)); +} + +void ath6kl_recovery_cleanup(struct ath6kl *ar) +{ + ar->fw_recovery.enable = false; + + del_timer_sync(&ar->fw_recovery.hb_timer); + cancel_work_sync(&ar->fw_recovery.recovery_work); +} + +void ath6kl_recovery_suspend(struct ath6kl *ar) +{ + ath6kl_recovery_cleanup(ar); + + if (!ar->fw_recovery.err_reason) + return; + + /* Process pending fw error detection */ + ar->fw_recovery.err_reason = 0; + WARN_ON(ar->state != ATH6KL_STATE_ON); + ar->state = ATH6KL_STATE_RECOVERY; + ath6kl_init_hw_restart(ar); + ar->state = ATH6KL_STATE_ON; +} + +void ath6kl_recovery_resume(struct ath6kl *ar) +{ + ar->fw_recovery.enable = true; + + if (!ar->fw_recovery.hb_poll) + return; + + ar->fw_recovery.hb_pending = false; + ar->fw_recovery.seq_num = 0; + ar->fw_recovery.hb_misscnt = 0; + mod_timer(&ar->fw_recovery.hb_timer, + jiffies + msecs_to_jiffies(ar->fw_recovery.hb_poll)); +} diff --git a/drivers/net/wireless/ath/ath6kl/regd.c b/drivers/net/wireless/ath/ath6kl/regd.c new file mode 100644 index 00000000..effe7a9f --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/regd.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include "regd.h" + +/* + * The regcode init number 0x809c is provided by QUALCOMM; + * This means the Ar6005 wifi chip support 13 channel; + */ +static unsigned short reg_domain = 0x809c; +module_param(reg_domain, ushort, 0644); + +/* + * Number of bytes in board data that we are interested + * in while setting regulatory domain from host + */ +#define REG_DMN_BOARD_DATA_LEN 16 + + + +int ath6kl_set_reg_dmn(struct ath6kl *ar) +{ + u8 buf[REG_DMN_BOARD_DATA_LEN]; + __le16 old_sum, old_ver, old_rd, old_rd_next; + __le32 brd_dat_addr = 0, new_sum, new_rd; + int ret; + + if (reg_domain == 0xffff) + return 0; + printk("%s enter \n",__FUNCTION__); + ret = ath6kl_bmi_read(ar, AR6003_BOARD_DATA_ADDR, + (u8 *)&brd_dat_addr, 4); + if (ret) + return ret; + + memset(buf, 0, sizeof(buf)); + ret = ath6kl_bmi_read(ar, brd_dat_addr, buf, sizeof(buf)); + if (ret) + return ret; + + memcpy((u8 *)&old_sum, buf + AR6003_BOARD_DATA_OFFSET, 2); + memcpy((u8 *)&old_ver, buf + AR6003_BOARD_DATA_OFFSET + 2, 2); + memcpy((u8 *)&old_rd, buf + AR6003_RD_OFFSET, 2); + memcpy((u8 *)&old_rd_next, buf + AR6003_RD_OFFSET + 2, 2); + + /* + * Overwrite the new regulatory domain and preserve the + * MAC addr which is in the same word. + */ + new_rd = cpu_to_le32((le32_to_cpu(old_rd_next) << 16) + reg_domain); + ret = ath6kl_bmi_write(ar, + cpu_to_le32(le32_to_cpu(brd_dat_addr) + AR6003_RD_OFFSET), + (u8 *)&new_rd, 4); + if (ret) + return ret; + + /* + * Recompute the board data checksum with the new regulatory + * domain, preserve the version information which is in the + * same word. + */ + new_sum = cpu_to_le32((le32_to_cpu(old_ver) << 16) + + (le32_to_cpu(old_sum) ^ le32_to_cpu(old_rd) ^ + reg_domain)); + ret = ath6kl_bmi_write(ar, + cpu_to_le32(le32_to_cpu(brd_dat_addr) + + AR6003_BOARD_DATA_OFFSET), + (u8 *)&new_sum, 4); + + return ret; +} diff --git a/drivers/net/wireless/ath/ath6kl/regd.h b/drivers/net/wireless/ath/ath6kl/regd.h new file mode 100644 index 00000000..4d4756a2 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/regd.h @@ -0,0 +1,13 @@ +#ifndef REGD_H +#define REGD_H + +#include "core.h" + +#define AR6003_BOARD_DATA_ADDR 0x00540654 +#define AR6003_BOARD_DATA_INIT_ADDR 0x00540658 +#define AR6003_BOARD_DATA_OFFSET 4 +#define AR6003_RD_OFFSET 12 + +int ath6kl_set_reg_dmn(struct ath6kl *ar); + +#endif diff --git a/drivers/net/wireless/ath/ath6kl/regd_ath.h b/drivers/net/wireless/ath/ath6kl/regd_ath.h new file mode 100644 index 00000000..03a8268c --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/regd_ath.h @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2008-2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef REGD_H +#define REGD_H + +#include +#include + +#include "ath.h" + +enum ctl_group { + CTL_FCC = 0x10, + CTL_MKK = 0x40, + CTL_ETSI = 0x30, +}; + +#define NO_CTL 0xff +#define SD_NO_CTL 0xE0 +#define NO_CTL 0xff +#define CTL_11A 0 +#define CTL_11B 1 +#define CTL_11G 2 +#define CTL_2GHT20 5 +#define CTL_5GHT20 6 +#define CTL_2GHT40 7 +#define CTL_5GHT40 8 + +#define CTRY_DEBUG 0x1ff +#define CTRY_DEFAULT 0 + +#define COUNTRY_ERD_FLAG 0x8000 +#define WORLDWIDE_ROAMING_FLAG 0x4000 + +#define MULTI_DOMAIN_MASK 0xFF00 + +#define WORLD_SKU_MASK 0x00F0 +#define WORLD_SKU_PREFIX 0x0060 + +#define CHANNEL_HALF_BW 10 +#define CHANNEL_QUARTER_BW 5 + +struct country_code_to_enum_rd { + u16 countryCode; + u16 regDmnEnum; + const char *isoName; +}; + +enum CountryCode { + CTRY_ALBANIA = 8, + CTRY_ALGERIA = 12, + CTRY_ARGENTINA = 32, + CTRY_ARMENIA = 51, + CTRY_ARUBA = 533, + CTRY_AUSTRALIA = 36, + CTRY_AUSTRIA = 40, + CTRY_AZERBAIJAN = 31, + CTRY_BAHRAIN = 48, + CTRY_BANGLADESH = 50, + CTRY_BARBADOS = 52, + CTRY_BELARUS = 112, + CTRY_BELGIUM = 56, + CTRY_BELIZE = 84, + CTRY_BOLIVIA = 68, + CTRY_BOSNIA_HERZ = 70, + CTRY_BRAZIL = 76, + CTRY_BRUNEI_DARUSSALAM = 96, + CTRY_BULGARIA = 100, + CTRY_CAMBODIA = 116, + CTRY_CANADA = 124, + CTRY_CHILE = 152, + CTRY_CHINA = 156, + CTRY_COLOMBIA = 170, + CTRY_COSTA_RICA = 188, + CTRY_CROATIA = 191, + CTRY_CYPRUS = 196, + CTRY_CZECH = 203, + CTRY_DENMARK = 208, + CTRY_DOMINICAN_REPUBLIC = 214, + CTRY_ECUADOR = 218, + CTRY_EGYPT = 818, + CTRY_EL_SALVADOR = 222, + CTRY_ESTONIA = 233, + CTRY_FAEROE_ISLANDS = 234, + CTRY_FINLAND = 246, + CTRY_FRANCE = 250, + CTRY_GEORGIA = 268, + CTRY_GERMANY = 276, + CTRY_GREECE = 300, + CTRY_GREENLAND = 304, + CTRY_GRENADA = 308, + CTRY_GUAM = 316, + CTRY_GUATEMALA = 320, + CTRY_HAITI = 332, + CTRY_HONDURAS = 340, + CTRY_HONG_KONG = 344, + CTRY_HUNGARY = 348, + CTRY_ICELAND = 352, + CTRY_INDIA = 356, + CTRY_INDONESIA = 360, + CTRY_IRAN = 364, + CTRY_IRAQ = 368, + CTRY_IRELAND = 372, + CTRY_ISRAEL = 376, + CTRY_ITALY = 380, + CTRY_JAMAICA = 388, + CTRY_JAPAN = 392, + CTRY_JORDAN = 400, + CTRY_KAZAKHSTAN = 398, + CTRY_KENYA = 404, + CTRY_KOREA_NORTH = 408, + CTRY_KOREA_ROC = 410, + CTRY_KOREA_ROC2 = 411, + CTRY_KOREA_ROC3 = 412, + CTRY_KUWAIT = 414, + CTRY_LATVIA = 428, + CTRY_LEBANON = 422, + CTRY_LIBYA = 434, + CTRY_LIECHTENSTEIN = 438, + CTRY_LITHUANIA = 440, + CTRY_LUXEMBOURG = 442, + CTRY_MACAU = 446, + CTRY_MACEDONIA = 807, + CTRY_MALAYSIA = 458, + CTRY_MALTA = 470, + CTRY_MEXICO = 484, + CTRY_MONACO = 492, + CTRY_MOROCCO = 504, + CTRY_NEPAL = 524, + CTRY_NETHERLANDS = 528, + CTRY_NETHERLANDS_ANTILLES = 530, + CTRY_NEW_ZEALAND = 554, + CTRY_NICARAGUA = 558, + CTRY_NORWAY = 578, + CTRY_OMAN = 512, + CTRY_PAKISTAN = 586, + CTRY_PANAMA = 591, + CTRY_PAPUA_NEW_GUINEA = 598, + CTRY_PARAGUAY = 600, + CTRY_PERU = 604, + CTRY_PHILIPPINES = 608, + CTRY_POLAND = 616, + CTRY_PORTUGAL = 620, + CTRY_PUERTO_RICO = 630, + CTRY_QATAR = 634, + CTRY_ROMANIA = 642, + CTRY_RUSSIA = 643, + CTRY_SAUDI_ARABIA = 682, + CTRY_SERBIA_MONTENEGRO = 891, + CTRY_SINGAPORE = 702, + CTRY_SLOVAKIA = 703, + CTRY_SLOVENIA = 705, + CTRY_SOUTH_AFRICA = 710, + CTRY_SPAIN = 724, + CTRY_SRI_LANKA = 144, + CTRY_SWEDEN = 752, + CTRY_SWITZERLAND = 756, + CTRY_SYRIA = 760, + CTRY_TAIWAN = 158, + CTRY_THAILAND = 764, + CTRY_TRINIDAD_Y_TOBAGO = 780, + CTRY_TUNISIA = 788, + CTRY_TURKEY = 792, + CTRY_UAE = 784, + CTRY_UKRAINE = 804, + CTRY_UNITED_KINGDOM = 826, + CTRY_UNITED_STATES = 840, + CTRY_UNITED_STATES_FCC49 = 842, + CTRY_URUGUAY = 858, + CTRY_UZBEKISTAN = 860, + CTRY_VENEZUELA = 862, + CTRY_VIET_NAM = 704, + CTRY_YEMEN = 887, + CTRY_ZIMBABWE = 716, + CTRY_JAPAN1 = 393, + CTRY_JAPAN2 = 394, + CTRY_JAPAN3 = 395, + CTRY_JAPAN4 = 396, + CTRY_JAPAN5 = 397, + CTRY_JAPAN6 = 4006, + CTRY_JAPAN7 = 4007, + CTRY_JAPAN8 = 4008, + CTRY_JAPAN9 = 4009, + CTRY_JAPAN10 = 4010, + CTRY_JAPAN11 = 4011, + CTRY_JAPAN12 = 4012, + CTRY_JAPAN13 = 4013, + CTRY_JAPAN14 = 4014, + CTRY_JAPAN15 = 4015, + CTRY_JAPAN16 = 4016, + CTRY_JAPAN17 = 4017, + CTRY_JAPAN18 = 4018, + CTRY_JAPAN19 = 4019, + CTRY_JAPAN20 = 4020, + CTRY_JAPAN21 = 4021, + CTRY_JAPAN22 = 4022, + CTRY_JAPAN23 = 4023, + CTRY_JAPAN24 = 4024, + CTRY_JAPAN25 = 4025, + CTRY_JAPAN26 = 4026, + CTRY_JAPAN27 = 4027, + CTRY_JAPAN28 = 4028, + CTRY_JAPAN29 = 4029, + CTRY_JAPAN30 = 4030, + CTRY_JAPAN31 = 4031, + CTRY_JAPAN32 = 4032, + CTRY_JAPAN33 = 4033, + CTRY_JAPAN34 = 4034, + CTRY_JAPAN35 = 4035, + CTRY_JAPAN36 = 4036, + CTRY_JAPAN37 = 4037, + CTRY_JAPAN38 = 4038, + CTRY_JAPAN39 = 4039, + CTRY_JAPAN40 = 4040, + CTRY_JAPAN41 = 4041, + CTRY_JAPAN42 = 4042, + CTRY_JAPAN43 = 4043, + CTRY_JAPAN44 = 4044, + CTRY_JAPAN45 = 4045, + CTRY_JAPAN46 = 4046, + CTRY_JAPAN47 = 4047, + CTRY_JAPAN48 = 4048, + CTRY_JAPAN49 = 4049, + CTRY_JAPAN50 = 4050, + CTRY_JAPAN51 = 4051, + CTRY_JAPAN52 = 4052, + CTRY_JAPAN53 = 4053, + CTRY_JAPAN54 = 4054, + CTRY_JAPAN55 = 4055, + CTRY_JAPAN56 = 4056, + CTRY_JAPAN57 = 4057, + CTRY_JAPAN58 = 4058, + CTRY_JAPAN59 = 4059, + CTRY_AUSTRALIA2 = 5000, + CTRY_CANADA2 = 5001, + CTRY_BELGIUM2 = 5002 +}; + +bool ath_is_world_regd(struct ath_regulatory *reg); +bool ath_is_49ghz_allowed(u16 redomain); +int ath_regd_init(struct ath_regulatory *reg, struct wiphy *wiphy, + int (*reg_notifier)(struct wiphy *wiphy, + struct regulatory_request *request)); +u32 ath_regd_get_band_ctl(struct ath_regulatory *reg, + enum ieee80211_band band); +int ath_reg_notifier_apply(struct wiphy *wiphy, + struct regulatory_request *request, + struct ath_regulatory *reg); + +#endif diff --git a/drivers/net/wireless/ath/ath6kl/regd_common.h b/drivers/net/wireless/ath/ath6kl/regd_common.h new file mode 100644 index 00000000..bdd2b4d6 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/regd_common.h @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2008-2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef REGD_COMMON_H +#define REGD_COMMON_H + +enum EnumRd { + NO_ENUMRD = 0x00, + NULL1_WORLD = 0x03, + NULL1_ETSIB = 0x07, + NULL1_ETSIC = 0x08, + FCC1_FCCA = 0x10, + FCC1_WORLD = 0x11, + FCC4_FCCA = 0x12, + FCC5_FCCA = 0x13, + FCC6_FCCA = 0x14, + + FCC2_FCCA = 0x20, + FCC2_WORLD = 0x21, + FCC2_ETSIC = 0x22, + FCC6_WORLD = 0x23, + FRANCE_RES = 0x31, + FCC3_FCCA = 0x3A, + FCC3_WORLD = 0x3B, + + ETSI1_WORLD = 0x37, + ETSI3_ETSIA = 0x32, + ETSI2_WORLD = 0x35, + ETSI3_WORLD = 0x36, + ETSI4_WORLD = 0x30, + ETSI4_ETSIC = 0x38, + ETSI5_WORLD = 0x39, + ETSI6_WORLD = 0x34, + ETSI_RESERVED = 0x33, + + MKK1_MKKA = 0x40, + MKK1_MKKB = 0x41, + APL4_WORLD = 0x42, + MKK2_MKKA = 0x43, + APL_RESERVED = 0x44, + APL2_WORLD = 0x45, + APL2_APLC = 0x46, + APL3_WORLD = 0x47, + MKK1_FCCA = 0x48, + APL2_APLD = 0x49, + MKK1_MKKA1 = 0x4A, + MKK1_MKKA2 = 0x4B, + MKK1_MKKC = 0x4C, + + APL3_FCCA = 0x50, + APL1_WORLD = 0x52, + APL1_FCCA = 0x53, + APL1_APLA = 0x54, + APL1_ETSIC = 0x55, + APL2_ETSIC = 0x56, + APL5_WORLD = 0x58, + APL6_WORLD = 0x5B, + APL7_FCCA = 0x5C, + APL8_WORLD = 0x5D, + APL9_WORLD = 0x5E, + + WOR0_WORLD = 0x60, + WOR1_WORLD = 0x61, + WOR2_WORLD = 0x62, + WOR3_WORLD = 0x63, + WOR4_WORLD = 0x64, + WOR5_ETSIC = 0x65, + + WOR01_WORLD = 0x66, + WOR02_WORLD = 0x67, + EU1_WORLD = 0x68, + + WOR9_WORLD = 0x69, + WORA_WORLD = 0x6A, + WORB_WORLD = 0x6B, + WORC_WORLD = 0x6C, + + MKK3_MKKB = 0x80, + MKK3_MKKA2 = 0x81, + MKK3_MKKC = 0x82, + + MKK4_MKKB = 0x83, + MKK4_MKKA2 = 0x84, + MKK4_MKKC = 0x85, + + MKK5_MKKB = 0x86, + MKK5_MKKA2 = 0x87, + MKK5_MKKC = 0x88, + + MKK6_MKKB = 0x89, + MKK6_MKKA2 = 0x8A, + MKK6_MKKC = 0x8B, + + MKK7_MKKB = 0x8C, + MKK7_MKKA2 = 0x8D, + MKK7_MKKC = 0x8E, + + MKK8_MKKB = 0x8F, + MKK8_MKKA2 = 0x90, + MKK8_MKKC = 0x91, + + MKK14_MKKA1 = 0x92, + MKK15_MKKA1 = 0x93, + + MKK10_FCCA = 0xD0, + MKK10_MKKA1 = 0xD1, + MKK10_MKKC = 0xD2, + MKK10_MKKA2 = 0xD3, + + MKK11_MKKA = 0xD4, + MKK11_FCCA = 0xD5, + MKK11_MKKA1 = 0xD6, + MKK11_MKKC = 0xD7, + MKK11_MKKA2 = 0xD8, + + MKK12_MKKA = 0xD9, + MKK12_FCCA = 0xDA, + MKK12_MKKA1 = 0xDB, + MKK12_MKKC = 0xDC, + MKK12_MKKA2 = 0xDD, + + MKK13_MKKB = 0xDE, + + MKK3_MKKA = 0xF0, + MKK3_MKKA1 = 0xF1, + MKK3_FCCA = 0xF2, + MKK4_MKKA = 0xF3, + MKK4_MKKA1 = 0xF4, + MKK4_FCCA = 0xF5, + MKK9_MKKA = 0xF6, + MKK10_MKKA = 0xF7, + MKK6_MKKA1 = 0xF8, + MKK6_FCCA = 0xF9, + MKK7_MKKA1 = 0xFA, + MKK7_FCCA = 0xFB, + MKK9_FCCA = 0xFC, + MKK9_MKKA1 = 0xFD, + MKK9_MKKC = 0xFE, + MKK9_MKKA2 = 0xFF, + + WORLD = 0x0199, + DEBUG_REG_DMN = 0x01ff, +}; + +/* Regpair to CTL band mapping */ +static struct reg_dmn_pair_mapping regDomainPairs[] = { + /* regpair, 5 GHz CTL, 2 GHz CTL */ + {NO_ENUMRD, DEBUG_REG_DMN, DEBUG_REG_DMN}, + {NULL1_WORLD, NO_CTL, CTL_ETSI}, + {NULL1_ETSIB, NO_CTL, CTL_ETSI}, + {NULL1_ETSIC, NO_CTL, CTL_ETSI}, + + {FCC2_FCCA, CTL_FCC, CTL_FCC}, + {FCC2_WORLD, CTL_FCC, CTL_ETSI}, + {FCC2_ETSIC, CTL_FCC, CTL_ETSI}, + {FCC3_FCCA, CTL_FCC, CTL_FCC}, + {FCC3_WORLD, CTL_FCC, CTL_ETSI}, + {FCC4_FCCA, CTL_FCC, CTL_FCC}, + {FCC5_FCCA, CTL_FCC, CTL_FCC}, + {FCC6_FCCA, CTL_FCC, CTL_FCC}, + {FCC6_WORLD, CTL_FCC, CTL_ETSI}, + + {ETSI1_WORLD, CTL_ETSI, CTL_ETSI}, + {ETSI2_WORLD, CTL_ETSI, CTL_ETSI}, + {ETSI3_WORLD, CTL_ETSI, CTL_ETSI}, + {ETSI4_WORLD, CTL_ETSI, CTL_ETSI}, + {ETSI5_WORLD, CTL_ETSI, CTL_ETSI}, + {ETSI6_WORLD, CTL_ETSI, CTL_ETSI}, + + /* XXX: For ETSI3_ETSIA, Was NO_CTL meant for the 2 GHz band ? */ + {ETSI3_ETSIA, CTL_ETSI, CTL_ETSI}, + {FRANCE_RES, CTL_ETSI, CTL_ETSI}, + + {FCC1_WORLD, CTL_FCC, CTL_ETSI}, + {FCC1_FCCA, CTL_FCC, CTL_FCC}, + {APL1_WORLD, CTL_FCC, CTL_ETSI}, + {APL2_WORLD, CTL_FCC, CTL_ETSI}, + {APL3_WORLD, CTL_FCC, CTL_ETSI}, + {APL4_WORLD, CTL_FCC, CTL_ETSI}, + {APL5_WORLD, CTL_FCC, CTL_ETSI}, + {APL6_WORLD, CTL_ETSI, CTL_ETSI}, + {APL8_WORLD, CTL_ETSI, CTL_ETSI}, + {APL9_WORLD, CTL_ETSI, CTL_ETSI}, + + {APL3_FCCA, CTL_FCC, CTL_FCC}, + {APL7_FCCA, CTL_FCC, CTL_FCC}, + {APL1_ETSIC, CTL_FCC, CTL_ETSI}, + {APL2_ETSIC, CTL_FCC, CTL_ETSI}, + {APL2_APLD, CTL_FCC, NO_CTL}, + + {MKK1_MKKA, CTL_MKK, CTL_MKK}, + {MKK1_MKKB, CTL_MKK, CTL_MKK}, + {MKK1_FCCA, CTL_MKK, CTL_FCC}, + {MKK1_MKKA1, CTL_MKK, CTL_MKK}, + {MKK1_MKKA2, CTL_MKK, CTL_MKK}, + {MKK1_MKKC, CTL_MKK, CTL_MKK}, + + {MKK2_MKKA, CTL_MKK, CTL_MKK}, + {MKK3_MKKA, CTL_MKK, CTL_MKK}, + {MKK3_MKKB, CTL_MKK, CTL_MKK}, + {MKK3_MKKA1, CTL_MKK, CTL_MKK}, + {MKK3_MKKA2, CTL_MKK, CTL_MKK}, + {MKK3_MKKC, CTL_MKK, CTL_MKK}, + {MKK3_FCCA, CTL_MKK, CTL_FCC}, + + {MKK4_MKKA, CTL_MKK, CTL_MKK}, + {MKK4_MKKB, CTL_MKK, CTL_MKK}, + {MKK4_MKKA1, CTL_MKK, CTL_MKK}, + {MKK4_MKKA2, CTL_MKK, CTL_MKK}, + {MKK4_MKKC, CTL_MKK, CTL_MKK}, + {MKK4_FCCA, CTL_MKK, CTL_FCC}, + + {MKK5_MKKB, CTL_MKK, CTL_MKK}, + {MKK5_MKKA2, CTL_MKK, CTL_MKK}, + {MKK5_MKKC, CTL_MKK, CTL_MKK}, + + {MKK6_MKKB, CTL_MKK, CTL_MKK}, + {MKK6_MKKA1, CTL_MKK, CTL_MKK}, + {MKK6_MKKA2, CTL_MKK, CTL_MKK}, + {MKK6_MKKC, CTL_MKK, CTL_MKK}, + {MKK6_FCCA, CTL_MKK, CTL_FCC}, + + {MKK7_MKKB, CTL_MKK, CTL_MKK}, + {MKK7_MKKA1, CTL_MKK, CTL_MKK}, + {MKK7_MKKA2, CTL_MKK, CTL_MKK}, + {MKK7_MKKC, CTL_MKK, CTL_MKK}, + {MKK7_FCCA, CTL_MKK, CTL_FCC}, + + {MKK8_MKKB, CTL_MKK, CTL_MKK}, + {MKK8_MKKA2, CTL_MKK, CTL_MKK}, + {MKK8_MKKC, CTL_MKK, CTL_MKK}, + + {MKK9_MKKA, CTL_MKK, CTL_MKK}, + {MKK9_FCCA, CTL_MKK, CTL_FCC}, + {MKK9_MKKA1, CTL_MKK, CTL_MKK}, + {MKK9_MKKA2, CTL_MKK, CTL_MKK}, + {MKK9_MKKC, CTL_MKK, CTL_MKK}, + + {MKK10_MKKA, CTL_MKK, CTL_MKK}, + {MKK10_FCCA, CTL_MKK, CTL_FCC}, + {MKK10_MKKA1, CTL_MKK, CTL_MKK}, + {MKK10_MKKA2, CTL_MKK, CTL_MKK}, + {MKK10_MKKC, CTL_MKK, CTL_MKK}, + + {MKK11_MKKA, CTL_MKK, CTL_MKK}, + {MKK11_FCCA, CTL_MKK, CTL_FCC}, + {MKK11_MKKA1, CTL_MKK, CTL_MKK}, + {MKK11_MKKA2, CTL_MKK, CTL_MKK}, + {MKK11_MKKC, CTL_MKK, CTL_MKK}, + + {MKK12_MKKA, CTL_MKK, CTL_MKK}, + {MKK12_FCCA, CTL_MKK, CTL_FCC}, + {MKK12_MKKA1, CTL_MKK, CTL_MKK}, + {MKK12_MKKA2, CTL_MKK, CTL_MKK}, + {MKK12_MKKC, CTL_MKK, CTL_MKK}, + + {MKK13_MKKB, CTL_MKK, CTL_MKK}, + {MKK14_MKKA1, CTL_MKK, CTL_MKK}, + {MKK15_MKKA1, CTL_MKK, CTL_MKK}, + + {WOR0_WORLD, NO_CTL, NO_CTL}, + {WOR1_WORLD, NO_CTL, NO_CTL}, + {WOR2_WORLD, NO_CTL, NO_CTL}, + {WOR3_WORLD, NO_CTL, NO_CTL}, + {WOR4_WORLD, NO_CTL, NO_CTL}, + {WOR5_ETSIC, NO_CTL, NO_CTL}, + {WOR01_WORLD, NO_CTL, NO_CTL}, + {WOR02_WORLD, NO_CTL, NO_CTL}, + {EU1_WORLD, NO_CTL, NO_CTL}, + {WOR9_WORLD, NO_CTL, NO_CTL}, + {WORA_WORLD, NO_CTL, NO_CTL}, + {WORB_WORLD, NO_CTL, NO_CTL}, + {WORC_WORLD, NO_CTL, NO_CTL}, +}; + +static struct country_code_to_enum_rd allCountries[] = { + {CTRY_DEBUG, NO_ENUMRD, "DB"}, + {CTRY_DEFAULT, FCC1_FCCA, "CO"}, + {CTRY_ALBANIA, NULL1_WORLD, "AL"}, + {CTRY_ALGERIA, NULL1_WORLD, "DZ"}, + {CTRY_ARGENTINA, FCC3_WORLD, "AR"}, + {CTRY_ARMENIA, ETSI4_WORLD, "AM"}, + {CTRY_ARUBA, ETSI1_WORLD, "AW"}, + {CTRY_AUSTRALIA, FCC2_WORLD, "AU"}, + {CTRY_AUSTRALIA2, FCC6_WORLD, "AU"}, + {CTRY_AUSTRIA, ETSI1_WORLD, "AT"}, + {CTRY_AZERBAIJAN, ETSI4_WORLD, "AZ"}, + {CTRY_BAHRAIN, APL6_WORLD, "BH"}, + {CTRY_BANGLADESH, NULL1_WORLD, "BD"}, + {CTRY_BARBADOS, FCC2_WORLD, "BB"}, + {CTRY_BELARUS, ETSI1_WORLD, "BY"}, + {CTRY_BELGIUM, ETSI1_WORLD, "BE"}, + {CTRY_BELGIUM2, ETSI4_WORLD, "BL"}, + {CTRY_BELIZE, APL1_ETSIC, "BZ"}, + {CTRY_BOLIVIA, APL1_ETSIC, "BO"}, + {CTRY_BOSNIA_HERZ, ETSI1_WORLD, "BA"}, + {CTRY_BRAZIL, FCC3_WORLD, "BR"}, + {CTRY_BRUNEI_DARUSSALAM, APL1_WORLD, "BN"}, + {CTRY_BULGARIA, ETSI6_WORLD, "BG"}, + {CTRY_CAMBODIA, ETSI1_WORLD, "KH"}, + {CTRY_CANADA, FCC3_FCCA, "CA"}, + {CTRY_CANADA2, FCC6_FCCA, "CA"}, + {CTRY_CHILE, APL6_WORLD, "CL"}, + {CTRY_CHINA, APL1_WORLD, "CN"}, + {CTRY_COLOMBIA, FCC1_FCCA, "CO"}, + {CTRY_COSTA_RICA, FCC1_WORLD, "CR"}, + {CTRY_CROATIA, ETSI1_WORLD, "HR"}, + {CTRY_CYPRUS, ETSI1_WORLD, "CY"}, + {CTRY_CZECH, ETSI3_WORLD, "CZ"}, + {CTRY_DENMARK, ETSI1_WORLD, "DK"}, + {CTRY_DOMINICAN_REPUBLIC, FCC1_FCCA, "DO"}, + {CTRY_ECUADOR, FCC1_WORLD, "EC"}, + {CTRY_EGYPT, ETSI3_WORLD, "EG"}, + {CTRY_EL_SALVADOR, FCC1_WORLD, "SV"}, + {CTRY_ESTONIA, ETSI1_WORLD, "EE"}, + {CTRY_FINLAND, ETSI1_WORLD, "FI"}, + {CTRY_FRANCE, ETSI1_WORLD, "FR"}, + {CTRY_GEORGIA, ETSI4_WORLD, "GE"}, + {CTRY_GERMANY, ETSI1_WORLD, "DE"}, + {CTRY_GREECE, ETSI1_WORLD, "GR"}, + {CTRY_GREENLAND, ETSI1_WORLD, "GL"}, + {CTRY_GRENADA, FCC3_FCCA, "GD"}, + {CTRY_GUAM, FCC1_FCCA, "GU"}, + {CTRY_GUATEMALA, FCC1_FCCA, "GT"}, + {CTRY_HAITI, ETSI1_WORLD, "HT"}, + {CTRY_HONDURAS, NULL1_WORLD, "HN"}, + {CTRY_HONG_KONG, FCC3_WORLD, "HK"}, + {CTRY_HUNGARY, ETSI1_WORLD, "HU"}, + {CTRY_ICELAND, ETSI1_WORLD, "IS"}, + {CTRY_INDIA, APL6_WORLD, "IN"}, + {CTRY_INDONESIA, NULL1_WORLD, "ID"}, + {CTRY_IRAN, APL1_WORLD, "IR"}, + {CTRY_IRELAND, ETSI1_WORLD, "IE"}, + {CTRY_ISRAEL, NULL1_WORLD, "IL"}, + {CTRY_ITALY, ETSI1_WORLD, "IT"}, + {CTRY_JAMAICA, FCC3_WORLD, "JM"}, + + {CTRY_JAPAN, MKK1_MKKA, "JP"}, + {CTRY_JAPAN1, MKK1_MKKB, "JP"}, + {CTRY_JAPAN2, MKK1_FCCA, "JP"}, + {CTRY_JAPAN3, MKK2_MKKA, "JP"}, + {CTRY_JAPAN4, MKK1_MKKA1, "JP"}, + {CTRY_JAPAN5, MKK1_MKKA2, "JP"}, + {CTRY_JAPAN6, MKK1_MKKC, "JP"}, + {CTRY_JAPAN7, MKK3_MKKB, "JP"}, + {CTRY_JAPAN8, MKK3_MKKA2, "JP"}, + {CTRY_JAPAN9, MKK3_MKKC, "JP"}, + {CTRY_JAPAN10, MKK4_MKKB, "JP"}, + {CTRY_JAPAN11, MKK4_MKKA2, "JP"}, + {CTRY_JAPAN12, MKK4_MKKC, "JP"}, + {CTRY_JAPAN13, MKK5_MKKB, "JP"}, + {CTRY_JAPAN14, MKK5_MKKA2, "JP"}, + {CTRY_JAPAN15, MKK5_MKKC, "JP"}, + {CTRY_JAPAN16, MKK6_MKKB, "JP"}, + {CTRY_JAPAN17, MKK6_MKKA2, "JP"}, + {CTRY_JAPAN18, MKK6_MKKC, "JP"}, + {CTRY_JAPAN19, MKK7_MKKB, "JP"}, + {CTRY_JAPAN20, MKK7_MKKA2, "JP"}, + {CTRY_JAPAN21, MKK7_MKKC, "JP"}, + {CTRY_JAPAN22, MKK8_MKKB, "JP"}, + {CTRY_JAPAN23, MKK8_MKKA2, "JP"}, + {CTRY_JAPAN24, MKK8_MKKC, "JP"}, + {CTRY_JAPAN25, MKK3_MKKA, "JP"}, + {CTRY_JAPAN26, MKK3_MKKA1, "JP"}, + {CTRY_JAPAN27, MKK3_FCCA, "JP"}, + {CTRY_JAPAN28, MKK4_MKKA1, "JP"}, + {CTRY_JAPAN29, MKK4_FCCA, "JP"}, + {CTRY_JAPAN30, MKK6_MKKA1, "JP"}, + {CTRY_JAPAN31, MKK6_FCCA, "JP"}, + {CTRY_JAPAN32, MKK7_MKKA1, "JP"}, + {CTRY_JAPAN33, MKK7_FCCA, "JP"}, + {CTRY_JAPAN34, MKK9_MKKA, "JP"}, + {CTRY_JAPAN35, MKK10_MKKA, "JP"}, + {CTRY_JAPAN36, MKK4_MKKA, "JP"}, + {CTRY_JAPAN37, MKK9_FCCA, "JP"}, + {CTRY_JAPAN38, MKK9_MKKA1, "JP"}, + {CTRY_JAPAN39, MKK9_MKKC, "JP"}, + {CTRY_JAPAN40, MKK9_MKKA2, "JP"}, + {CTRY_JAPAN41, MKK10_FCCA, "JP"}, + {CTRY_JAPAN42, MKK10_MKKA1, "JP"}, + {CTRY_JAPAN43, MKK10_MKKC, "JP"}, + {CTRY_JAPAN44, MKK10_MKKA2, "JP"}, + {CTRY_JAPAN45, MKK11_MKKA, "JP"}, + {CTRY_JAPAN46, MKK11_FCCA, "JP"}, + {CTRY_JAPAN47, MKK11_MKKA1, "JP"}, + {CTRY_JAPAN48, MKK11_MKKC, "JP"}, + {CTRY_JAPAN49, MKK11_MKKA2, "JP"}, + {CTRY_JAPAN50, MKK12_MKKA, "JP"}, + {CTRY_JAPAN51, MKK12_FCCA, "JP"}, + {CTRY_JAPAN52, MKK12_MKKA1, "JP"}, + {CTRY_JAPAN53, MKK12_MKKC, "JP"}, + {CTRY_JAPAN54, MKK12_MKKA2, "JP"}, + {CTRY_JAPAN57, MKK13_MKKB, "JP"}, + {CTRY_JAPAN58, MKK14_MKKA1, "JP"}, + {CTRY_JAPAN59, MKK15_MKKA1, "JP"}, + + {CTRY_JORDAN, ETSI2_WORLD, "JO"}, + {CTRY_KAZAKHSTAN, NULL1_WORLD, "KZ"}, + {CTRY_KOREA_NORTH, APL9_WORLD, "KP"}, + {CTRY_KOREA_ROC, APL9_WORLD, "KR"}, + {CTRY_KOREA_ROC2, APL2_WORLD, "K2"}, + {CTRY_KOREA_ROC3, APL9_WORLD, "K3"}, + {CTRY_KUWAIT, ETSI3_WORLD, "KW"}, + {CTRY_LATVIA, ETSI1_WORLD, "LV"}, + {CTRY_LEBANON, NULL1_WORLD, "LB"}, + {CTRY_LIECHTENSTEIN, ETSI1_WORLD, "LI"}, + {CTRY_LITHUANIA, ETSI1_WORLD, "LT"}, + {CTRY_LUXEMBOURG, ETSI1_WORLD, "LU"}, + {CTRY_MACAU, FCC2_WORLD, "MO"}, + {CTRY_MACEDONIA, NULL1_WORLD, "MK"}, + {CTRY_MALAYSIA, APL8_WORLD, "MY"}, + {CTRY_MALTA, ETSI1_WORLD, "MT"}, + {CTRY_MEXICO, FCC1_FCCA, "MX"}, + {CTRY_MONACO, ETSI4_WORLD, "MC"}, + {CTRY_MOROCCO, APL4_WORLD, "MA"}, + {CTRY_NEPAL, APL1_WORLD, "NP"}, + {CTRY_NETHERLANDS, ETSI1_WORLD, "NL"}, + {CTRY_NETHERLANDS_ANTILLES, ETSI1_WORLD, "AN"}, + {CTRY_NEW_ZEALAND, FCC2_ETSIC, "NZ"}, + {CTRY_NORWAY, ETSI1_WORLD, "NO"}, + {CTRY_OMAN, FCC3_WORLD, "OM"}, + {CTRY_PAKISTAN, NULL1_WORLD, "PK"}, + {CTRY_PANAMA, FCC1_FCCA, "PA"}, + {CTRY_PAPUA_NEW_GUINEA, FCC1_WORLD, "PG"}, + {CTRY_PERU, APL1_WORLD, "PE"}, + {CTRY_PHILIPPINES, APL1_WORLD, "PH"}, + {CTRY_POLAND, ETSI1_WORLD, "PL"}, + {CTRY_PORTUGAL, ETSI1_WORLD, "PT"}, + {CTRY_PUERTO_RICO, FCC1_FCCA, "PR"}, + {CTRY_QATAR, APL1_WORLD, "QA"}, + {CTRY_ROMANIA, NULL1_WORLD, "RO"}, + {CTRY_RUSSIA, NULL1_WORLD, "RU"}, + {CTRY_SAUDI_ARABIA, NULL1_WORLD, "SA"}, + {CTRY_SERBIA_MONTENEGRO, ETSI1_WORLD, "CS"}, + {CTRY_SINGAPORE, APL6_WORLD, "SG"}, + {CTRY_SLOVAKIA, ETSI1_WORLD, "SK"}, + {CTRY_SLOVENIA, ETSI1_WORLD, "SI"}, + {CTRY_SOUTH_AFRICA, FCC3_WORLD, "ZA"}, + {CTRY_SPAIN, ETSI1_WORLD, "ES"}, + {CTRY_SRI_LANKA, FCC3_WORLD, "LK"}, + {CTRY_SWEDEN, ETSI1_WORLD, "SE"}, + {CTRY_SWITZERLAND, ETSI1_WORLD, "CH"}, + {CTRY_SYRIA, NULL1_WORLD, "SY"}, + {CTRY_TAIWAN, APL3_FCCA, "TW"}, + {CTRY_THAILAND, FCC3_WORLD, "TH"}, + {CTRY_TRINIDAD_Y_TOBAGO, FCC3_WORLD, "TT"}, + {CTRY_TUNISIA, ETSI3_WORLD, "TN"}, + {CTRY_TURKEY, ETSI3_WORLD, "TR"}, + {CTRY_UKRAINE, NULL1_WORLD, "UA"}, + {CTRY_UAE, NULL1_WORLD, "AE"}, + {CTRY_UNITED_KINGDOM, ETSI1_WORLD, "GB"}, + {CTRY_UNITED_STATES, FCC3_FCCA, "US"}, + /* This "PS" is for US public safety actually... to support this we + * would need to assign new special alpha2 to CRDA db as with the world + * regdomain and use another alpha2 */ + {CTRY_UNITED_STATES_FCC49, FCC4_FCCA, "PS"}, + {CTRY_URUGUAY, FCC3_WORLD, "UY"}, + {CTRY_UZBEKISTAN, FCC3_FCCA, "UZ"}, + {CTRY_VENEZUELA, APL2_ETSIC, "VE"}, + {CTRY_VIET_NAM, NULL1_WORLD, "VN"}, + {CTRY_YEMEN, NULL1_WORLD, "YE"}, + {CTRY_ZIMBABWE, NULL1_WORLD, "ZW"}, +}; + +#endif diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 53528648..c9c76402 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -32,7 +32,6 @@ struct ath6kl_sdio { struct sdio_func *func; - /* protects access to bus_req_freeq */ spinlock_t lock; /* free list */ @@ -54,17 +53,13 @@ struct ath6kl_sdio { atomic_t irq_handling; wait_queue_head_t irq_wq; - /* protects access to scat_req */ spinlock_t scat_lock; - bool scatter_enabled; bool is_disabled; const struct sdio_device_id *id; struct work_struct wr_async_work; struct list_head wr_asyncq; - - /* protects access to wr_asyncq */ spinlock_t wr_async_lock; }; @@ -74,6 +69,8 @@ struct ath6kl_sdio { #define CMD53_ARG_FIXED_ADDRESS 0 #define CMD53_ARG_INCR_ADDRESS 1 +extern wait_queue_head_t init_wq; + static inline struct ath6kl_sdio *ath6kl_sdio_priv(struct ath6kl *ar) { return ar->hif_priv; @@ -335,10 +332,10 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio, { struct hif_scatter_req *s_req; struct bus_request *bus_req; - int i, scat_req_sz, scat_list_sz, sg_sz, buf_sz; + int i, scat_req_sz, scat_list_sz, sg_sz = 0, buf_sz; u8 *virt_buf; - scat_list_sz = (n_scat_entry - 1) * sizeof(struct hif_scatter_item); + scat_list_sz = n_scat_entry * sizeof(struct hif_scatter_item); scat_req_sz = sizeof(*s_req) + scat_list_sz; if (!virt_scat) @@ -480,7 +477,6 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func) status = ath6kl_hif_intr_bh_handler(ar_sdio->ar); sdio_claim_host(ar_sdio->func); - atomic_set(&ar_sdio->irq_handling, 0); wake_up(&ar_sdio->irq_wq); @@ -602,7 +598,7 @@ static void ath6kl_sdio_irq_disable(struct ath6kl *ar) sdio_release_host(ar_sdio->func); ret = wait_event_interruptible(ar_sdio->irq_wq, - ath6kl_sdio_is_on_irq(ar)); + ath6kl_sdio_is_on_irq(ar)); if (ret) return; @@ -661,8 +657,8 @@ static int ath6kl_sdio_async_rw_scatter(struct ath6kl *ar, return -EINVAL; ath6kl_dbg(ATH6KL_DBG_SCATTER, - "hif-scatter: total len: %d scatter entries: %d\n", - scat_req->len, scat_req->scat_entries); + "hif-scatter: total len: %d scatter entries: %d\n", + scat_req->len, scat_req->scat_entries); if (request & HIF_SYNCHRONOUS) status = ath6kl_sdio_scat_rw(ar_sdio, scat_req->busrequest); @@ -709,7 +705,7 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); struct htc_target *target = ar->htc_target; - int ret; + int ret = 0; bool virt_scat = false; if (ar_sdio->scatter_enabled) @@ -836,6 +832,7 @@ static int ath6kl_set_sdio_pm_caps(struct ath6kl *ar) return ret; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); @@ -843,22 +840,8 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) mmc_pm_flag_t flags; bool try_deepsleep = false; int ret; - - if (ar->state == ATH6KL_STATE_SCHED_SCAN) { - ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sched scan is in progress\n"); - - ret = ath6kl_set_sdio_pm_caps(ar); - if (ret) - goto cut_pwr; - - ret = ath6kl_cfg80211_suspend(ar, - ATH6KL_CFG_SUSPEND_SCHED_SCAN, - NULL); - if (ret) - goto cut_pwr; - - return 0; - } + struct ath6kl_vif *vif; + int no_if_up; if (ar->suspend_mode == WLAN_POWER_STATE_WOW || (!ar->suspend_mode && wow)) { @@ -867,17 +850,28 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) if (ret) goto cut_pwr; + // check to see if any interfaces are up + // if not, then we can skip WOW + no_if_up = 1; + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) + if (vif->ndev->flags & IFF_UP) no_if_up = 0; + spin_unlock_bh(&ar->list_lock); + + if (no_if_up) { + ret = -ENOTCONN; + } else { ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, wow); + } if (ret && ret != -ENOTCONN) ath6kl_err("wow suspend failed: %d\n", ret); - if (ret && - (!ar->wow_suspend_mode || - ar->wow_suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP)) - try_deepsleep = true; + if (ret && (!ar->wow_suspend_mode || + ar->wow_suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP)) + try_deepsleep = true; else if (ret && ar->wow_suspend_mode == WLAN_POWER_STATE_CUT_PWR) - goto cut_pwr; + goto cut_pwr; if (!ret) return 0; } @@ -915,6 +909,9 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) } cut_pwr: + if (func->card && func->card->host) + func->card->host->pm_flags &= ~MMC_PM_KEEP_POWER; + return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_CUTPOWER, NULL); } @@ -939,20 +936,21 @@ static int ath6kl_sdio_resume(struct ath6kl *ar) case ATH6KL_STATE_WOW: break; - case ATH6KL_STATE_SCHED_SCAN: - break; - case ATH6KL_STATE_SUSPENDING: break; case ATH6KL_STATE_RESUMING: break; + + case ATH6KL_STATE_RECOVERY: + break; } ath6kl_cfg80211_resume(ar); return 0; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ /* set the window address register (using 4-byte register access ). */ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr) @@ -1025,7 +1023,7 @@ static int ath6kl_sdio_diag_read32(struct ath6kl *ar, u32 address, u32 *data) (u8 *)data, sizeof(u32), HIF_RD_SYNC_BYTE_INC); if (status) { ath6kl_err("%s: failed to read from window data addr\n", - __func__); + __func__); return status; } @@ -1254,8 +1252,10 @@ static const struct ath6kl_hif_ops ath6kl_sdio_ops = { .enable_scatter = ath6kl_sdio_enable_scatter, .scat_req_rw = ath6kl_sdio_async_rw_scatter, .cleanup_scatter = ath6kl_sdio_cleanup_scatter, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) .suspend = ath6kl_sdio_suspend, .resume = ath6kl_sdio_resume, +#endif .diag_read32 = ath6kl_sdio_diag_read32, .diag_write32 = ath6kl_sdio_diag_write32, .bmi_read = ath6kl_sdio_bmi_read, @@ -1265,7 +1265,7 @@ static const struct ath6kl_hif_ops ath6kl_sdio_ops = { .stop = ath6kl_sdio_stop, }; -#ifdef CONFIG_PM_SLEEP +#if defined(CONFIG_PM_SLEEP) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) /* * Empty handlers so that mmc subsystem doesn't remove us entirely during @@ -1335,13 +1335,12 @@ static int ath6kl_sdio_probe(struct sdio_func *func, INIT_LIST_HEAD(&ar_sdio->wr_asyncq); INIT_WORK(&ar_sdio->wr_async_work, ath6kl_sdio_write_async_work); - init_waitqueue_head(&ar_sdio->irq_wq); for (count = 0; count < BUS_REQUEST_MAX_NUM; count++) ath6kl_sdio_free_bus_req(ar_sdio, &ar_sdio->bus_req[count]); - ar = ath6kl_core_create(&ar_sdio->func->dev); + ar = ath6kl_core_alloc(&ar_sdio->func->dev); if (!ar) { ath6kl_err("Failed to alloc ath6kl core\n"); ret = -ENOMEM; @@ -1368,10 +1367,11 @@ static int ath6kl_sdio_probe(struct sdio_func *func, goto err_core_alloc; } + ath6kl_notify_init_done(); return ret; err_core_alloc: - ath6kl_core_destroy(ar_sdio->ar); + ath6kl_core_free(ar_sdio->ar); err_dma: kfree(ar_sdio->dma_buffer); err_hif: @@ -1394,7 +1394,6 @@ static void ath6kl_sdio_remove(struct sdio_func *func) cancel_work_sync(&ar_sdio->wr_async_work); ath6kl_core_cleanup(ar_sdio->ar); - ath6kl_core_destroy(ar_sdio->ar); kfree(ar_sdio->dma_buffer); kfree(ar_sdio); @@ -1415,16 +1414,26 @@ static struct sdio_driver ath6kl_sdio_driver = { .id_table = ath6kl_sdio_devices, .probe = ath6kl_sdio_probe, .remove = ath6kl_sdio_remove, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) .drv.pm = ATH6KL_SDIO_PM_OPS, +#endif }; static int __init ath6kl_sdio_init(void) { int ret; + ath6kl_sdio_init_platform(); + + init_waitqueue_head(&init_wq); + ret = sdio_register_driver(&ath6kl_sdio_driver); - if (ret) + if (ret) { ath6kl_err("sdio driver registration failed: %d\n", ret); + return ret; + } + + ret = ath6kl_wait_for_init_comp(); return ret; } @@ -1432,6 +1441,7 @@ static int __init ath6kl_sdio_init(void) static void __exit ath6kl_sdio_exit(void) { sdio_unregister_driver(&ath6kl_sdio_driver); + ath6kl_sdio_exit_platform(); } module_init(ath6kl_sdio_init); diff --git a/drivers/net/wireless/ath/ath6kl/softmac.c b/drivers/net/wireless/ath/ath6kl/softmac.c new file mode 100644 index 00000000..8b223a9b --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/softmac.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "core.h" +#include "debug.h" +#include +#define MAC_FILE "ath6k/AR6003/hw2.1.1/softmac" + +typedef char A_CHAR; +extern int android_readwrite_file(const A_CHAR *filename, A_CHAR *rbuf, const A_CHAR *wbuf, size_t length); + +/* Bleh, same offsets. */ +#define AR6003_MAC_ADDRESS_OFFSET 0x16 +#define AR6004_MAC_ADDRESS_OFFSET 0x16 + +/* Global variables, sane coding be damned. */ +u8 *ath6kl_softmac; +size_t ath6kl_softmac_len; + +static void ath6kl_calculate_crc(u32 target_type, u8 *data, size_t len) +{ + u16 *crc, *data_idx; + u16 checksum; + int i; + + if (target_type == TARGET_TYPE_AR6003) { + crc = (u16 *)(data + 0x04); + } else if (target_type == TARGET_TYPE_AR6004) { + len = 1024; + crc = (u16 *)(data + 0x04); + } else { + ath6kl_err("Invalid target type\n"); + return; + } + + ath6kl_dbg(ATH6KL_DBG_BOOT, "Old Checksum: %u\n", *crc); + + *crc = 0; + checksum = 0; + data_idx = (u16 *)data; + + for (i = 0; i < len; i += 2) { + checksum = checksum ^ (*data_idx); + data_idx++; + } + + *crc = cpu_to_le16(checksum); + + ath6kl_dbg(ATH6KL_DBG_BOOT, "New Checksum: %u\n", checksum); +} + +#ifndef WIFI_MAC_FROM_NV + +#ifdef CONFIG_MACH_PX +static int ath6kl_fetch_nvmac_info(struct ath6kl *ar) +{ + char softmac_filename[256]; + int ret = 0; + + do { + snprintf(softmac_filename, sizeof(softmac_filename), + "/data/.nvmac.info"); + + if ( (ret = android_readwrite_file( + softmac_filename, NULL, NULL, 0)) < 0) { + break; + } else { + ath6kl_softmac_len = ret; + } + ath6kl_softmac = vmalloc(ath6kl_softmac_len); + if (!ath6kl_softmac) { + ath6kl_dbg(ATH6KL_DBG_BOOT, + "%s: Cannot allocate buffer for nvmac.info" + "(%d)\n", __func__,ath6kl_softmac_len); + ret = -ENOMEM; + break; + } + + if ( (ret = android_readwrite_file(softmac_filename, + (char*)ath6kl_softmac, NULL, ath6kl_softmac_len)) != + ath6kl_softmac_len) { + ath6kl_dbg(ATH6KL_DBG_BOOT,"%s: file read error, length %d\n", + __func__, ath6kl_softmac_len); + vfree(ath6kl_softmac); + ret = -1; + break; + } + + if (!strncmp(ath6kl_softmac,"00:00:00:00:00:00",17)) { + ath6kl_dbg(ATH6KL_DBG_BOOT,"%s: mac address is zero\n", + __func__); + vfree(ath6kl_softmac); + ret = -1; + break; + } + + ret = 0; + } while (0); + + return ret; +} + +#else +static int ath6kl_fetch_mac_file(struct ath6kl *ar) +{ + const struct firmware *fw_entry; + int ret = 0; + + + ret = request_firmware(&fw_entry, MAC_FILE, ar->dev); + if (ret) + return ret; + + ath6kl_softmac_len = fw_entry->size; + ath6kl_softmac = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); + + if (ath6kl_softmac == NULL) + ret = -ENOMEM; + + release_firmware(fw_entry); + + return ret; +} +#endif /* CONFIG_MACH_PX */ + +#else + +extern int read_nv(unsigned int nv_item, void *buf); + +#endif /* WIFI_MAC_FROM_NV */ + +void ath6kl_mangle_mac_address(struct ath6kl *ar, u8 locally_administered_bit) +{ + u8 *ptr_mac; + int i = 0; + int ret; +#if defined(CONFIG_MACH_PX) || defined(FIX_HUAWEI_BUTCHERING) + unsigned int softmac[6]; +#endif +#ifdef WIFI_MAC_FROM_NV + u8 nvdata[24]; +#endif + + switch (ar->target_type) { + case TARGET_TYPE_AR6003: + ptr_mac = ar->fw_board + AR6003_MAC_ADDRESS_OFFSET; + break; + case TARGET_TYPE_AR6004: + ptr_mac = ar->fw_board + AR6004_MAC_ADDRESS_OFFSET; + break; + default: + ath6kl_err("Invalid Target Type\n"); + return; + } + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "MAC from EEPROM %02X:%02X:%02X:%02X:%02X:%02X\n", + ptr_mac[0], ptr_mac[1], ptr_mac[2], + ptr_mac[3], ptr_mac[4], ptr_mac[5]); + printk("MAC from EEPROM %02X:%02X:%02X:%02X:%02X:%02X\n", + ptr_mac[0], ptr_mac[1], ptr_mac[2], + ptr_mac[3], ptr_mac[4], ptr_mac[5]); + +#ifdef WIFI_MAC_FROM_NV + nvdata[0] = (u8)i; + ret = read_nv(4678, nvdata); + if (ret == 0) { + memcpy(ptr_mac, nvdata, ETH_ALEN); + printk("MAC from NV %02X:%02X:%02X:%02X:%02X:%02X\n", + ptr_mac[0], ptr_mac[1], ptr_mac[2], + ptr_mac[3], ptr_mac[4], ptr_mac[5]); + } + +#else + +#if defined(CONFIG_MACH_PX) || defined(CONFIG_MACH_JENA) +#ifdef CONFIG_MACH_JENA + ret = ath6kl_fetch_mac_file(ar); +#else + ret = ath6kl_fetch_nvmac_info(ar); +#endif + + if (ret) { + ath6kl_err("MAC address file not found\n"); + return; + } + + if (sscanf(ath6kl_softmac, "%02x:%02x:%02x:%02x:%02x:%02x", + &softmac[0], &softmac[1], &softmac[2], + &softmac[3], &softmac[4], &softmac[5])==6) { + + for (i=0; i<6; ++i) { + ptr_mac[i] = softmac[i] & 0xff; + } + } + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "MAC from SoftMAC %02X:%02X:%02X:%02X:%02X:%02X\n", + ptr_mac[0], ptr_mac[1], ptr_mac[2], + ptr_mac[3], ptr_mac[4], ptr_mac[5]); + printk("MAC from softmac %02X:%02X:%02X:%02X:%02X:%02X\n", + softmac[0], softmac[1], softmac[2], + softmac[3], softmac[4], softmac[5]); + printk("MAC from ptr_mac %02X:%02X:%02X:%02X:%02X:%02X\n", + ptr_mac[0], ptr_mac[1], ptr_mac[2], + ptr_mac[3], ptr_mac[4], ptr_mac[5]); +#ifdef CONFIG_MACH_JENA + kfree(ath6kl_softmac); +#else + vfree(ath6kl_softmac); +#endif +#else + ret = ath6kl_fetch_mac_file(ar); + if (ret) { + ath6kl_err("MAC address file not found\n"); + return; + } + + for (i = 0; i < ETH_ALEN; ++i) { + ptr_mac[i] = ath6kl_softmac[i] & 0xff; + } + + kfree(ath6kl_softmac); +#endif /* CONFIG_MACH_PX */ + +#endif /* WIFI_MAC_FROM_NV */ + + if (locally_administered_bit) + ptr_mac[0] |= 0x02; + + if (locally_administered_bit) + ptr_mac[0] |= 0x02; + + ath6kl_calculate_crc(ar->target_type, ar->fw_board, ar->fw_board_len); +} diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h index 78e0ef45..a98c12ba 100644 --- a/drivers/net/wireless/ath/ath6kl/target.h +++ b/drivers/net/wireless/ath/ath6kl/target.h @@ -45,6 +45,7 @@ #define LPO_CAL_ENABLE_S 20 #define LPO_CAL_ENABLE 0x00100000 +#define GPIO_PIN9_ADDRESS 0x0000004c #define GPIO_PIN10_ADDRESS 0x00000050 #define GPIO_PIN11_ADDRESS 0x00000054 #define GPIO_PIN12_ADDRESS 0x00000058 diff --git a/drivers/net/wireless/ath/ath6kl/testmode.c b/drivers/net/wireless/ath/ath6kl/testmode.c index 6675c92b..942537cf 100644 --- a/drivers/net/wireless/ath/ath6kl/testmode.c +++ b/drivers/net/wireless/ath/ath6kl/testmode.c @@ -17,8 +17,10 @@ #include "testmode.h" #include "debug.h" +#include "cfg80211.h" #include +#include "wmiconfig.h" enum ath6kl_tm_attr { __ATH6KL_TM_ATTR_INVALID = 0, @@ -33,6 +35,7 @@ enum ath6kl_tm_attr { enum ath6kl_tm_cmd { ATH6KL_TM_CMD_TCMD = 0, ATH6KL_TM_CMD_RX_REPORT = 1, /* not used anymore */ + ATH6KL_TM_CMD_WMI_CMD = 0xF000, }; #define ATH6KL_TM_DATA_MAX_LEN 5000 @@ -43,6 +46,7 @@ static const struct nla_policy ath6kl_tm_policy[ATH6KL_TM_ATTR_MAX + 1] = { .len = ATH6KL_TM_DATA_MAX_LEN }, }; + void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len) { struct sk_buff *skb; @@ -69,8 +73,18 @@ int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) { struct ath6kl *ar = wiphy_priv(wiphy); struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1]; + struct ath6kl_vif *vif; int err, buf_len; void *buf; + u32 wmi_cmd; + struct sk_buff *skb; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len, ath6kl_tm_policy); @@ -81,6 +95,25 @@ int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) return -EINVAL; switch (nla_get_u32(tb[ATH6KL_TM_ATTR_CMD])) { + case ATH6KL_TM_CMD_WMI_CMD: + if (!tb[ATH6KL_TM_ATTR_DATA]) + return -EINVAL; + + buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); + + /* First four bytes hold the wmi_cmd and the rest is the data */ + skb = ath6kl_wmi_get_buf(buf_len-4); + if (!skb) + return -ENOMEM; + + memcpy(&wmi_cmd, buf, sizeof(wmi_cmd)); + memcpy(skb->data, (u32 *)buf + 1, buf_len - 4); + ath6kl_wmi_cmd_send(ar->wmi, 0, skb, wmi_cmd, NO_SYNC_WMIFLAG); + + return 0; + + break; case ATH6KL_TM_CMD_TCMD: if (!tb[ATH6KL_TM_ATTR_DATA]) return -EINVAL; diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index f85353fd..cbf51225 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -140,6 +140,8 @@ static bool ath6kl_process_uapsdq(struct ath6kl_sta *conn, if (ether_type == IP_ETHERTYPE) up = ath6kl_wmi_determine_user_priority( ip_hdr, 0); + } else { + up = 0; } traffic_class = ath6kl_wmi_get_traffic_class(up); @@ -159,8 +161,8 @@ static bool ath6kl_process_uapsdq(struct ath6kl_sta *conn, */ if (is_apsdq_empty) { ath6kl_wmi_set_apsd_bfrd_traf(ar->wmi, - vif->fw_vif_idx, - conn->aid, 1, 0); + vif->fw_vif_idx, + conn->aid, 1, 0); } *flags |= WMI_DATA_HDR_FLAGS_UAPSD; @@ -285,8 +287,16 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb, int status = 0; struct ath6kl_cookie *cookie = NULL; - if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) + if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) { + dev_kfree_skb(skb); return -EACCES; + } + + if (WARN_ON_ONCE(eid == ENDPOINT_UNUSED || + eid >= ENDPOINT_MAX)) { + status = -EINVAL; + goto fail_ctrl_tx; + } spin_lock_bh(&ar->lock); @@ -303,7 +313,7 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb, ath6kl_err("wmi ctrl ep full, dropping pkt : 0x%p, len:%d\n", skb, skb->len); } else - cookie = ath6kl_alloc_cookie(ar); + cookie = ath6kl_alloc_cookie(ar, eid == ar->ctrl_ep); if (cookie == NULL) { spin_unlock_bh(&ar->lock); @@ -379,7 +389,7 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) if (test_bit(WMI_ENABLED, &ar->flag)) { if ((dev->features & NETIF_F_IP_CSUM) && - (csum == CHECKSUM_PARTIAL)) { + (csum == CHECKSUM_PARTIAL)) { csum_start = skb->csum_start - (skb_network_header(skb) - skb->head) + sizeof(struct ath6kl_llc_snap_hdr); @@ -403,7 +413,7 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) } if ((dev->features & NETIF_F_IP_CSUM) && - (csum == CHECKSUM_PARTIAL)) { + (csum == CHECKSUM_PARTIAL)) { meta_v2.csum_start = csum_start; meta_v2.csum_dest = csum_dest; @@ -428,7 +438,7 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) } if ((vif->nw_type == ADHOC_NETWORK) && - ar->ibss_ps_enable && test_bit(CONNECTED, &vif->flags)) + ar->ibss_ps_enable && test_bit(CONNECTED, &vif->flags)) chk_adhoc_ps_mapping = true; else { /* get the stream mapping */ @@ -455,7 +465,7 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) } /* allocate resource for this packet */ - cookie = ath6kl_alloc_cookie(ar); + cookie = ath6kl_alloc_cookie(ar, eid == ar->ctrl_ep); if (!cookie) { spin_unlock_bh(&ar->lock); @@ -590,6 +600,7 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, */ set_bit(WMI_CTRL_EP_FULL, &ar->flag); ath6kl_err("wmi ctrl ep is full\n"); + ath6kl_recovery_err_notify(ar, ATH6KL_FW_EP_FULL); return action; } @@ -600,7 +611,7 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, * The last MAX_HI_COOKIE_NUM "batch" of cookies are reserved for * the highest active stream. */ - if (ar->ac_stream_pri_map[ar->ep2ac_map[endpoint]] < + if (ar->ac_stream_pri_map[ar->ep2ac_map[endpoint]] <= ar->hiac_stream_active_pri && ar->cookie_count <= target->endpoint[endpoint].tx_drop_packet_threshold) @@ -666,9 +677,10 @@ static void ath6kl_tx_clear_node_map(struct ath6kl_vif *vif, } } -void ath6kl_tx_complete(void *context, struct list_head *packet_queue) +void ath6kl_tx_complete(struct htc_target *target, + struct list_head *packet_queue) { - struct ath6kl *ar = context; + struct ath6kl *ar = target->dev->ar; struct sk_buff_head skb_queue; struct htc_packet *packet; struct sk_buff *skb; @@ -693,22 +705,33 @@ void ath6kl_tx_complete(void *context, struct list_head *packet_queue) list); list_del(&packet->list); + if (WARN_ON_ONCE(packet->endpoint == ENDPOINT_UNUSED || + packet->endpoint >= ENDPOINT_MAX)) + continue; + ath6kl_cookie = (struct ath6kl_cookie *)packet->pkt_cntxt; - if (!ath6kl_cookie) - goto fatal; + if (WARN_ON_ONCE(!ath6kl_cookie)) + continue; status = packet->status; skb = ath6kl_cookie->skb; eid = packet->endpoint; map_no = ath6kl_cookie->map_no; - if (!skb || !skb->data) - goto fatal; + if (WARN_ON_ONCE(!skb || !skb->data)) { + dev_kfree_skb(skb); + ath6kl_free_cookie(ar, ath6kl_cookie, + eid == ar->ctrl_ep); + continue; + } __skb_queue_tail(&skb_queue, skb); - if (!status && (packet->act_len != skb->len)) - goto fatal; + if (WARN_ON_ONCE(!status && (packet->act_len != skb->len))) { + ath6kl_free_cookie(ar, ath6kl_cookie, + eid == ar->ctrl_ep); + continue; + } ar->tx_pending[eid]--; @@ -733,7 +756,8 @@ void ath6kl_tx_complete(void *context, struct list_head *packet_queue) vif = ath6kl_get_vif_by_index(ar, if_idx); if (!vif) { - ath6kl_free_cookie(ar, ath6kl_cookie); + ath6kl_free_cookie(ar, ath6kl_cookie, + eid == ar->ctrl_ep); continue; } @@ -764,7 +788,7 @@ void ath6kl_tx_complete(void *context, struct list_head *packet_queue) ath6kl_tx_clear_node_map(vif, eid, map_no); - ath6kl_free_cookie(ar, ath6kl_cookie); + ath6kl_free_cookie(ar, ath6kl_cookie, eid == ar->ctrl_ep); if (test_bit(NETQ_STOPPED, &vif->flags)) clear_bit(NETQ_STOPPED, &vif->flags); @@ -790,11 +814,6 @@ void ath6kl_tx_complete(void *context, struct list_head *packet_queue) wake_up(&ar->event_wq); return; - -fatal: - WARN_ON(1); - spin_unlock_bh(&ar->lock); - return; } void ath6kl_tx_data_cleanup(struct ath6kl *ar) @@ -883,10 +902,13 @@ void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint) break; packet = (struct htc_packet *) skb->head; - if (!IS_ALIGNED((unsigned long) skb->data, 4)) + if (!IS_ALIGNED((unsigned long) skb->data, 4)) { + size_t len = skb_headlen(skb); skb->data = PTR_ALIGN(skb->data - 4, 4); + skb_set_tail_pointer(skb, len); + } set_htc_rxpkt_info(packet, skb, skb->data, - ATH6KL_BUFFER_SIZE, endpoint); + ATH6KL_BUFFER_SIZE, endpoint); list_add_tail(&packet->list, &queue); } @@ -905,8 +927,11 @@ void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count) return; packet = (struct htc_packet *) skb->head; - if (!IS_ALIGNED((unsigned long) skb->data, 4)) + if (!IS_ALIGNED((unsigned long) skb->data, 4)) { + size_t len = skb_headlen(skb); skb->data = PTR_ALIGN(skb->data - 4, 4); + skb_set_tail_pointer(skb, len); + } set_htc_rxpkt_info(packet, skb, skb->data, ATH6KL_AMSDU_BUFFER_SIZE, 0); spin_lock_bh(&ar->lock); @@ -1031,6 +1056,7 @@ static void aggr_deque_frms(struct aggr_info_conn *agg_conn, u8 tid, rxtid = &agg_conn->rx_tid[tid]; stats = &agg_conn->stat[tid]; + spin_lock_bh(&rxtid->lock); idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz); /* @@ -1049,8 +1075,6 @@ static void aggr_deque_frms(struct aggr_info_conn *agg_conn, u8 tid, seq_end = seq_no ? seq_no : rxtid->seq_next; idx_end = AGGR_WIN_IDX(seq_end, rxtid->hold_q_sz); - spin_lock_bh(&rxtid->lock); - do { node = &rxtid->hold_q[idx]; if ((order == 1) && (!node->skb)) @@ -1122,11 +1146,13 @@ static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid, ((end > extended_end) && (cur > extended_end) && (cur < end))) { aggr_deque_frms(agg_conn, tid, 0, 0); + spin_lock_bh(&rxtid->lock); if (cur >= rxtid->hold_q_sz - 1) rxtid->seq_next = cur - (rxtid->hold_q_sz - 1); else rxtid->seq_next = ATH6KL_MAX_SEQ_NO - (rxtid->hold_q_sz - 2 - cur); + spin_unlock_bh(&rxtid->lock); } else { /* * Dequeue only those frames that are outside the @@ -1180,25 +1206,27 @@ static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid, aggr_deque_frms(agg_conn, tid, 0, 1); if (agg_conn->timer_scheduled) - rxtid->progress = true; - else - for (idx = 0 ; idx < rxtid->hold_q_sz; idx++) { - if (rxtid->hold_q[idx].skb) { - /* - * There is a frame in the queue and no - * timer so start a timer to ensure that - * the frame doesn't remain stuck - * forever. - */ - agg_conn->timer_scheduled = true; - mod_timer(&agg_conn->timer, - (jiffies + - HZ * (AGGR_RX_TIMEOUT) / 1000)); - rxtid->progress = false; - rxtid->timer_mon = true; - break; - } + return is_queued; + + spin_lock_bh(&rxtid->lock); + for (idx = 0 ; idx < rxtid->hold_q_sz; idx++) { + if (rxtid->hold_q[idx].skb) { + /* + * There is a frame in the queue and no + * timer so start a timer to ensure that + * the frame doesn't remain stuck + * forever. + */ + agg_conn->timer_scheduled = true; + mod_timer(&agg_conn->timer, + (jiffies + + HZ * (AGGR_RX_TIMEOUT) / 1000)); + rxtid->progress = false; + rxtid->timer_mon = true; + break; } + } + spin_unlock_bh(&rxtid->lock); return is_queued; } @@ -1266,8 +1294,8 @@ static void ath6kl_uapsd_trigger_frame_rx(struct ath6kl_vif *vif, flags = 0; ath6kl_wmi_set_apsd_bfrd_traf(ar->wmi, - vif->fw_vif_idx, - conn->aid, 0, flags); + vif->fw_vif_idx, + conn->aid, 0, flags); } return; @@ -1306,15 +1334,7 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) skb_put(skb, packet->act_len + HTC_HDR_LENGTH); skb_pull(skb, HTC_HDR_LENGTH); - ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ", - skb->data, skb->len); - if (ept == ar->ctrl_ep) { - if (test_bit(WMI_ENABLED, &ar->flag)) { - ath6kl_check_wow_status(ar); - ath6kl_wmi_control_rx(ar->wmi, skb); - return; - } if_idx = wmi_cmd_hdr_get_if_idx((struct wmi_cmd_hdr *) skb->data); } else { @@ -1339,6 +1359,10 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) spin_unlock_bh(&vif->if_lock); + + ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ", + skb->data, skb->len); + skb->dev = vif->ndev; if (!test_bit(WMI_ENABLED, &ar->flag)) { @@ -1348,13 +1372,19 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) return; } - ath6kl_check_wow_status(ar); + if (ept == ar->ctrl_ep) { + ath6kl_check_wow_status(ar, skb, true); + ath6kl_wmi_control_rx(ar->wmi, skb); + return; + } min_hdr_len = sizeof(struct ethhdr) + sizeof(struct wmi_data_hdr) + sizeof(struct ath6kl_llc_snap_hdr); dhdr = (struct wmi_data_hdr *) skb->data; + ath6kl_check_wow_status(ar, skb, false); + /* * In the case of AP mode we may receive NULL data frames * that do not have LLC hdr. They are 16 bytes in size. @@ -1529,6 +1559,8 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) return; } + ath6kl_check_wow_status(ar, skb, false); + if (vif->nw_type == AP_NETWORK) { datap = (struct ethhdr *) skb->data; if (is_multicast_ether_addr(datap->h_dest)) @@ -1575,11 +1607,12 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) aggr_conn = vif->aggr_cntxt->aggr_conn; if (aggr_process_recv_frm(aggr_conn, tid, seq_no, - is_amsdu, skb)) { + is_amsdu, skb)) { /* aggregation code will handle the skb */ return; } - } + } else if (!is_broadcast_ether_addr(datap->h_dest)) + vif->net_stats.multicast++; ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb); } @@ -1613,6 +1646,7 @@ static void aggr_timeout(unsigned long arg) rxtid = &aggr_conn->rx_tid[i]; if (rxtid->aggr && rxtid->hold_q) { + spin_lock_bh(&rxtid->lock); for (j = 0; j < rxtid->hold_q_sz; j++) { if (rxtid->hold_q[j].skb) { aggr_conn->timer_scheduled = true; @@ -1621,6 +1655,7 @@ static void aggr_timeout(unsigned long arg) break; } } + spin_unlock_bh(&rxtid->lock); if (j >= rxtid->hold_q_sz) rxtid->timer_mon = false; diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index 325b1224..daf51222 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -353,7 +353,7 @@ static int ath6kl_usb_probe(struct usb_interface *interface, goto err_usb_put; } - ar = ath6kl_core_create(&ar_usb->udev->dev); + ar = ath6kl_core_alloc(&ar_usb->udev->dev); if (ar == NULL) { ath6kl_err("Failed to alloc ath6kl core\n"); ret = -ENOMEM; @@ -377,7 +377,7 @@ static int ath6kl_usb_probe(struct usb_interface *interface, return ret; err_core_free: - ath6kl_core_destroy(ar); + ath6kl_core_free(ar); err_usb_destroy: ath6kl_usb_destroy(ar_usb); err_usb_put: @@ -424,9 +424,10 @@ module_exit(ath6kl_usb_exit); MODULE_AUTHOR("Atheros Communications, Inc."); MODULE_DESCRIPTION("Driver support for Atheros AR600x USB devices"); MODULE_LICENSE("Dual BSD/GPL"); -MODULE_FIRMWARE(AR6004_HW_1_0_FIRMWARE_FILE); + +MODULE_FIRMWARE(AR6004_HW_1_0_FW_DIR "/" AR6004_HW_1_0_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE); -MODULE_FIRMWARE(AR6004_HW_1_1_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_FW_DIR "/" AR6004_HW_1_1_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 2b442332..9c7369f9 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -19,8 +19,10 @@ #include "core.h" #include "debug.h" #include "testmode.h" -#include "../regd.h" -#include "../regd_common.h" +#include "regd_ath.h" +#include "regd_common.h" +#include "btc.h" +#include "debugfs_pri.h" static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx); @@ -127,7 +129,7 @@ int ath6kl_wmi_dix_2_dot3(struct wmi *wmi, struct sk_buff *skb) if (!is_ethertype(be16_to_cpu(type))) { ath6kl_dbg(ATH6KL_DBG_WMI, - "%s: pkt is already in 802.3 format\n", __func__); + "%s: pkt is already in 802.3 format\n", __func__); return 0; } @@ -289,6 +291,13 @@ int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, u8 if_idx, layer2_priority); } else usr_pri = layer2_priority & 0x7; + + /* + * Queue the EAPOL frames in the same WMM_AC_VO queue + * as that of management frames. + */ + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) + usr_pri = WMI_VOICE_USER_PRIORITY; } /* @@ -427,6 +436,9 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len) ath6kl_dbg(ATH6KL_DBG_WMI, "comp: %d %d %d\n", evt->num_msg, evt->msg_len, evt->msg_type); + if (!AR_DBG_LVL_CHECK(ATH6KL_DBG_WMI)) + return 0; + for (index = 0; index < evt->num_msg; index++) { size = sizeof(struct wmi_tx_complete_event) + (index * sizeof(struct tx_complete_msg_v1)); @@ -557,8 +569,7 @@ static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len, dlen, freq, vif->probe_req_report); if (vif->probe_req_report || vif->nw_type == AP_NETWORK) - cfg80211_rx_mgmt(vif->ndev, freq, 0, - ev->data, dlen, GFP_ATOMIC); + cfg80211_rx_mgmt(vif->ndev, freq, ev->data, dlen, GFP_ATOMIC); return 0; } @@ -597,8 +608,7 @@ static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len, return -EINVAL; } ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq); - cfg80211_rx_mgmt(vif->ndev, freq, 0, - ev->data, dlen, GFP_ATOMIC); + cfg80211_rx_mgmt(vif->ndev, freq, ev->data, dlen, GFP_ATOMIC); return 0; } @@ -715,7 +725,7 @@ int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi) cmd->info.params.lrssi_roam_threshold = a_cpu_to_sle16(lrssi); cmd->info.params.roam_rssi_floor = DEF_LRSSI_ROAM_FLOOR; cmd->roam_ctrl = WMI_SET_LRSSI_SCAN_PARAMS; - + ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_ROAM_CTRL_CMDID, NO_SYNC_WMIFLAG); @@ -828,8 +838,8 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len, if (pie[1] > 3 && pie[2] == 0x00 && pie[3] == 0x50 && pie[4] == 0xf2 && pie[5] == WMM_OUI_TYPE) { /* WMM OUT (00:50:F2) */ - if (pie[1] > 5 && - pie[6] == WMM_PARAM_OUI_SUBTYPE) + if (pie[1] > 5 + && pie[6] == WMM_PARAM_OUI_SUBTYPE) wmi->is_wmm_enabled = true; } break; @@ -912,18 +922,22 @@ static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len) regpair = ath6kl_get_regpair((u16) reg_code); country = ath6kl_regd_find_country_by_rd((u16) reg_code); + if (regpair) ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n", - regpair->regDmnEnum); + regpair->regDmnEnum); + else + ath6kl_warn("Regpair not found reg_code 0x%0x\n", + reg_code); } - if (country && wmi->parent_dev->wiphy_registered) { + if (country) { alpha2[0] = country->isoName[0]; alpha2[1] = country->isoName[1]; regulatory_hint(wmi->parent_dev->wiphy, alpha2); ath6kl_dbg(ATH6KL_DBG_WMI, "Country alpha2 being used: %c%c\n", - alpha2[0], alpha2[1]); + alpha2[0], alpha2[1]); } } @@ -1034,9 +1048,8 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len, if (len < 8 + 2 + 2) return -EINVAL; - if (bih->frame_type == BEACON_FTYPE && - test_bit(CONNECTED, &vif->flags) && - memcmp(bih->bssid, vif->bssid, ETH_ALEN) == 0) { + if (bih->frame_type == BEACON_FTYPE && test_bit(CONNECTED, &vif->flags) + && memcmp(bih->bssid, vif->bssid, ETH_ALEN) == 0) { const u8 *tim; tim = cfg80211_find_ie(WLAN_EID_TIM, buf + 8 + 2 + 2, len - 8 - 2 - 2); @@ -1092,7 +1105,7 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len, * the timer would not ever fire if the scan interval is short * enough. */ - if (ar->state == ATH6KL_STATE_SCHED_SCAN && + if (test_bit(SCHED_SCANNING, &vif->flags) && !timer_pending(&vif->sched_scan_timer)) { mod_timer(&vif->sched_scan_timer, jiffies + msecs_to_jiffies(ATH6KL_SCHED_SCAN_RESULT_DELAY)); @@ -1146,6 +1159,9 @@ static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len) rate = RATE_AUTO; } else { index = reply->rate_index & 0x7f; + if (WARN_ON_ONCE(index > (RATE_MCS_7_40 + 1))) + return -EINVAL; + sgi = (reply->rate_index & 0x80) ? 1 : 0; rate = wmi_rate_tbl[index][sgi]; } @@ -1368,8 +1384,8 @@ static int ath6kl_wmi_rssi_threshold_event_rx(struct wmi *wmi, u8 *datap, /* Upper threshold breached */ if (rssi < sq_thresh->upper_threshold[0]) { ath6kl_dbg(ATH6KL_DBG_WMI, - "spurious upper rssi threshold event: %d\n", - rssi); + "spurious upper rssi threshold event: %d\n", + rssi); } else if ((rssi < sq_thresh->upper_threshold[1]) && (rssi >= sq_thresh->upper_threshold[0])) { new_threshold = WMI_RSSI_THRESHOLD1_ABOVE; @@ -1392,7 +1408,7 @@ static int ath6kl_wmi_rssi_threshold_event_rx(struct wmi *wmi, u8 *datap, /* Lower threshold breached */ if (rssi > sq_thresh->lower_threshold[0]) { ath6kl_dbg(ATH6KL_DBG_WMI, - "spurious lower rssi threshold event: %d %d\n", + "spurious lower rssi threshold event: %d %d\n", rssi, sq_thresh->lower_threshold[0]); } else if ((rssi > sq_thresh->lower_threshold[1]) && (rssi <= sq_thresh->lower_threshold[0])) { @@ -1506,6 +1522,69 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len, return 0; } +static int ath6kl_wmi_txe_notify_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_txe_notify_event *ev; + u32 rate; + u32 pkts; + + if (len < sizeof(*ev)) + return -EINVAL; + + if (vif->sme_state != SME_CONNECTED) + return -ENOTCONN; + + ev = (struct wmi_txe_notify_event *) datap; + rate = le32_to_cpu(ev->rate); + pkts = le32_to_cpu(ev->pkts); + + ath6kl_dbg(ATH6KL_DBG_WMI, "TXE notify event: peer %pM rate %d pkts %d\n", + vif->bssid, rate, pkts); + + cfg80211_cqm_txe_notify(vif->ndev, vif->bssid, pkts, + rate, vif->txe_intvl, GFP_KERNEL); + + return 0; +} + +int ath6kl_wmi_set_txe_notify(struct wmi *wmi, u8 idx, + u32 rate, u32 pkts, u32 intvl) +{ + struct sk_buff *skb; + struct wmi_txe_notify_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_txe_notify_cmd *) skb->data; + cmd->rate = cpu_to_le32(rate); + cmd->pkts = cpu_to_le32(pkts); + cmd->intvl = cpu_to_le32(intvl); + + return ath6kl_wmi_cmd_send(wmi, idx, skb, WMI_SET_TXE_NOTIFY_CMD, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_rssi_filter_cmd(struct wmi *wmi, u8 if_idx, s8 rssi) +{ + struct sk_buff *skb; + struct wmi_set_rssi_filter_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_rssi_filter_cmd *) skb->data; + cmd->rssi = rssi; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_RSSI_FILTER_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + static int ath6kl_wmi_send_snr_threshold_params(struct wmi *wmi, struct wmi_snr_threshold_params_cmd *snr_cmd) { @@ -1553,8 +1632,8 @@ static int ath6kl_wmi_snr_threshold_event_rx(struct wmi *wmi, u8 *datap, /* Upper threshold breached */ if (snr < sq_thresh->upper_threshold[0]) { ath6kl_dbg(ATH6KL_DBG_WMI, - "spurious upper snr threshold event: %d\n", - snr); + "spurious upper snr threshold event: %d\n", + snr); } else if ((snr < sq_thresh->upper_threshold[1]) && (snr >= sq_thresh->upper_threshold[0])) { new_threshold = WMI_SNR_THRESHOLD1_ABOVE; @@ -1571,8 +1650,8 @@ static int ath6kl_wmi_snr_threshold_event_rx(struct wmi *wmi, u8 *datap, /* Lower threshold breached */ if (snr > sq_thresh->lower_threshold[0]) { ath6kl_dbg(ATH6KL_DBG_WMI, - "spurious lower snr threshold event: %d\n", - sq_thresh->lower_threshold[0]); + "spurious lower snr threshold event: %d\n", + sq_thresh->lower_threshold[0]); } else if ((snr > sq_thresh->lower_threshold[1]) && (snr <= sq_thresh->lower_threshold[0])) { new_threshold = WMI_SNR_THRESHOLD4_BELOW; @@ -1652,8 +1731,11 @@ int ath6kl_wmi_cmd_send(struct wmi *wmi, u8 if_idx, struct sk_buff *skb, int ret; u16 info1; - if (WARN_ON(skb == NULL || (if_idx > (wmi->parent_dev->vif_max - 1)))) + if (WARN_ON(skb == NULL || + (if_idx > (wmi->parent_dev->vif_max - 1)))) { + dev_kfree_skb(skb); return -EINVAL; + } ath6kl_dbg(ATH6KL_DBG_WMI, "wmi tx id %d len %d flag %d\n", cmd_id, skb->len, sync_flag); @@ -1821,6 +1903,15 @@ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, struct ath6kl *ar = wmi->parent_dev; int num_rates; + if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, + ar->fw_capabilities)) { + return ath6kl_wmi_startscan_cmd(wmi, if_idx, + scan_type, force_fgscan, + is_legacy, home_dwell_time, + force_scan_interval, + num_chan, ch_list); + } + size = sizeof(struct wmi_begin_scan_cmd); if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN)) @@ -1917,6 +2008,27 @@ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, return ret; } +int ath6kl_wmi_enable_sched_scan_cmd(struct wmi *wmi, u8 if_idx, bool enable) +{ + struct sk_buff *skb; + struct wmi_enable_sched_scan_cmd *sc; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*sc)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "%s scheduled scan on vif %d\n", + enable ? "enabling" : "disabling", if_idx); + sc = (struct wmi_enable_sched_scan_cmd *) skb->data; + sc->enable = enable ? 1 : 0; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_ENABLE_SCHED_SCAN_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u8 if_idx, u16 fg_start_sec, u16 fg_end_sec, u16 bg_sec, @@ -1979,7 +2091,7 @@ int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 if_idx, u8 index, u8 flag, struct wmi_probed_ssid_cmd *cmd; int ret; - if (index > MAX_PROBED_SSID_INDEX) + if (index >= MAX_PROBED_SSIDS) return -EINVAL; if (ssid_len > sizeof(cmd->ssid)) @@ -2245,8 +2357,10 @@ static int ath6kl_wmi_data_sync_send(struct wmi *wmi, struct sk_buff *skb, struct wmi_data_hdr *data_hdr; int ret; - if (WARN_ON(skb == NULL || ep_id == wmi->ep_id)) + if (WARN_ON(skb == NULL || ep_id == wmi->ep_id)) { + dev_kfree_skb(skb); return -EINVAL; + } skb_push(skb, sizeof(struct wmi_data_hdr)); @@ -2283,10 +2397,8 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) spin_unlock_bh(&wmi->lock); skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); - if (!skb) { - ret = -ENOMEM; - goto free_skb; - } + if (!skb) + return -ENOMEM; cmd = (struct wmi_sync_cmd *) skb->data; @@ -2309,7 +2421,7 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) * then do not send the Synchronize cmd on the control ep */ if (ret) - goto free_skb; + goto free_cmd_skb; /* * Send sync cmd followed by sync data messages on all @@ -2319,15 +2431,12 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) NO_SYNC_WMIFLAG); if (ret) - goto free_skb; - - /* cmd buffer sent, we no longer own it */ - skb = NULL; + goto free_data_skb; for (index = 0; index < num_pri_streams; index++) { if (WARN_ON(!data_sync_bufs[index].skb)) - break; + goto free_data_skb; ep_id = ath6kl_ac2_endpoint_id(wmi->parent_dev, data_sync_bufs[index]. @@ -2336,17 +2445,20 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) ath6kl_wmi_data_sync_send(wmi, data_sync_bufs[index].skb, ep_id, if_idx); - if (ret) - break; - data_sync_bufs[index].skb = NULL; + + if (ret) + goto free_data_skb; } -free_skb: + return 0; + +free_cmd_skb: /* free up any resources left over (possibly due to an error) */ if (skb) dev_kfree_skb(skb); +free_data_skb: for (index = 0; index < num_pri_streams; index++) { if (data_sync_bufs[index].skb != NULL) { dev_kfree_skb((struct sk_buff *)data_sync_bufs[index]. @@ -2583,6 +2695,115 @@ static void ath6kl_wmi_relinquish_implicit_pstream_credits(struct wmi *wmi) spin_unlock_bh(&wmi->lock); } +static int ath6kl_set_bitrate_mask64(struct wmi *wmi, u8 if_idx, + const struct cfg80211_bitrate_mask *mask) +{ + struct sk_buff *skb; + int ret, mode, band; + u64 mcsrate, ratemask[IEEE80211_NUM_BANDS]; + struct wmi_set_tx_select_rates64_cmd *cmd; + + memset(&ratemask, 0, sizeof(ratemask)); + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + /* copy legacy rate mask */ + ratemask[band] = mask->control[band].legacy; + if (band == IEEE80211_BAND_5GHZ) + ratemask[band] = + mask->control[band].legacy << 4; + + /* copy mcs rate mask */ + mcsrate = mask->control[band].mcs[1]; + mcsrate <<= 8; + mcsrate |= mask->control[band].mcs[0]; + ratemask[band] |= mcsrate << 12; + ratemask[band] |= mcsrate << 28; + } + + ath6kl_dbg(ATH6KL_DBG_WMI, + "Ratemask 64 bit: 2.4:%llx 5:%llx\n", + ratemask[0], ratemask[1]); + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd) * WMI_RATES_MODE_MAX); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_tx_select_rates64_cmd *) skb->data; + for (mode = 0; mode < WMI_RATES_MODE_MAX; mode++) { + /* A mode operate in 5GHZ band */ + if (mode == WMI_RATES_MODE_11A || + mode == WMI_RATES_MODE_11A_HT20 || + mode == WMI_RATES_MODE_11A_HT40) + band = IEEE80211_BAND_5GHZ; + else + band = IEEE80211_BAND_2GHZ; + cmd->ratemask[mode] = cpu_to_le64(ratemask[band]); + } + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_TX_SELECT_RATES_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +static int ath6kl_set_bitrate_mask32(struct wmi *wmi, u8 if_idx, + const struct cfg80211_bitrate_mask *mask) +{ + struct sk_buff *skb; + int ret, mode, band; + u32 mcsrate, ratemask[IEEE80211_NUM_BANDS]; + struct wmi_set_tx_select_rates32_cmd *cmd; + + memset(&ratemask, 0, sizeof(ratemask)); + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + /* copy legacy rate mask */ + ratemask[band] = mask->control[band].legacy; + if (band == IEEE80211_BAND_5GHZ) + ratemask[band] = + mask->control[band].legacy << 4; + + /* copy mcs rate mask */ + mcsrate = mask->control[band].mcs[0]; + ratemask[band] |= mcsrate << 12; + ratemask[band] |= mcsrate << 20; + } + + ath6kl_dbg(ATH6KL_DBG_WMI, + "Ratemask 32 bit: 2.4:%x 5:%x\n", + ratemask[0], ratemask[1]); + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd) * WMI_RATES_MODE_MAX); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_tx_select_rates32_cmd *) skb->data; + for (mode = 0; mode < WMI_RATES_MODE_MAX; mode++) { + /* A mode operate in 5GHZ band */ + if (mode == WMI_RATES_MODE_11A || + mode == WMI_RATES_MODE_11A_HT20 || + mode == WMI_RATES_MODE_11A_HT40) + band = IEEE80211_BAND_5GHZ; + else + band = IEEE80211_BAND_2GHZ; + cmd->ratemask[mode] = cpu_to_le32(ratemask[band]); + } + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_TX_SELECT_RATES_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx, + const struct cfg80211_bitrate_mask *mask) +{ + struct ath6kl *ar = wmi->parent_dev; + + if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) + return ath6kl_set_bitrate_mask64(wmi, if_idx, mask); + else + return ath6kl_set_bitrate_mask32(wmi, if_idx, mask); +} + int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx, enum ath6kl_host_mode host_mode) { @@ -2635,7 +2856,7 @@ int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx, int ret; if ((wow_mode != ATH6KL_WOW_MODE_ENABLE) && - wow_mode != ATH6KL_WOW_MODE_DISABLE) { + wow_mode != ATH6KL_WOW_MODE_DISABLE) { ath6kl_err("invalid wow mode: %d\n", wow_mode); return -EINVAL; } @@ -2882,6 +3103,43 @@ int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx, return ret; } +int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx, + enum ieee80211_band band, + struct ath6kl_htcap *htcap) +{ + struct sk_buff *skb; + struct wmi_set_htcap_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_htcap_cmd *) skb->data; + + /* + * NOTE: Band in firmware matches enum ieee80211_band, it is unlikely + * this will be changed in firmware. If at all there is any change in + * band value, the host needs to be fixed. + */ + cmd->band = band; + cmd->ht_enable = !!htcap->ht_enable; + cmd->ht20_sgi = !!(htcap->cap_info & IEEE80211_HT_CAP_SGI_20); + cmd->ht40_supported = + !!(htcap->cap_info & IEEE80211_HT_CAP_SUP_WIDTH_20_40); + cmd->ht40_sgi = !!(htcap->cap_info & IEEE80211_HT_CAP_SGI_40); + cmd->intolerant_40mhz = + !!(htcap->cap_info & IEEE80211_HT_CAP_40MHZ_INTOLERANT); + cmd->max_ampdu_len_exp = htcap->ampdu_factor; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "Set htcap: band:%d ht_enable:%d 40mhz:%d sgi_20mhz:%d sgi_40mhz:%d 40mhz_intolerant:%d ampdu_len_exp:%d\n", + cmd->band, cmd->ht_enable, cmd->ht40_supported, + cmd->ht20_sgi, cmd->ht40_sgi, cmd->intolerant_40mhz, + cmd->max_ampdu_len_exp); + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_HT_CAP_CMDID, + NO_SYNC_WMIFLAG); +} + int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len) { struct sk_buff *skb; @@ -2944,6 +3202,42 @@ int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, return ret; } +int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enhance) +{ + struct sk_buff *skb; + struct wmi_sta_bmiss_enhance_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_sta_bmiss_enhance_cmd *) skb->data; + cmd->enable = enhance ? 1 : 0; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_STA_BMISS_ENHANCE_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2) +{ + struct sk_buff *skb; + struct wmi_set_regdomain_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_regdomain_cmd *) skb->data; + memcpy(cmd->iso_name, alpha2, 2); + + return ath6kl_wmi_cmd_send(wmi, 0, skb, + WMI_SET_REGDOMAIN_CMDID, + NO_SYNC_WMIFLAG); +} + s32 ath6kl_wmi_get_rate(s8 rate_index) { if (rate_index == RATE_AUTO) @@ -3032,6 +3326,9 @@ int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 if_idx, u8 cmd, const u8 *mac, cm->reason = cpu_to_le16(reason); cm->cmd = cmd; + ath6kl_dbg(ATH6KL_DBG_WMI, "ap_set_mlme: cmd=%d reason=%d\n", cm->cmd, + cm->reason); + return ath6kl_wmi_cmd_send(wmip, if_idx, skb, WMI_AP_SET_MLME_CMDID, NO_SYNC_WMIFLAG); } @@ -3181,6 +3478,29 @@ int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, NO_SYNC_WMIFLAG); } +int ath6kl_wmi_set_ie_cmd(struct wmi *wmi, u8 if_idx, u8 ie_id, u8 ie_field, + const u8 *ie_info, u8 ie_len) +{ + struct sk_buff *skb; + struct wmi_set_ie_cmd *p; + + skb = ath6kl_wmi_get_new_buf(sizeof(*p) + ie_len); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "set_ie_cmd: ie_id=%u ie_ie_field=%u " + "ie_len=%u\n", ie_id, ie_field, ie_len); + p = (struct wmi_set_ie_cmd *) skb->data; + p->ie_id = ie_id; + p->ie_field = ie_field; + p->ie_len = ie_len; + if (ie_info != NULL && ie_len > 0) + memcpy(p->ie_info, ie_info, ie_len); + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_IE_CMDID, + NO_SYNC_WMIFLAG); +} + int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable) { struct sk_buff *skb; @@ -3392,6 +3712,91 @@ int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx) WMI_CANCEL_REMAIN_ON_CHNL_CMDID); } +int ath6kl_wmi_set_ch_params(struct wmi *wmi, u8 if_idx, + enum wmi_phy_mode phy_mode) +{ + struct sk_buff *skb; + struct wmi_set_ch_params *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_ch_params *) skb->data; + cmd->phy_mode = phy_mode; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_CHANNEL_PARAMS_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_set_acl_policy(struct wmi *wmi, u8 if_idx, bool enable_acl) +{ + struct sk_buff *skb; + struct wmi_ap_acl_policy_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_ap_acl_policy_cmd *) skb->data; + cmd->policy = enable_acl ? WMI_ACL_BLWL_MAC : 0; + + ath6kl_dbg(ATH6KL_DBG_WMI, "Set acl policy=%d\n", cmd->policy); + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_ACL_POLICY_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_acl_list(struct wmi *wmi, u8 if_idx, int index, + const u8 *mac_addr, + enum nl80211_acl_policy_attr acl_policy, bool reset) +{ + struct sk_buff *skb; + struct wmi_set_acl_list_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_acl_list_cmd *) skb->data; + cmd->index = index; + cmd->wildcard = 0; + memcpy(cmd->mac, mac_addr, ETH_ALEN); + if (reset) { + if (acl_policy == NL80211_ACL_POLICY_ACCEPT) + cmd->action = WMI_ACL_RESET_WHITE_LIST; + else + cmd->action = WMI_ACL_RESET_BLACK_LIST; + } else { + if (acl_policy == NL80211_ACL_POLICY_ACCEPT) + cmd->action = WMI_ACL_ADD_WHITE_MAC_ADDR; + else + cmd->action = WMI_ACL_ADD_BLACK_MAC_ADDR; + } + + ath6kl_dbg(ATH6KL_DBG_WMI, "set acl mac: index:%d action:%d mac:%pM reset:%d\n", + cmd->index, cmd->action, cmd->mac, reset); + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_ACL_MAC_LIST_CMDID, + NO_SYNC_WMIFLAG); +} + +static void ath6kl_wmi_hb_challenge_resp_event(struct wmi *wmi, u8 *datap, + int len) +{ + struct wmix_hb_challenge_resp_cmd *cmd; + + if (len < sizeof(struct wmix_hb_challenge_resp_cmd)) + return; + + cmd = (struct wmix_hb_challenge_resp_cmd *) datap; + ath6kl_recovery_hb_event(wmi->parent_dev, + le32_to_cpu(cmd->cookie)); +} + static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) { struct wmix_cmd_hdr *cmd; @@ -3416,6 +3821,7 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) switch (id) { case WMIX_HB_CHALLENGE_RESP_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event hb challenge resp\n"); + ath6kl_wmi_hb_challenge_resp_event(wmi, datap, len); break; case WMIX_DBGLOG_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event dbglog len %d\n", len); @@ -3435,101 +3841,32 @@ static int ath6kl_wmi_roam_tbl_event_rx(struct wmi *wmi, u8 *datap, int len) return ath6kl_debug_roam_tbl_event(wmi->parent_dev, datap, len); } -/* Process interface specific wmi events, caller would free the datap */ -static int ath6kl_wmi_proc_events_vif(struct wmi *wmi, u16 if_idx, u16 cmd_id, - u8 *datap, u32 len) +/* Control Path */ +int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) { + struct wmi_cmd_hdr *cmd; struct ath6kl_vif *vif; + u32 len; + u16 id; + u8 if_idx; + u8 *datap; + int ret = 0; - vif = ath6kl_get_vif_by_index(wmi->parent_dev, if_idx); - if (!vif) { - ath6kl_dbg(ATH6KL_DBG_WMI, - "Wmi event for unavailable vif, vif_index:%d\n", - if_idx); + if (WARN_ON(skb == NULL)) return -EINVAL; - } - switch (cmd_id) { - case WMI_CONNECT_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CONNECT_EVENTID\n"); - return ath6kl_wmi_connect_event_rx(wmi, datap, len, vif); - case WMI_DISCONNECT_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DISCONNECT_EVENTID\n"); - return ath6kl_wmi_disconnect_event_rx(wmi, datap, len, vif); - case WMI_TKIP_MICERR_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TKIP_MICERR_EVENTID\n"); - return ath6kl_wmi_tkip_micerr_event_rx(wmi, datap, len, vif); - case WMI_BSSINFO_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n"); - return ath6kl_wmi_bssinfo_event_rx(wmi, datap, len, vif); - case WMI_NEIGHBOR_REPORT_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NEIGHBOR_REPORT_EVENTID\n"); - return ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len, - vif); - case WMI_SCAN_COMPLETE_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n"); - return ath6kl_wmi_scan_complete_rx(wmi, datap, len, vif); - case WMI_REPORT_STATISTICS_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_STATISTICS_EVENTID\n"); - return ath6kl_wmi_stats_event_rx(wmi, datap, len, vif); - case WMI_CAC_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CAC_EVENTID\n"); - return ath6kl_wmi_cac_event_rx(wmi, datap, len, vif); - case WMI_PSPOLL_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSPOLL_EVENTID\n"); - return ath6kl_wmi_pspoll_event_rx(wmi, datap, len, vif); - case WMI_DTIMEXPIRY_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DTIMEXPIRY_EVENTID\n"); - return ath6kl_wmi_dtimexpiry_event_rx(wmi, datap, len, vif); - case WMI_ADDBA_REQ_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_REQ_EVENTID\n"); - return ath6kl_wmi_addba_req_event_rx(wmi, datap, len, vif); - case WMI_DELBA_REQ_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DELBA_REQ_EVENTID\n"); - return ath6kl_wmi_delba_req_event_rx(wmi, datap, len, vif); - case WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, - "WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID"); - return ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(wmi, vif); - case WMI_REMAIN_ON_CHNL_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n"); - return ath6kl_wmi_remain_on_chnl_event_rx(wmi, datap, len, vif); - case WMI_CANCEL_REMAIN_ON_CHNL_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, - "WMI_CANCEL_REMAIN_ON_CHNL_EVENTID\n"); - return ath6kl_wmi_cancel_remain_on_chnl_event_rx(wmi, datap, - len, vif); - case WMI_TX_STATUS_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n"); - return ath6kl_wmi_tx_status_event_rx(wmi, datap, len, vif); - case WMI_RX_PROBE_REQ_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n"); - return ath6kl_wmi_rx_probe_req_event_rx(wmi, datap, len, vif); - case WMI_RX_ACTION_EVENTID: - ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n"); - return ath6kl_wmi_rx_action_event_rx(wmi, datap, len, vif); - default: - ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", cmd_id); + if (skb->len < sizeof(struct wmi_cmd_hdr)) { + ath6kl_err("bad packet 1\n"); + dev_kfree_skb(skb); return -EINVAL; } - return 0; -} - -static int ath6kl_wmi_proc_events(struct wmi *wmi, struct sk_buff *skb) -{ - struct wmi_cmd_hdr *cmd; - int ret = 0; - u32 len; - u16 id; - u8 if_idx; - u8 *datap; - cmd = (struct wmi_cmd_hdr *) skb->data; id = le16_to_cpu(cmd->cmd_id); if_idx = le16_to_cpu(cmd->info1) & WMI_CMD_HDR_IF_ID_MASK; skb_pull(skb, sizeof(struct wmi_cmd_hdr)); + datap = skb->data; len = skb->len; @@ -3537,6 +3874,15 @@ static int ath6kl_wmi_proc_events(struct wmi *wmi, struct sk_buff *skb) ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi rx ", datap, len); + vif = ath6kl_get_vif_by_index(wmi->parent_dev, if_idx); + if (!vif) { + ath6kl_dbg(ATH6KL_DBG_WMI, + "Wmi event for unavailable vif, vif_index:%d\n", + if_idx); + dev_kfree_skb(skb); + return -EINVAL; + } + switch (id) { case WMI_GET_BITRATE_CMDID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_BITRATE_CMDID\n"); @@ -3553,10 +3899,30 @@ static int ath6kl_wmi_proc_events(struct wmi *wmi, struct sk_buff *skb) case WMI_READY_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_READY_EVENTID\n"); ret = ath6kl_wmi_ready_event_rx(wmi, datap, len); + cfg80211_send_event_to_app(skb->dev, id, datap, len); + break; + case WMI_CONNECT_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CONNECT_EVENTID\n"); + ret = ath6kl_wmi_connect_event_rx(wmi, datap, len, vif); + cfg80211_send_genevent_to_app(skb->dev, id, datap, len); + break; + case WMI_DISCONNECT_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DISCONNECT_EVENTID\n"); + ret = ath6kl_wmi_disconnect_event_rx(wmi, datap, len, vif); + cfg80211_send_event_to_app(skb->dev, id, datap, len); break; case WMI_PEER_NODE_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PEER_NODE_EVENTID\n"); ret = ath6kl_wmi_peer_node_event_rx(wmi, datap, len); + cfg80211_send_event_to_app(skb->dev, id, datap, len); + break; + case WMI_TKIP_MICERR_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TKIP_MICERR_EVENTID\n"); + ret = ath6kl_wmi_tkip_micerr_event_rx(wmi, datap, len, vif); + break; + case WMI_BSSINFO_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n"); + ret = ath6kl_wmi_bssinfo_event_rx(wmi, datap, len, vif); break; case WMI_REGDOMAIN_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REGDOMAIN_EVENTID\n"); @@ -3565,17 +3931,36 @@ static int ath6kl_wmi_proc_events(struct wmi *wmi, struct sk_buff *skb) case WMI_PSTREAM_TIMEOUT_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSTREAM_TIMEOUT_EVENTID\n"); ret = ath6kl_wmi_pstream_timeout_event_rx(wmi, datap, len); + cfg80211_send_event_to_app(skb->dev, id, datap, len); + break; + case WMI_NEIGHBOR_REPORT_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NEIGHBOR_REPORT_EVENTID\n"); + ret = ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len, + vif); + break; + case WMI_SCAN_COMPLETE_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n"); + ret = ath6kl_wmi_scan_complete_rx(wmi, datap, len, vif); + cfg80211_send_event_to_app(skb->dev, id, datap, len); break; case WMI_CMDERROR_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CMDERROR_EVENTID\n"); ret = ath6kl_wmi_error_event_rx(wmi, datap, len); break; + case WMI_REPORT_STATISTICS_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_STATISTICS_EVENTID\n"); + ret = ath6kl_wmi_stats_event_rx(wmi, datap, len, vif); + break; case WMI_RSSI_THRESHOLD_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RSSI_THRESHOLD_EVENTID\n"); ret = ath6kl_wmi_rssi_threshold_event_rx(wmi, datap, len); break; case WMI_ERROR_REPORT_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ERROR_REPORT_EVENTID\n"); +#if 0 + ath6kl_wmi_error_report_event(wmi, datap, len); +#endif + cfg80211_send_event_to_app(skb->dev, id, datap, len); break; case WMI_OPT_RX_FRAME_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_OPT_RX_FRAME_EVENTID\n"); @@ -3589,6 +3974,10 @@ static int ath6kl_wmi_proc_events(struct wmi *wmi, struct sk_buff *skb) ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_EXTENSION_EVENTID\n"); ret = ath6kl_wmi_control_rx_xtnd(wmi, skb); break; + case WMI_CAC_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CAC_EVENTID\n"); + ret = ath6kl_wmi_cac_event_rx(wmi, datap, len, vif); + break; case WMI_CHANNEL_CHANGE_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CHANNEL_CHANGE_EVENTID\n"); break; @@ -3605,6 +3994,7 @@ static int ath6kl_wmi_proc_events(struct wmi *wmi, struct sk_buff *skb) break; case WMI_TX_RETRY_ERR_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_RETRY_ERR_EVENTID\n"); + cfg80211_send_event_to_app(skb->dev, id, datap, len); break; case WMI_SNR_THRESHOLD_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SNR_THRESHOLD_EVENTID\n"); @@ -3612,6 +4002,7 @@ static int ath6kl_wmi_proc_events(struct wmi *wmi, struct sk_buff *skb) break; case WMI_LQ_THRESHOLD_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_LQ_THRESHOLD_EVENTID\n"); + cfg80211_send_event_to_app(skb->dev, id, datap, len); break; case WMI_APLIST_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_APLIST_EVENTID\n"); @@ -3628,12 +4019,28 @@ static int ath6kl_wmi_proc_events(struct wmi *wmi, struct sk_buff *skb) ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_PMKID_LIST_EVENTID\n"); ret = ath6kl_wmi_get_pmkid_list_event_rx(wmi, datap, len); break; + case WMI_PSPOLL_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSPOLL_EVENTID\n"); + ret = ath6kl_wmi_pspoll_event_rx(wmi, datap, len, vif); + break; + case WMI_DTIMEXPIRY_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DTIMEXPIRY_EVENTID\n"); + ret = ath6kl_wmi_dtimexpiry_event_rx(wmi, datap, len, vif); + break; case WMI_SET_PARAMS_REPLY_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SET_PARAMS_REPLY_EVENTID\n"); break; + case WMI_ADDBA_REQ_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_REQ_EVENTID\n"); + ret = ath6kl_wmi_addba_req_event_rx(wmi, datap, len, vif); + break; case WMI_ADDBA_RESP_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_RESP_EVENTID\n"); break; + case WMI_DELBA_REQ_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DELBA_REQ_EVENTID\n"); + ret = ath6kl_wmi_delba_req_event_rx(wmi, datap, len, vif); + break; case WMI_REPORT_BTCOEX_CONFIG_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_BTCOEX_CONFIG_EVENTID\n"); @@ -3646,37 +4053,54 @@ static int ath6kl_wmi_proc_events(struct wmi *wmi, struct sk_buff *skb) ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_COMPLETE_EVENTID\n"); ret = ath6kl_wmi_tx_complete_event_rx(datap, len); break; + case WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, + "WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID"); + ret = ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(wmi, vif); + break; + case WMI_REMAIN_ON_CHNL_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n"); + ret = ath6kl_wmi_remain_on_chnl_event_rx(wmi, datap, len, vif); + break; + case WMI_CANCEL_REMAIN_ON_CHNL_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, + "WMI_CANCEL_REMAIN_ON_CHNL_EVENTID\n"); + ret = ath6kl_wmi_cancel_remain_on_chnl_event_rx(wmi, datap, + len, vif); + break; + case WMI_TX_STATUS_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n"); + ret = ath6kl_wmi_tx_status_event_rx(wmi, datap, len, vif); + break; + case WMI_RX_PROBE_REQ_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n"); + ret = ath6kl_wmi_rx_probe_req_event_rx(wmi, datap, len, vif); + break; case WMI_P2P_CAPABILITIES_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_CAPABILITIES_EVENTID\n"); ret = ath6kl_wmi_p2p_capabilities_event_rx(datap, len); break; + case WMI_RX_ACTION_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n"); + ret = ath6kl_wmi_rx_action_event_rx(wmi, datap, len, vif); + break; case WMI_P2P_INFO_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_INFO_EVENTID\n"); ret = ath6kl_wmi_p2p_info_event_rx(datap, len); break; + case WMI_TXE_NOTIFY_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TXE_NOTIFY_EVENTID\n"); + ret = ath6kl_wmi_txe_notify_event_rx(wmi, datap, len, vif); + break; default: - /* may be the event is interface specific */ - ret = ath6kl_wmi_proc_events_vif(wmi, if_idx, id, datap, len); + ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", id); + ret = -EINVAL; break; } dev_kfree_skb(skb); - return ret; -} -/* Control Path */ -int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) -{ - if (WARN_ON(skb == NULL)) - return -EINVAL; - - if (skb->len < sizeof(struct wmi_cmd_hdr)) { - ath6kl_err("bad packet 1\n"); - dev_kfree_skb(skb); - return -EINVAL; - } - - return ath6kl_wmi_proc_events(wmi, skb); + return ret; } void ath6kl_wmi_reset(struct wmi *wmi) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 4092e3e8..9cee4eee 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -60,6 +60,9 @@ * Use a_ prefix so that it doesn't conflict if we get proper support to * linux/types.h. */ + + + typedef __s16 __bitwise a_sle16; typedef __s32 __bitwise a_sle32; @@ -106,13 +109,13 @@ struct wmi_data_sync_bufs { #define WMM_AC_VI 2 /* video */ #define WMM_AC_VO 3 /* voice */ +#define WMI_VOICE_USER_PRIORITY 0x7 + struct wmi { u16 stream_exist_for_ac[WMM_NUM_AC]; u8 fat_pipe_exist; struct ath6kl *parent_dev; u8 pwr_mode; - - /* protects fat_pipe_exist and stream_exist_for_ac */ spinlock_t lock; enum htc_endpoint_id ep_id; struct sq_threshold_params @@ -423,6 +426,7 @@ enum wmi_cmd_id { WMI_SET_FRAMERATES_CMDID, WMI_SET_AP_PS_CMDID, WMI_SET_QOS_SUPP_CMDID, + WMI_SET_IE_CMDID, /* WMI_THIN_RESERVED_... mark the start and end * values for WMI_THIN_RESERVED command IDs. These @@ -618,6 +622,24 @@ enum wmi_cmd_id { WMI_SEND_MGMT_CMDID, WMI_BEGIN_SCAN_CMDID, + WMI_SET_BLACK_LIST, + WMI_SET_MCASTRATE, + + WMI_STA_BMISS_ENHANCE_CMDID, + + WMI_SET_REGDOMAIN_CMDID, + + WMI_SET_RSSI_FILTER_CMDID, + + WMI_SET_KEEP_ALIVE_EXT, + + WMI_VOICE_DETECTION_ENABLE_CMDID, + + WMI_SET_TXE_NOTIFY_CMD, + + WMI_SET_RECOVERY_TEST_PARAMETER_CMDID, /*0xf094*/ + + WMI_ENABLE_SCHED_SCAN_CMDID, }; enum wmi_mgmt_frame_type { @@ -629,6 +651,11 @@ enum wmi_mgmt_frame_type { WMI_NUM_MGMT_FRAME }; +enum wmi_ie_field_type { + WMI_RSN_IE_CAPB = 0x1, + WMI_IE_FULL = 0xFF /* indicats full IE */ +}; + /* WMI_CONNECT_CMDID */ enum network_type { INFRA_NETWORK = 0x01, @@ -926,6 +953,11 @@ struct wmi_scan_params_cmd { __le32 max_dfsch_act_time; } __packed; +/* WMI_ENABLE_SCHED_SCAN_CMDID */ +struct wmi_enable_sched_scan_cmd { + u8 enable; +} __packed; + /* WMI_SET_BSS_FILTER_CMDID */ enum wmi_bss_filter { /* no beacons forwarded */ @@ -949,6 +981,9 @@ enum wmi_bss_filter { /* beacons matching probed ssid */ PROBED_SSID_FILTER, + /* beacons matching matched ssid */ + MATCHED_SSID_FILTER, + /* marker only */ LAST_BSS_FILTER, }; @@ -967,7 +1002,7 @@ struct wmi_bss_filter_cmd { } __packed; /* WMI_SET_PROBED_SSID_CMDID */ -#define MAX_PROBED_SSID_INDEX 9 +#define MAX_PROBED_SSIDS 16 enum wmi_ssid_flag { /* disables entry */ @@ -978,10 +1013,13 @@ enum wmi_ssid_flag { /* probes for any ssid */ ANY_SSID_FLAG = 0x02, + + /* match for ssid */ + MATCH_SSID_FLAG = 0x08, }; struct wmi_probed_ssid_cmd { - /* 0 to MAX_PROBED_SSID_INDEX */ + /* 0 to MAX_PROBED_SSIDS - 1 */ u8 entry_index; /* see, enum wmi_ssid_flg */ @@ -1006,6 +1044,16 @@ struct wmi_bmiss_time_cmd { __le16 num_beacons; }; +/* WMI_STA_ENHANCE_BMISS_CMDID */ +struct wmi_sta_bmiss_enhance_cmd { + u8 enable; +} __packed; + +struct wmi_set_regdomain_cmd { + u8 length; + u8 iso_name[2]; +} __packed; + /* WMI_SET_POWER_MODE_CMDID */ enum wmi_power_mode { REC_POWER = 0x01, @@ -1023,7 +1071,7 @@ struct wmi_power_mode_cmd { */ enum power_save_fail_event_policy { SEND_POWER_SAVE_FAIL_EVENT_ALWAYS = 1, - IGNORE_PS_FAIL_DURING_SCAN = 2, + IGNORE_POWER_SAVE_FAIL_EVENT_DURING_SCAN = 2, }; struct wmi_power_params_cmd { @@ -1037,6 +1085,36 @@ struct wmi_power_params_cmd { __le16 ps_fail_event_policy; } __packed; +/* + * Ratemask for below modes should be passed + * to WMI_SET_TX_SELECT_RATES_CMDID. + * AR6003 has 32 bit mask for each modes. + * First 12 bits for legacy rates, 13 to 20 + * bits for HT 20 rates and 21 to 28 bits for + * HT 40 rates + */ +enum wmi_mode_phy { + WMI_RATES_MODE_11A = 0, + WMI_RATES_MODE_11G, + WMI_RATES_MODE_11B, + WMI_RATES_MODE_11GONLY, + WMI_RATES_MODE_11A_HT20, + WMI_RATES_MODE_11G_HT20, + WMI_RATES_MODE_11A_HT40, + WMI_RATES_MODE_11G_HT40, + WMI_RATES_MODE_MAX +}; + +/* WMI_SET_TX_SELECT_RATES_CMDID */ +struct wmi_set_tx_select_rates32_cmd { + __le32 ratemask[WMI_RATES_MODE_MAX]; +} __packed; + +/* WMI_SET_TX_SELECT_RATES_CMDID */ +struct wmi_set_tx_select_rates64_cmd { + __le64 ratemask[WMI_RATES_MODE_MAX]; +} __packed; + /* WMI_SET_DISC_TIMEOUT_CMDID */ struct wmi_disc_timeout_cmd { /* seconds */ @@ -1142,8 +1220,17 @@ enum wmi_phy_mode { WMI_11AG_MODE = 0x3, WMI_11B_MODE = 0x4, WMI_11GONLY_MODE = 0x5, + WMI_11G_HT20 = 0x6, }; +struct wmi_set_ch_params { + u8 rsved; + u8 sca; + u8 phy_mode; + u8 no_ch; + __le16 ch_list[1]; +} __packed; + #define WMI_MAX_CHANNELS 32 /* @@ -1219,9 +1306,14 @@ struct wmi_snr_threshold_params_cmd { u8 reserved[3]; } __packed; +/* Don't report BSSs with signal (RSSI) below this threshold */ +struct wmi_set_rssi_filter_cmd { + s8 rssi; +} __packed; + enum wmi_preamble_policy { WMI_IGNORE_BARKER_IN_ERP = 0, - WMI_FOLLOW_BARKER_IN_ERP, + WMI_DONOT_IGNORE_BARKER_IN_ERP }; struct wmi_set_lpreamble_cmd { @@ -1268,6 +1360,16 @@ struct wmi_mcast_filter_add_del_cmd { u8 mcast_mac[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE]; } __packed; +struct wmi_set_htcap_cmd { + u8 band; + u8 ht_enable; + u8 ht40_supported; + u8 ht20_sgi; + u8 ht40_sgi; + u8 intolerant_40mhz; + u8 max_ampdu_len_exp; +} __packed; + /* Command Replies */ /* WMI_GET_CHANNEL_LIST_CMDID reply */ @@ -1388,6 +1490,20 @@ enum wmi_event_id { WMI_P2P_CAPABILITIES_EVENTID, WMI_RX_ACTION_EVENTID, WMI_P2P_INFO_EVENTID, + + /* WPS Events */ + WMI_WPS_GET_STATUS_EVENTID, + WMI_WPS_PROFILE_EVENTID, + + /* more P2P events */ + WMI_NOA_INFO_EVENTID, + WMI_OPPPS_INFO_EVENTID, + WMI_PORT_STATUS_EVENTID, + + /* 802.11w */ + WMI_GET_RSN_CAP_EVENTID, + + WMI_TXE_NOTIFY_EVENTID, }; struct wmi_ready_event_2 { @@ -1449,6 +1565,17 @@ enum wmi_disconnect_reason { IBSS_MERGE = 0xe, }; +/* AP mode disconnect proto_reasons */ +enum ap_disconnect_reason { + WMI_AP_REASON_STA_LEFT = 101, + WMI_AP_REASON_FROM_HOST = 102, + WMI_AP_REASON_COMM_TIMEOUT = 103, + WMI_AP_REASON_MAX_STA = 104, + WMI_AP_REASON_ACL = 105, + WMI_AP_REASON_STA_ROAM = 106, + WMI_AP_REASON_DFS_CHANNEL = 107, +}; + #define ATH6KL_COUNTRY_RD_SHIFT 16 struct ath6kl_wmi_regdomain { @@ -1913,6 +2040,14 @@ struct wmi_set_appie_cmd { u8 ie_info[0]; } __packed; +struct wmi_set_ie_cmd { + u8 ie_id; + u8 ie_field; /* enum wmi_ie_field_type */ + u8 ie_len; + u8 reserved; + u8 ie_info[0]; +} __packed; + /* Notify the WSC registration status to the target */ #define WSC_REG_ACTIVE 1 #define WSC_REG_INACTIVE 0 @@ -1987,6 +2122,19 @@ struct wmi_del_wow_pattern_cmd { __le16 filter_id; } __packed; +/* WMI_SET_TXE_NOTIFY_CMD */ +struct wmi_txe_notify_cmd { + __le32 rate; + __le32 pkts; + __le32 intvl; +} __packed; + +/* WMI_TXE_NOTIFY_EVENTID */ +struct wmi_txe_notify_event { + __le32 rate; + __le32 pkts; +} __packed; + /* WMI_SET_AKMP_PARAMS_CMD */ struct wmi_pmkid { @@ -2178,6 +2326,35 @@ struct wmi_ap_mode_stat { struct wmi_per_sta_stat sta[AP_MAX_NUM_STA + 1]; } __packed; +#define MAX_ACL_MAC_ADDRS 10 + +/* Special mac index to notify eol */ +#define MAC_ACL_INDEX_EOL 0xff + +#define WMI_ACL_BLWL_MAC 0x3 + +enum wmi_mac_acl_action { + WMI_ACL_ADD_WHITE_MAC_ADDR = 0x01, + WMI_ACL_ADD_BLACK_MAC_ADDR, + WMI_ACL_RESET_WHITE_LIST, + WMI_ACL_RESET_BLACK_LIST, + WMI_ACL_RESET_BW_LIST = 0x10, +}; + +struct wmi_set_acl_list_cmd { + /* Takes one of actions from enum wmi_mac_acl_action */ + u8 action; + + u8 index; + u8 mac[ETH_ALEN]; + u8 wildcard; +} __packed; + +struct wmi_ap_acl_policy_cmd { + /* Type of acl that fw supports, WMI_ACL_BLWL_MAC */ + u8 policy; +} __packed; + /* End of AP mode definitions */ struct wmi_remain_on_chnl_cmd { @@ -2412,6 +2589,7 @@ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, u32 home_dwell_time, u32 force_scan_interval, s8 num_chan, u16 *ch_list, u32 no_cck, u32 *rates); +int ath6kl_wmi_enable_sched_scan_cmd(struct wmi *wmi, u8 if_idx, bool enable); int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u8 if_idx, u16 fg_start_sec, u16 fg_end_sec, u16 bg_sec, @@ -2462,9 +2640,14 @@ int ath6kl_wmi_set_tx_pwr_cmd(struct wmi *wmi, u8 if_idx, u8 dbM); int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi, u8 if_idx); int ath6kl_wmi_get_roam_tbl_cmd(struct wmi *wmi); + int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, u8 if_idx, enum wmi_txop_cfg cfg); +int ath6kl_wmi_ap_set_num_sta(struct wmi *wmip, u8 if_idx, u8 num_sta); int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx, u8 keep_alive_intvl); +int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx, + enum ieee80211_band band, + struct ath6kl_htcap *htcap); int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len); s32 ath6kl_wmi_get_rate(s8 rate_index); @@ -2473,6 +2656,8 @@ int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, u8 if_idx, __be32 ips0, __be32 ips1); int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx, enum ath6kl_host_mode host_mode); +int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx, + const struct cfg80211_bitrate_mask *mask); int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx, enum ath6kl_wow_mode wow_mode, u32 filter, u16 host_req_delay); @@ -2482,12 +2667,18 @@ int ath6kl_wmi_add_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, const u8 *mask); int ath6kl_wmi_del_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, u16 list_id, u16 filter_id); +int ath6kl_wmi_set_rssi_filter_cmd(struct wmi *wmi, u8 if_idx, s8 rssi); int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi); int ath6kl_wmi_force_roam_cmd(struct wmi *wmi, const u8 *bssid); int ath6kl_wmi_set_roam_mode_cmd(struct wmi *wmi, enum wmi_roam_mode mode); int ath6kl_wmi_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, bool mc_all_on); int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, u8 *filter, bool add_filter); +int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enable); +int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2); +int ath6kl_wmi_set_txe_notify(struct wmi *wmi, u8 idx, + u32 rate, u32 pkts, u32 intvl); + /* AP mode uAPSD */ int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable); @@ -2495,6 +2686,10 @@ int ath6kl_wmi_set_apsd_bfrd_traf(struct wmi *wmi, u8 if_idx, u16 aid, u16 bitmap, u32 flags); +int ath6kl_wmi_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, bool mc_all_on); +int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, + u8 *filter, bool add_filter); + u8 ath6kl_wmi_get_traffic_class(u8 user_priority); u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri); @@ -2515,6 +2710,14 @@ int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 if_idx, int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, const u8 *ie, u8 ie_len); +int ath6kl_wmi_set_ie_cmd(struct wmi *wmi, u8 if_idx, u8 ie_id, u8 ie_field, + const u8 *ie_info, u8 ie_len); + +int ath6kl_wmi_set_acl_policy(struct wmi *wmi, u8 if_idx, bool enable_acl); +int ath6kl_wmi_set_acl_list(struct wmi *wmi, u8 if_idx, int index, + const u8 *mac_addr, + enum nl80211_acl_policy_attr acl_policy, + bool reset); /* P2P */ int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable); @@ -2537,9 +2740,12 @@ int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx); int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, const u8 *ie, u8 ie_len); - +int ath6kl_wmi_set_ch_params(struct wmi *wmi, u8 if_idx, + enum wmi_phy_mode phy_mode); void ath6kl_wmi_sscan_timer(unsigned long ptr); +int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source); + struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx); void *ath6kl_wmi_init(struct ath6kl *devt); void ath6kl_wmi_shutdown(struct wmi *wmi); diff --git a/drivers/net/wireless/ath/ath6kl/wmi_btcoex.c b/drivers/net/wireless/ath/ath6kl/wmi_btcoex.c new file mode 100644 index 00000000..0a5a8ed4 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/wmi_btcoex.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include "core.h" +#include "wmi_btcoex.h" +#include "debug.h" + +static inline struct sk_buff *ath6kl_wmi_btcoex_get_new_buf(u32 size) +{ + struct sk_buff *skb; + + skb = ath6kl_buf_alloc(size); + if (!skb) + return NULL; + + skb_put(skb, size); + if (size) + memset(skb->data, 0, size); + + return skb; +} + +static int ath6kl_get_wmi_cmd(int nl_cmd) +{ + int wmi_cmd = 0; + switch (nl_cmd) { + case NL80211_WMI_SET_BT_STATUS: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT status\n"); + wmi_cmd = WMI_SET_BT_STATUS_CMDID; + break; + + case NL80211_WMI_SET_BT_PARAMS: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT params\n"); + wmi_cmd = WMI_SET_BT_PARAMS_CMDID; + break; + + case NL80211_WMI_SET_BT_FT_ANT: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT FT antenna\n"); + wmi_cmd = WMI_SET_BTCOEX_FE_ANT_CMDID; + break; + + case NL80211_WMI_SET_COLOCATED_BT_DEV: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT collocated dev\n"); + wmi_cmd = WMI_SET_BTCOEX_COLOCATED_BT_DEV_CMDID; + break; + + case NL80211_WMI_SET_BT_INQUIRY_PAGE_CONFIG: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT inquiry page\n"); + wmi_cmd = WMI_SET_BTCOEX_BTINQUIRY_PAGE_CONFIG_CMDID; + break; + + case NL80211_WMI_SET_BT_SCO_CONFIG: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT sco config\n"); + wmi_cmd = WMI_SET_BTCOEX_SCO_CONFIG_CMDID; + break; + + case NL80211_WMI_SET_BT_A2DP_CONFIG: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT a2dp config\n"); + wmi_cmd = WMI_SET_BTCOEX_A2DP_CONFIG_CMDID; + break; + + case NL80211_WMI_SET_BT_ACLCOEX_CONFIG: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT acl config\n"); + wmi_cmd = WMI_SET_BTCOEX_ACLCOEX_CONFIG_CMDID; + break; + + case NL80211_WMI_SET_BT_DEBUG: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT bt debug\n"); + wmi_cmd = WMI_SET_BTCOEX_DEBUG_CMDID; + break; + + case NL80211_WMI_SET_BT_OPSTATUS: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT op status\n"); + wmi_cmd = WMI_SET_BTCOEX_BT_OPERATING_STATUS_CMDID; + break; + + case NL80211_WMI_GET_BT_CONFIG: + ath6kl_dbg(ATH6KL_DBG_WMI, "Get BT config\n"); + wmi_cmd = WMI_GET_BTCOEX_CONFIG_CMDID; + break; + + case NL80211_WMI_GET_BT_STATS: + ath6kl_dbg(ATH6KL_DBG_WMI, "Get BT status\n"); + wmi_cmd = WMI_GET_BTCOEX_STATS_CMDID; + break; + } + return wmi_cmd; +} + +int ath6kl_wmi_send_btcoex_cmd(struct wmi *wmi, + u8 *buf, int len) +{ + struct sk_buff *skb; + u32 nl_cmd; + int wmi_cmd; + + nl_cmd = *(u32 *)buf; + buf += sizeof(u32); + len -= sizeof(u32); + wmi_cmd = ath6kl_get_wmi_cmd(nl_cmd); + if (wmi_cmd == 0) + return -ENOMEM; + + skb = ath6kl_wmi_btcoex_get_new_buf(len); + if (!skb) + return -ENOMEM; + + memcpy(skb->data, buf, len); + + return ath6kl_wmi_cmd_send(wmi, 0, skb, + (enum wmi_cmd_id)wmi_cmd, + NO_SYNC_WMIFLAG); +} diff --git a/drivers/net/wireless/ath/ath6kl/wmi_btcoex.h b/drivers/net/wireless/ath/ath6kl/wmi_btcoex.h new file mode 100644 index 00000000..d07194ca --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/wmi_btcoex.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file contains the definitions of the WMI protocol specified in the + * Wireless Module Interface (WMI). It includes definitions of all the + * commands and events. Commands are messages from the host to the WM. + * Events and Replies are messages from the WM to the host. + */ + +#ifndef WMI_BTCOEX_H +#define WMI_BTCOEX_H + +#include + +int ath6kl_wmi_send_btcoex_cmd(struct wmi *wmi, + u8 *buf, int len); + +#endif /* WMI_BTCOEX_H */ diff --git a/drivers/net/wireless/ath/ath6kl/wmiconfig.c b/drivers/net/wireless/ath/ath6kl/wmiconfig.c new file mode 100644 index 00000000..bb60ed23 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/wmiconfig.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "testmode.h" +#include "debug.h" +#include "wmi.h" + +#include + +enum ath6kl_tm_attr { + __ATH6KL_TM_ATTR_INVALID = 0, + ATH6KL_TM_ATTR_CMD = 1, + ATH6KL_TM_ATTR_DATA = 2, + + /* keep last */ + __ATH6KL_TM_ATTR_AFTER_LAST, + ATH6KL_TM_ATTR_MAX = __ATH6KL_TM_ATTR_AFTER_LAST - 1, +}; + +enum ath6kl_tm_cmd { + ATH6KL_TM_CMD_TCMD = 0, + ATH6KL_TM_CMD_RX_REPORT = 1, /* not used anymore */ + ATH6KL_TM_CMD_WMI_CMD = 0xF000, +}; + +struct sk_buff *ath6kl_wmi_get_buf(u32 size) +{ + struct sk_buff *skb; + + skb = ath6kl_buf_alloc(size); + if (!skb) + return NULL; + + skb_put(skb, size); + if (size) + memset(skb->data, 0, size); + + return skb; +} +void ath6kl_tm_rx_wmi_event(struct ath6kl *ar, void *buf, size_t buf_len) +{ + struct sk_buff *skb; + + + if (!buf || buf_len == 0) + return; + + skb = cfg80211_testmode_alloc_event_skb(ar->wiphy, buf_len, GFP_KERNEL); + if (!skb) { + ath6kl_warn("failed to allocate testmode rx skb!\n"); + return; + } + NLA_PUT_U32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_WMI_CMD); + NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf); + cfg80211_testmode_event(skb, GFP_KERNEL); + return; + +nla_put_failure: + kfree_skb(skb); + ath6kl_warn("nla_put failed on testmode rx skb!\n"); +} + + +void ath6kl_wmicfg_send_stats(struct ath6kl_vif *vif, + struct target_stats *stats) +{ + u32 *buff = kzalloc(sizeof(*stats) + 4, GFP_KERNEL); + + buff[0] = WMI_REPORT_STATISTICS_EVENTID; + memcpy(buff+1, stats, sizeof(struct target_stats)); + ath6kl_tm_rx_wmi_event(vif->ar->wmi->parent_dev, buff, + sizeof(struct target_stats)+4); + kfree(buff); +} diff --git a/drivers/net/wireless/ath/ath6kl/wmiconfig.h b/drivers/net/wireless/ath/ath6kl/wmiconfig.h new file mode 100644 index 00000000..bcc29ae2 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/wmiconfig.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WMICONFIG_H +#define WMICONFIG_H + +struct sk_buff *ath6kl_wmi_get_buf(u32 size); +void ath6kl_tm_rx_wmi_event(struct ath6kl *ar, void *buf, size_t buf_len); +void ath6kl_wmicfg_send_stats(struct ath6kl_vif *vif, + struct target_stats *stats); +#endif diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig old mode 100644 new mode 100755 index 3b8c9306..c4d4db9b --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -2,17 +2,6 @@ config ATH9K_HW tristate config ATH9K_COMMON tristate -config ATH9K_DFS_DEBUGFS - def_bool y - depends on ATH9K_DEBUGFS && ATH9K_DFS_CERTIFIED - -config ATH9K_BTCOEX_SUPPORT - bool "Atheros bluetooth coexistence support" - depends on (ATH9K || ATH9K_HTC) - default y - ---help--- - Say Y, if you want to use the ath9k/ath9k_htc radios together with - Bluetooth modules in the same system. config ATH9K tristate "Atheros 802.11n wireless cards support" @@ -62,33 +51,6 @@ config ATH9K_DEBUGFS Also required for changing debug message flags at run time. -config ATH9K_DFS_CERTIFIED - bool "Atheros DFS support for certified platforms" - depends on ATH9K && EXPERT - default n - ---help--- - This option enables DFS support for initiating radiation on - ath9k. There is no way to dynamically detect if a card was DFS - certified and as such this is left as a build time option. This - option should only be enabled by system integrators that can - guarantee that all the platforms that their kernel will run on - have obtained appropriate regulatory body certification for a - respective Atheros card by using ath9k on the target shipping - platforms. - - This is currently only a placeholder for future DFS support, - as DFS support requires more components that still need to be - developed. At this point enabling this option won't do anything - except increase code size. - -config ATH9K_MAC_DEBUG - bool "Atheros MAC statistics" - depends on ATH9K_DEBUGFS - default y - ---help--- - This option enables collection of statistics for Rx/Tx status - data and some other MAC related statistics - config ATH9K_LEGACY_RATE_CONTROL bool "Atheros ath9k rate control" depends on ATH9K diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 24ae2e6a..49d3f25f 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -3,15 +3,13 @@ ath9k-y += beacon.o \ init.o \ main.o \ recv.o \ - xmit.o + xmit.o \ + mci.o \ -ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o -ath9k-$(CONFIG_ATH9K_LEGACY_RATE_CONTROL) += rc.o +ath9k-$(CONFIG_ATH9K_RATE_CONTROL) += rc.o ath9k-$(CONFIG_ATH9K_PCI) += pci.o ath9k-$(CONFIG_ATH9K_AHB) += ahb.o ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o -ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o -ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o obj-$(CONFIG_ATH9K) += ath9k.o @@ -31,14 +29,13 @@ ath9k_hw-y:= \ eeprom_4k.o \ eeprom_9287.o \ ani.o \ + btcoex.o \ mac.o \ ar9002_mac.o \ ar9003_mac.o \ ar9003_eeprom.o \ ar9003_paprd.o -ath9k_hw-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \ - ar9003_mci.o obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 7e0ea4e9..a639b94f 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -136,8 +136,8 @@ static void ath9k_ani_restart(struct ath_hw *ah) cck_base = AR_PHY_COUNTMAX - ah->config.cck_trig_high; } - ath_dbg(common, ANI, "Writing ofdmbase=%u cckbase=%u\n", - ofdm_base, cck_base); + ath_dbg(common, ATH_DBG_ANI, + "Writing ofdmbase=%u cckbase=%u\n", ofdm_base, cck_base); ENABLE_REGWRITE_BUFFER(ah); @@ -268,7 +268,8 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) aniState->noiseFloor = BEACON_RSSI(ah); - ath_dbg(common, ANI, "**** ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", + ath_dbg(common, ATH_DBG_ANI, + "**** ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", aniState->ofdmNoiseImmunityLevel, immunityLevel, aniState->noiseFloor, aniState->rssiThrLow, aniState->rssiThrHigh); @@ -335,7 +336,8 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel) const struct ani_cck_level_entry *entry_cck; aniState->noiseFloor = BEACON_RSSI(ah); - ath_dbg(common, ANI, "**** ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", + ath_dbg(common, ATH_DBG_ANI, + "**** ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", aniState->cckNoiseImmunityLevel, immunityLevel, aniState->noiseFloor, aniState->rssiThrLow, aniState->rssiThrHigh); @@ -407,20 +409,20 @@ static void ath9k_hw_ani_lower_immunity_old(struct ath_hw *ah) if (aniState->ofdmWeakSigDetectOff) { if (ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, - true)) + true) == true) return; } if (aniState->firstepLevel > 0) { if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel - 1)) + aniState->firstepLevel - 1) == true) return; } } else { if (aniState->firstepLevel > 0) { if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel - 1)) + aniState->firstepLevel - 1) == true) return; } } @@ -479,7 +481,8 @@ static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning) if (ah->opmode != NL80211_IFTYPE_STATION && ah->opmode != NL80211_IFTYPE_ADHOC) { - ath_dbg(common, ANI, "Reset ANI state opmode %u\n", ah->opmode); + ath_dbg(common, ATH_DBG_ANI, + "Reset ANI state opmode %u\n", ah->opmode); ah->stats.ast_ani_reset++; if (ah->opmode == NL80211_IFTYPE_AP) { @@ -579,7 +582,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) ATH9K_ANI_OFDM_DEF_LEVEL || aniState->cckNoiseImmunityLevel != ATH9K_ANI_CCK_DEF_LEVEL) { - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "Restore defaults: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n", ah->opmode, chan->channel, @@ -596,7 +599,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) /* * restore historical levels for this channel */ - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "Restore history: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n", ah->opmode, chan->channel, @@ -659,7 +662,7 @@ static bool ath9k_hw_ani_read_counters(struct ath_hw *ah) if (!use_new_ani(ah) && (phyCnt1 < ofdm_base || phyCnt2 < cck_base)) { if (phyCnt1 < ofdm_base) { - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "phyCnt1 0x%x, resetting counter value to 0x%x\n", phyCnt1, ofdm_base); REG_WRITE(ah, AR_PHY_ERR_1, ofdm_base); @@ -667,7 +670,7 @@ static bool ath9k_hw_ani_read_counters(struct ath_hw *ah) AR_PHY_ERR_OFDM_TIMING); } if (phyCnt2 < cck_base) { - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "phyCnt2 0x%x, resetting counter value to 0x%x\n", phyCnt2, cck_base); REG_WRITE(ah, AR_PHY_ERR_2, cck_base); @@ -710,7 +713,7 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan) cckPhyErrRate = aniState->cckPhyErrCount * 1000 / aniState->listenTime; - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "listenTime=%d OFDM:%d errs=%d/s CCK:%d errs=%d/s ofdm_turn=%d\n", aniState->listenTime, aniState->ofdmNoiseImmunityLevel, @@ -745,7 +748,7 @@ void ath9k_enable_mib_counters(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); - ath_dbg(common, ANI, "Enable MIB counters\n"); + ath_dbg(common, ATH_DBG_ANI, "Enable MIB counters\n"); ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); @@ -767,7 +770,7 @@ void ath9k_hw_disable_mib_counters(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); - ath_dbg(common, ANI, "Disable MIB counters\n"); + ath_dbg(common, ATH_DBG_ANI, "Disable MIB counters\n"); REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC); ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); @@ -842,7 +845,7 @@ void ath9k_hw_ani_init(struct ath_hw *ah) struct ath_common *common = ath9k_hw_common(ah); int i; - ath_dbg(common, ANI, "Initialize ANI\n"); + ath_dbg(common, ATH_DBG_ANI, "Initialize ANI\n"); if (use_new_ani(ah)) { ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_NEW; diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index aba08800..f199e9e2 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -158,7 +158,7 @@ static void ar5008_hw_force_bias(struct ath_hw *ah, u16 synth_freq) /* pre-reverse this field */ tmp_reg = ath9k_hw_reverse_bits(new_bias, 3); - ath_dbg(common, CONFIG, "Force rf_pwd_icsyndiv to %1d on %4d\n", + ath_dbg(common, ATH_DBG_CONFIG, "Force rf_pwd_icsyndiv to %1d on %4d\n", new_bias, synth_freq); /* swizzle rf_pwd_icsyndiv */ @@ -489,6 +489,8 @@ static int ar5008_hw_rf_alloc_ext_banks(struct ath_hw *ah) ATH_ALLOC_BANK(ah->analogBank6Data, ah->iniBank6.ia_rows); ATH_ALLOC_BANK(ah->analogBank6TPCData, ah->iniBank6TPC.ia_rows); ATH_ALLOC_BANK(ah->analogBank7Data, ah->iniBank7.ia_rows); + ATH_ALLOC_BANK(ah->addac5416_21, + ah->iniAddac.ia_rows * ah->iniAddac.ia_columns); ATH_ALLOC_BANK(ah->bank6Temp, ah->iniBank6.ia_rows); return 0; @@ -517,6 +519,7 @@ static void ar5008_hw_rf_free_ext_banks(struct ath_hw *ah) ATH_FREE_BANK(ah->analogBank6Data); ATH_FREE_BANK(ah->analogBank6TPCData); ATH_FREE_BANK(ah->analogBank7Data); + ATH_FREE_BANK(ah->addac5416_21); ATH_FREE_BANK(ah->bank6Temp); #undef ATH_FREE_BANK @@ -802,7 +805,27 @@ static int ar5008_hw_process_ini(struct ath_hw *ah, if (ah->eep_ops->set_addac) ah->eep_ops->set_addac(ah, chan); - REG_WRITE_ARRAY(&ah->iniAddac, 1, regWrites); + if (AR_SREV_5416_22_OR_LATER(ah)) { + REG_WRITE_ARRAY(&ah->iniAddac, 1, regWrites); + } else { + struct ar5416IniArray temp; + u32 addacSize = + sizeof(u32) * ah->iniAddac.ia_rows * + ah->iniAddac.ia_columns; + + /* For AR5416 2.0/2.1 */ + memcpy(ah->addac5416_21, + ah->iniAddac.ia_array, addacSize); + + /* override CLKDRV value at [row, column] = [31, 1] */ + (ah->addac5416_21)[31 * ah->iniAddac.ia_columns + 1] = 0; + + temp.ia_array = ah->addac5416_21; + temp.ia_columns = ah->iniAddac.ia_columns; + temp.ia_rows = ah->iniAddac.ia_rows; + REG_WRITE_ARRAY(&temp, 1, regWrites); + } + REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC); ENABLE_REGWRITE_BUFFER(ah); @@ -834,10 +857,9 @@ static int ar5008_hw_process_ini(struct ath_hw *ah, AR_SREV_9287_11_OR_LATER(ah)) REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites); - if (AR_SREV_9271_10(ah)) { - REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, AR_PHY_SPECTRAL_SCAN_ENA); - REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_ADC_ON, 0xa); - } + if (AR_SREV_9271_10(ah)) + REG_WRITE_ARRAY(&ah->iniModes_9271_1_0_only, + modesIndex, regWrites); ENABLE_REGWRITE_BUFFER(ah); @@ -859,17 +881,27 @@ static int ar5008_hw_process_ini(struct ath_hw *ah, REGWRITE_BUFFER_FLUSH(ah); + if (AR_SREV_9271(ah)) { + if (ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE) == 1) + REG_WRITE_ARRAY(&ah->iniModes_high_power_tx_gain_9271, + modesIndex, regWrites); + else + REG_WRITE_ARRAY(&ah->iniModes_normal_power_tx_gain_9271, + modesIndex, regWrites); + } + REG_WRITE_ARRAY(&ah->iniBB_RfGain, freqIndex, regWrites); - if (IS_CHAN_A_FAST_CLOCK(ah, chan)) - REG_WRITE_ARRAY(&ah->iniModesFastClock, modesIndex, + if (IS_CHAN_A_FAST_CLOCK(ah, chan)) { + REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex, regWrites); + } ar5008_hw_override_ini(ah, chan); ar5008_hw_set_channel_regs(ah, chan); ar5008_hw_init_chain_masks(ah); ath9k_olc_init(ah); - ath9k_hw_apply_txpower(ah, chan, false); + ath9k_hw_apply_txpower(ah, chan); /* Write analog registers */ if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) { @@ -1021,7 +1053,8 @@ static bool ar5008_hw_ani_control_old(struct ath_hw *ah, u32 level = param; if (level >= ARRAY_SIZE(ah->totalSizeDesired)) { - ath_dbg(common, ANI, "level out of range (%u > %zu)\n", + ath_dbg(common, ATH_DBG_ANI, + "level out of range (%u > %zu)\n", level, ARRAY_SIZE(ah->totalSizeDesired)); return false; } @@ -1124,7 +1157,8 @@ static bool ar5008_hw_ani_control_old(struct ath_hw *ah, u32 level = param; if (level >= ARRAY_SIZE(firstep)) { - ath_dbg(common, ANI, "level out of range (%u > %zu)\n", + ath_dbg(common, ATH_DBG_ANI, + "level out of range (%u > %zu)\n", level, ARRAY_SIZE(firstep)); return false; } @@ -1143,7 +1177,8 @@ static bool ar5008_hw_ani_control_old(struct ath_hw *ah, u32 level = param; if (level >= ARRAY_SIZE(cycpwrThr1)) { - ath_dbg(common, ANI, "level out of range (%u > %zu)\n", + ath_dbg(common, ATH_DBG_ANI, + "level out of range (%u > %zu)\n", level, ARRAY_SIZE(cycpwrThr1)); return false; } @@ -1160,22 +1195,23 @@ static bool ar5008_hw_ani_control_old(struct ath_hw *ah, case ATH9K_ANI_PRESENT: break; default: - ath_dbg(common, ANI, "invalid cmd %u\n", cmd); + ath_dbg(common, ATH_DBG_ANI, "invalid cmd %u\n", cmd); return false; } - ath_dbg(common, ANI, "ANI parameters:\n"); - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "ANI parameters:\n"); + ath_dbg(common, ATH_DBG_ANI, "noiseImmunityLevel=%d, spurImmunityLevel=%d, ofdmWeakSigDetectOff=%d\n", aniState->noiseImmunityLevel, aniState->spurImmunityLevel, !aniState->ofdmWeakSigDetectOff); - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "cckWeakSigThreshold=%d, firstepLevel=%d, listenTime=%d\n", aniState->cckWeakSigThreshold, aniState->firstepLevel, aniState->listenTime); - ath_dbg(common, ANI, "ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n", + ath_dbg(common, ATH_DBG_ANI, + "ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n", aniState->ofdmPhyErrCount, aniState->cckPhyErrCount); @@ -1259,7 +1295,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); if (!on != aniState->ofdmWeakSigDetectOff) { - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "** ch %d: ofdm weak signal: %s=>%s\n", chan->channel, !aniState->ofdmWeakSigDetectOff ? @@ -1277,7 +1313,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, u32 level = param; if (level >= ARRAY_SIZE(firstep_table)) { - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "ATH9K_ANI_FIRSTEP_LEVEL: level out of range (%u > %zu)\n", level, ARRAY_SIZE(firstep_table)); return false; @@ -1314,7 +1350,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, AR_PHY_FIND_SIG_FIRSTEP_LOW, value2); if (level != aniState->firstepLevel) { - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "** ch %d: level %d=>%d[def:%d] firstep[level]=%d ini=%d\n", chan->channel, aniState->firstepLevel, @@ -1322,7 +1358,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, ATH9K_ANI_FIRSTEP_LVL_NEW, value, aniState->iniDef.firstep); - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "** ch %d: level %d=>%d[def:%d] firstep_low[level]=%d ini=%d\n", chan->channel, aniState->firstepLevel, @@ -1342,7 +1378,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, u32 level = param; if (level >= ARRAY_SIZE(cycpwrThr1_table)) { - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "ATH9K_ANI_SPUR_IMMUNITY_LEVEL: level out of range (%u > %zu)\n", level, ARRAY_SIZE(cycpwrThr1_table)); return false; @@ -1378,7 +1414,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, AR_PHY_EXT_TIMING5_CYCPWR_THR1, value2); if (level != aniState->spurImmunityLevel) { - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "** ch %d: level %d=>%d[def:%d] cycpwrThr1[level]=%d ini=%d\n", chan->channel, aniState->spurImmunityLevel, @@ -1386,7 +1422,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, ATH9K_ANI_SPUR_IMMUNE_LVL_NEW, value, aniState->iniDef.cycpwrThr1); - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "** ch %d: level %d=>%d[def:%d] cycpwrThr1Ext[level]=%d ini=%d\n", chan->channel, aniState->spurImmunityLevel, @@ -1412,11 +1448,11 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, case ATH9K_ANI_PRESENT: break; default: - ath_dbg(common, ANI, "invalid cmd %u\n", cmd); + ath_dbg(common, ATH_DBG_ANI, "invalid cmd %u\n", cmd); return false; } - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "ANI parameters: SI=%d, ofdmWS=%s FS=%d MRCcck=%s listenTime=%d ofdmErrs=%d cckErrs=%d\n", aniState->spurImmunityLevel, !aniState->ofdmWeakSigDetectOff ? "on" : "off", @@ -1470,7 +1506,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah) iniDef = &aniState->iniDef; - ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n", + ath_dbg(common, ATH_DBG_ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n", ah->hw_version.macVersion, ah->hw_version.macRev, ah->opmode, diff --git a/drivers/net/wireless/ath/ath9k/ar9001_initvals.h b/drivers/net/wireless/ath/ath9k/ar9001_initvals.h index ea4a2309..e8bdc754 100644 --- a/drivers/net/wireless/ath/ath9k/ar9001_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9001_initvals.h @@ -459,6 +459,97 @@ static const u32 ar5416Common_9100[][2] = { {0x0000a3e0, 0x000001ce}, }; +static const u32 ar5416Bank0_9100[][2] = { + /* Addr allmodes */ + {0x000098b0, 0x1e5795e5}, + {0x000098e0, 0x02008020}, +}; + +static const u32 ar5416BB_RfGain_9100[][3] = { + /* Addr 5G_HT20 5G_HT40 */ + {0x00009a00, 0x00000000, 0x00000000}, + {0x00009a04, 0x00000040, 0x00000040}, + {0x00009a08, 0x00000080, 0x00000080}, + {0x00009a0c, 0x000001a1, 0x00000141}, + {0x00009a10, 0x000001e1, 0x00000181}, + {0x00009a14, 0x00000021, 0x000001c1}, + {0x00009a18, 0x00000061, 0x00000001}, + {0x00009a1c, 0x00000168, 0x00000041}, + {0x00009a20, 0x000001a8, 0x000001a8}, + {0x00009a24, 0x000001e8, 0x000001e8}, + {0x00009a28, 0x00000028, 0x00000028}, + {0x00009a2c, 0x00000068, 0x00000068}, + {0x00009a30, 0x00000189, 0x000000a8}, + {0x00009a34, 0x000001c9, 0x00000169}, + {0x00009a38, 0x00000009, 0x000001a9}, + {0x00009a3c, 0x00000049, 0x000001e9}, + {0x00009a40, 0x00000089, 0x00000029}, + {0x00009a44, 0x00000170, 0x00000069}, + {0x00009a48, 0x000001b0, 0x00000190}, + {0x00009a4c, 0x000001f0, 0x000001d0}, + {0x00009a50, 0x00000030, 0x00000010}, + {0x00009a54, 0x00000070, 0x00000050}, + {0x00009a58, 0x00000191, 0x00000090}, + {0x00009a5c, 0x000001d1, 0x00000151}, + {0x00009a60, 0x00000011, 0x00000191}, + {0x00009a64, 0x00000051, 0x000001d1}, + {0x00009a68, 0x00000091, 0x00000011}, + {0x00009a6c, 0x000001b8, 0x00000051}, + {0x00009a70, 0x000001f8, 0x00000198}, + {0x00009a74, 0x00000038, 0x000001d8}, + {0x00009a78, 0x00000078, 0x00000018}, + {0x00009a7c, 0x00000199, 0x00000058}, + {0x00009a80, 0x000001d9, 0x00000098}, + {0x00009a84, 0x00000019, 0x00000159}, + {0x00009a88, 0x00000059, 0x00000199}, + {0x00009a8c, 0x00000099, 0x000001d9}, + {0x00009a90, 0x000000d9, 0x00000019}, + {0x00009a94, 0x000000f9, 0x00000059}, + {0x00009a98, 0x000000f9, 0x00000099}, + {0x00009a9c, 0x000000f9, 0x000000d9}, + {0x00009aa0, 0x000000f9, 0x000000f9}, + {0x00009aa4, 0x000000f9, 0x000000f9}, + {0x00009aa8, 0x000000f9, 0x000000f9}, + {0x00009aac, 0x000000f9, 0x000000f9}, + {0x00009ab0, 0x000000f9, 0x000000f9}, + {0x00009ab4, 0x000000f9, 0x000000f9}, + {0x00009ab8, 0x000000f9, 0x000000f9}, + {0x00009abc, 0x000000f9, 0x000000f9}, + {0x00009ac0, 0x000000f9, 0x000000f9}, + {0x00009ac4, 0x000000f9, 0x000000f9}, + {0x00009ac8, 0x000000f9, 0x000000f9}, + {0x00009acc, 0x000000f9, 0x000000f9}, + {0x00009ad0, 0x000000f9, 0x000000f9}, + {0x00009ad4, 0x000000f9, 0x000000f9}, + {0x00009ad8, 0x000000f9, 0x000000f9}, + {0x00009adc, 0x000000f9, 0x000000f9}, + {0x00009ae0, 0x000000f9, 0x000000f9}, + {0x00009ae4, 0x000000f9, 0x000000f9}, + {0x00009ae8, 0x000000f9, 0x000000f9}, + {0x00009aec, 0x000000f9, 0x000000f9}, + {0x00009af0, 0x000000f9, 0x000000f9}, + {0x00009af4, 0x000000f9, 0x000000f9}, + {0x00009af8, 0x000000f9, 0x000000f9}, + {0x00009afc, 0x000000f9, 0x000000f9}, +}; + +static const u32 ar5416Bank1_9100[][2] = { + /* Addr allmodes */ + {0x000098b0, 0x02108421}, + {0x000098ec, 0x00000008}, +}; + +static const u32 ar5416Bank2_9100[][2] = { + /* Addr allmodes */ + {0x000098b0, 0x0e73ff17}, + {0x000098e0, 0x00000420}, +}; + +static const u32 ar5416Bank3_9100[][3] = { + /* Addr 5G_HT20 5G_HT40 */ + {0x000098f0, 0x01400018, 0x01c00018}, +}; + static const u32 ar5416Bank6_9100[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x0000989c, 0x00000000, 0x00000000}, @@ -533,6 +624,13 @@ static const u32 ar5416Bank6TPC_9100[][3] = { {0x000098d0, 0x0000000f, 0x0010000f}, }; +static const u32 ar5416Bank7_9100[][2] = { + /* Addr allmodes */ + {0x0000989c, 0x00000500}, + {0x0000989c, 0x00000800}, + {0x000098cc, 0x0000000e}, +}; + static const u32 ar5416Addac_9100[][2] = { /* Addr allmodes */ {0x0000989c, 0x00000000}, @@ -1015,6 +1113,178 @@ static const u32 ar5416Common_9160[][2] = { {0x0000a3e0, 0x000001ce}, }; +static const u32 ar5416Bank0_9160[][2] = { + /* Addr allmodes */ + {0x000098b0, 0x1e5795e5}, + {0x000098e0, 0x02008020}, +}; + +static const u32 ar5416BB_RfGain_9160[][3] = { + /* Addr 5G_HT20 5G_HT40 */ + {0x00009a00, 0x00000000, 0x00000000}, + {0x00009a04, 0x00000040, 0x00000040}, + {0x00009a08, 0x00000080, 0x00000080}, + {0x00009a0c, 0x000001a1, 0x00000141}, + {0x00009a10, 0x000001e1, 0x00000181}, + {0x00009a14, 0x00000021, 0x000001c1}, + {0x00009a18, 0x00000061, 0x00000001}, + {0x00009a1c, 0x00000168, 0x00000041}, + {0x00009a20, 0x000001a8, 0x000001a8}, + {0x00009a24, 0x000001e8, 0x000001e8}, + {0x00009a28, 0x00000028, 0x00000028}, + {0x00009a2c, 0x00000068, 0x00000068}, + {0x00009a30, 0x00000189, 0x000000a8}, + {0x00009a34, 0x000001c9, 0x00000169}, + {0x00009a38, 0x00000009, 0x000001a9}, + {0x00009a3c, 0x00000049, 0x000001e9}, + {0x00009a40, 0x00000089, 0x00000029}, + {0x00009a44, 0x00000170, 0x00000069}, + {0x00009a48, 0x000001b0, 0x00000190}, + {0x00009a4c, 0x000001f0, 0x000001d0}, + {0x00009a50, 0x00000030, 0x00000010}, + {0x00009a54, 0x00000070, 0x00000050}, + {0x00009a58, 0x00000191, 0x00000090}, + {0x00009a5c, 0x000001d1, 0x00000151}, + {0x00009a60, 0x00000011, 0x00000191}, + {0x00009a64, 0x00000051, 0x000001d1}, + {0x00009a68, 0x00000091, 0x00000011}, + {0x00009a6c, 0x000001b8, 0x00000051}, + {0x00009a70, 0x000001f8, 0x00000198}, + {0x00009a74, 0x00000038, 0x000001d8}, + {0x00009a78, 0x00000078, 0x00000018}, + {0x00009a7c, 0x00000199, 0x00000058}, + {0x00009a80, 0x000001d9, 0x00000098}, + {0x00009a84, 0x00000019, 0x00000159}, + {0x00009a88, 0x00000059, 0x00000199}, + {0x00009a8c, 0x00000099, 0x000001d9}, + {0x00009a90, 0x000000d9, 0x00000019}, + {0x00009a94, 0x000000f9, 0x00000059}, + {0x00009a98, 0x000000f9, 0x00000099}, + {0x00009a9c, 0x000000f9, 0x000000d9}, + {0x00009aa0, 0x000000f9, 0x000000f9}, + {0x00009aa4, 0x000000f9, 0x000000f9}, + {0x00009aa8, 0x000000f9, 0x000000f9}, + {0x00009aac, 0x000000f9, 0x000000f9}, + {0x00009ab0, 0x000000f9, 0x000000f9}, + {0x00009ab4, 0x000000f9, 0x000000f9}, + {0x00009ab8, 0x000000f9, 0x000000f9}, + {0x00009abc, 0x000000f9, 0x000000f9}, + {0x00009ac0, 0x000000f9, 0x000000f9}, + {0x00009ac4, 0x000000f9, 0x000000f9}, + {0x00009ac8, 0x000000f9, 0x000000f9}, + {0x00009acc, 0x000000f9, 0x000000f9}, + {0x00009ad0, 0x000000f9, 0x000000f9}, + {0x00009ad4, 0x000000f9, 0x000000f9}, + {0x00009ad8, 0x000000f9, 0x000000f9}, + {0x00009adc, 0x000000f9, 0x000000f9}, + {0x00009ae0, 0x000000f9, 0x000000f9}, + {0x00009ae4, 0x000000f9, 0x000000f9}, + {0x00009ae8, 0x000000f9, 0x000000f9}, + {0x00009aec, 0x000000f9, 0x000000f9}, + {0x00009af0, 0x000000f9, 0x000000f9}, + {0x00009af4, 0x000000f9, 0x000000f9}, + {0x00009af8, 0x000000f9, 0x000000f9}, + {0x00009afc, 0x000000f9, 0x000000f9}, +}; + +static const u32 ar5416Bank1_9160[][2] = { + /* Addr allmodes */ + {0x000098b0, 0x02108421}, + {0x000098ec, 0x00000008}, +}; + +static const u32 ar5416Bank2_9160[][2] = { + /* Addr allmodes */ + {0x000098b0, 0x0e73ff17}, + {0x000098e0, 0x00000420}, +}; + +static const u32 ar5416Bank3_9160[][3] = { + /* Addr 5G_HT20 5G_HT40 */ + {0x000098f0, 0x01400018, 0x01c00018}, +}; + +static const u32 ar5416Bank6_9160[][3] = { + /* Addr 5G_HT20 5G_HT40 */ + {0x0000989c, 0x00000000, 0x00000000}, + {0x0000989c, 0x00000000, 0x00000000}, + {0x0000989c, 0x00000000, 0x00000000}, + {0x0000989c, 0x00e00000, 0x00e00000}, + {0x0000989c, 0x005e0000, 0x005e0000}, + {0x0000989c, 0x00120000, 0x00120000}, + {0x0000989c, 0x00620000, 0x00620000}, + {0x0000989c, 0x00020000, 0x00020000}, + {0x0000989c, 0x00ff0000, 0x00ff0000}, + {0x0000989c, 0x00ff0000, 0x00ff0000}, + {0x0000989c, 0x00ff0000, 0x00ff0000}, + {0x0000989c, 0x40ff0000, 0x40ff0000}, + {0x0000989c, 0x005f0000, 0x005f0000}, + {0x0000989c, 0x00870000, 0x00870000}, + {0x0000989c, 0x00f90000, 0x00f90000}, + {0x0000989c, 0x007b0000, 0x007b0000}, + {0x0000989c, 0x00ff0000, 0x00ff0000}, + {0x0000989c, 0x00f50000, 0x00f50000}, + {0x0000989c, 0x00dc0000, 0x00dc0000}, + {0x0000989c, 0x00110000, 0x00110000}, + {0x0000989c, 0x006100a8, 0x006100a8}, + {0x0000989c, 0x004210a2, 0x004210a2}, + {0x0000989c, 0x0014008f, 0x0014008f}, + {0x0000989c, 0x00c40003, 0x00c40003}, + {0x0000989c, 0x003000f2, 0x003000f2}, + {0x0000989c, 0x00440016, 0x00440016}, + {0x0000989c, 0x00410040, 0x00410040}, + {0x0000989c, 0x0001805e, 0x0001805e}, + {0x0000989c, 0x0000c0ab, 0x0000c0ab}, + {0x0000989c, 0x000000f1, 0x000000f1}, + {0x0000989c, 0x00002081, 0x00002081}, + {0x0000989c, 0x000000d4, 0x000000d4}, + {0x000098d0, 0x0000000f, 0x0010000f}, +}; + +static const u32 ar5416Bank6TPC_9160[][3] = { + /* Addr 5G_HT20 5G_HT40 */ + {0x0000989c, 0x00000000, 0x00000000}, + {0x0000989c, 0x00000000, 0x00000000}, + {0x0000989c, 0x00000000, 0x00000000}, + {0x0000989c, 0x00e00000, 0x00e00000}, + {0x0000989c, 0x005e0000, 0x005e0000}, + {0x0000989c, 0x00120000, 0x00120000}, + {0x0000989c, 0x00620000, 0x00620000}, + {0x0000989c, 0x00020000, 0x00020000}, + {0x0000989c, 0x00ff0000, 0x00ff0000}, + {0x0000989c, 0x00ff0000, 0x00ff0000}, + {0x0000989c, 0x00ff0000, 0x00ff0000}, + {0x0000989c, 0x40ff0000, 0x40ff0000}, + {0x0000989c, 0x005f0000, 0x005f0000}, + {0x0000989c, 0x00870000, 0x00870000}, + {0x0000989c, 0x00f90000, 0x00f90000}, + {0x0000989c, 0x007b0000, 0x007b0000}, + {0x0000989c, 0x00ff0000, 0x00ff0000}, + {0x0000989c, 0x00f50000, 0x00f50000}, + {0x0000989c, 0x00dc0000, 0x00dc0000}, + {0x0000989c, 0x00110000, 0x00110000}, + {0x0000989c, 0x006100a8, 0x006100a8}, + {0x0000989c, 0x00423022, 0x00423022}, + {0x0000989c, 0x2014008f, 0x2014008f}, + {0x0000989c, 0x00c40002, 0x00c40002}, + {0x0000989c, 0x003000f2, 0x003000f2}, + {0x0000989c, 0x00440016, 0x00440016}, + {0x0000989c, 0x00410040, 0x00410040}, + {0x0000989c, 0x0001805e, 0x0001805e}, + {0x0000989c, 0x0000c0ab, 0x0000c0ab}, + {0x0000989c, 0x000000e1, 0x000000e1}, + {0x0000989c, 0x00007080, 0x00007080}, + {0x0000989c, 0x000000d4, 0x000000d4}, + {0x000098d0, 0x0000000f, 0x0010000f}, +}; + +static const u32 ar5416Bank7_9160[][2] = { + /* Addr allmodes */ + {0x0000989c, 0x00000500}, + {0x0000989c, 0x00000800}, + {0x000098cc, 0x0000000e}, +}; + static const u32 ar5416Addac_9160[][2] = { /* Addr allmodes */ {0x0000989c, 0x00000000}, diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index c55e5bba..88279e32 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c @@ -61,16 +61,18 @@ static void ar9002_hw_setup_calibration(struct ath_hw *ah, switch (currCal->calData->calType) { case IQ_MISMATCH_CAL: REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "starting IQ Mismatch Calibration\n"); break; case ADC_GAIN_CAL: REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN); - ath_dbg(common, CALIBRATE, "starting ADC Gain Calibration\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, + "starting ADC Gain Calibration\n"); break; case ADC_DC_CAL: REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER); - ath_dbg(common, CALIBRATE, "starting ADC DC Calibration\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, + "starting ADC DC Calibration\n"); break; } @@ -127,7 +129,7 @@ static void ar9002_hw_iqcal_collect(struct ath_hw *ah) REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); ah->totalIqCorrMeas[i] += (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); - ath_dbg(ath9k_hw_common(ah), CALIBRATE, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n", ah->cal_samples, i, ah->totalPowerMeasI[i], ah->totalPowerMeasQ[i], @@ -149,7 +151,7 @@ static void ar9002_hw_adc_gaincal_collect(struct ath_hw *ah) ah->totalAdcQEvenPhase[i] += REG_READ(ah, AR_PHY_CAL_MEAS_3(i)); - ath_dbg(ath9k_hw_common(ah), CALIBRATE, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, "%d: Chn %d oddi=0x%08x; eveni=0x%08x; oddq=0x%08x; evenq=0x%08x;\n", ah->cal_samples, i, ah->totalAdcIOddPhase[i], @@ -173,7 +175,7 @@ static void ar9002_hw_adc_dccal_collect(struct ath_hw *ah) ah->totalAdcDcOffsetQEvenPhase[i] += (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_3(i)); - ath_dbg(ath9k_hw_common(ah), CALIBRATE, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, "%d: Chn %d oddi=0x%08x; eveni=0x%08x; oddq=0x%08x; evenq=0x%08x;\n", ah->cal_samples, i, ah->totalAdcDcOffsetIOddPhase[i], @@ -196,12 +198,12 @@ static void ar9002_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) powerMeasQ = ah->totalPowerMeasQ[i]; iqCorrMeas = ah->totalIqCorrMeas[i]; - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "Starting IQ Cal and Correction for Chain %d\n", i); - ath_dbg(common, CALIBRATE, - "Original: Chn %d iq_corr_meas = 0x%08x\n", + ath_dbg(common, ATH_DBG_CALIBRATE, + "Orignal: Chn %diq_corr_meas = 0x%08x\n", i, ah->totalIqCorrMeas[i]); iqCorrNeg = 0; @@ -211,11 +213,12 @@ static void ar9002_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) iqCorrNeg = 1; } - ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_i = 0x%08x\n", - i, powerMeasI); - ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_q = 0x%08x\n", - i, powerMeasQ); - ath_dbg(common, CALIBRATE, "iqCorrNeg is 0x%08x\n", iqCorrNeg); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ); + ath_dbg(common, ATH_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n", + iqCorrNeg); iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 128; qCoffDenom = powerMeasQ / 64; @@ -224,13 +227,13 @@ static void ar9002_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) (qCoffDenom != 0)) { iCoff = iqCorrMeas / iCoffDenom; qCoff = powerMeasI / qCoffDenom - 64; - ath_dbg(common, CALIBRATE, "Chn %d iCoff = 0x%08x\n", - i, iCoff); - ath_dbg(common, CALIBRATE, "Chn %d qCoff = 0x%08x\n", - i, qCoff); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d iCoff = 0x%08x\n", i, iCoff); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d qCoff = 0x%08x\n", i, qCoff); iCoff = iCoff & 0x3f; - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "New: Chn %d iCoff = 0x%08x\n", i, iCoff); if (iqCorrNeg == 0x0) iCoff = 0x40 - iCoff; @@ -240,7 +243,7 @@ static void ar9002_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) else if (qCoff <= -16) qCoff = -16; - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "Chn %d : iCoff = 0x%x qCoff = 0x%x\n", i, iCoff, qCoff); @@ -250,7 +253,7 @@ static void ar9002_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i), AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, qCoff); - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "IQ Cal and Correction done for Chain %d\n", i); } @@ -272,17 +275,21 @@ static void ar9002_hw_adc_gaincal_calibrate(struct ath_hw *ah, u8 numChains) qOddMeasOffset = ah->totalAdcQOddPhase[i]; qEvenMeasOffset = ah->totalAdcQEvenPhase[i]; - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "Starting ADC Gain Cal for Chain %d\n", i); - ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_odd_i = 0x%08x\n", - i, iOddMeasOffset); - ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_even_i = 0x%08x\n", - i, iEvenMeasOffset); - ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_odd_q = 0x%08x\n", - i, qOddMeasOffset); - ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_even_q = 0x%08x\n", - i, qEvenMeasOffset); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_i = 0x%08x\n", i, + iOddMeasOffset); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_i = 0x%08x\n", i, + iEvenMeasOffset); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_q = 0x%08x\n", i, + qOddMeasOffset); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_q = 0x%08x\n", i, + qEvenMeasOffset); if (iOddMeasOffset != 0 && qEvenMeasOffset != 0) { iGainMismatch = @@ -292,19 +299,19 @@ static void ar9002_hw_adc_gaincal_calibrate(struct ath_hw *ah, u8 numChains) ((qOddMeasOffset * 32) / qEvenMeasOffset) & 0x3f; - ath_dbg(common, CALIBRATE, - "Chn %d gain_mismatch_i = 0x%08x\n", - i, iGainMismatch); - ath_dbg(common, CALIBRATE, - "Chn %d gain_mismatch_q = 0x%08x\n", - i, qGainMismatch); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d gain_mismatch_i = 0x%08x\n", i, + iGainMismatch); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d gain_mismatch_q = 0x%08x\n", i, + qGainMismatch); val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i)); val &= 0xfffff000; val |= (qGainMismatch) | (iGainMismatch << 6); REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val); - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "ADC Gain Cal done for Chain %d\n", i); } } @@ -330,36 +337,40 @@ static void ar9002_hw_adc_dccal_calibrate(struct ath_hw *ah, u8 numChains) qOddMeasOffset = ah->totalAdcDcOffsetQOddPhase[i]; qEvenMeasOffset = ah->totalAdcDcOffsetQEvenPhase[i]; - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "Starting ADC DC Offset Cal for Chain %d\n", i); - ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_odd_i = %d\n", - i, iOddMeasOffset); - ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_even_i = %d\n", - i, iEvenMeasOffset); - ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_odd_q = %d\n", - i, qOddMeasOffset); - ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_even_q = %d\n", - i, qEvenMeasOffset); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_i = %d\n", i, + iOddMeasOffset); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_i = %d\n", i, + iEvenMeasOffset); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_q = %d\n", i, + qOddMeasOffset); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_q = %d\n", i, + qEvenMeasOffset); iDcMismatch = (((iEvenMeasOffset - iOddMeasOffset) * 2) / numSamples) & 0x1ff; qDcMismatch = (((qOddMeasOffset - qEvenMeasOffset) * 2) / numSamples) & 0x1ff; - ath_dbg(common, CALIBRATE, - "Chn %d dc_offset_mismatch_i = 0x%08x\n", - i, iDcMismatch); - ath_dbg(common, CALIBRATE, - "Chn %d dc_offset_mismatch_q = 0x%08x\n", - i, qDcMismatch); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d dc_offset_mismatch_i = 0x%08x\n", i, + iDcMismatch); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d dc_offset_mismatch_q = 0x%08x\n", i, + qDcMismatch); val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i)); val &= 0xc0000fff; val |= (qDcMismatch << 12) | (iDcMismatch << 21); REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val); - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "ADC DC Offset Cal done for Chain %d\n", i); } @@ -549,7 +560,7 @@ static inline void ar9285_hw_pa_cal(struct ath_hw *ah, bool is_reset) { 0x7838, 0 }, }; - ath_dbg(common, CALIBRATE, "Running PA Calibration\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, "Running PA Calibration\n"); /* PA CAL is not needed for high power solution */ if (ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE) == @@ -730,7 +741,7 @@ static bool ar9285_hw_cl_cal(struct ath_hw *ah, struct ath9k_channel *chan) REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT)) { - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "offset calibration failed to complete in 1ms; noisy environment?\n"); return false; } @@ -744,7 +755,7 @@ static bool ar9285_hw_cl_cal(struct ath_hw *ah, struct ath9k_channel *chan) REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT)) { - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "offset calibration failed to complete in 1ms; noisy environment?\n"); return false; } @@ -840,7 +851,7 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT)) { - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "offset calibration failed to complete in 1ms; noisy environment?\n"); return false; } @@ -875,21 +886,22 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) if (ar9002_hw_is_cal_supported(ah, chan, ADC_GAIN_CAL)) { INIT_CAL(&ah->adcgain_caldata); INSERT_CAL(ah, &ah->adcgain_caldata); - ath_dbg(common, CALIBRATE, - "enabling ADC Gain Calibration\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, + "enabling ADC Gain Calibration.\n"); } if (ar9002_hw_is_cal_supported(ah, chan, ADC_DC_CAL)) { INIT_CAL(&ah->adcdc_caldata); INSERT_CAL(ah, &ah->adcdc_caldata); - ath_dbg(common, CALIBRATE, - "enabling ADC DC Calibration\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, + "enabling ADC DC Calibration.\n"); } if (ar9002_hw_is_cal_supported(ah, chan, IQ_MISMATCH_CAL)) { INIT_CAL(&ah->iq_caldata); INSERT_CAL(ah, &ah->iq_caldata); - ath_dbg(common, CALIBRATE, "enabling IQ Calibration\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, + "enabling IQ Calibration.\n"); } ah->cal_list_curr = ah->cal_list; diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index d9a69fc4..11f192a1 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -34,37 +34,74 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah) ARRAY_SIZE(ar9271Modes_9271), 5); INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271, ARRAY_SIZE(ar9271Common_9271), 2); + INIT_INI_ARRAY(&ah->iniCommon_normal_cck_fir_coeff_9271, + ar9271Common_normal_cck_fir_coeff_9271, + ARRAY_SIZE(ar9271Common_normal_cck_fir_coeff_9271), 2); + INIT_INI_ARRAY(&ah->iniCommon_japan_2484_cck_fir_coeff_9271, + ar9271Common_japan_2484_cck_fir_coeff_9271, + ARRAY_SIZE(ar9271Common_japan_2484_cck_fir_coeff_9271), 2); + INIT_INI_ARRAY(&ah->iniModes_9271_1_0_only, + ar9271Modes_9271_1_0_only, + ARRAY_SIZE(ar9271Modes_9271_1_0_only), 5); INIT_INI_ARRAY(&ah->iniModes_9271_ANI_reg, ar9271Modes_9271_ANI_reg, ARRAY_SIZE(ar9271Modes_9271_ANI_reg), 5); + INIT_INI_ARRAY(&ah->iniModes_high_power_tx_gain_9271, + ar9271Modes_high_power_tx_gain_9271, + ARRAY_SIZE(ar9271Modes_high_power_tx_gain_9271), 5); + INIT_INI_ARRAY(&ah->iniModes_normal_power_tx_gain_9271, + ar9271Modes_normal_power_tx_gain_9271, + ARRAY_SIZE(ar9271Modes_normal_power_tx_gain_9271), 5); return; } - if (ah->config.pcie_clock_req) - INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9280PciePhy_clkreq_off_L1_9280, - ARRAY_SIZE(ar9280PciePhy_clkreq_off_L1_9280), 2); - else - INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9280PciePhy_clkreq_always_on_L1_9280, - ARRAY_SIZE(ar9280PciePhy_clkreq_always_on_L1_9280), 2); - if (AR_SREV_9287_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModes, ar9287Modes_9287_1_1, ARRAY_SIZE(ar9287Modes_9287_1_1), 5); INIT_INI_ARRAY(&ah->iniCommon, ar9287Common_9287_1_1, ARRAY_SIZE(ar9287Common_9287_1_1), 2); + if (ah->config.pcie_clock_req) + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9287PciePhy_clkreq_off_L1_9287_1_1, + ARRAY_SIZE(ar9287PciePhy_clkreq_off_L1_9287_1_1), 2); + else + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9287PciePhy_clkreq_always_on_L1_9287_1_1, + ARRAY_SIZE(ar9287PciePhy_clkreq_always_on_L1_9287_1_1), + 2); } else if (AR_SREV_9285_12_OR_LATER(ah)) { + + INIT_INI_ARRAY(&ah->iniModes, ar9285Modes_9285_1_2, ARRAY_SIZE(ar9285Modes_9285_1_2), 5); INIT_INI_ARRAY(&ah->iniCommon, ar9285Common_9285_1_2, ARRAY_SIZE(ar9285Common_9285_1_2), 2); + + if (ah->config.pcie_clock_req) { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9285PciePhy_clkreq_off_L1_9285_1_2, + ARRAY_SIZE(ar9285PciePhy_clkreq_off_L1_9285_1_2), 2); + } else { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9285PciePhy_clkreq_always_on_L1_9285_1_2, + ARRAY_SIZE(ar9285PciePhy_clkreq_always_on_L1_9285_1_2), + 2); + } } else if (AR_SREV_9280_20_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModes, ar9280Modes_9280_2, ARRAY_SIZE(ar9280Modes_9280_2), 5); INIT_INI_ARRAY(&ah->iniCommon, ar9280Common_9280_2, ARRAY_SIZE(ar9280Common_9280_2), 2); - INIT_INI_ARRAY(&ah->iniModesFastClock, + if (ah->config.pcie_clock_req) { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9280PciePhy_clkreq_off_L1_9280, + ARRAY_SIZE(ar9280PciePhy_clkreq_off_L1_9280), 2); + } else { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9280PciePhy_clkreq_always_on_L1_9280, + ARRAY_SIZE(ar9280PciePhy_clkreq_always_on_L1_9280), 2); + } + INIT_INI_ARRAY(&ah->iniModesAdditional, ar9280Modes_fast_clock_9280_2, ARRAY_SIZE(ar9280Modes_fast_clock_9280_2), 3); } else if (AR_SREV_9160_10_OR_LATER(ah)) { @@ -72,6 +109,22 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah) ARRAY_SIZE(ar5416Modes_9160), 5); INIT_INI_ARRAY(&ah->iniCommon, ar5416Common_9160, ARRAY_SIZE(ar5416Common_9160), 2); + INIT_INI_ARRAY(&ah->iniBank0, ar5416Bank0_9160, + ARRAY_SIZE(ar5416Bank0_9160), 2); + INIT_INI_ARRAY(&ah->iniBB_RfGain, ar5416BB_RfGain_9160, + ARRAY_SIZE(ar5416BB_RfGain_9160), 3); + INIT_INI_ARRAY(&ah->iniBank1, ar5416Bank1_9160, + ARRAY_SIZE(ar5416Bank1_9160), 2); + INIT_INI_ARRAY(&ah->iniBank2, ar5416Bank2_9160, + ARRAY_SIZE(ar5416Bank2_9160), 2); + INIT_INI_ARRAY(&ah->iniBank3, ar5416Bank3_9160, + ARRAY_SIZE(ar5416Bank3_9160), 3); + INIT_INI_ARRAY(&ah->iniBank6, ar5416Bank6_9160, + ARRAY_SIZE(ar5416Bank6_9160), 3); + INIT_INI_ARRAY(&ah->iniBank6TPC, ar5416Bank6TPC_9160, + ARRAY_SIZE(ar5416Bank6TPC_9160), 3); + INIT_INI_ARRAY(&ah->iniBank7, ar5416Bank7_9160, + ARRAY_SIZE(ar5416Bank7_9160), 2); if (AR_SREV_9160_11(ah)) { INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac_9160_1_1, @@ -85,8 +138,22 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah) ARRAY_SIZE(ar5416Modes_9100), 5); INIT_INI_ARRAY(&ah->iniCommon, ar5416Common_9100, ARRAY_SIZE(ar5416Common_9100), 2); + INIT_INI_ARRAY(&ah->iniBank0, ar5416Bank0_9100, + ARRAY_SIZE(ar5416Bank0_9100), 2); + INIT_INI_ARRAY(&ah->iniBB_RfGain, ar5416BB_RfGain_9100, + ARRAY_SIZE(ar5416BB_RfGain_9100), 3); + INIT_INI_ARRAY(&ah->iniBank1, ar5416Bank1_9100, + ARRAY_SIZE(ar5416Bank1_9100), 2); + INIT_INI_ARRAY(&ah->iniBank2, ar5416Bank2_9100, + ARRAY_SIZE(ar5416Bank2_9100), 2); + INIT_INI_ARRAY(&ah->iniBank3, ar5416Bank3_9100, + ARRAY_SIZE(ar5416Bank3_9100), 3); INIT_INI_ARRAY(&ah->iniBank6, ar5416Bank6_9100, ARRAY_SIZE(ar5416Bank6_9100), 3); + INIT_INI_ARRAY(&ah->iniBank6TPC, ar5416Bank6TPC_9100, + ARRAY_SIZE(ar5416Bank6TPC_9100), 3); + INIT_INI_ARRAY(&ah->iniBank7, ar5416Bank7_9100, + ARRAY_SIZE(ar5416Bank7_9100), 2); INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac_9100, ARRAY_SIZE(ar5416Addac_9100), 2); } else { @@ -94,57 +161,30 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah) ARRAY_SIZE(ar5416Modes), 5); INIT_INI_ARRAY(&ah->iniCommon, ar5416Common, ARRAY_SIZE(ar5416Common), 2); - INIT_INI_ARRAY(&ah->iniBank6TPC, ar5416Bank6TPC, - ARRAY_SIZE(ar5416Bank6TPC), 3); - INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac, - ARRAY_SIZE(ar5416Addac), 2); - } - - if (!AR_SREV_9280_20_OR_LATER(ah)) { - /* Common for AR5416, AR913x, AR9160 */ - INIT_INI_ARRAY(&ah->iniBB_RfGain, ar5416BB_RfGain, - ARRAY_SIZE(ar5416BB_RfGain), 3); - INIT_INI_ARRAY(&ah->iniBank0, ar5416Bank0, ARRAY_SIZE(ar5416Bank0), 2); + INIT_INI_ARRAY(&ah->iniBB_RfGain, ar5416BB_RfGain, + ARRAY_SIZE(ar5416BB_RfGain), 3); INIT_INI_ARRAY(&ah->iniBank1, ar5416Bank1, ARRAY_SIZE(ar5416Bank1), 2); INIT_INI_ARRAY(&ah->iniBank2, ar5416Bank2, ARRAY_SIZE(ar5416Bank2), 2); INIT_INI_ARRAY(&ah->iniBank3, ar5416Bank3, ARRAY_SIZE(ar5416Bank3), 3); + INIT_INI_ARRAY(&ah->iniBank6, ar5416Bank6, + ARRAY_SIZE(ar5416Bank6), 3); + INIT_INI_ARRAY(&ah->iniBank6TPC, ar5416Bank6TPC, + ARRAY_SIZE(ar5416Bank6TPC), 3); INIT_INI_ARRAY(&ah->iniBank7, ar5416Bank7, ARRAY_SIZE(ar5416Bank7), 2); - - /* Common for AR5416, AR9160 */ - if (!AR_SREV_9100(ah)) - INIT_INI_ARRAY(&ah->iniBank6, ar5416Bank6, - ARRAY_SIZE(ar5416Bank6), 3); - - /* Common for AR913x, AR9160 */ - if (!AR_SREV_5416(ah)) - INIT_INI_ARRAY(&ah->iniBank6TPC, ar5416Bank6TPC_9100, - ARRAY_SIZE(ar5416Bank6TPC_9100), 3); + INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac, + ARRAY_SIZE(ar5416Addac), 2); } +} - /* iniAddac needs to be modified for these chips */ - if (AR_SREV_9160(ah) || !AR_SREV_5416_22_OR_LATER(ah)) { - struct ar5416IniArray *addac = &ah->iniAddac; - u32 size = sizeof(u32) * addac->ia_rows * addac->ia_columns; - u32 *data; - - data = kmalloc(size, GFP_KERNEL); - if (!data) - return; - - memcpy(data, addac->ia_array, size); - addac->ia_array = data; - - if (!AR_SREV_5416_22_OR_LATER(ah)) { - /* override CLKDRV value */ - INI_RA(addac, 31,1) = 0; - } - } +/* Support for Japan ch.14 (2484) spread */ +void ar9002_hw_cck_chan14_spread(struct ath_hw *ah) +{ if (AR_SREV_9287_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniCckfirNormal, ar9287Common_normal_cck_fir_coeff_9287_1_1, @@ -184,10 +224,14 @@ static void ar9280_20_hw_init_rxgain_ini(struct ath_hw *ah) } } -static void ar9280_20_hw_init_txgain_ini(struct ath_hw *ah, u32 txgain_type) +static void ar9280_20_hw_init_txgain_ini(struct ath_hw *ah) { + u32 txgain_type; + if (ah->eep_ops->get_eeprom(ah, EEP_MINOR_REV) >= AR5416_EEP_MINOR_VER_19) { + txgain_type = ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE); + if (txgain_type == AR5416_EEP_TXGAIN_HIGH_POWER) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9280Modes_high_power_tx_gain_9280_2, @@ -203,22 +247,8 @@ static void ar9280_20_hw_init_txgain_ini(struct ath_hw *ah, u32 txgain_type) } } -static void ar9271_hw_init_txgain_ini(struct ath_hw *ah, u32 txgain_type) -{ - if (txgain_type == AR5416_EEP_TXGAIN_HIGH_POWER) - INIT_INI_ARRAY(&ah->iniModesTxGain, - ar9271Modes_high_power_tx_gain_9271, - ARRAY_SIZE(ar9271Modes_high_power_tx_gain_9271), 5); - else - INIT_INI_ARRAY(&ah->iniModesTxGain, - ar9271Modes_normal_power_tx_gain_9271, - ARRAY_SIZE(ar9271Modes_normal_power_tx_gain_9271), 5); -} - static void ar9002_hw_init_mode_gain_regs(struct ath_hw *ah) { - u32 txgain_type = ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE); - if (AR_SREV_9287_11_OR_LATER(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9287Modes_rx_gain_9287_1_1, @@ -226,15 +256,15 @@ static void ar9002_hw_init_mode_gain_regs(struct ath_hw *ah) else if (AR_SREV_9280_20(ah)) ar9280_20_hw_init_rxgain_ini(ah); - if (AR_SREV_9271(ah)) { - ar9271_hw_init_txgain_ini(ah, txgain_type); - } else if (AR_SREV_9287_11_OR_LATER(ah)) { + if (AR_SREV_9287_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModesTxGain, ar9287Modes_tx_gain_9287_1_1, ARRAY_SIZE(ar9287Modes_tx_gain_9287_1_1), 5); } else if (AR_SREV_9280_20(ah)) { - ar9280_20_hw_init_txgain_ini(ah, txgain_type); + ar9280_20_hw_init_txgain_ini(ah); } else if (AR_SREV_9285_12_OR_LATER(ah)) { + u32 txgain_type = ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE); + /* txgain table */ if (txgain_type == AR5416_EEP_TXGAIN_HIGH_POWER) { if (AR_SREV_9285E_20(ah)) { diff --git a/drivers/net/wireless/ath/ath9k/ar9002_initvals.h b/drivers/net/wireless/ath/ath9k/ar9002_initvals.h index 4d18c66a..863db321 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9002_initvals.h @@ -925,6 +925,34 @@ static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = { {0x00004044, 0x00000000}, }; +static const u32 ar9285PciePhy_clkreq_always_on_L1_9285[][2] = { + /* Addr allmodes */ + {0x00004040, 0x9248fd00}, + {0x00004040, 0x24924924}, + {0x00004040, 0xa8000019}, + {0x00004040, 0x13160820}, + {0x00004040, 0xe5980560}, + {0x00004040, 0xc01dcffd}, + {0x00004040, 0x1aaabe41}, + {0x00004040, 0xbe105554}, + {0x00004040, 0x00043007}, + {0x00004044, 0x00000000}, +}; + +static const u32 ar9285PciePhy_clkreq_off_L1_9285[][2] = { + /* Addr allmodes */ + {0x00004040, 0x9248fd00}, + {0x00004040, 0x24924924}, + {0x00004040, 0xa8000019}, + {0x00004040, 0x13160820}, + {0x00004040, 0xe5980560}, + {0x00004040, 0xc01dcffc}, + {0x00004040, 0x1aaabe41}, + {0x00004040, 0xbe105554}, + {0x00004040, 0x00043007}, + {0x00004044, 0x00000000}, +}; + static const u32 ar9285Modes_9285_1_2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, @@ -1715,6 +1743,34 @@ static const u32 ar9285Modes_XE2_0_high_power[][5] = { {0x0000a3e0, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7}, }; +static const u32 ar9285PciePhy_clkreq_always_on_L1_9285_1_2[][2] = { + /* Addr allmodes */ + {0x00004040, 0x9248fd00}, + {0x00004040, 0x24924924}, + {0x00004040, 0xa8000019}, + {0x00004040, 0x13160820}, + {0x00004040, 0xe5980560}, + {0x00004040, 0xc01dcffd}, + {0x00004040, 0x1aaabe41}, + {0x00004040, 0xbe105554}, + {0x00004040, 0x00043007}, + {0x00004044, 0x00000000}, +}; + +static const u32 ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = { + /* Addr allmodes */ + {0x00004040, 0x9248fd00}, + {0x00004040, 0x24924924}, + {0x00004040, 0xa8000019}, + {0x00004040, 0x13160820}, + {0x00004040, 0xe5980560}, + {0x00004040, 0xc01dcffc}, + {0x00004040, 0x1aaabe41}, + {0x00004040, 0xbe105554}, + {0x00004040, 0x00043007}, + {0x00004044, 0x00000000}, +}; + static const u32 ar9287Modes_9287_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000000, 0x00000000, 0x000002c0, 0x00000160}, @@ -2456,6 +2512,34 @@ static const u32 ar9287Modes_rx_gain_9287_1_1[][5] = { {0x0000a848, 0x00000000, 0x00000000, 0x00001067, 0x00001067}, }; +static const u32 ar9287PciePhy_clkreq_always_on_L1_9287_1_1[][2] = { + /* Addr allmodes */ + {0x00004040, 0x9248fd00}, + {0x00004040, 0x24924924}, + {0x00004040, 0xa8000019}, + {0x00004040, 0x13160820}, + {0x00004040, 0xe5980560}, + {0x00004040, 0xc01dcffd}, + {0x00004040, 0x1aaabe41}, + {0x00004040, 0xbe105554}, + {0x00004040, 0x00043007}, + {0x00004044, 0x00000000}, +}; + +static const u32 ar9287PciePhy_clkreq_off_L1_9287_1_1[][2] = { + /* Addr allmodes */ + {0x00004040, 0x9248fd00}, + {0x00004040, 0x24924924}, + {0x00004040, 0xa8000019}, + {0x00004040, 0x13160820}, + {0x00004040, 0xe5980560}, + {0x00004040, 0xc01dcffc}, + {0x00004040, 0x1aaabe41}, + {0x00004040, 0xbe105554}, + {0x00004040, 0x00043007}, + {0x00004044, 0x00000000}, +}; + static const u32 ar9271Modes_9271[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, @@ -3092,6 +3176,26 @@ static const u32 ar9271Common_9271[][2] = { {0x0000d384, 0xf3307ff0}, }; +static const u32 ar9271Common_normal_cck_fir_coeff_9271[][2] = { + /* Addr allmodes */ + {0x0000a1f4, 0x00fffeff}, + {0x0000a1f8, 0x00f5f9ff}, + {0x0000a1fc, 0xb79f6427}, +}; + +static const u32 ar9271Common_japan_2484_cck_fir_coeff_9271[][2] = { + /* Addr allmodes */ + {0x0000a1f4, 0x00000000}, + {0x0000a1f8, 0xefff0301}, + {0x0000a1fc, 0xca9228ee}, +}; + +static const u32 ar9271Modes_9271_1_0_only[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009910, 0x30002311, 0x30002311, 0x30002311, 0x30002311}, + {0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001}, +}; + static const u32 ar9271Modes_9271_ANI_reg[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2}, diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c old mode 100644 new mode 100755 index fa7581a5..f4caeb32 --- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c @@ -126,7 +126,7 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) } if (isr & AR_ISR_RXORN) { - ath_dbg(common, INTERRUPT, + ath_dbg(common, ATH_DBG_INTERRUPT, "receive FIFO overrun interrupt\n"); } @@ -177,24 +177,24 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) if (fatal_int) { if (sync_cause & AR_INTR_SYNC_HOST1_FATAL) { - ath_dbg(common, ANY, + ath_dbg(common, ATH_DBG_ANY, "received PCI FATAL interrupt\n"); } if (sync_cause & AR_INTR_SYNC_HOST1_PERR) { - ath_dbg(common, ANY, + ath_dbg(common, ATH_DBG_ANY, "received PCI PERR interrupt\n"); } *masked |= ATH9K_INT_FATAL; } if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) { - ath_dbg(common, INTERRUPT, + ath_dbg(common, ATH_DBG_INTERRUPT, "AR_INTR_SYNC_RADM_CPL_TIMEOUT\n"); REG_WRITE(ah, AR_RC, AR_RC_HOSTIF); REG_WRITE(ah, AR_RC, 0); *masked |= ATH9K_INT_FATAL; } if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) { - ath_dbg(common, INTERRUPT, + ath_dbg(common, ATH_DBG_INTERRUPT, "AR_INTR_SYNC_LOCAL_TIMEOUT\n"); } @@ -381,12 +381,15 @@ void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, u32 size, u32 flags) { struct ar5416_desc *ads = AR5416DESC(ds); + struct ath9k_hw_capabilities *pCap = &ah->caps; ads->ds_ctl1 = size & AR_BufLen; if (flags & ATH9K_RXDESC_INTREQ) ads->ds_ctl1 |= AR_RxIntrReq; - memset(&ads->u.rx, 0, sizeof(ads->u.rx)); + ads->ds_rxstatus8 &= ~AR_RxDone; + if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) + memset(&(ads->u), 0, sizeof(ads->u)); } EXPORT_SYMBOL(ath9k_hw_setuprxdesc); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.h b/drivers/net/wireless/ath/ath9k/ar9002_phy.h index f9eb2c35..453af6dc 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.h @@ -60,8 +60,6 @@ #define AR_PHY_RF_CTL3 0x9828 #define AR_PHY_TX_END_TO_A2_RX_ON 0x00FF0000 #define AR_PHY_TX_END_TO_A2_RX_ON_S 16 -#define AR_PHY_TX_END_TO_ADC_ON 0xFF000000 -#define AR_PHY_TX_END_TO_ADC_ON_S 24 #define AR_PHY_ADC_CTL 0x982C #define AR_PHY_ADC_CTL_OFF_INBUFGAIN 0x00000003 diff --git a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h old mode 100644 new mode 100755 index 4e2a52c3..498a3c11 --- a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h @@ -295,6 +295,266 @@ static const u32 ar9300_2p2_radio_core[][2] = { {0x00016bd4, 0x00000000}, }; +static const u32 ar9300Common_rx_gain_table_merlin_2p2[][2] = { + /* Addr allmodes */ + {0x0000a000, 0x02000101}, + {0x0000a004, 0x02000102}, + {0x0000a008, 0x02000103}, + {0x0000a00c, 0x02000104}, + {0x0000a010, 0x02000200}, + {0x0000a014, 0x02000201}, + {0x0000a018, 0x02000202}, + {0x0000a01c, 0x02000203}, + {0x0000a020, 0x02000204}, + {0x0000a024, 0x02000205}, + {0x0000a028, 0x02000208}, + {0x0000a02c, 0x02000302}, + {0x0000a030, 0x02000303}, + {0x0000a034, 0x02000304}, + {0x0000a038, 0x02000400}, + {0x0000a03c, 0x02010300}, + {0x0000a040, 0x02010301}, + {0x0000a044, 0x02010302}, + {0x0000a048, 0x02000500}, + {0x0000a04c, 0x02010400}, + {0x0000a050, 0x02020300}, + {0x0000a054, 0x02020301}, + {0x0000a058, 0x02020302}, + {0x0000a05c, 0x02020303}, + {0x0000a060, 0x02020400}, + {0x0000a064, 0x02030300}, + {0x0000a068, 0x02030301}, + {0x0000a06c, 0x02030302}, + {0x0000a070, 0x02030303}, + {0x0000a074, 0x02030400}, + {0x0000a078, 0x02040300}, + {0x0000a07c, 0x02040301}, + {0x0000a080, 0x02040302}, + {0x0000a084, 0x02040303}, + {0x0000a088, 0x02030500}, + {0x0000a08c, 0x02040400}, + {0x0000a090, 0x02050203}, + {0x0000a094, 0x02050204}, + {0x0000a098, 0x02050205}, + {0x0000a09c, 0x02040500}, + {0x0000a0a0, 0x02050301}, + {0x0000a0a4, 0x02050302}, + {0x0000a0a8, 0x02050303}, + {0x0000a0ac, 0x02050400}, + {0x0000a0b0, 0x02050401}, + {0x0000a0b4, 0x02050402}, + {0x0000a0b8, 0x02050403}, + {0x0000a0bc, 0x02050500}, + {0x0000a0c0, 0x02050501}, + {0x0000a0c4, 0x02050502}, + {0x0000a0c8, 0x02050503}, + {0x0000a0cc, 0x02050504}, + {0x0000a0d0, 0x02050600}, + {0x0000a0d4, 0x02050601}, + {0x0000a0d8, 0x02050602}, + {0x0000a0dc, 0x02050603}, + {0x0000a0e0, 0x02050604}, + {0x0000a0e4, 0x02050700}, + {0x0000a0e8, 0x02050701}, + {0x0000a0ec, 0x02050702}, + {0x0000a0f0, 0x02050703}, + {0x0000a0f4, 0x02050704}, + {0x0000a0f8, 0x02050705}, + {0x0000a0fc, 0x02050708}, + {0x0000a100, 0x02050709}, + {0x0000a104, 0x0205070a}, + {0x0000a108, 0x0205070b}, + {0x0000a10c, 0x0205070c}, + {0x0000a110, 0x0205070d}, + {0x0000a114, 0x02050710}, + {0x0000a118, 0x02050711}, + {0x0000a11c, 0x02050712}, + {0x0000a120, 0x02050713}, + {0x0000a124, 0x02050714}, + {0x0000a128, 0x02050715}, + {0x0000a12c, 0x02050730}, + {0x0000a130, 0x02050731}, + {0x0000a134, 0x02050732}, + {0x0000a138, 0x02050733}, + {0x0000a13c, 0x02050734}, + {0x0000a140, 0x02050735}, + {0x0000a144, 0x02050750}, + {0x0000a148, 0x02050751}, + {0x0000a14c, 0x02050752}, + {0x0000a150, 0x02050753}, + {0x0000a154, 0x02050754}, + {0x0000a158, 0x02050755}, + {0x0000a15c, 0x02050770}, + {0x0000a160, 0x02050771}, + {0x0000a164, 0x02050772}, + {0x0000a168, 0x02050773}, + {0x0000a16c, 0x02050774}, + {0x0000a170, 0x02050775}, + {0x0000a174, 0x00000776}, + {0x0000a178, 0x00000776}, + {0x0000a17c, 0x00000776}, + {0x0000a180, 0x00000776}, + {0x0000a184, 0x00000776}, + {0x0000a188, 0x00000776}, + {0x0000a18c, 0x00000776}, + {0x0000a190, 0x00000776}, + {0x0000a194, 0x00000776}, + {0x0000a198, 0x00000776}, + {0x0000a19c, 0x00000776}, + {0x0000a1a0, 0x00000776}, + {0x0000a1a4, 0x00000776}, + {0x0000a1a8, 0x00000776}, + {0x0000a1ac, 0x00000776}, + {0x0000a1b0, 0x00000776}, + {0x0000a1b4, 0x00000776}, + {0x0000a1b8, 0x00000776}, + {0x0000a1bc, 0x00000776}, + {0x0000a1c0, 0x00000776}, + {0x0000a1c4, 0x00000776}, + {0x0000a1c8, 0x00000776}, + {0x0000a1cc, 0x00000776}, + {0x0000a1d0, 0x00000776}, + {0x0000a1d4, 0x00000776}, + {0x0000a1d8, 0x00000776}, + {0x0000a1dc, 0x00000776}, + {0x0000a1e0, 0x00000776}, + {0x0000a1e4, 0x00000776}, + {0x0000a1e8, 0x00000776}, + {0x0000a1ec, 0x00000776}, + {0x0000a1f0, 0x00000776}, + {0x0000a1f4, 0x00000776}, + {0x0000a1f8, 0x00000776}, + {0x0000a1fc, 0x00000776}, + {0x0000b000, 0x02000101}, + {0x0000b004, 0x02000102}, + {0x0000b008, 0x02000103}, + {0x0000b00c, 0x02000104}, + {0x0000b010, 0x02000200}, + {0x0000b014, 0x02000201}, + {0x0000b018, 0x02000202}, + {0x0000b01c, 0x02000203}, + {0x0000b020, 0x02000204}, + {0x0000b024, 0x02000205}, + {0x0000b028, 0x02000208}, + {0x0000b02c, 0x02000302}, + {0x0000b030, 0x02000303}, + {0x0000b034, 0x02000304}, + {0x0000b038, 0x02000400}, + {0x0000b03c, 0x02010300}, + {0x0000b040, 0x02010301}, + {0x0000b044, 0x02010302}, + {0x0000b048, 0x02000500}, + {0x0000b04c, 0x02010400}, + {0x0000b050, 0x02020300}, + {0x0000b054, 0x02020301}, + {0x0000b058, 0x02020302}, + {0x0000b05c, 0x02020303}, + {0x0000b060, 0x02020400}, + {0x0000b064, 0x02030300}, + {0x0000b068, 0x02030301}, + {0x0000b06c, 0x02030302}, + {0x0000b070, 0x02030303}, + {0x0000b074, 0x02030400}, + {0x0000b078, 0x02040300}, + {0x0000b07c, 0x02040301}, + {0x0000b080, 0x02040302}, + {0x0000b084, 0x02040303}, + {0x0000b088, 0x02030500}, + {0x0000b08c, 0x02040400}, + {0x0000b090, 0x02050203}, + {0x0000b094, 0x02050204}, + {0x0000b098, 0x02050205}, + {0x0000b09c, 0x02040500}, + {0x0000b0a0, 0x02050301}, + {0x0000b0a4, 0x02050302}, + {0x0000b0a8, 0x02050303}, + {0x0000b0ac, 0x02050400}, + {0x0000b0b0, 0x02050401}, + {0x0000b0b4, 0x02050402}, + {0x0000b0b8, 0x02050403}, + {0x0000b0bc, 0x02050500}, + {0x0000b0c0, 0x02050501}, + {0x0000b0c4, 0x02050502}, + {0x0000b0c8, 0x02050503}, + {0x0000b0cc, 0x02050504}, + {0x0000b0d0, 0x02050600}, + {0x0000b0d4, 0x02050601}, + {0x0000b0d8, 0x02050602}, + {0x0000b0dc, 0x02050603}, + {0x0000b0e0, 0x02050604}, + {0x0000b0e4, 0x02050700}, + {0x0000b0e8, 0x02050701}, + {0x0000b0ec, 0x02050702}, + {0x0000b0f0, 0x02050703}, + {0x0000b0f4, 0x02050704}, + {0x0000b0f8, 0x02050705}, + {0x0000b0fc, 0x02050708}, + {0x0000b100, 0x02050709}, + {0x0000b104, 0x0205070a}, + {0x0000b108, 0x0205070b}, + {0x0000b10c, 0x0205070c}, + {0x0000b110, 0x0205070d}, + {0x0000b114, 0x02050710}, + {0x0000b118, 0x02050711}, + {0x0000b11c, 0x02050712}, + {0x0000b120, 0x02050713}, + {0x0000b124, 0x02050714}, + {0x0000b128, 0x02050715}, + {0x0000b12c, 0x02050730}, + {0x0000b130, 0x02050731}, + {0x0000b134, 0x02050732}, + {0x0000b138, 0x02050733}, + {0x0000b13c, 0x02050734}, + {0x0000b140, 0x02050735}, + {0x0000b144, 0x02050750}, + {0x0000b148, 0x02050751}, + {0x0000b14c, 0x02050752}, + {0x0000b150, 0x02050753}, + {0x0000b154, 0x02050754}, + {0x0000b158, 0x02050755}, + {0x0000b15c, 0x02050770}, + {0x0000b160, 0x02050771}, + {0x0000b164, 0x02050772}, + {0x0000b168, 0x02050773}, + {0x0000b16c, 0x02050774}, + {0x0000b170, 0x02050775}, + {0x0000b174, 0x00000776}, + {0x0000b178, 0x00000776}, + {0x0000b17c, 0x00000776}, + {0x0000b180, 0x00000776}, + {0x0000b184, 0x00000776}, + {0x0000b188, 0x00000776}, + {0x0000b18c, 0x00000776}, + {0x0000b190, 0x00000776}, + {0x0000b194, 0x00000776}, + {0x0000b198, 0x00000776}, + {0x0000b19c, 0x00000776}, + {0x0000b1a0, 0x00000776}, + {0x0000b1a4, 0x00000776}, + {0x0000b1a8, 0x00000776}, + {0x0000b1ac, 0x00000776}, + {0x0000b1b0, 0x00000776}, + {0x0000b1b4, 0x00000776}, + {0x0000b1b8, 0x00000776}, + {0x0000b1bc, 0x00000776}, + {0x0000b1c0, 0x00000776}, + {0x0000b1c4, 0x00000776}, + {0x0000b1c8, 0x00000776}, + {0x0000b1cc, 0x00000776}, + {0x0000b1d0, 0x00000776}, + {0x0000b1d4, 0x00000776}, + {0x0000b1d8, 0x00000776}, + {0x0000b1dc, 0x00000776}, + {0x0000b1e0, 0x00000776}, + {0x0000b1e4, 0x00000776}, + {0x0000b1e8, 0x00000776}, + {0x0000b1ec, 0x00000776}, + {0x0000b1f0, 0x00000776}, + {0x0000b1f4, 0x00000776}, + {0x0000b1f8, 0x00000776}, + {0x0000b1fc, 0x00000776}, +}; + static const u32 ar9300_2p2_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, @@ -312,6 +572,48 @@ static const u32 ar9300_2p2_soc_postamble[][5] = { {0x00007010, 0x00000023, 0x00000023, 0x00000023, 0x00000023}, }; +static const u32 ar9200_merlin_2p2_radio_core[][2] = { + /* Addr allmodes */ + {0x00007800, 0x00040000}, + {0x00007804, 0xdb005012}, + {0x00007808, 0x04924914}, + {0x0000780c, 0x21084210}, + {0x00007810, 0x6d801300}, + {0x00007814, 0x0019beff}, + {0x00007818, 0x07e41000}, + {0x0000781c, 0x00392000}, + {0x00007820, 0x92592480}, + {0x00007824, 0x00040000}, + {0x00007828, 0xdb005012}, + {0x0000782c, 0x04924914}, + {0x00007830, 0x21084210}, + {0x00007834, 0x6d801300}, + {0x00007838, 0x0019beff}, + {0x0000783c, 0x07e40000}, + {0x00007840, 0x00392000}, + {0x00007844, 0x92592480}, + {0x00007848, 0x00100000}, + {0x0000784c, 0x773f0567}, + {0x00007850, 0x54214514}, + {0x00007854, 0x12035828}, + {0x00007858, 0x92592692}, + {0x0000785c, 0x00000000}, + {0x00007860, 0x56400000}, + {0x00007864, 0x0a8e370e}, + {0x00007868, 0xc0102850}, + {0x0000786c, 0x812d4000}, + {0x00007870, 0x807ec400}, + {0x00007874, 0x001b6db0}, + {0x00007878, 0x00376b63}, + {0x0000787c, 0x06db6db6}, + {0x00007880, 0x006d8000}, + {0x00007884, 0xffeffffe}, + {0x00007888, 0xffeffffe}, + {0x0000788c, 0x00010000}, + {0x00007890, 0x02060aeb}, + {0x00007894, 0x5a108000}, +}; + static const u32 ar9300_2p2_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c old mode 100644 new mode 100755 index 7c864150..39653561 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -18,7 +18,6 @@ #include "hw-ops.h" #include "ar9003_phy.h" #include "ar9003_rtt.h" -#include "ar9003_mci.h" #define MAX_MEASUREMENT MAX_IQCAL_MEASUREMENT #define MAX_MAG_DELTA 11 @@ -52,7 +51,7 @@ static void ar9003_hw_setup_calibration(struct ath_hw *ah, currCal->calData->calCountMax); REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "starting IQ Mismatch Calibration\n"); /* Kick-off cal */ @@ -64,7 +63,7 @@ static void ar9003_hw_setup_calibration(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM, AR_PHY_65NM_CH0_THERM_START, 1); - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "starting Temperature Compensation Calibration\n"); break; } @@ -194,7 +193,7 @@ static void ar9003_hw_iqcal_collect(struct ath_hw *ah) REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); ah->totalIqCorrMeas[i] += (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); - ath_dbg(ath9k_hw_common(ah), CALIBRATE, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n", ah->cal_samples, i, ah->totalPowerMeasI[i], ah->totalPowerMeasQ[i], @@ -221,11 +220,12 @@ static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) powerMeasQ = ah->totalPowerMeasQ[i]; iqCorrMeas = ah->totalIqCorrMeas[i]; - ath_dbg(common, CALIBRATE, - "Starting IQ Cal and Correction for Chain %d\n", i); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Starting IQ Cal and Correction for Chain %d\n", + i); - ath_dbg(common, CALIBRATE, - "Original: Chn %d iq_corr_meas = 0x%08x\n", + ath_dbg(common, ATH_DBG_CALIBRATE, + "Orignal: Chn %diq_corr_meas = 0x%08x\n", i, ah->totalIqCorrMeas[i]); iqCorrNeg = 0; @@ -235,11 +235,12 @@ static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) iqCorrNeg = 1; } - ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_i = 0x%08x\n", - i, powerMeasI); - ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_q = 0x%08x\n", - i, powerMeasQ); - ath_dbg(common, CALIBRATE, "iqCorrNeg is 0x%08x\n", iqCorrNeg); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ); + ath_dbg(common, ATH_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n", + iqCorrNeg); iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 256; qCoffDenom = powerMeasQ / 64; @@ -247,10 +248,10 @@ static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) if ((iCoffDenom != 0) && (qCoffDenom != 0)) { iCoff = iqCorrMeas / iCoffDenom; qCoff = powerMeasI / qCoffDenom - 64; - ath_dbg(common, CALIBRATE, "Chn %d iCoff = 0x%08x\n", - i, iCoff); - ath_dbg(common, CALIBRATE, "Chn %d qCoff = 0x%08x\n", - i, qCoff); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d iCoff = 0x%08x\n", i, iCoff); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Chn %d qCoff = 0x%08x\n", i, qCoff); /* Force bounds on iCoff */ if (iCoff >= 63) @@ -271,10 +272,10 @@ static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) iCoff = iCoff & 0x7f; qCoff = qCoff & 0x7f; - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "Chn %d : iCoff = 0x%x qCoff = 0x%x\n", i, iCoff, qCoff); - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "Register offset (0x%04x) before update = 0x%x\n", offset_array[i], REG_READ(ah, offset_array[i])); @@ -285,25 +286,25 @@ static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) REG_RMW_FIELD(ah, offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, qCoff); - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "Register offset (0x%04x) QI COFF (bitfields 0x%08x) after update = 0x%x\n", offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, REG_READ(ah, offset_array[i])); - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "Register offset (0x%04x) QQ COFF (bitfields 0x%08x) after update = 0x%x\n", offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, REG_READ(ah, offset_array[i])); - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "IQ Cal and Correction done for Chain %d\n", i); } } REG_SET_BIT(ah, AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE); - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "IQ Cal and Correction (offset 0x%04x) enabled (bit position 0x%08x). New Value 0x%08x\n", (unsigned) (AR_PHY_RX_IQCAL_CORR_B0), AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE, @@ -347,7 +348,7 @@ static bool ar9003_hw_solve_iq_cal(struct ath_hw *ah, f2 = (f1 * f1 + f3 * f3) / result_shift; if (!f2) { - ath_dbg(common, CALIBRATE, "Divide by 0\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, "Divide by 0\n"); return false; } @@ -468,7 +469,7 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, if ((i2_p_q2_a0_d0 == 0) || (i2_p_q2_a0_d1 == 0) || (i2_p_q2_a1_d0 == 0) || (i2_p_q2_a1_d1 == 0)) { - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "Divide by 0:\n" "a0_d0=%d\n" "a0_d1=%d\n" @@ -508,7 +509,8 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, mag2 = ar9003_hw_find_mag_approx(ah, cos_2phi_2, sin_2phi_2); if ((mag1 == 0) || (mag2 == 0)) { - ath_dbg(common, CALIBRATE, "Divide by 0: mag1=%d, mag2=%d\n", + ath_dbg(common, ATH_DBG_CALIBRATE, + "Divide by 0: mag1=%d, mag2=%d\n", mag1, mag2); return false; } @@ -526,8 +528,8 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, mag_a0_d0, phs_a0_d0, mag_a1_d0, phs_a1_d0, solved_eq)) { - ath_dbg(common, CALIBRATE, - "Call to ar9003_hw_solve_iq_cal() failed\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Call to ar9003_hw_solve_iq_cal() failed.\n"); return false; } @@ -536,12 +538,12 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, mag_rx = solved_eq[2]; phs_rx = solved_eq[3]; - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "chain %d: mag mismatch=%d phase mismatch=%d\n", chain_idx, mag_tx/res_scale, phs_tx/res_scale); if (res_scale == mag_tx) { - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "Divide by 0: mag_tx=%d, res_scale=%d\n", mag_tx, res_scale); return false; @@ -554,7 +556,8 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, q_q_coff = (mag_corr_tx * 128 / res_scale); q_i_coff = (phs_corr_tx * 256 / res_scale); - ath_dbg(common, CALIBRATE, "tx chain %d: mag corr=%d phase corr=%d\n", + ath_dbg(common, ATH_DBG_CALIBRATE, + "tx chain %d: mag corr=%d phase corr=%d\n", chain_idx, q_q_coff, q_i_coff); if (q_i_coff < -63) @@ -568,11 +571,12 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, iqc_coeff[0] = (q_q_coff * 128) + q_i_coff; - ath_dbg(common, CALIBRATE, "tx chain %d: iq corr coeff=%x\n", + ath_dbg(common, ATH_DBG_CALIBRATE, + "tx chain %d: iq corr coeff=%x\n", chain_idx, iqc_coeff[0]); if (-mag_rx == res_scale) { - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "Divide by 0: mag_rx=%d, res_scale=%d\n", mag_rx, res_scale); return false; @@ -585,7 +589,8 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, q_q_coff = (mag_corr_rx * 128 / res_scale); q_i_coff = (phs_corr_rx * 256 / res_scale); - ath_dbg(common, CALIBRATE, "rx chain %d: mag corr=%d phase corr=%d\n", + ath_dbg(common, ATH_DBG_CALIBRATE, + "rx chain %d: mag corr=%d phase corr=%d\n", chain_idx, q_q_coff, q_i_coff); if (q_i_coff < -63) @@ -599,7 +604,8 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, iqc_coeff[1] = (q_q_coff * 128) + q_i_coff; - ath_dbg(common, CALIBRATE, "rx chain %d: iq corr coeff=%x\n", + ath_dbg(common, ATH_DBG_CALIBRATE, + "rx chain %d: iq corr coeff=%x\n", chain_idx, iqc_coeff[1]); return true; @@ -746,7 +752,8 @@ static bool ar9003_hw_tx_iq_cal_run(struct ath_hw *ah) if (!ath9k_hw_wait(ah, AR_PHY_TX_IQCAL_START, AR_PHY_TX_IQCAL_START_DO_CAL, 0, AH_WAIT_TIMEOUT)) { - ath_dbg(common, CALIBRATE, "Tx IQ Cal is not completed\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Tx IQ Cal is not completed.\n"); return false; } return true; @@ -784,13 +791,13 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable) nmeasurement = MAX_MEASUREMENT; for (im = 0; im < nmeasurement; im++) { - ath_dbg(common, CALIBRATE, - "Doing Tx IQ Cal for chain %d\n", i); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Doing Tx IQ Cal for chain %d.\n", i); if (REG_READ(ah, txiqcal_status[i]) & AR_PHY_TX_IQCAL_STATUS_FAILED) { - ath_dbg(common, CALIBRATE, - "Tx IQ Cal failed for chain %d\n", i); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Tx IQ Cal failed for chain %d.\n", i); goto tx_iqcal_fail; } @@ -816,16 +823,18 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable) iq_res[idx + 1] = 0xffff & REG_READ(ah, chan_info_tab[i] + offset); - ath_dbg(common, CALIBRATE, - "IQ_RES[%d]=0x%x IQ_RES[%d]=0x%x\n", + ath_dbg(common, ATH_DBG_CALIBRATE, + "IQ RES[%d]=0x%x" + "IQ_RES[%d]=0x%x\n", idx, iq_res[idx], idx + 1, iq_res[idx + 1]); } if (!ar9003_hw_calc_iq_corr(ah, i, iq_res, coeff.iqc_coeff)) { - ath_dbg(common, CALIBRATE, - "Failed in calculation of IQ correction\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Failed in calculation of \ + IQ correction.\n"); goto tx_iqcal_fail; } @@ -845,7 +854,7 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable) return; tx_iqcal_fail: - ath_dbg(common, CALIBRATE, "Tx IQ Cal failed\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, "Tx IQ Cal failed\n"); return; } @@ -929,7 +938,6 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, bool is_reusable = true, status = true; bool run_rtt_cal = false, run_agc_cal; bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT); - bool mci = !!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI); u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL | AR_PHY_AGC_CONTROL_FLTR_CAL | AR_PHY_AGC_CONTROL_PKDET_CAL; @@ -945,7 +953,7 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, if (!ar9003_hw_rtt_restore(ah, chan)) run_rtt_cal = true; - ath_dbg(common, CALIBRATE, "RTT restore %s\n", + ath_dbg(common, ATH_DBG_CALIBRATE, "RTT restore %s\n", run_rtt_cal ? "failed" : "succeed"); } run_agc_cal = run_rtt_cal; @@ -1000,9 +1008,6 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, } else if (caldata && !caldata->done_txiqcal_once) run_agc_cal = true; - if (mci && IS_CHAN_2GHZ(chan) && run_agc_cal) - ar9003_mci_init_cal_req(ah, &is_reusable); - txiqcal_done = ar9003_hw_tx_iq_cal_run(ah); REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); udelay(5); @@ -1020,10 +1025,6 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT); } - - if (mci && IS_CHAN_2GHZ(chan) && run_agc_cal) - ar9003_mci_init_cal_done(ah); - if (rtt && !run_rtt_cal) { agc_ctrl |= agc_supp_cals; REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl); @@ -1033,8 +1034,9 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, if (run_rtt_cal) ar9003_hw_rtt_disable(ah); - ath_dbg(common, CALIBRATE, - "offset calibration failed to complete in 1ms; noisy environment?\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, + "offset calibration failed to complete in 1ms;" + "noisy environment?\n"); return false; } @@ -1096,14 +1098,15 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, if (ah->supp_cals & IQ_MISMATCH_CAL) { INIT_CAL(&ah->iq_caldata); INSERT_CAL(ah, &ah->iq_caldata); - ath_dbg(common, CALIBRATE, "enabling IQ Calibration\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, + "enabling IQ Calibration.\n"); } if (ah->supp_cals & TEMP_COMP_CAL) { INIT_CAL(&ah->tempCompCalData); INSERT_CAL(ah, &ah->tempCompCalData); - ath_dbg(common, CALIBRATE, - "enabling Temperature Compensation Calibration\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, + "enabling Temperature Compensation Calibration.\n"); } /* Initialize current pointer to first element in list */ diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 6f2fff30..2bee2240 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -3043,7 +3043,8 @@ static bool ar9300_read_eeprom(struct ath_hw *ah, int address, u8 *buffer, int i; if ((address < 0) || ((address + count) / 2 > AR9300_EEPROM_SIZE - 1)) { - ath_dbg(common, EEPROM, "eeprom address not in range\n"); + ath_dbg(common, ATH_DBG_EEPROM, + "eeprom address not in range\n"); return false; } @@ -3074,8 +3075,8 @@ static bool ar9300_read_eeprom(struct ath_hw *ah, int address, u8 *buffer, return true; error: - ath_dbg(common, EEPROM, "unable to read eeprom region at offset %d\n", - address); + ath_dbg(common, ATH_DBG_EEPROM, + "unable to read eeprom region at offset %d\n", address); return false; } @@ -3159,13 +3160,13 @@ static bool ar9300_uncompress_block(struct ath_hw *ah, length &= 0xff; if (length > 0 && spot >= 0 && spot+length <= mdataSize) { - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "Restore at %d: spot=%d offset=%d length=%d\n", it, spot, offset, length); memcpy(&mptr[spot], &block[it+2], length); spot += length; } else if (length > 0) { - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "Bad restore at %d: spot=%d offset=%d length=%d\n", it, spot, offset, length); return false; @@ -3187,13 +3188,13 @@ static int ar9300_compress_decision(struct ath_hw *ah, switch (code) { case _CompressNone: if (length != mdata_size) { - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "EEPROM structure size mismatch memory=%d eeprom=%d\n", mdata_size, length); return -1; } memcpy(mptr, (u8 *) (word + COMP_HDR_LEN), length); - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "restored eeprom %d: uncompressed, length %d\n", it, length); break; @@ -3202,21 +3203,22 @@ static int ar9300_compress_decision(struct ath_hw *ah, } else { eep = ar9003_eeprom_struct_find_by_id(reference); if (eep == NULL) { - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "can't find reference eeprom struct %d\n", reference); return -1; } memcpy(mptr, eep, mdata_size); } - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "restore eeprom %d: block, reference %d, length %d\n", it, reference, length); ar9300_uncompress_block(ah, mptr, mdata_size, (u8 *) (word + COMP_HDR_LEN), length); break; default: - ath_dbg(common, EEPROM, "unknown compression code %d\n", code); + ath_dbg(common, ATH_DBG_EEPROM, + "unknown compression code %d\n", code); return -1; } return 0; @@ -3292,32 +3294,34 @@ static int ar9300_eeprom_restore_internal(struct ath_hw *ah, cptr = AR9300_BASE_ADDR_512; else cptr = AR9300_BASE_ADDR; - ath_dbg(common, EEPROM, "Trying EEPROM access at Address 0x%04x\n", - cptr); + ath_dbg(common, ATH_DBG_EEPROM, + "Trying EEPROM access at Address 0x%04x\n", cptr); if (ar9300_check_eeprom_header(ah, read, cptr)) goto found; cptr = AR9300_BASE_ADDR_512; - ath_dbg(common, EEPROM, "Trying EEPROM access at Address 0x%04x\n", - cptr); + ath_dbg(common, ATH_DBG_EEPROM, + "Trying EEPROM access at Address 0x%04x\n", cptr); if (ar9300_check_eeprom_header(ah, read, cptr)) goto found; read = ar9300_read_otp; cptr = AR9300_BASE_ADDR; - ath_dbg(common, EEPROM, "Trying OTP access at Address 0x%04x\n", cptr); + ath_dbg(common, ATH_DBG_EEPROM, + "Trying OTP access at Address 0x%04x\n", cptr); if (ar9300_check_eeprom_header(ah, read, cptr)) goto found; cptr = AR9300_BASE_ADDR_512; - ath_dbg(common, EEPROM, "Trying OTP access at Address 0x%04x\n", cptr); + ath_dbg(common, ATH_DBG_EEPROM, + "Trying OTP access at Address 0x%04x\n", cptr); if (ar9300_check_eeprom_header(ah, read, cptr)) goto found; goto fail; found: - ath_dbg(common, EEPROM, "Found valid EEPROM data\n"); + ath_dbg(common, ATH_DBG_EEPROM, "Found valid EEPROM data\n"); for (it = 0; it < MSTATE; it++) { if (!read(ah, cptr, word, COMP_HDR_LEN)) @@ -3328,12 +3332,13 @@ static int ar9300_eeprom_restore_internal(struct ath_hw *ah, ar9300_comp_hdr_unpack(word, &code, &reference, &length, &major, &minor); - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "Found block at %x: code=%d ref=%d length=%d major=%d minor=%d\n", cptr, code, reference, length, major, minor); if ((!AR_SREV_9485(ah) && length >= 1024) || (AR_SREV_9485(ah) && length > EEPROM_DATA_LEN_9485)) { - ath_dbg(common, EEPROM, "Skipping bad header\n"); + ath_dbg(common, ATH_DBG_EEPROM, + "Skipping bad header\n"); cptr -= COMP_HDR_LEN; continue; } @@ -3342,13 +3347,13 @@ static int ar9300_eeprom_restore_internal(struct ath_hw *ah, read(ah, cptr, word, COMP_HDR_LEN + osize + COMP_CKSUM_LEN); checksum = ar9300_comp_cksum(&word[COMP_HDR_LEN], length); mchecksum = get_unaligned_le16(&word[COMP_HDR_LEN + osize]); - ath_dbg(common, EEPROM, "checksum %x %x\n", - checksum, mchecksum); + ath_dbg(common, ATH_DBG_EEPROM, + "checksum %x %x\n", checksum, mchecksum); if (checksum == mchecksum) { ar9300_compress_decision(ah, it, code, reference, mptr, word, length, mdata_size); } else { - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "skipping block with bad checksum\n"); } cptr -= (COMP_HDR_LEN + osize + COMP_CKSUM_LEN); @@ -3538,13 +3543,13 @@ static void ar9003_hw_xpa_bias_level_apply(struct ath_hw *ah, bool is2ghz) static u16 ar9003_switch_com_spdt_get(struct ath_hw *ah, bool is_2ghz) { struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; - __le16 val; + __le32 val; if (is_2ghz) val = eep->modalHeader2G.switchcomspdt; else val = eep->modalHeader5G.switchcomspdt; - return le16_to_cpu(val); + return le32_to_cpu(val); } @@ -3607,6 +3612,10 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) value = ar9003_hw_ant_ctrl_common_get(ah, is2ghz); if (AR_SREV_9462(ah)) { + if (AR_SREV_9462_10(ah)) { + value &= ~AR_SWITCH_TABLE_COM_SPDT; + value |= 0x00100000; + } REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, AR_SWITCH_TABLE_COM_AR9462_ALL, value); } else @@ -4419,8 +4428,8 @@ static void ar9003_hw_set_target_power_eeprom(struct ath_hw *ah, u16 freq, is2GHz) + ht40PowerIncForPdadc; for (i = 0; i < ar9300RateSize; i++) { - ath_dbg(common, EEPROM, "TPC[%02d] 0x%08x\n", - i, targetPowerValT2[i]); + ath_dbg(common, ATH_DBG_EEPROM, + "TPC[%02d] 0x%08x\n", i, targetPowerValT2[i]); } } @@ -4439,7 +4448,7 @@ static int ar9003_hw_cal_pier_get(struct ath_hw *ah, struct ath_common *common = ath9k_hw_common(ah); if (ichain >= AR9300_MAX_CHAINS) { - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "Invalid chain index, must be less than %d\n", AR9300_MAX_CHAINS); return -1; @@ -4447,7 +4456,7 @@ static int ar9003_hw_cal_pier_get(struct ath_hw *ah, if (mode) { /* 5GHz */ if (ipier >= AR9300_NUM_5G_CAL_PIERS) { - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "Invalid 5GHz cal pier index, must be less than %d\n", AR9300_NUM_5G_CAL_PIERS); return -1; @@ -4457,7 +4466,7 @@ static int ar9003_hw_cal_pier_get(struct ath_hw *ah, is2GHz = 0; } else { if (ipier >= AR9300_NUM_2G_CAL_PIERS) { - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "Invalid 2GHz cal pier index, must be less than %d\n", AR9300_NUM_2G_CAL_PIERS); return -1; @@ -4619,7 +4628,8 @@ static int ar9003_hw_calibration_apply(struct ath_hw *ah, int frequency) /* interpolate */ for (ichain = 0; ichain < AR9300_MAX_CHAINS; ichain++) { - ath_dbg(common, EEPROM, "ch=%d f=%d low=%d %d h=%d %d\n", + ath_dbg(common, ATH_DBG_EEPROM, + "ch=%d f=%d low=%d %d h=%d %d\n", ichain, frequency, lfrequency[ichain], lcorrection[ichain], hfrequency[ichain], hcorrection[ichain]); @@ -4674,7 +4684,7 @@ static int ar9003_hw_calibration_apply(struct ath_hw *ah, int frequency) ar9003_hw_power_control_override(ah, frequency, correction, voltage, temperature); - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "for frequency=%d, calibration correction = %d %d %d\n", frequency, correction[0], correction[1], correction[2]); @@ -4773,7 +4783,7 @@ static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah, { struct ath_common *common = ath9k_hw_common(ah); struct ar9300_eeprom *pEepData = &ah->eeprom.ar9300_eep; - u16 twiceMaxEdgePower; + u16 twiceMaxEdgePower = MAX_RATE_POWER; int i; u16 scaledPower = 0, minCtlPower; static const u16 ctlModesFor11a[] = { @@ -4860,7 +4870,7 @@ static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah, else freq = centers.ctl_center; - ath_dbg(common, REGULATORY, + ath_dbg(common, ATH_DBG_REGULATORY, "LOOP-Mode ctlMode %d < %d, isHt40CtlMode %d, EXT_ADDITIVE %d\n", ctlMode, numCtlModes, isHt40CtlMode, (pCtlMode[ctlMode] & EXT_ADDITIVE)); @@ -4874,9 +4884,8 @@ static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah, ctlNum = AR9300_NUM_CTLS_5G; } - twiceMaxEdgePower = MAX_RATE_POWER; for (i = 0; (i < ctlNum) && ctlIndex[i]; i++) { - ath_dbg(common, REGULATORY, + ath_dbg(common, ATH_DBG_REGULATORY, "LOOP-Ctlidx %d: cfgCtl 0x%2.2x pCtlMode 0x%2.2x ctlIndex 0x%2.2x chan %d\n", i, cfgCtl, pCtlMode[ctlMode], ctlIndex[i], chan->channel); @@ -4918,7 +4927,7 @@ static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah, minCtlPower = (u8)min(twiceMaxEdgePower, scaledPower); - ath_dbg(common, REGULATORY, + ath_dbg(common, ATH_DBG_REGULATORY, "SEL-Min ctlMode %d pCtlMode %d 2xMaxEdge %d sP %d minCtlPwr %d\n", ctlMode, pCtlMode[ctlMode], twiceMaxEdgePower, scaledPower, minCtlPower); @@ -5042,7 +5051,7 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah, target_power_val_t2_eep[i]) > paprd_scale_factor)) { ah->paprd_ratemask &= ~(1 << i); - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "paprd disabled for mcs %d\n", i); } } @@ -5060,8 +5069,8 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah, return; for (i = 0; i < ar9300RateSize; i++) { - ath_dbg(common, EEPROM, "TPC[%02d] 0x%08x\n", - i, targetPowerValT2[i]); + ath_dbg(common, ATH_DBG_EEPROM, + "TPC[%02d] 0x%08x\n", i, targetPowerValT2[i]); } ah->txpower_limit = regulatory->max_power_level; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h old mode 100644 new mode 100755 diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 42ad784b..979d2254 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -22,6 +22,7 @@ #include "ar9330_1p1_initvals.h" #include "ar9330_1p2_initvals.h" #include "ar9580_1p0_initvals.h" +#include "ar9462_1p0_initvals.h" #include "ar9462_2p0_initvals.h" /* General hardware code for the AR9003 hadware family */ @@ -84,11 +85,11 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) /* additional clock settings */ if (ah->is_clk_25mhz) - INIT_INI_ARRAY(&ah->iniAdditional, + INIT_INI_ARRAY(&ah->iniModesAdditional, ar9331_1p1_xtal_25M, ARRAY_SIZE(ar9331_1p1_xtal_25M), 2); else - INIT_INI_ARRAY(&ah->iniAdditional, + INIT_INI_ARRAY(&ah->iniModesAdditional, ar9331_1p1_xtal_40M, ARRAY_SIZE(ar9331_1p1_xtal_40M), 2); } else if (AR_SREV_9330_12(ah)) { @@ -137,11 +138,11 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) /* additional clock settings */ if (ah->is_clk_25mhz) - INIT_INI_ARRAY(&ah->iniAdditional, + INIT_INI_ARRAY(&ah->iniModesAdditional, ar9331_1p2_xtal_25M, ARRAY_SIZE(ar9331_1p2_xtal_25M), 2); else - INIT_INI_ARRAY(&ah->iniAdditional, + INIT_INI_ARRAY(&ah->iniModesAdditional, ar9331_1p2_xtal_40M, ARRAY_SIZE(ar9331_1p2_xtal_40M), 2); } else if (AR_SREV_9340(ah)) { @@ -191,16 +192,15 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) ARRAY_SIZE(ar9340Modes_high_ob_db_tx_gain_table_1p0), 5); - INIT_INI_ARRAY(&ah->iniModesFastClock, + INIT_INI_ARRAY(&ah->iniModesAdditional, ar9340Modes_fast_clock_1p0, ARRAY_SIZE(ar9340Modes_fast_clock_1p0), 3); - if (!ah->is_clk_25mhz) - INIT_INI_ARRAY(&ah->iniAdditional, - ar9340_1p0_radio_core_40M, - ARRAY_SIZE(ar9340_1p0_radio_core_40M), - 2); + INIT_INI_ARRAY(&ah->iniModesAdditional_40M, + ar9340_1p0_radio_core_40M, + ARRAY_SIZE(ar9340_1p0_radio_core_40M), + 2); } else if (AR_SREV_9485_11(ah)) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_PRE], NULL, 0, 0); @@ -261,6 +261,63 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) ar9485_1_1_pcie_phy_clkreq_disable_L1, ARRAY_SIZE(ar9485_1_1_pcie_phy_clkreq_disable_L1), 2); + } else if (AR_SREV_9462_10(ah)) { + INIT_INI_ARRAY(&ah->iniMac[ATH_INI_PRE], NULL, 0, 0); + INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9462_1p0_mac_core, + ARRAY_SIZE(ar9462_1p0_mac_core), 2); + INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], + ar9462_1p0_mac_postamble, + ARRAY_SIZE(ar9462_1p0_mac_postamble), + 5); + + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_PRE], NULL, 0, 0); + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], + ar9462_1p0_baseband_core, + ARRAY_SIZE(ar9462_1p0_baseband_core), + 2); + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], + ar9462_1p0_baseband_postamble, + ARRAY_SIZE(ar9462_1p0_baseband_postamble), 5); + + INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_PRE], NULL, 0, 0); + INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], + ar9462_1p0_radio_core, + ARRAY_SIZE(ar9462_1p0_radio_core), 2); + INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], + ar9462_1p0_radio_postamble, + ARRAY_SIZE(ar9462_1p0_radio_postamble), 5); + + INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], + ar9462_1p0_soc_preamble, + ARRAY_SIZE(ar9462_1p0_soc_preamble), 2); + INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_CORE], NULL, 0, 0); + INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST], + ar9462_1p0_soc_postamble, + ARRAY_SIZE(ar9462_1p0_soc_postamble), 5); + + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9462_common_rx_gain_table_1p0, + ARRAY_SIZE(ar9462_common_rx_gain_table_1p0), 2); + + /* Awake -> Sleep Setting */ + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9462_pcie_phy_clkreq_disable_L1_1p0, + ARRAY_SIZE(ar9462_pcie_phy_clkreq_disable_L1_1p0), + 2); + + /* Sleep -> Awake Setting */ + INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, + ar9462_pcie_phy_clkreq_disable_L1_1p0, + ARRAY_SIZE(ar9462_pcie_phy_clkreq_disable_L1_1p0), + 2); + + INIT_INI_ARRAY(&ah->iniModesAdditional, + ar9462_modes_fast_clock_1p0, + ARRAY_SIZE(ar9462_modes_fast_clock_1p0), 3); + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + AR9462_BB_CTX_COEFJ(1p0), + ARRAY_SIZE(AR9462_BB_CTX_COEFJ(1p0)), 2); + } else if (AR_SREV_9462_20(ah)) { INIT_INI_ARRAY(&ah->iniMac[ATH_INI_PRE], NULL, 0, 0); @@ -319,7 +376,7 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) 2); /* Fast clock modal settings */ - INIT_INI_ARRAY(&ah->iniModesFastClock, + INIT_INI_ARRAY(&ah->iniModesAdditional, ar9462_modes_fast_clock_2p0, ARRAY_SIZE(ar9462_modes_fast_clock_2p0), 3); @@ -376,7 +433,7 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) ARRAY_SIZE(ar9580_1p0_low_ob_db_tx_gain_table), 5); - INIT_INI_ARRAY(&ah->iniModesFastClock, + INIT_INI_ARRAY(&ah->iniModesAdditional, ar9580_1p0_modes_fast_clock, ARRAY_SIZE(ar9580_1p0_modes_fast_clock), 3); @@ -443,7 +500,7 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) 2); /* Fast clock modal settings */ - INIT_INI_ARRAY(&ah->iniModesFastClock, + INIT_INI_ARRAY(&ah->iniModesAdditional, ar9300Modes_fast_clock_2p2, ARRAY_SIZE(ar9300Modes_fast_clock_2p2), 3); @@ -477,6 +534,11 @@ static void ar9003_tx_gain_table_mode0(struct ath_hw *ah) ar9580_1p0_lowest_ob_db_tx_gain_table, ARRAY_SIZE(ar9580_1p0_lowest_ob_db_tx_gain_table), 5); + else if (AR_SREV_9462_10(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9462_modes_low_ob_db_tx_gain_table_1p0, + ARRAY_SIZE(ar9462_modes_low_ob_db_tx_gain_table_1p0), + 5); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9462_modes_low_ob_db_tx_gain_table_2p0, @@ -516,6 +578,11 @@ static void ar9003_tx_gain_table_mode1(struct ath_hw *ah) ar9580_1p0_high_ob_db_tx_gain_table, ARRAY_SIZE(ar9580_1p0_high_ob_db_tx_gain_table), 5); + else if (AR_SREV_9462_10(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9462_modes_high_ob_db_tx_gain_table_1p0, + ARRAY_SIZE(ar9462_modes_high_ob_db_tx_gain_table_1p0), + 5); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9462_modes_high_ob_db_tx_gain_table_2p0, @@ -642,6 +709,11 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah) ar9580_1p0_rx_gain_table, ARRAY_SIZE(ar9580_1p0_rx_gain_table), 2); + else if (AR_SREV_9462_10(ah)) + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9462_common_rx_gain_table_1p0, + ARRAY_SIZE(ar9462_common_rx_gain_table_1p0), + 2); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9462_common_rx_gain_table_2p0, @@ -676,6 +748,11 @@ static void ar9003_rx_gain_table_mode1(struct ath_hw *ah) ar9485Common_wo_xlna_rx_gain_1_1, ARRAY_SIZE(ar9485Common_wo_xlna_rx_gain_1_1), 2); + else if (AR_SREV_9462_10(ah)) + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9462_common_wo_xlna_rx_gain_table_1p0, + ARRAY_SIZE(ar9462_common_wo_xlna_rx_gain_table_1p0), + 2); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9462_common_wo_xlna_rx_gain_table_2p0, @@ -695,10 +772,14 @@ static void ar9003_rx_gain_table_mode1(struct ath_hw *ah) static void ar9003_rx_gain_table_mode2(struct ath_hw *ah) { - if (AR_SREV_9462_20(ah)) + if (AR_SREV_9462_10(ah)) + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9462_common_mixed_rx_gain_table_1p0, + ARRAY_SIZE(ar9462_common_mixed_rx_gain_table_1p0), 2); + else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, - ar9462_common_mixed_rx_gain_table_2p0, - ARRAY_SIZE(ar9462_common_mixed_rx_gain_table_2p0), 2); + ar9462_common_mixed_rx_gain_table_2p0, + ARRAY_SIZE(ar9462_common_mixed_rx_gain_table_2p0), 2); } static void ar9003_rx_gain_table_apply(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index a66a13b7..ccde784a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -16,7 +16,6 @@ #include #include "hw.h" #include "ar9003_mac.h" -#include "ar9003_mci.h" static void ar9003_hw_rx_enable(struct ath_hw *hw) { @@ -29,14 +28,11 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) struct ar9003_txc *ads = ds; int checksum = 0; u32 val, ctl12, ctl17; - u8 desc_len; - - desc_len = (AR_SREV_9462(ah) ? 0x18 : 0x17); val = (ATHEROS_VENDOR_ID << AR_DescId_S) | (1 << AR_TxRxDesc_S) | (1 << AR_CtrlStat_S) | - (i->qcu << AR_TxQcuNum_S) | desc_len; + (i->qcu << AR_TxQcuNum_S) | 0x17; checksum += val; ACCESS_ONCE(ads->info) = val; @@ -85,7 +81,6 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) ads->ctl20 = 0; ads->ctl21 = 0; ads->ctl22 = 0; - ads->ctl23 = 0; ctl17 = SM(i->keytype, AR_EncrType); if (!i->is_first) { @@ -180,23 +175,20 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) u32 isr = 0; u32 mask2 = 0; struct ath9k_hw_capabilities *pCap = &ah->caps; + u32 sync_cause = 0; struct ath_common *common = ath9k_hw_common(ah); - u32 sync_cause = 0, async_cause; - - async_cause = REG_READ(ah, AR_INTR_ASYNC_CAUSE); - if (async_cause & (AR_INTR_MAC_IRQ | AR_INTR_ASYNC_MASK_MCI)) { + if (REG_READ(ah, AR_INTR_ASYNC_CAUSE) & AR_INTR_MAC_IRQ) { if ((REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) == AR_RTC_STATUS_ON) isr = REG_READ(ah, AR_ISR); } - sync_cause = REG_READ(ah, AR_INTR_SYNC_CAUSE) & AR_INTR_SYNC_DEFAULT; *masked = 0; - if (!isr && !sync_cause && !async_cause) + if (!isr && !sync_cause) return false; if (isr) { @@ -302,9 +294,6 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) ar9003_hw_bb_watchdog_read(ah); } - if (async_cause & AR_INTR_ASYNC_MASK_MCI) - ar9003_mci_get_isr(ah, masked); - if (sync_cause) { if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) { REG_WRITE(ah, AR_RC, AR_RC_HOSTIF); @@ -313,7 +302,7 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) } if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) - ath_dbg(common, INTERRUPT, + ath_dbg(common, ATH_DBG_INTERRUPT, "AR_INTR_SYNC_LOCAL_TIMEOUT\n"); REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause); @@ -326,6 +315,7 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_status *ts) { + struct ar9003_txc *txc = (struct ar9003_txc *) ds; struct ar9003_txs *ads; u32 status; @@ -335,11 +325,15 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds, if ((status & AR_TxDone) == 0) return -EINPROGRESS; - ah->ts_tail = (ah->ts_tail + 1) % ah->ts_size; + ts->qid = MS(ads->ds_info, AR_TxQcuNum); + if (!txc || (MS(txc->info, AR_TxQcuNum) == ts->qid)) + ah->ts_tail = (ah->ts_tail + 1) % ah->ts_size; + else + return -ENOENT; if ((MS(ads->ds_info, AR_DescId) != ATHEROS_VENDOR_ID) || (MS(ads->ds_info, AR_TxRxDesc) != 1)) { - ath_dbg(ath9k_hw_common(ah), XMIT, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_XMIT, "Tx Descriptor error %x\n", ads->ds_info); memset(ads, 0, sizeof(*ads)); return -EIO; @@ -349,7 +343,6 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds, ts->ts_seqnum = MS(status, AR_SeqNum); ts->tid = MS(status, AR_TxTid); - ts->qid = MS(ads->ds_info, AR_TxQcuNum); ts->desc_id = MS(ads->status1, AR_TxDescId); ts->ts_tstamp = ads->status4; ts->ts_status = 0; @@ -436,14 +429,20 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, struct ar9003_rxs *rxsp = (struct ar9003_rxs *) buf_addr; unsigned int phyerr; - if ((rxsp->status11 & AR_RxDone) == 0) - return -EINPROGRESS; + /* TODO: byte swap on big endian for ar9300_10 */ - if (MS(rxsp->ds_info, AR_DescId) != 0x168c) - return -EINVAL; + if (!rxs) { + if ((rxsp->status11 & AR_RxDone) == 0) + return -EINPROGRESS; - if ((rxsp->ds_info & (AR_TxRxDesc | AR_CtrlStat)) != 0) - return -EINPROGRESS; + if (MS(rxsp->ds_info, AR_DescId) != 0x168c) + return -EINVAL; + + if ((rxsp->ds_info & (AR_TxRxDesc | AR_CtrlStat)) != 0) + return -EINPROGRESS; + + return 0; + } rxs->rs_status = 0; rxs->rs_flags = 0; @@ -500,11 +499,7 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, */ if (rxsp->status11 & AR_CRCErr) rxs->rs_status |= ATH9K_RXERR_CRC; - else if (rxsp->status11 & AR_DecryptCRCErr) - rxs->rs_status |= ATH9K_RXERR_DECRYPT; - else if (rxsp->status11 & AR_MichaelErr) - rxs->rs_status |= ATH9K_RXERR_MIC; - if (rxsp->status11 & AR_PHYErr) { + else if (rxsp->status11 & AR_PHYErr) { phyerr = MS(rxsp->status11, AR_PHYErrCode); /* * If we reach a point here where AR_PostDelimCRCErr is @@ -526,11 +521,14 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, rxs->rs_status |= ATH9K_RXERR_PHY; rxs->rs_phyerr = phyerr; } - }; - } - if (rxsp->status11 & AR_KeyMiss) - rxs->rs_status |= ATH9K_RXERR_KEYMISS; + } else if (rxsp->status11 & AR_DecryptCRCErr) + rxs->rs_status |= ATH9K_RXERR_DECRYPT; + else if (rxsp->status11 & AR_MichaelErr) + rxs->rs_status |= ATH9K_RXERR_MIC; + if (rxsp->status11 & AR_KeyMiss) + rxs->rs_status |= ATH9K_RXERR_KEYMISS; + } return 0; } @@ -543,7 +541,7 @@ void ath9k_hw_reset_txstatus_ring(struct ath_hw *ah) memset((void *) ah->ts_ring, 0, ah->ts_size * sizeof(struct ar9003_txs)); - ath_dbg(ath9k_hw_common(ah), XMIT, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_XMIT, "TS Start 0x%x End 0x%x Virt %p, Size %d\n", ah->ts_paddr_start, ah->ts_paddr_end, ah->ts_ring, ah->ts_size); @@ -554,7 +552,7 @@ void ath9k_hw_reset_txstatus_ring(struct ath_hw *ah) void ath9k_hw_setup_statusring(struct ath_hw *ah, void *ts_start, u32 ts_paddr_start, - u16 size) + u8 size) { ah->ts_paddr_start = ts_paddr_start; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.h b/drivers/net/wireless/ath/ath9k/ar9003_mac.h index cbf60b09..c5044938 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.h @@ -92,8 +92,7 @@ struct ar9003_txc { u32 ctl20; /* DMA control 20 */ u32 ctl21; /* DMA control 21 */ u32 ctl22; /* DMA control 22 */ - u32 ctl23; /* DMA control 23 */ - u32 pad[8]; /* pad to cache line (128 bytes/32 dwords) */ + u32 pad[9]; /* pad to cache line (128 bytes/32 dwords) */ } __packed __aligned(4); struct ar9003_txs { @@ -119,5 +118,5 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, void ath9k_hw_reset_txstatus_ring(struct ath_hw *ah); void ath9k_hw_setup_statusring(struct ath_hw *ah, void *ts_start, u32 ts_paddr_start, - u16 size); + u8 size); #endif diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c index 3d400e8d..a4450cba 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c @@ -54,7 +54,7 @@ void ar9003_paprd_enable(struct ath_hw *ah, bool val) if (val) { ah->paprd_table_write_done = true; - ath9k_hw_apply_txpower(ah, chan, false); + ath9k_hw_apply_txpower(ah, chan); } REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL0_B0, @@ -119,8 +119,8 @@ static int ar9003_get_training_power_5g(struct ath_hw *ah) break; default: delta = 0; - ath_dbg(common, CALIBRATE, "Invalid tx-chainmask: %u\n", - ah->txchainmask); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Invalid tx-chainmask: %u\n", ah->txchainmask); } power += delta; @@ -148,12 +148,13 @@ static int ar9003_paprd_setup_single_table(struct ath_hw *ah) else training_power = ar9003_get_training_power_5g(ah); - ath_dbg(common, CALIBRATE, "Training power: %d, Target power: %d\n", + ath_dbg(common, ATH_DBG_CALIBRATE, + "Training power: %d, Target power: %d\n", training_power, ah->paprd_target_power); if (training_power < 0) { - ath_dbg(common, CALIBRATE, - "PAPRD target power delta out of range\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, + "PAPRD target power delta out of range"); return -ERANGE; } ah->paprd_training_power = training_power; @@ -310,8 +311,8 @@ static unsigned int ar9003_get_desired_gain(struct ath_hw *ah, int chain, reg_cl_gain = AR_PHY_CL_TAB_2; break; default: - ath_dbg(ath9k_hw_common(ah), CALIBRATE, - "Invalid chainmask: %d\n", chain); + ath_dbg(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, + "Invalid chainmask: %d\n", chain); break; } @@ -849,7 +850,7 @@ bool ar9003_paprd_is_done(struct ath_hw *ah) agc2_pwr = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_STAT1, AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_AGC2_PWR); - ath_dbg(ath9k_hw_common(ah), CALIBRATE, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, "AGC2_PWR = 0x%x training done = 0x%x\n", agc2_pwr, paprd_done); /* diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 8783500a..aa2eb463 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -46,7 +46,7 @@ static const int m2ThreshExt_off = 127; * @chan: * * This is the function to change channel on single-chip devices, that is - * for AR9300 family of chipsets. + * all devices after ar9280. * * This function takes the channel value in MHz and sets * hardware channel value. Assumes writes have been enabled to analog bus. @@ -373,7 +373,7 @@ static void ar9003_hw_spur_ofdm_work(struct ath_hw *ah, else spur_subchannel_sd = 0; - spur_freq_sd = ((freq_offset + 10) << 9) / 11; + spur_freq_sd = (freq_offset << 9) / 11; } else { if (REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, @@ -382,7 +382,7 @@ static void ar9003_hw_spur_ofdm_work(struct ath_hw *ah, else spur_subchannel_sd = 1; - spur_freq_sd = ((freq_offset - 10) << 9) / 11; + spur_freq_sd = (freq_offset << 9) / 11; } @@ -666,22 +666,23 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, * different modal values. */ if (IS_CHAN_A_FAST_CLOCK(ah, chan)) - REG_WRITE_ARRAY(&ah->iniModesFastClock, + REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex, regWrites); - REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites); + if (AR_SREV_9330(ah)) + REG_WRITE_ARRAY(&ah->iniModesAdditional, 1, regWrites); + + if (AR_SREV_9340(ah) && !ah->is_clk_25mhz) + REG_WRITE_ARRAY(&ah->iniModesAdditional_40M, 1, regWrites); if (AR_SREV_9462(ah)) ar9003_hw_prog_ini(ah, &ah->ini_BTCOEX_MAX_TXPWR, 1); - if (chan->channel == 2484) - ar9003_hw_prog_ini(ah, &ah->ini_japan2484, 1); - ah->modes_index = modesIndex; ar9003_hw_override_ini(ah); ar9003_hw_set_channel_regs(ah, chan); ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask); - ath9k_hw_apply_txpower(ah, chan, false); + ath9k_hw_apply_txpower(ah, chan); if (AR_SREV_9462(ah)) { if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0, @@ -868,7 +869,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); if (!on != aniState->ofdmWeakSigDetectOff) { - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "** ch %d: ofdm weak signal: %s=>%s\n", chan->channel, !aniState->ofdmWeakSigDetectOff ? @@ -886,7 +887,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, u32 level = param; if (level >= ARRAY_SIZE(firstep_table)) { - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "ATH9K_ANI_FIRSTEP_LEVEL: level out of range (%u > %zu)\n", level, ARRAY_SIZE(firstep_table)); return false; @@ -923,7 +924,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW, value2); if (level != aniState->firstepLevel) { - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "** ch %d: level %d=>%d[def:%d] firstep[level]=%d ini=%d\n", chan->channel, aniState->firstepLevel, @@ -931,7 +932,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, ATH9K_ANI_FIRSTEP_LVL_NEW, value, aniState->iniDef.firstep); - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "** ch %d: level %d=>%d[def:%d] firstep_low[level]=%d ini=%d\n", chan->channel, aniState->firstepLevel, @@ -951,7 +952,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, u32 level = param; if (level >= ARRAY_SIZE(cycpwrThr1_table)) { - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "ATH9K_ANI_SPUR_IMMUNITY_LEVEL: level out of range (%u > %zu)\n", level, ARRAY_SIZE(cycpwrThr1_table)); return false; @@ -987,7 +988,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, AR_PHY_EXT_CYCPWR_THR1, value2); if (level != aniState->spurImmunityLevel) { - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "** ch %d: level %d=>%d[def:%d] cycpwrThr1[level]=%d ini=%d\n", chan->channel, aniState->spurImmunityLevel, @@ -995,7 +996,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, ATH9K_ANI_SPUR_IMMUNE_LVL_NEW, value, aniState->iniDef.cycpwrThr1); - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "** ch %d: level %d=>%d[def:%d] cycpwrThr1Ext[level]=%d ini=%d\n", chan->channel, aniState->spurImmunityLevel, @@ -1026,7 +1027,8 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL, AR_PHY_MRC_CCK_MUX_REG, is_on); if (!is_on != aniState->mrcCCKOff) { - ath_dbg(common, ANI, "** ch %d: MRC CCK: %s=>%s\n", + ath_dbg(common, ATH_DBG_ANI, + "** ch %d: MRC CCK: %s=>%s\n", chan->channel, !aniState->mrcCCKOff ? "on" : "off", is_on ? "on" : "off"); @@ -1041,11 +1043,11 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, case ATH9K_ANI_PRESENT: break; default: - ath_dbg(common, ANI, "invalid cmd %u\n", cmd); + ath_dbg(common, ATH_DBG_ANI, "invalid cmd %u\n", cmd); return false; } - ath_dbg(common, ANI, + ath_dbg(common, ATH_DBG_ANI, "ANI parameters: SI=%d, ofdmWS=%s FS=%d MRCcck=%s listenTime=%d ofdmErrs=%d cckErrs=%d\n", aniState->spurImmunityLevel, !aniState->ofdmWeakSigDetectOff ? "on" : "off", @@ -1089,20 +1091,13 @@ static void ar9003_hw_set_nf_limits(struct ath_hw *ah) { ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9300_2GHZ; ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9300_2GHZ; - ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9300_2GHZ; + if (AR_SREV_9330(ah)) + ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9330_2GHZ; + else + ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9300_2GHZ; ah->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_9300_5GHZ; ah->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_9300_5GHZ; ah->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_9300_5GHZ; - - if (AR_SREV_9330(ah)) - ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9330_2GHZ; - - if (AR_SREV_9462(ah)) { - ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9462_2GHZ; - ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9462_2GHZ; - ah->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_9462_5GHZ; - ah->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_9462_5GHZ; - } } /* @@ -1121,7 +1116,8 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) aniState = &ah->curchan->ani; iniDef = &aniState->iniDef; - ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n", + ath_dbg(common, ATH_DBG_ANI, + "ver %d.%d opmode %u chan %d Mhz/0x%x\n", ah->hw_version.macVersion, ah->hw_version.macRev, ah->opmode, @@ -1310,9 +1306,13 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah, * different modal values. */ if (IS_CHAN_A_FAST_CLOCK(ah, chan)) - REG_WRITE_ARRAY(&ah->iniModesFastClock, modesIndex, regWrites); + REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex, regWrites); + + if (AR_SREV_9330(ah)) + REG_WRITE_ARRAY(&ah->iniModesAdditional, 1, regWrites); - REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites); + if (AR_SREV_9340(ah) && !ah->is_clk_25mhz) + REG_WRITE_ARRAY(&ah->iniModesAdditional_40M, 1, regWrites); ah->modes_index = modesIndex; *ini_reloaded = true; @@ -1379,7 +1379,7 @@ void ar9003_hw_bb_watchdog_config(struct ath_hw *ah) ~(AR_PHY_WATCHDOG_NON_IDLE_ENABLE | AR_PHY_WATCHDOG_IDLE_ENABLE)); - ath_dbg(common, RESET, "Disabled BB Watchdog\n"); + ath_dbg(common, ATH_DBG_RESET, "Disabled BB Watchdog\n"); return; } @@ -1415,7 +1415,8 @@ void ar9003_hw_bb_watchdog_config(struct ath_hw *ah) AR_PHY_WATCHDOG_IDLE_MASK | (AR_PHY_WATCHDOG_NON_IDLE_MASK & (idle_count << 2))); - ath_dbg(common, RESET, "Enabled BB Watchdog timeout (%u ms)\n", + ath_dbg(common, ATH_DBG_RESET, + "Enabled BB Watchdog timeout (%u ms)\n", idle_tmo_ms); } @@ -1444,9 +1445,9 @@ void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah) return; status = ah->bb_watchdog_last_status; - ath_dbg(common, RESET, + ath_dbg(common, ATH_DBG_RESET, "\n==== BB update: BB status=0x%08x ====\n", status); - ath_dbg(common, RESET, + ath_dbg(common, ATH_DBG_RESET, "** BB state: wd=%u det=%u rdar=%u rOFDM=%d rCCK=%u tOFDM=%u tCCK=%u agc=%u src=%u **\n", MS(status, AR_PHY_WATCHDOG_INFO), MS(status, AR_PHY_WATCHDOG_DET_HANG), @@ -1458,19 +1459,22 @@ void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah) MS(status, AR_PHY_WATCHDOG_AGC_SM), MS(status, AR_PHY_WATCHDOG_SRCH_SM)); - ath_dbg(common, RESET, "** BB WD cntl: cntl1=0x%08x cntl2=0x%08x **\n", + ath_dbg(common, ATH_DBG_RESET, + "** BB WD cntl: cntl1=0x%08x cntl2=0x%08x **\n", REG_READ(ah, AR_PHY_WATCHDOG_CTL_1), REG_READ(ah, AR_PHY_WATCHDOG_CTL_2)); - ath_dbg(common, RESET, "** BB mode: BB_gen_controls=0x%08x **\n", + ath_dbg(common, ATH_DBG_RESET, + "** BB mode: BB_gen_controls=0x%08x **\n", REG_READ(ah, AR_PHY_GEN_CTRL)); #define PCT(_field) (common->cc_survey._field * 100 / common->cc_survey.cycles) if (common->cc_survey.cycles) - ath_dbg(common, RESET, + ath_dbg(common, ATH_DBG_RESET, "** BB busy times: rx_clear=%d%%, rx_frame=%d%%, tx_frame=%d%% **\n", PCT(rx_busy), PCT(rx_frame), PCT(tx_frame)); - ath_dbg(common, RESET, "==== BB update: done ====\n\n"); + ath_dbg(common, ATH_DBG_RESET, + "==== BB update: done ====\n\n"); } EXPORT_SYMBOL(ar9003_hw_bb_watchdog_dbg_info); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index 6402b9e4..55931054 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -325,18 +325,13 @@ #define AR_PHY_RX_OCGAIN (AR_AGC_BASE + 0x200) -#define AR_PHY_CCA_NOM_VAL_9300_2GHZ -110 -#define AR_PHY_CCA_NOM_VAL_9300_5GHZ -115 -#define AR_PHY_CCA_MIN_GOOD_VAL_9300_2GHZ -125 -#define AR_PHY_CCA_MIN_GOOD_VAL_9300_5GHZ -125 +#define AR_PHY_CCA_NOM_VAL_9300_2GHZ (AR_SREV_9462(ah) ? -127 : -110) +#define AR_PHY_CCA_NOM_VAL_9300_5GHZ (AR_SREV_9462(ah) ? -127 : -115) +#define AR_PHY_CCA_MIN_GOOD_VAL_9300_2GHZ (AR_SREV_9462(ah) ? -127 : -125) +#define AR_PHY_CCA_MIN_GOOD_VAL_9300_5GHZ (AR_SREV_9462(ah) ? -127 : -125) #define AR_PHY_CCA_MAX_GOOD_VAL_9300_2GHZ -95 #define AR_PHY_CCA_MAX_GOOD_VAL_9300_5GHZ -100 -#define AR_PHY_CCA_NOM_VAL_9462_2GHZ -127 -#define AR_PHY_CCA_MIN_GOOD_VAL_9462_2GHZ -127 -#define AR_PHY_CCA_NOM_VAL_9462_5GHZ -127 -#define AR_PHY_CCA_MIN_GOOD_VAL_9462_5GHZ -127 - #define AR_PHY_CCA_NOM_VAL_9330_2GHZ -118 #define AR9300_EXT_LNA_CTL_GPIO_AR9485 9 @@ -497,8 +492,6 @@ #define AR_PHY_TEST_CTL_TSTADC_EN_S 8 #define AR_PHY_TEST_CTL_RX_OBS_SEL 0x3C00 #define AR_PHY_TEST_CTL_RX_OBS_SEL_S 10 -#define AR_PHY_TEST_CTL_DEBUGPORT_SEL 0xe0000000 -#define AR_PHY_TEST_CTL_DEBUGPORT_SEL_S 29 #define AR_PHY_TSTDAC (AR_SM_BASE + 0x168) @@ -619,14 +612,16 @@ #define AR_PHY_AIC_CTRL_1_B0 (AR_SM_BASE + 0x4b4) #define AR_PHY_AIC_CTRL_2_B0 (AR_SM_BASE + 0x4b8) #define AR_PHY_AIC_CTRL_3_B0 (AR_SM_BASE + 0x4bc) -#define AR_PHY_AIC_STAT_0_B0 (AR_SM_BASE + 0x4c4)) -#define AR_PHY_AIC_STAT_1_B0 (AR_SM_BASE + 0x4c8)) +#define AR_PHY_AIC_STAT_0_B0 (AR_SM_BASE + (AR_SREV_9462_10(ah) ? \ + 0x4c0 : 0x4c4)) +#define AR_PHY_AIC_STAT_1_B0 (AR_SM_BASE + (AR_SREV_9462_10(ah) ? \ + 0x4c4 : 0x4c8)) #define AR_PHY_AIC_CTRL_4_B0 (AR_SM_BASE + 0x4c0) #define AR_PHY_AIC_STAT_2_B0 (AR_SM_BASE + 0x4cc) #define AR_PHY_65NM_CH0_SYNTH4 0x1608c -#define AR_PHY_SYNTH4_LONG_SHIFT_SELECT (AR_SREV_9462(ah) ? 0x00000001 : 0x00000002) -#define AR_PHY_SYNTH4_LONG_SHIFT_SELECT_S (AR_SREV_9462(ah) ? 0 : 1) +#define AR_PHY_SYNTH4_LONG_SHIFT_SELECT 0x00000002 +#define AR_PHY_SYNTH4_LONG_SHIFT_SELECT_S 1 #define AR_PHY_65NM_CH0_SYNTH7 0x16098 #define AR_PHY_65NM_CH0_BIAS1 0x160c0 #define AR_PHY_65NM_CH0_BIAS2 0x160c4 @@ -1008,7 +1003,6 @@ /* GLB Registers */ #define AR_GLB_BASE 0x20000 -#define AR_GLB_GPIO_CONTROL (AR_GLB_BASE) #define AR_PHY_GLB_CONTROL (AR_GLB_BASE + 0x44) #define AR_GLB_SCRATCH(_ah) (AR_GLB_BASE + \ (AR_SREV_9462_20(_ah) ? 0x4c : 0x50)) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_rtt.c b/drivers/net/wireless/ath/ath9k/ar9003_rtt.c index 458bedf0..48803ee9 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_rtt.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_rtt.c @@ -16,7 +16,6 @@ #include "hw.h" #include "ar9003_phy.h" -#include "ar9003_rtt.h" #define RTT_RESTORE_TIMEOUT 1000 #define RTT_ACCESS_TIMEOUT 100 diff --git a/drivers/net/wireless/ath/ath9k/ar9462_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_1p0_initvals.h new file mode 100644 index 00000000..5c55ae38 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ar9462_1p0_initvals.h @@ -0,0 +1,1833 @@ +/* + * Copyright (c) 2010 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef INITVALS_9462_1P0_H +#define INITVALS_9462_1P0_H + +/* AR9462 1.0 */ + +static const u32 ar9462_1p0_mac_core[][2] = { + /* Addr allmodes */ + {0x00000008, 0x00000000}, + {0x00000030, 0x00060085}, + {0x00000034, 0x00000005}, + {0x00000040, 0x00000000}, + {0x00000044, 0x00000000}, + {0x00000048, 0x00000008}, + {0x0000004c, 0x00000010}, + {0x00000050, 0x00000000}, + {0x00001040, 0x002ffc0f}, + {0x00001044, 0x002ffc0f}, + {0x00001048, 0x002ffc0f}, + {0x0000104c, 0x002ffc0f}, + {0x00001050, 0x002ffc0f}, + {0x00001054, 0x002ffc0f}, + {0x00001058, 0x002ffc0f}, + {0x0000105c, 0x002ffc0f}, + {0x00001060, 0x002ffc0f}, + {0x00001064, 0x002ffc0f}, + {0x000010f0, 0x00000100}, + {0x00001270, 0x00000000}, + {0x000012b0, 0x00000000}, + {0x000012f0, 0x00000000}, + {0x0000143c, 0x00000000}, + {0x0000147c, 0x00000000}, + {0x00001810, 0x0f000003}, + {0x00008000, 0x00000000}, + {0x00008004, 0x00000000}, + {0x00008008, 0x00000000}, + {0x0000800c, 0x00000000}, + {0x00008018, 0x00000000}, + {0x00008020, 0x00000000}, + {0x00008038, 0x00000000}, + {0x0000803c, 0x00080000}, + {0x00008040, 0x00000000}, + {0x00008044, 0x00000000}, + {0x00008048, 0x00000000}, + {0x0000804c, 0xffffffff}, + {0x00008050, 0xffffffff}, + {0x00008054, 0x00000000}, + {0x00008058, 0x00000000}, + {0x0000805c, 0x000fc78f}, + {0x00008060, 0x0000000f}, + {0x00008064, 0x00000000}, + {0x00008070, 0x00000310}, + {0x00008074, 0x00000020}, + {0x00008078, 0x00000000}, + {0x0000809c, 0x0000000f}, + {0x000080a0, 0x00000000}, + {0x000080a4, 0x02ff0000}, + {0x000080a8, 0x0e070605}, + {0x000080ac, 0x0000000d}, + {0x000080b0, 0x00000000}, + {0x000080b4, 0x00000000}, + {0x000080b8, 0x00000000}, + {0x000080bc, 0x00000000}, + {0x000080c0, 0x2a800000}, + {0x000080c4, 0x06900168}, + {0x000080c8, 0x13881c20}, + {0x000080cc, 0x01f40000}, + {0x000080d0, 0x00252500}, + {0x000080d4, 0x00a00005}, + {0x000080d8, 0x00400002}, + {0x000080dc, 0x00000000}, + {0x000080e0, 0xffffffff}, + {0x000080e4, 0x0000ffff}, + {0x000080e8, 0x3f3f3f3f}, + {0x000080ec, 0x00000000}, + {0x000080f0, 0x00000000}, + {0x000080f4, 0x00000000}, + {0x000080fc, 0x00020000}, + {0x00008100, 0x00000000}, + {0x00008108, 0x00000052}, + {0x0000810c, 0x00000000}, + {0x00008110, 0x00000000}, + {0x00008114, 0x000007ff}, + {0x00008118, 0x000000aa}, + {0x0000811c, 0x00003210}, + {0x00008124, 0x00000000}, + {0x00008128, 0x00000000}, + {0x0000812c, 0x00000000}, + {0x00008130, 0x00000000}, + {0x00008134, 0x00000000}, + {0x00008138, 0x00000000}, + {0x0000813c, 0x0000ffff}, + {0x00008144, 0xffffffff}, + {0x00008168, 0x00000000}, + {0x0000816c, 0x00000000}, + {0x00008170, 0x18486e00}, + {0x00008174, 0x33332210}, + {0x00008178, 0x00000000}, + {0x0000817c, 0x00020000}, + {0x000081c4, 0x33332210}, + {0x000081c8, 0x00000000}, + {0x000081cc, 0x00000000}, + {0x000081d4, 0x00000000}, + {0x000081ec, 0x00000000}, + {0x000081f0, 0x00000000}, + {0x000081f4, 0x00000000}, + {0x000081f8, 0x00000000}, + {0x000081fc, 0x00000000}, + {0x00008240, 0x00100000}, + {0x00008244, 0x0010f400}, + {0x00008248, 0x00000800}, + {0x0000824c, 0x0001e800}, + {0x00008250, 0x00000000}, + {0x00008254, 0x00000000}, + {0x00008258, 0x00000000}, + {0x0000825c, 0x40000000}, + {0x00008260, 0x00080922}, + {0x00008264, 0x99c00010}, + {0x00008268, 0xffffffff}, + {0x0000826c, 0x0000ffff}, + {0x00008270, 0x00000000}, + {0x00008274, 0x40000000}, + {0x00008278, 0x003e4180}, + {0x0000827c, 0x00000004}, + {0x00008284, 0x0000002c}, + {0x00008288, 0x0000002c}, + {0x0000828c, 0x000000ff}, + {0x00008294, 0x00000000}, + {0x00008298, 0x00000000}, + {0x0000829c, 0x00000000}, + {0x00008300, 0x00000140}, + {0x00008314, 0x00000000}, + {0x0000831c, 0x0000010d}, + {0x00008328, 0x00000000}, + {0x0000832c, 0x0000001f}, + {0x00008330, 0x00000302}, + {0x00008334, 0x00000700}, + {0x00008338, 0xffff0000}, + {0x0000833c, 0x02400000}, + {0x00008340, 0x000107ff}, + {0x00008344, 0xaa48105b}, + {0x00008348, 0x008f0000}, + {0x0000835c, 0x00000000}, + {0x00008360, 0xffffffff}, + {0x00008364, 0xffffffff}, + {0x00008368, 0x00000000}, + {0x00008370, 0x00000000}, + {0x00008374, 0x000000ff}, + {0x00008378, 0x00000000}, + {0x0000837c, 0x00000000}, + {0x00008380, 0xffffffff}, + {0x00008384, 0xffffffff}, + {0x00008390, 0xffffffff}, + {0x00008394, 0xffffffff}, + {0x00008398, 0x00000000}, + {0x0000839c, 0x00000000}, + {0x000083a4, 0x0000fa14}, + {0x000083a8, 0x000f0c00}, + {0x000083ac, 0x33332210}, + {0x000083b0, 0x33332210}, + {0x000083b4, 0x33332210}, + {0x000083b8, 0x33332210}, + {0x000083bc, 0x00000000}, + {0x000083c0, 0x00000000}, + {0x000083c4, 0x00000000}, + {0x000083c8, 0x00000000}, + {0x000083cc, 0x00000200}, + {0x000083d0, 0x000301ff}, +}; + +static const u32 ar9462_1p0_baseband_core_txfir_coeff_japan_2484[][2] = { + /* Addr allmodes */ + {0x0000a398, 0x00000000}, + {0x0000a39c, 0x6f7f0301}, + {0x0000a3a0, 0xca9228ee}, +}; + +static const u32 ar9462_1p0_sys3ant[][2] = { + /* Addr allmodes */ + {0x00063280, 0x00040807}, + {0x00063284, 0x104ccccc}, +}; + +static const u32 ar9462_pcie_phy_clkreq_enable_L1_1p0[][2] = { + /* Addr allmodes */ + {0x00018c00, 0x10053e5e}, + {0x00018c04, 0x000801d8}, + {0x00018c08, 0x0000580c}, +}; + +static const u32 ar9462_1p0_mac_core_emulation[][2] = { + /* Addr allmodes */ + {0x00000030, 0x00060085}, + {0x00000044, 0x00000008}, + {0x0000805c, 0xffffc7ff}, + {0x00008344, 0xaa4a105b}, +}; + +static const u32 ar9462_common_rx_gain_table_ar9280_2p0_1p0[][2] = { + /* Addr allmodes */ + {0x0000a000, 0x02000101}, + {0x0000a004, 0x02000102}, + {0x0000a008, 0x02000103}, + {0x0000a00c, 0x02000104}, + {0x0000a010, 0x02000200}, + {0x0000a014, 0x02000201}, + {0x0000a018, 0x02000202}, + {0x0000a01c, 0x02000203}, + {0x0000a020, 0x02000204}, + {0x0000a024, 0x02000205}, + {0x0000a028, 0x02000208}, + {0x0000a02c, 0x02000302}, + {0x0000a030, 0x02000303}, + {0x0000a034, 0x02000304}, + {0x0000a038, 0x02000400}, + {0x0000a03c, 0x02010300}, + {0x0000a040, 0x02010301}, + {0x0000a044, 0x02010302}, + {0x0000a048, 0x02000500}, + {0x0000a04c, 0x02010400}, + {0x0000a050, 0x02020300}, + {0x0000a054, 0x02020301}, + {0x0000a058, 0x02020302}, + {0x0000a05c, 0x02020303}, + {0x0000a060, 0x02020400}, + {0x0000a064, 0x02030300}, + {0x0000a068, 0x02030301}, + {0x0000a06c, 0x02030302}, + {0x0000a070, 0x02030303}, + {0x0000a074, 0x02030400}, + {0x0000a078, 0x02040300}, + {0x0000a07c, 0x02040301}, + {0x0000a080, 0x02040302}, + {0x0000a084, 0x02040303}, + {0x0000a088, 0x02030500}, + {0x0000a08c, 0x02040400}, + {0x0000a090, 0x02050203}, + {0x0000a094, 0x02050204}, + {0x0000a098, 0x02050205}, + {0x0000a09c, 0x02040500}, + {0x0000a0a0, 0x02050301}, + {0x0000a0a4, 0x02050302}, + {0x0000a0a8, 0x02050303}, + {0x0000a0ac, 0x02050400}, + {0x0000a0b0, 0x02050401}, + {0x0000a0b4, 0x02050402}, + {0x0000a0b8, 0x02050403}, + {0x0000a0bc, 0x02050500}, + {0x0000a0c0, 0x02050501}, + {0x0000a0c4, 0x02050502}, + {0x0000a0c8, 0x02050503}, + {0x0000a0cc, 0x02050504}, + {0x0000a0d0, 0x02050600}, + {0x0000a0d4, 0x02050601}, + {0x0000a0d8, 0x02050602}, + {0x0000a0dc, 0x02050603}, + {0x0000a0e0, 0x02050604}, + {0x0000a0e4, 0x02050700}, + {0x0000a0e8, 0x02050701}, + {0x0000a0ec, 0x02050702}, + {0x0000a0f0, 0x02050703}, + {0x0000a0f4, 0x02050704}, + {0x0000a0f8, 0x02050705}, + {0x0000a0fc, 0x02050708}, + {0x0000a100, 0x02050709}, + {0x0000a104, 0x0205070a}, + {0x0000a108, 0x0205070b}, + {0x0000a10c, 0x0205070c}, + {0x0000a110, 0x0205070d}, + {0x0000a114, 0x02050710}, + {0x0000a118, 0x02050711}, + {0x0000a11c, 0x02050712}, + {0x0000a120, 0x02050713}, + {0x0000a124, 0x02050714}, + {0x0000a128, 0x02050715}, + {0x0000a12c, 0x02050730}, + {0x0000a130, 0x02050731}, + {0x0000a134, 0x02050732}, + {0x0000a138, 0x02050733}, + {0x0000a13c, 0x02050734}, + {0x0000a140, 0x02050735}, + {0x0000a144, 0x02050750}, + {0x0000a148, 0x02050751}, + {0x0000a14c, 0x02050752}, + {0x0000a150, 0x02050753}, + {0x0000a154, 0x02050754}, + {0x0000a158, 0x02050755}, + {0x0000a15c, 0x02050770}, + {0x0000a160, 0x02050771}, + {0x0000a164, 0x02050772}, + {0x0000a168, 0x02050773}, + {0x0000a16c, 0x02050774}, + {0x0000a170, 0x02050775}, + {0x0000a174, 0x00000776}, + {0x0000a178, 0x00000776}, + {0x0000a17c, 0x00000776}, + {0x0000a180, 0x00000776}, + {0x0000a184, 0x00000776}, + {0x0000a188, 0x00000776}, + {0x0000a18c, 0x00000776}, + {0x0000a190, 0x00000776}, + {0x0000a194, 0x00000776}, + {0x0000a198, 0x00000776}, + {0x0000a19c, 0x00000776}, + {0x0000a1a0, 0x00000776}, + {0x0000a1a4, 0x00000776}, + {0x0000a1a8, 0x00000776}, + {0x0000a1ac, 0x00000776}, + {0x0000a1b0, 0x00000776}, + {0x0000a1b4, 0x00000776}, + {0x0000a1b8, 0x00000776}, + {0x0000a1bc, 0x00000776}, + {0x0000a1c0, 0x00000776}, + {0x0000a1c4, 0x00000776}, + {0x0000a1c8, 0x00000776}, + {0x0000a1cc, 0x00000776}, + {0x0000a1d0, 0x00000776}, + {0x0000a1d4, 0x00000776}, + {0x0000a1d8, 0x00000776}, + {0x0000a1dc, 0x00000776}, + {0x0000a1e0, 0x00000776}, + {0x0000a1e4, 0x00000776}, + {0x0000a1e8, 0x00000776}, + {0x0000a1ec, 0x00000776}, + {0x0000a1f0, 0x00000776}, + {0x0000a1f4, 0x00000776}, + {0x0000a1f8, 0x00000776}, + {0x0000a1fc, 0x00000776}, + {0x0000b000, 0x02000101}, + {0x0000b004, 0x02000102}, + {0x0000b008, 0x02000103}, + {0x0000b00c, 0x02000104}, + {0x0000b010, 0x02000200}, + {0x0000b014, 0x02000201}, + {0x0000b018, 0x02000202}, + {0x0000b01c, 0x02000203}, + {0x0000b020, 0x02000204}, + {0x0000b024, 0x02000205}, + {0x0000b028, 0x02000208}, + {0x0000b02c, 0x02000302}, + {0x0000b030, 0x02000303}, + {0x0000b034, 0x02000304}, + {0x0000b038, 0x02000400}, + {0x0000b03c, 0x02010300}, + {0x0000b040, 0x02010301}, + {0x0000b044, 0x02010302}, + {0x0000b048, 0x02000500}, + {0x0000b04c, 0x02010400}, + {0x0000b050, 0x02020300}, + {0x0000b054, 0x02020301}, + {0x0000b058, 0x02020302}, + {0x0000b05c, 0x02020303}, + {0x0000b060, 0x02020400}, + {0x0000b064, 0x02030300}, + {0x0000b068, 0x02030301}, + {0x0000b06c, 0x02030302}, + {0x0000b070, 0x02030303}, + {0x0000b074, 0x02030400}, + {0x0000b078, 0x02040300}, + {0x0000b07c, 0x02040301}, + {0x0000b080, 0x02040302}, + {0x0000b084, 0x02040303}, + {0x0000b088, 0x02030500}, + {0x0000b08c, 0x02040400}, + {0x0000b090, 0x02050203}, + {0x0000b094, 0x02050204}, + {0x0000b098, 0x02050205}, + {0x0000b09c, 0x02040500}, + {0x0000b0a0, 0x02050301}, + {0x0000b0a4, 0x02050302}, + {0x0000b0a8, 0x02050303}, + {0x0000b0ac, 0x02050400}, + {0x0000b0b0, 0x02050401}, + {0x0000b0b4, 0x02050402}, + {0x0000b0b8, 0x02050403}, + {0x0000b0bc, 0x02050500}, + {0x0000b0c0, 0x02050501}, + {0x0000b0c4, 0x02050502}, + {0x0000b0c8, 0x02050503}, + {0x0000b0cc, 0x02050504}, + {0x0000b0d0, 0x02050600}, + {0x0000b0d4, 0x02050601}, + {0x0000b0d8, 0x02050602}, + {0x0000b0dc, 0x02050603}, + {0x0000b0e0, 0x02050604}, + {0x0000b0e4, 0x02050700}, + {0x0000b0e8, 0x02050701}, + {0x0000b0ec, 0x02050702}, + {0x0000b0f0, 0x02050703}, + {0x0000b0f4, 0x02050704}, + {0x0000b0f8, 0x02050705}, + {0x0000b0fc, 0x02050708}, + {0x0000b100, 0x02050709}, + {0x0000b104, 0x0205070a}, + {0x0000b108, 0x0205070b}, + {0x0000b10c, 0x0205070c}, + {0x0000b110, 0x0205070d}, + {0x0000b114, 0x02050710}, + {0x0000b118, 0x02050711}, + {0x0000b11c, 0x02050712}, + {0x0000b120, 0x02050713}, + {0x0000b124, 0x02050714}, + {0x0000b128, 0x02050715}, + {0x0000b12c, 0x02050730}, + {0x0000b130, 0x02050731}, + {0x0000b134, 0x02050732}, + {0x0000b138, 0x02050733}, + {0x0000b13c, 0x02050734}, + {0x0000b140, 0x02050735}, + {0x0000b144, 0x02050750}, + {0x0000b148, 0x02050751}, + {0x0000b14c, 0x02050752}, + {0x0000b150, 0x02050753}, + {0x0000b154, 0x02050754}, + {0x0000b158, 0x02050755}, + {0x0000b15c, 0x02050770}, + {0x0000b160, 0x02050771}, + {0x0000b164, 0x02050772}, + {0x0000b168, 0x02050773}, + {0x0000b16c, 0x02050774}, + {0x0000b170, 0x02050775}, + {0x0000b174, 0x00000776}, + {0x0000b178, 0x00000776}, + {0x0000b17c, 0x00000776}, + {0x0000b180, 0x00000776}, + {0x0000b184, 0x00000776}, + {0x0000b188, 0x00000776}, + {0x0000b18c, 0x00000776}, + {0x0000b190, 0x00000776}, + {0x0000b194, 0x00000776}, + {0x0000b198, 0x00000776}, + {0x0000b19c, 0x00000776}, + {0x0000b1a0, 0x00000776}, + {0x0000b1a4, 0x00000776}, + {0x0000b1a8, 0x00000776}, + {0x0000b1ac, 0x00000776}, + {0x0000b1b0, 0x00000776}, + {0x0000b1b4, 0x00000776}, + {0x0000b1b8, 0x00000776}, + {0x0000b1bc, 0x00000776}, + {0x0000b1c0, 0x00000776}, + {0x0000b1c4, 0x00000776}, + {0x0000b1c8, 0x00000776}, + {0x0000b1cc, 0x00000776}, + {0x0000b1d0, 0x00000776}, + {0x0000b1d4, 0x00000776}, + {0x0000b1d8, 0x00000776}, + {0x0000b1dc, 0x00000776}, + {0x0000b1e0, 0x00000776}, + {0x0000b1e4, 0x00000776}, + {0x0000b1e8, 0x00000776}, + {0x0000b1ec, 0x00000776}, + {0x0000b1f0, 0x00000776}, + {0x0000b1f4, 0x00000776}, + {0x0000b1f8, 0x00000776}, + {0x0000b1fc, 0x00000776}, +}; + +static const u32 ar9200_ar9280_2p0_radio_core_1p0[][2] = { + /* Addr allmodes */ + {0x00007800, 0x00040000}, + {0x00007804, 0xdb005012}, + {0x00007808, 0x04924914}, + {0x0000780c, 0x21084210}, + {0x00007810, 0x6d801300}, + {0x00007814, 0x0019beff}, + {0x00007818, 0x07e41000}, + {0x0000781c, 0x00392000}, + {0x00007820, 0x92592480}, + {0x00007824, 0x00040000}, + {0x00007828, 0xdb005012}, + {0x0000782c, 0x04924914}, + {0x00007830, 0x21084210}, + {0x00007834, 0x6d801300}, + {0x00007838, 0x0019beff}, + {0x0000783c, 0x07e40000}, + {0x00007840, 0x00392000}, + {0x00007844, 0x92592480}, + {0x00007848, 0x00100000}, + {0x0000784c, 0x773f0567}, + {0x00007850, 0x54214514}, + {0x00007854, 0x12035828}, + {0x00007858, 0x92592692}, + {0x0000785c, 0x00000000}, + {0x00007860, 0x56400000}, + {0x00007864, 0x0a8e370e}, + {0x00007868, 0xc0102850}, + {0x0000786c, 0x812d4000}, + {0x00007870, 0x807ec400}, + {0x00007874, 0x001b6db0}, + {0x00007878, 0x00376b63}, + {0x0000787c, 0x06db6db6}, + {0x00007880, 0x006d8000}, + {0x00007884, 0xffeffffe}, + {0x00007888, 0xffeffffe}, + {0x0000788c, 0x00010000}, + {0x00007890, 0x02060aeb}, + {0x00007894, 0x5a108000}, +}; + +static const u32 ar9462_1p0_baseband_postamble_emulation[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00009e3c, 0xcf946221, 0xcf946221, 0xcf946221, 0xcf946221}, + {0x00009e44, 0x005c0000, 0x005c0000, 0x005c0000, 0x005c0000}, + {0x0000a258, 0x02020200, 0x02020200, 0x02020200, 0x02020200}, + {0x0000a25c, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, + {0x0000a28c, 0x00011111, 0x00011111, 0x00011111, 0x00011111}, + {0x0000a2c4, 0x00148d18, 0x00148d18, 0x00148d20, 0x00148d20}, + {0x0000a2d8, 0xf999a800, 0xf999a800, 0xf999a80c, 0xf999a80c}, + {0x0000a50c, 0x0000c00a, 0x0000c00a, 0x0000c00a, 0x0000c00a}, + {0x0000a538, 0x00038e8c, 0x00038e8c, 0x00038e8c, 0x00038e8c}, + {0x0000a53c, 0x0003cecc, 0x0003cecc, 0x0003cecc, 0x0003cecc}, + {0x0000a540, 0x00040ed4, 0x00040ed4, 0x00040ed4, 0x00040ed4}, + {0x0000a544, 0x00044edc, 0x00044edc, 0x00044edc, 0x00044edc}, + {0x0000a548, 0x00048ede, 0x00048ede, 0x00048ede, 0x00048ede}, + {0x0000a54c, 0x0004cf1e, 0x0004cf1e, 0x0004cf1e, 0x0004cf1e}, + {0x0000a550, 0x00050f5e, 0x00050f5e, 0x00050f5e, 0x00050f5e}, + {0x0000a554, 0x00054f9e, 0x00054f9e, 0x00054f9e, 0x00054f9e}, + {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, +}; + +static const u32 ar9462_pcie_phy_pll_on_clkreq_disable_L1_1p0[][2] = { + /* Addr allmodes */ + {0x00018c00, 0x10012e5e}, + {0x00018c04, 0x000801d8}, + {0x00018c08, 0x0000580c}, +}; + +static const u32 ar9462_common_rx_gain_table_1p0[][2] = { + /* Addr allmodes */ + {0x0000a000, 0x00010000}, + {0x0000a004, 0x00030002}, + {0x0000a008, 0x00050004}, + {0x0000a00c, 0x00810080}, + {0x0000a010, 0x00830082}, + {0x0000a014, 0x01810180}, + {0x0000a018, 0x01830182}, + {0x0000a01c, 0x01850184}, + {0x0000a020, 0x01890188}, + {0x0000a024, 0x018b018a}, + {0x0000a028, 0x018d018c}, + {0x0000a02c, 0x01910190}, + {0x0000a030, 0x01930192}, + {0x0000a034, 0x01950194}, + {0x0000a038, 0x038a0196}, + {0x0000a03c, 0x038c038b}, + {0x0000a040, 0x0390038d}, + {0x0000a044, 0x03920391}, + {0x0000a048, 0x03940393}, + {0x0000a04c, 0x03960395}, + {0x0000a050, 0x00000000}, + {0x0000a054, 0x00000000}, + {0x0000a058, 0x00000000}, + {0x0000a05c, 0x00000000}, + {0x0000a060, 0x00000000}, + {0x0000a064, 0x00000000}, + {0x0000a068, 0x00000000}, + {0x0000a06c, 0x00000000}, + {0x0000a070, 0x00000000}, + {0x0000a074, 0x00000000}, + {0x0000a078, 0x00000000}, + {0x0000a07c, 0x00000000}, + {0x0000a080, 0x22222229}, + {0x0000a084, 0x1d1d1d1d}, + {0x0000a088, 0x1d1d1d1d}, + {0x0000a08c, 0x1d1d1d1d}, + {0x0000a090, 0x171d1d1d}, + {0x0000a094, 0x11111717}, + {0x0000a098, 0x00030311}, + {0x0000a09c, 0x00000000}, + {0x0000a0a0, 0x00000000}, + {0x0000a0a4, 0x00000000}, + {0x0000a0a8, 0x00000000}, + {0x0000a0ac, 0x00000000}, + {0x0000a0b0, 0x00000000}, + {0x0000a0b4, 0x00000000}, + {0x0000a0b8, 0x00000000}, + {0x0000a0bc, 0x00000000}, + {0x0000a0c0, 0x001f0000}, + {0x0000a0c4, 0x01000101}, + {0x0000a0c8, 0x011e011f}, + {0x0000a0cc, 0x011c011d}, + {0x0000a0d0, 0x02030204}, + {0x0000a0d4, 0x02010202}, + {0x0000a0d8, 0x021f0200}, + {0x0000a0dc, 0x0302021e}, + {0x0000a0e0, 0x03000301}, + {0x0000a0e4, 0x031e031f}, + {0x0000a0e8, 0x0402031d}, + {0x0000a0ec, 0x04000401}, + {0x0000a0f0, 0x041e041f}, + {0x0000a0f4, 0x0502041d}, + {0x0000a0f8, 0x05000501}, + {0x0000a0fc, 0x051e051f}, + {0x0000a100, 0x06010602}, + {0x0000a104, 0x061f0600}, + {0x0000a108, 0x061d061e}, + {0x0000a10c, 0x07020703}, + {0x0000a110, 0x07000701}, + {0x0000a114, 0x00000000}, + {0x0000a118, 0x00000000}, + {0x0000a11c, 0x00000000}, + {0x0000a120, 0x00000000}, + {0x0000a124, 0x00000000}, + {0x0000a128, 0x00000000}, + {0x0000a12c, 0x00000000}, + {0x0000a130, 0x00000000}, + {0x0000a134, 0x00000000}, + {0x0000a138, 0x00000000}, + {0x0000a13c, 0x00000000}, + {0x0000a140, 0x001f0000}, + {0x0000a144, 0x01000101}, + {0x0000a148, 0x011e011f}, + {0x0000a14c, 0x011c011d}, + {0x0000a150, 0x02030204}, + {0x0000a154, 0x02010202}, + {0x0000a158, 0x021f0200}, + {0x0000a15c, 0x0302021e}, + {0x0000a160, 0x03000301}, + {0x0000a164, 0x031e031f}, + {0x0000a168, 0x0402031d}, + {0x0000a16c, 0x04000401}, + {0x0000a170, 0x041e041f}, + {0x0000a174, 0x0502041d}, + {0x0000a178, 0x05000501}, + {0x0000a17c, 0x051e051f}, + {0x0000a180, 0x06010602}, + {0x0000a184, 0x061f0600}, + {0x0000a188, 0x061d061e}, + {0x0000a18c, 0x07020703}, + {0x0000a190, 0x07000701}, + {0x0000a194, 0x00000000}, + {0x0000a198, 0x00000000}, + {0x0000a19c, 0x00000000}, + {0x0000a1a0, 0x00000000}, + {0x0000a1a4, 0x00000000}, + {0x0000a1a8, 0x00000000}, + {0x0000a1ac, 0x00000000}, + {0x0000a1b0, 0x00000000}, + {0x0000a1b4, 0x00000000}, + {0x0000a1b8, 0x00000000}, + {0x0000a1bc, 0x00000000}, + {0x0000a1c0, 0x00000000}, + {0x0000a1c4, 0x00000000}, + {0x0000a1c8, 0x00000000}, + {0x0000a1cc, 0x00000000}, + {0x0000a1d0, 0x00000000}, + {0x0000a1d4, 0x00000000}, + {0x0000a1d8, 0x00000000}, + {0x0000a1dc, 0x00000000}, + {0x0000a1e0, 0x00000000}, + {0x0000a1e4, 0x00000000}, + {0x0000a1e8, 0x00000000}, + {0x0000a1ec, 0x00000000}, + {0x0000a1f0, 0x00000396}, + {0x0000a1f4, 0x00000396}, + {0x0000a1f8, 0x00000396}, + {0x0000a1fc, 0x00000196}, + {0x0000b000, 0x00010000}, + {0x0000b004, 0x00030002}, + {0x0000b008, 0x00050004}, + {0x0000b00c, 0x00810080}, + {0x0000b010, 0x00830082}, + {0x0000b014, 0x01810180}, + {0x0000b018, 0x01830182}, + {0x0000b01c, 0x01850184}, + {0x0000b020, 0x02810280}, + {0x0000b024, 0x02830282}, + {0x0000b028, 0x02850284}, + {0x0000b02c, 0x02890288}, + {0x0000b030, 0x028b028a}, + {0x0000b034, 0x0388028c}, + {0x0000b038, 0x038a0389}, + {0x0000b03c, 0x038c038b}, + {0x0000b040, 0x0390038d}, + {0x0000b044, 0x03920391}, + {0x0000b048, 0x03940393}, + {0x0000b04c, 0x03960395}, + {0x0000b050, 0x00000000}, + {0x0000b054, 0x00000000}, + {0x0000b058, 0x00000000}, + {0x0000b05c, 0x00000000}, + {0x0000b060, 0x00000000}, + {0x0000b064, 0x00000000}, + {0x0000b068, 0x00000000}, + {0x0000b06c, 0x00000000}, + {0x0000b070, 0x00000000}, + {0x0000b074, 0x00000000}, + {0x0000b078, 0x00000000}, + {0x0000b07c, 0x00000000}, + {0x0000b080, 0x2a2d2f32}, + {0x0000b084, 0x21232328}, + {0x0000b088, 0x19191c1e}, + {0x0000b08c, 0x12141417}, + {0x0000b090, 0x07070e0e}, + {0x0000b094, 0x03030305}, + {0x0000b098, 0x00000003}, + {0x0000b09c, 0x00000000}, + {0x0000b0a0, 0x00000000}, + {0x0000b0a4, 0x00000000}, + {0x0000b0a8, 0x00000000}, + {0x0000b0ac, 0x00000000}, + {0x0000b0b0, 0x00000000}, + {0x0000b0b4, 0x00000000}, + {0x0000b0b8, 0x00000000}, + {0x0000b0bc, 0x00000000}, + {0x0000b0c0, 0x003f0020}, + {0x0000b0c4, 0x00400041}, + {0x0000b0c8, 0x0140005f}, + {0x0000b0cc, 0x0160015f}, + {0x0000b0d0, 0x017e017f}, + {0x0000b0d4, 0x02410242}, + {0x0000b0d8, 0x025f0240}, + {0x0000b0dc, 0x027f0260}, + {0x0000b0e0, 0x0341027e}, + {0x0000b0e4, 0x035f0340}, + {0x0000b0e8, 0x037f0360}, + {0x0000b0ec, 0x04400441}, + {0x0000b0f0, 0x0460045f}, + {0x0000b0f4, 0x0541047f}, + {0x0000b0f8, 0x055f0540}, + {0x0000b0fc, 0x057f0560}, + {0x0000b100, 0x06400641}, + {0x0000b104, 0x0660065f}, + {0x0000b108, 0x067e067f}, + {0x0000b10c, 0x07410742}, + {0x0000b110, 0x075f0740}, + {0x0000b114, 0x077f0760}, + {0x0000b118, 0x07800781}, + {0x0000b11c, 0x07a0079f}, + {0x0000b120, 0x07c107bf}, + {0x0000b124, 0x000007c0}, + {0x0000b128, 0x00000000}, + {0x0000b12c, 0x00000000}, + {0x0000b130, 0x00000000}, + {0x0000b134, 0x00000000}, + {0x0000b138, 0x00000000}, + {0x0000b13c, 0x00000000}, + {0x0000b140, 0x003f0020}, + {0x0000b144, 0x00400041}, + {0x0000b148, 0x0140005f}, + {0x0000b14c, 0x0160015f}, + {0x0000b150, 0x017e017f}, + {0x0000b154, 0x02410242}, + {0x0000b158, 0x025f0240}, + {0x0000b15c, 0x027f0260}, + {0x0000b160, 0x0341027e}, + {0x0000b164, 0x035f0340}, + {0x0000b168, 0x037f0360}, + {0x0000b16c, 0x04400441}, + {0x0000b170, 0x0460045f}, + {0x0000b174, 0x0541047f}, + {0x0000b178, 0x055f0540}, + {0x0000b17c, 0x057f0560}, + {0x0000b180, 0x06400641}, + {0x0000b184, 0x0660065f}, + {0x0000b188, 0x067e067f}, + {0x0000b18c, 0x07410742}, + {0x0000b190, 0x075f0740}, + {0x0000b194, 0x077f0760}, + {0x0000b198, 0x07800781}, + {0x0000b19c, 0x07a0079f}, + {0x0000b1a0, 0x07c107bf}, + {0x0000b1a4, 0x000007c0}, + {0x0000b1a8, 0x00000000}, + {0x0000b1ac, 0x00000000}, + {0x0000b1b0, 0x00000000}, + {0x0000b1b4, 0x00000000}, + {0x0000b1b8, 0x00000000}, + {0x0000b1bc, 0x00000000}, + {0x0000b1c0, 0x00000000}, + {0x0000b1c4, 0x00000000}, + {0x0000b1c8, 0x00000000}, + {0x0000b1cc, 0x00000000}, + {0x0000b1d0, 0x00000000}, + {0x0000b1d4, 0x00000000}, + {0x0000b1d8, 0x00000000}, + {0x0000b1dc, 0x00000000}, + {0x0000b1e0, 0x00000000}, + {0x0000b1e4, 0x00000000}, + {0x0000b1e8, 0x00000000}, + {0x0000b1ec, 0x00000000}, + {0x0000b1f0, 0x00000396}, + {0x0000b1f4, 0x00000396}, + {0x0000b1f8, 0x00000396}, + {0x0000b1fc, 0x00000196}, +}; + +static const u32 ar9462_modes_high_ob_db_tx_gain_table_1p0[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, + {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, + {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002}, + {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004}, + {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x18022622, 0x18022622, 0x11000400, 0x11000400}, + {0x0000a518, 0x1b022822, 0x1b022822, 0x15000402, 0x15000402}, + {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404}, + {0x0000a520, 0x22022c41, 0x22022c41, 0x1b000603, 0x1b000603}, + {0x0000a524, 0x28023042, 0x28023042, 0x1f000a02, 0x1f000a02}, + {0x0000a528, 0x2c023044, 0x2c023044, 0x23000a04, 0x23000a04}, + {0x0000a52c, 0x2f023644, 0x2f023644, 0x26000a20, 0x26000a20}, + {0x0000a530, 0x34025643, 0x34025643, 0x2a000e20, 0x2a000e20}, + {0x0000a534, 0x38025a44, 0x38025a44, 0x2e000e22, 0x2e000e22}, + {0x0000a538, 0x3b025e45, 0x3b025e45, 0x31000e24, 0x31000e24}, + {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x34001640, 0x34001640}, + {0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660}, + {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861}, + {0x0000a548, 0x53025eb2, 0x53025eb2, 0x3e001a81, 0x3e001a81}, + {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83}, + {0x0000a550, 0x5f025ef6, 0x5f025ef6, 0x44001c84, 0x44001c84}, + {0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3}, + {0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5}, + {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9}, + {0x0000a560, 0x70049f56, 0x70049f56, 0x54001ceb, 0x54001ceb}, + {0x0000a564, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a568, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a56c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a570, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a574, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a578, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a57c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, + {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, + {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, + {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, + {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x00016044, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4}, + {0x00016048, 0x8db49060, 0x8db49060, 0x8db49060, 0x8db49060}, + {0x00016444, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4}, + {0x00016448, 0x8db49000, 0x8db49000, 0x8db49000, 0x8db49000}, +}; + +static const u32 ar9462_common_wo_xlna_rx_gain_table_1p0[][2] = { + /* Addr allmodes */ + {0x0000a000, 0x00010000}, + {0x0000a004, 0x00030002}, + {0x0000a008, 0x00050004}, + {0x0000a00c, 0x00810080}, + {0x0000a010, 0x00830082}, + {0x0000a014, 0x01810180}, + {0x0000a018, 0x01830182}, + {0x0000a01c, 0x01850184}, + {0x0000a020, 0x01890188}, + {0x0000a024, 0x018b018a}, + {0x0000a028, 0x018d018c}, + {0x0000a02c, 0x03820190}, + {0x0000a030, 0x03840383}, + {0x0000a034, 0x03880385}, + {0x0000a038, 0x038a0389}, + {0x0000a03c, 0x038c038b}, + {0x0000a040, 0x0390038d}, + {0x0000a044, 0x03920391}, + {0x0000a048, 0x03940393}, + {0x0000a04c, 0x03960395}, + {0x0000a050, 0x00000000}, + {0x0000a054, 0x00000000}, + {0x0000a058, 0x00000000}, + {0x0000a05c, 0x00000000}, + {0x0000a060, 0x00000000}, + {0x0000a064, 0x00000000}, + {0x0000a068, 0x00000000}, + {0x0000a06c, 0x00000000}, + {0x0000a070, 0x00000000}, + {0x0000a074, 0x00000000}, + {0x0000a078, 0x00000000}, + {0x0000a07c, 0x00000000}, + {0x0000a080, 0x29292929}, + {0x0000a084, 0x29292929}, + {0x0000a088, 0x29292929}, + {0x0000a08c, 0x29292929}, + {0x0000a090, 0x22292929}, + {0x0000a094, 0x1d1d2222}, + {0x0000a098, 0x0c111117}, + {0x0000a09c, 0x00030303}, + {0x0000a0a0, 0x00000000}, + {0x0000a0a4, 0x00000000}, + {0x0000a0a8, 0x00000000}, + {0x0000a0ac, 0x00000000}, + {0x0000a0b0, 0x00000000}, + {0x0000a0b4, 0x00000000}, + {0x0000a0b8, 0x00000000}, + {0x0000a0bc, 0x00000000}, + {0x0000a0c0, 0x001f0000}, + {0x0000a0c4, 0x01000101}, + {0x0000a0c8, 0x011e011f}, + {0x0000a0cc, 0x011c011d}, + {0x0000a0d0, 0x02030204}, + {0x0000a0d4, 0x02010202}, + {0x0000a0d8, 0x021f0200}, + {0x0000a0dc, 0x0302021e}, + {0x0000a0e0, 0x03000301}, + {0x0000a0e4, 0x031e031f}, + {0x0000a0e8, 0x0402031d}, + {0x0000a0ec, 0x04000401}, + {0x0000a0f0, 0x041e041f}, + {0x0000a0f4, 0x0502041d}, + {0x0000a0f8, 0x05000501}, + {0x0000a0fc, 0x051e051f}, + {0x0000a100, 0x06010602}, + {0x0000a104, 0x061f0600}, + {0x0000a108, 0x061d061e}, + {0x0000a10c, 0x07020703}, + {0x0000a110, 0x07000701}, + {0x0000a114, 0x00000000}, + {0x0000a118, 0x00000000}, + {0x0000a11c, 0x00000000}, + {0x0000a120, 0x00000000}, + {0x0000a124, 0x00000000}, + {0x0000a128, 0x00000000}, + {0x0000a12c, 0x00000000}, + {0x0000a130, 0x00000000}, + {0x0000a134, 0x00000000}, + {0x0000a138, 0x00000000}, + {0x0000a13c, 0x00000000}, + {0x0000a140, 0x001f0000}, + {0x0000a144, 0x01000101}, + {0x0000a148, 0x011e011f}, + {0x0000a14c, 0x011c011d}, + {0x0000a150, 0x02030204}, + {0x0000a154, 0x02010202}, + {0x0000a158, 0x021f0200}, + {0x0000a15c, 0x0302021e}, + {0x0000a160, 0x03000301}, + {0x0000a164, 0x031e031f}, + {0x0000a168, 0x0402031d}, + {0x0000a16c, 0x04000401}, + {0x0000a170, 0x041e041f}, + {0x0000a174, 0x0502041d}, + {0x0000a178, 0x05000501}, + {0x0000a17c, 0x051e051f}, + {0x0000a180, 0x06010602}, + {0x0000a184, 0x061f0600}, + {0x0000a188, 0x061d061e}, + {0x0000a18c, 0x07020703}, + {0x0000a190, 0x07000701}, + {0x0000a194, 0x00000000}, + {0x0000a198, 0x00000000}, + {0x0000a19c, 0x00000000}, + {0x0000a1a0, 0x00000000}, + {0x0000a1a4, 0x00000000}, + {0x0000a1a8, 0x00000000}, + {0x0000a1ac, 0x00000000}, + {0x0000a1b0, 0x00000000}, + {0x0000a1b4, 0x00000000}, + {0x0000a1b8, 0x00000000}, + {0x0000a1bc, 0x00000000}, + {0x0000a1c0, 0x00000000}, + {0x0000a1c4, 0x00000000}, + {0x0000a1c8, 0x00000000}, + {0x0000a1cc, 0x00000000}, + {0x0000a1d0, 0x00000000}, + {0x0000a1d4, 0x00000000}, + {0x0000a1d8, 0x00000000}, + {0x0000a1dc, 0x00000000}, + {0x0000a1e0, 0x00000000}, + {0x0000a1e4, 0x00000000}, + {0x0000a1e8, 0x00000000}, + {0x0000a1ec, 0x00000000}, + {0x0000a1f0, 0x00000396}, + {0x0000a1f4, 0x00000396}, + {0x0000a1f8, 0x00000396}, + {0x0000a1fc, 0x00000196}, + {0x0000b000, 0x00010000}, + {0x0000b004, 0x00030002}, + {0x0000b008, 0x00050004}, + {0x0000b00c, 0x00810080}, + {0x0000b010, 0x00830082}, + {0x0000b014, 0x01810180}, + {0x0000b018, 0x01830182}, + {0x0000b01c, 0x01850184}, + {0x0000b020, 0x02810280}, + {0x0000b024, 0x02830282}, + {0x0000b028, 0x02850284}, + {0x0000b02c, 0x02890288}, + {0x0000b030, 0x028b028a}, + {0x0000b034, 0x0388028c}, + {0x0000b038, 0x038a0389}, + {0x0000b03c, 0x038c038b}, + {0x0000b040, 0x0390038d}, + {0x0000b044, 0x03920391}, + {0x0000b048, 0x03940393}, + {0x0000b04c, 0x03960395}, + {0x0000b050, 0x00000000}, + {0x0000b054, 0x00000000}, + {0x0000b058, 0x00000000}, + {0x0000b05c, 0x00000000}, + {0x0000b060, 0x00000000}, + {0x0000b064, 0x00000000}, + {0x0000b068, 0x00000000}, + {0x0000b06c, 0x00000000}, + {0x0000b070, 0x00000000}, + {0x0000b074, 0x00000000}, + {0x0000b078, 0x00000000}, + {0x0000b07c, 0x00000000}, + {0x0000b080, 0x32323232}, + {0x0000b084, 0x2f2f3232}, + {0x0000b088, 0x23282a2d}, + {0x0000b08c, 0x1c1e2123}, + {0x0000b090, 0x14171919}, + {0x0000b094, 0x0e0e1214}, + {0x0000b098, 0x03050707}, + {0x0000b09c, 0x00030303}, + {0x0000b0a0, 0x00000000}, + {0x0000b0a4, 0x00000000}, + {0x0000b0a8, 0x00000000}, + {0x0000b0ac, 0x00000000}, + {0x0000b0b0, 0x00000000}, + {0x0000b0b4, 0x00000000}, + {0x0000b0b8, 0x00000000}, + {0x0000b0bc, 0x00000000}, + {0x0000b0c0, 0x003f0020}, + {0x0000b0c4, 0x00400041}, + {0x0000b0c8, 0x0140005f}, + {0x0000b0cc, 0x0160015f}, + {0x0000b0d0, 0x017e017f}, + {0x0000b0d4, 0x02410242}, + {0x0000b0d8, 0x025f0240}, + {0x0000b0dc, 0x027f0260}, + {0x0000b0e0, 0x0341027e}, + {0x0000b0e4, 0x035f0340}, + {0x0000b0e8, 0x037f0360}, + {0x0000b0ec, 0x04400441}, + {0x0000b0f0, 0x0460045f}, + {0x0000b0f4, 0x0541047f}, + {0x0000b0f8, 0x055f0540}, + {0x0000b0fc, 0x057f0560}, + {0x0000b100, 0x06400641}, + {0x0000b104, 0x0660065f}, + {0x0000b108, 0x067e067f}, + {0x0000b10c, 0x07410742}, + {0x0000b110, 0x075f0740}, + {0x0000b114, 0x077f0760}, + {0x0000b118, 0x07800781}, + {0x0000b11c, 0x07a0079f}, + {0x0000b120, 0x07c107bf}, + {0x0000b124, 0x000007c0}, + {0x0000b128, 0x00000000}, + {0x0000b12c, 0x00000000}, + {0x0000b130, 0x00000000}, + {0x0000b134, 0x00000000}, + {0x0000b138, 0x00000000}, + {0x0000b13c, 0x00000000}, + {0x0000b140, 0x003f0020}, + {0x0000b144, 0x00400041}, + {0x0000b148, 0x0140005f}, + {0x0000b14c, 0x0160015f}, + {0x0000b150, 0x017e017f}, + {0x0000b154, 0x02410242}, + {0x0000b158, 0x025f0240}, + {0x0000b15c, 0x027f0260}, + {0x0000b160, 0x0341027e}, + {0x0000b164, 0x035f0340}, + {0x0000b168, 0x037f0360}, + {0x0000b16c, 0x04400441}, + {0x0000b170, 0x0460045f}, + {0x0000b174, 0x0541047f}, + {0x0000b178, 0x055f0540}, + {0x0000b17c, 0x057f0560}, + {0x0000b180, 0x06400641}, + {0x0000b184, 0x0660065f}, + {0x0000b188, 0x067e067f}, + {0x0000b18c, 0x07410742}, + {0x0000b190, 0x075f0740}, + {0x0000b194, 0x077f0760}, + {0x0000b198, 0x07800781}, + {0x0000b19c, 0x07a0079f}, + {0x0000b1a0, 0x07c107bf}, + {0x0000b1a4, 0x000007c0}, + {0x0000b1a8, 0x00000000}, + {0x0000b1ac, 0x00000000}, + {0x0000b1b0, 0x00000000}, + {0x0000b1b4, 0x00000000}, + {0x0000b1b8, 0x00000000}, + {0x0000b1bc, 0x00000000}, + {0x0000b1c0, 0x00000000}, + {0x0000b1c4, 0x00000000}, + {0x0000b1c8, 0x00000000}, + {0x0000b1cc, 0x00000000}, + {0x0000b1d0, 0x00000000}, + {0x0000b1d4, 0x00000000}, + {0x0000b1d8, 0x00000000}, + {0x0000b1dc, 0x00000000}, + {0x0000b1e0, 0x00000000}, + {0x0000b1e4, 0x00000000}, + {0x0000b1e8, 0x00000000}, + {0x0000b1ec, 0x00000000}, + {0x0000b1f0, 0x00000396}, + {0x0000b1f4, 0x00000396}, + {0x0000b1f8, 0x00000396}, + {0x0000b1fc, 0x00000196}, +}; + +static const u32 ar9462_1p0_mac_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, + {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, + {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, + {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, + {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, + {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, + {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, + {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, +}; + +static const u32 ar9462_1p0_mac_postamble_emulation[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00008014, 0x10f810f8, 0x10f810f8, 0x10f810f8, 0x10f810f8}, + {0x0000801c, 0x0e8d8017, 0x0e8d8017, 0x0e8d8017, 0x0e8d8017}, +}; + +static const u32 ar9462_1p0_tx_gain_table_baseband_postamble_emulation[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a410, 0x000000d5, 0x000000d5, 0x000000d5, 0x000000d5}, + {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a504, 0x00004002, 0x00004002, 0x00004002, 0x00004002}, + {0x0000a508, 0x00008004, 0x00008004, 0x00008004, 0x00008004}, + {0x0000a510, 0x0001000c, 0x0001000c, 0x0001000c, 0x0001000c}, + {0x0000a514, 0x0001420b, 0x0001420b, 0x0001420b, 0x0001420b}, + {0x0000a518, 0x0001824a, 0x0001824a, 0x0001824a, 0x0001824a}, + {0x0000a51c, 0x0001c44a, 0x0001c44a, 0x0001c44a, 0x0001c44a}, + {0x0000a520, 0x0002064a, 0x0002064a, 0x0002064a, 0x0002064a}, + {0x0000a524, 0x0002484a, 0x0002484a, 0x0002484a, 0x0002484a}, + {0x0000a528, 0x00028a4a, 0x00028a4a, 0x00028a4a, 0x00028a4a}, + {0x0000a52c, 0x0002cc4a, 0x0002cc4a, 0x0002cc4a, 0x0002cc4a}, + {0x0000a530, 0x00030e4a, 0x00030e4a, 0x00030e4a, 0x00030e4a}, + {0x0000a534, 0x00034e8a, 0x00034e8a, 0x00034e8a, 0x00034e8a}, +}; + +static const u32 ar9462_1p0_radio_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0001609c, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524}, + {0x000160ac, 0xa4646c08, 0xa4646c08, 0x24646c08, 0x24646c08}, + {0x000160b0, 0x01d67f70, 0x01d67f70, 0x01d67f70, 0x01d67f70}, + {0x0001610c, 0x48000000, 0x40000000, 0x40000000, 0x40000000}, + {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, + {0x0001650c, 0x48000000, 0x40000000, 0x40000000, 0x40000000}, + {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, +}; + +static const u32 ar9462_1p0_soc_postamble_emulation[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00007010, 0x00001133, 0x00001133, 0x00001133, 0x00001133}, +}; + +static const u32 ar9462_1p0_baseband_core[][2] = { + /* Addr allmodes */ + {0x00009800, 0xafe68e30}, + {0x00009804, 0xfd14e000}, + {0x00009808, 0x9c0a9f6b}, + {0x0000980c, 0x04900000}, + {0x00009814, 0x9280c00a}, + {0x00009818, 0x00000000}, + {0x0000981c, 0x00020028}, + {0x00009834, 0x6400a290}, + {0x00009838, 0x0108ecff}, + {0x0000983c, 0x0d000600}, + {0x00009880, 0x201fff00}, + {0x00009884, 0x00001042}, + {0x000098a4, 0x00200400}, + {0x000098b0, 0x32840bbe}, + {0x000098d0, 0x004b6a8e}, + {0x000098d4, 0x00000820}, + {0x000098dc, 0x00000000}, + {0x000098e4, 0x01ffffff}, + {0x000098e8, 0x01ffffff}, + {0x000098ec, 0x01ffffff}, + {0x000098f0, 0x00000000}, + {0x000098f4, 0x00000000}, + {0x00009c04, 0xff55ff55}, + {0x00009c08, 0x0320ff55}, + {0x00009c0c, 0x00000000}, + {0x00009c10, 0x00000000}, + {0x00009c14, 0x00046384}, + {0x00009c18, 0x05b6b440}, + {0x00009c1c, 0x00b6b440}, + {0x00009d00, 0xc080a333}, + {0x00009d04, 0x40206c10}, + {0x00009d08, 0x009c4060}, + {0x00009d0c, 0x9883800a}, + {0x00009d10, 0x01834061}, + {0x00009d14, 0x00c0040b}, + {0x00009d18, 0x00000000}, + {0x00009e08, 0x0038230c}, + {0x00009e24, 0x990bb514}, + {0x00009e28, 0x0c6f0000}, + {0x00009e30, 0x06336f77}, + {0x00009e34, 0x6af6532f}, + {0x00009e38, 0x0cc80c00}, + {0x00009e40, 0x0d261820}, + {0x00009e4c, 0x00001004}, + {0x00009e50, 0x00ff03f1}, + {0x00009e54, 0x64c355c7}, + {0x00009e58, 0xfd897735}, + {0x00009e5c, 0xe9198724}, + {0x00009fc0, 0x803e4788}, + {0x00009fc4, 0x0001efb5}, + {0x00009fcc, 0x40000014}, + {0x00009fd0, 0x01193b93}, + {0x0000a20c, 0x00000000}, + {0x0000a220, 0x00000000}, + {0x0000a224, 0x00000000}, + {0x0000a228, 0x10002310}, + {0x0000a23c, 0x00000000}, + {0x0000a244, 0x0c000000}, + {0x0000a2a0, 0x00000001}, + {0x0000a2c0, 0x00000001}, + {0x0000a2c8, 0x00000000}, + {0x0000a2cc, 0x18c43433}, + {0x0000a2d4, 0x00000000}, + {0x0000a2ec, 0x00000000}, + {0x0000a2f0, 0x00000000}, + {0x0000a2f4, 0x00000000}, + {0x0000a2f8, 0x00000000}, + {0x0000a344, 0x00000000}, + {0x0000a34c, 0x00000000}, + {0x0000a350, 0x0000a000}, + {0x0000a364, 0x00000000}, + {0x0000a370, 0x00000000}, + {0x0000a390, 0x00000001}, + {0x0000a394, 0x00000444}, + {0x0000a398, 0x001f0e0f}, + {0x0000a39c, 0x0075393f}, + {0x0000a3a0, 0xb79f6427}, + {0x0000a3a4, 0x00000000}, + {0x0000a3a8, 0xaaaaaaaa}, + {0x0000a3ac, 0x3c466478}, + {0x0000a3c0, 0x20202020}, + {0x0000a3c4, 0x22222220}, + {0x0000a3c8, 0x20200020}, + {0x0000a3cc, 0x20202020}, + {0x0000a3d0, 0x20202020}, + {0x0000a3d4, 0x20202020}, + {0x0000a3d8, 0x20202020}, + {0x0000a3dc, 0x20202020}, + {0x0000a3e0, 0x20202020}, + {0x0000a3e4, 0x20202020}, + {0x0000a3e8, 0x20202020}, + {0x0000a3ec, 0x20202020}, + {0x0000a3f0, 0x00000000}, + {0x0000a3f4, 0x00000006}, + {0x0000a3f8, 0x0c9bd380}, + {0x0000a3fc, 0x000f0f01}, + {0x0000a400, 0x8fa91f01}, + {0x0000a404, 0x00000000}, + {0x0000a408, 0x0e79e5c6}, + {0x0000a40c, 0x00820820}, + {0x0000a414, 0x1ce739ce}, + {0x0000a418, 0x2d001dce}, + {0x0000a41c, 0x1ce739ce}, + {0x0000a420, 0x000001ce}, + {0x0000a424, 0x1ce739ce}, + {0x0000a428, 0x000001ce}, + {0x0000a42c, 0x1ce739ce}, + {0x0000a430, 0x1ce739ce}, + {0x0000a434, 0x00000000}, + {0x0000a438, 0x00001801}, + {0x0000a43c, 0x00100000}, + {0x0000a440, 0x00000000}, + {0x0000a444, 0x00000000}, + {0x0000a448, 0x05000080}, + {0x0000a44c, 0x00000001}, + {0x0000a450, 0x00010000}, + {0x0000a458, 0x00000000}, + {0x0000a644, 0xbfad9d74}, + {0x0000a648, 0x0048060a}, + {0x0000a64c, 0x00003c37}, + {0x0000a670, 0x03020100}, + {0x0000a674, 0x09080504}, + {0x0000a678, 0x0d0c0b0a}, + {0x0000a67c, 0x13121110}, + {0x0000a680, 0x31301514}, + {0x0000a684, 0x35343332}, + {0x0000a688, 0x00000036}, + {0x0000a690, 0x00000838}, + {0x0000a6b0, 0x0000000a}, + {0x0000a6b4, 0x28f12c01}, + {0x0000a7c0, 0x00000000}, + {0x0000a7c4, 0xfffffffc}, + {0x0000a7c8, 0x00000000}, + {0x0000a7cc, 0x00000000}, + {0x0000a7d0, 0x00000000}, + {0x0000a7d4, 0x00000004}, + {0x0000a7dc, 0x00000001}, + {0x0000a8d0, 0x004b6a8e}, + {0x0000a8d4, 0x00000820}, + {0x0000a8dc, 0x00000000}, + {0x0000a8f0, 0x00000000}, + {0x0000a8f4, 0x00000000}, + {0x0000b2d0, 0x00000080}, + {0x0000b2d4, 0x00000000}, + {0x0000b2ec, 0x00000000}, + {0x0000b2f0, 0x00000000}, + {0x0000b2f4, 0x00000000}, + {0x0000b2f8, 0x00000000}, + {0x0000b408, 0x0e79e5c0}, + {0x0000b40c, 0x00820820}, + {0x0000b420, 0x00000000}, + {0x0000b6b0, 0x0000000a}, + {0x0000b6b4, 0x00c00001}, +}; + +static const u32 ar9462_1p0_baseband_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, + {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e}, + {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, + {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, + {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, + {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, + {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, + {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, + {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, + {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, + {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e}, + {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, + {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, + {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, + {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, + {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c782}, + {0x00009e44, 0x02321e27, 0x02321e27, 0x02291e27, 0x02291e27}, + {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, + {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, + {0x0000a204, 0x0131b7c0, 0x0131b7c4, 0x0131b7c4, 0x0131b7c0}, + {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, + {0x0000a22c, 0x01026a2f, 0x01026a27, 0x01026a2f, 0x01026a2f}, + {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b}, + {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff}, + {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, + {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, + {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, + {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, + {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, + {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501}, + {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, + {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, + {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, + {0x0000a288, 0x00000110, 0x00000110, 0x00100110, 0x00100110}, + {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, + {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, + {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982}, + {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, + {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, + {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x00100000}, + {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, + {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, + {0x0000b284, 0x00000000, 0x00000000, 0x00000550, 0x00000550}, +}; + +static const u32 ar9462_modes_fast_clock_1p0[][3] = { + /* Addr 5G_HT20 5G_HT40 */ + {0x00001030, 0x00000268, 0x000004d0}, + {0x00001070, 0x0000018c, 0x00000318}, + {0x000010b0, 0x00000fd0, 0x00001fa0}, + {0x00008014, 0x044c044c, 0x08980898}, + {0x0000801c, 0x148ec02b, 0x148ec057}, + {0x00008318, 0x000044c0, 0x00008980}, + {0x00009e00, 0x0372131c, 0x0372131c}, + {0x0000a230, 0x0000400b, 0x00004016}, + {0x0000a254, 0x00000898, 0x00001130}, +}; + +static const u32 ar9462_modes_low_ob_db_tx_gain_table_1p0[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, + {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, + {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, + {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, + {0x0000a518, 0x21020220, 0x21020220, 0x16000402, 0x16000402}, + {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404}, + {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, + {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, + {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, + {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, + {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, + {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, + {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, + {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, + {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, + {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, + {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, + {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x47001a83, 0x47001a83}, + {0x0000a550, 0x61042a6c, 0x61042a6c, 0x4a001c84, 0x4a001c84}, + {0x0000a554, 0x66062a6c, 0x66062a6c, 0x4e001ce3, 0x4e001ce3}, + {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x52001ce5, 0x52001ce5}, + {0x0000a55c, 0x7006308c, 0x7006308c, 0x56001ce9, 0x56001ce9}, + {0x0000a560, 0x730a308a, 0x730a308a, 0x5a001ceb, 0x5a001ceb}, + {0x0000a564, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a568, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a56c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a570, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a574, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a578, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a57c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, + {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, + {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, + {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, + {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x00016044, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4}, + {0x00016048, 0x64992060, 0x64992060, 0x64992060, 0x64992060}, + {0x00016444, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4}, + {0x00016448, 0x64992000, 0x64992000, 0x64992000, 0x64992000}, +}; + +static const u32 ar9462_1p0_soc_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00007010, 0x00002233, 0x00002233, 0x00002233, 0x00002233}, +}; + +static const u32 ar9462_common_mixed_rx_gain_table_1p0[][2] = { + /* Addr allmodes */ + {0x0000a000, 0x00010000}, + {0x0000a004, 0x00030002}, + {0x0000a008, 0x00050004}, + {0x0000a00c, 0x00810080}, + {0x0000a010, 0x00830082}, + {0x0000a014, 0x01810180}, + {0x0000a018, 0x01830182}, + {0x0000a01c, 0x01850184}, + {0x0000a020, 0x01890188}, + {0x0000a024, 0x018b018a}, + {0x0000a028, 0x018d018c}, + {0x0000a02c, 0x03820190}, + {0x0000a030, 0x03840383}, + {0x0000a034, 0x03880385}, + {0x0000a038, 0x038a0389}, + {0x0000a03c, 0x038c038b}, + {0x0000a040, 0x0390038d}, + {0x0000a044, 0x03920391}, + {0x0000a048, 0x03940393}, + {0x0000a04c, 0x03960395}, + {0x0000a050, 0x00000000}, + {0x0000a054, 0x00000000}, + {0x0000a058, 0x00000000}, + {0x0000a05c, 0x00000000}, + {0x0000a060, 0x00000000}, + {0x0000a064, 0x00000000}, + {0x0000a068, 0x00000000}, + {0x0000a06c, 0x00000000}, + {0x0000a070, 0x00000000}, + {0x0000a074, 0x00000000}, + {0x0000a078, 0x00000000}, + {0x0000a07c, 0x00000000}, + {0x0000a080, 0x29292929}, + {0x0000a084, 0x29292929}, + {0x0000a088, 0x29292929}, + {0x0000a08c, 0x29292929}, + {0x0000a090, 0x22292929}, + {0x0000a094, 0x1d1d2222}, + {0x0000a098, 0x0c111117}, + {0x0000a09c, 0x00030303}, + {0x0000a0a0, 0x00000000}, + {0x0000a0a4, 0x00000000}, + {0x0000a0a8, 0x00000000}, + {0x0000a0ac, 0x00000000}, + {0x0000a0b0, 0x00000000}, + {0x0000a0b4, 0x00000000}, + {0x0000a0b8, 0x00000000}, + {0x0000a0bc, 0x00000000}, + {0x0000a0c0, 0x001f0000}, + {0x0000a0c4, 0x01000101}, + {0x0000a0c8, 0x011e011f}, + {0x0000a0cc, 0x011c011d}, + {0x0000a0d0, 0x02030204}, + {0x0000a0d4, 0x02010202}, + {0x0000a0d8, 0x021f0200}, + {0x0000a0dc, 0x0302021e}, + {0x0000a0e0, 0x03000301}, + {0x0000a0e4, 0x031e031f}, + {0x0000a0e8, 0x0402031d}, + {0x0000a0ec, 0x04000401}, + {0x0000a0f0, 0x041e041f}, + {0x0000a0f4, 0x0502041d}, + {0x0000a0f8, 0x05000501}, + {0x0000a0fc, 0x051e051f}, + {0x0000a100, 0x06010602}, + {0x0000a104, 0x061f0600}, + {0x0000a108, 0x061d061e}, + {0x0000a10c, 0x07020703}, + {0x0000a110, 0x07000701}, + {0x0000a114, 0x00000000}, + {0x0000a118, 0x00000000}, + {0x0000a11c, 0x00000000}, + {0x0000a120, 0x00000000}, + {0x0000a124, 0x00000000}, + {0x0000a128, 0x00000000}, + {0x0000a12c, 0x00000000}, + {0x0000a130, 0x00000000}, + {0x0000a134, 0x00000000}, + {0x0000a138, 0x00000000}, + {0x0000a13c, 0x00000000}, + {0x0000a140, 0x001f0000}, + {0x0000a144, 0x01000101}, + {0x0000a148, 0x011e011f}, + {0x0000a14c, 0x011c011d}, + {0x0000a150, 0x02030204}, + {0x0000a154, 0x02010202}, + {0x0000a158, 0x021f0200}, + {0x0000a15c, 0x0302021e}, + {0x0000a160, 0x03000301}, + {0x0000a164, 0x031e031f}, + {0x0000a168, 0x0402031d}, + {0x0000a16c, 0x04000401}, + {0x0000a170, 0x041e041f}, + {0x0000a174, 0x0502041d}, + {0x0000a178, 0x05000501}, + {0x0000a17c, 0x051e051f}, + {0x0000a180, 0x06010602}, + {0x0000a184, 0x061f0600}, + {0x0000a188, 0x061d061e}, + {0x0000a18c, 0x07020703}, + {0x0000a190, 0x07000701}, + {0x0000a194, 0x00000000}, + {0x0000a198, 0x00000000}, + {0x0000a19c, 0x00000000}, + {0x0000a1a0, 0x00000000}, + {0x0000a1a4, 0x00000000}, + {0x0000a1a8, 0x00000000}, + {0x0000a1ac, 0x00000000}, + {0x0000a1b0, 0x00000000}, + {0x0000a1b4, 0x00000000}, + {0x0000a1b8, 0x00000000}, + {0x0000a1bc, 0x00000000}, + {0x0000a1c0, 0x00000000}, + {0x0000a1c4, 0x00000000}, + {0x0000a1c8, 0x00000000}, + {0x0000a1cc, 0x00000000}, + {0x0000a1d0, 0x00000000}, + {0x0000a1d4, 0x00000000}, + {0x0000a1d8, 0x00000000}, + {0x0000a1dc, 0x00000000}, + {0x0000a1e0, 0x00000000}, + {0x0000a1e4, 0x00000000}, + {0x0000a1e8, 0x00000000}, + {0x0000a1ec, 0x00000000}, + {0x0000a1f0, 0x00000396}, + {0x0000a1f4, 0x00000396}, + {0x0000a1f8, 0x00000396}, + {0x0000a1fc, 0x00000196}, + {0x0000b000, 0x00010000}, + {0x0000b004, 0x00030002}, + {0x0000b008, 0x00050004}, + {0x0000b00c, 0x00810080}, + {0x0000b010, 0x00830082}, + {0x0000b014, 0x01810180}, + {0x0000b018, 0x01830182}, + {0x0000b01c, 0x01850184}, + {0x0000b020, 0x02810280}, + {0x0000b024, 0x02830282}, + {0x0000b028, 0x02850284}, + {0x0000b02c, 0x02890288}, + {0x0000b030, 0x028b028a}, + {0x0000b034, 0x0388028c}, + {0x0000b038, 0x038a0389}, + {0x0000b03c, 0x038c038b}, + {0x0000b040, 0x0390038d}, + {0x0000b044, 0x03920391}, + {0x0000b048, 0x03940393}, + {0x0000b04c, 0x03960395}, + {0x0000b050, 0x00000000}, + {0x0000b054, 0x00000000}, + {0x0000b058, 0x00000000}, + {0x0000b05c, 0x00000000}, + {0x0000b060, 0x00000000}, + {0x0000b064, 0x00000000}, + {0x0000b068, 0x00000000}, + {0x0000b06c, 0x00000000}, + {0x0000b070, 0x00000000}, + {0x0000b074, 0x00000000}, + {0x0000b078, 0x00000000}, + {0x0000b07c, 0x00000000}, + {0x0000b080, 0x2a2d2f32}, + {0x0000b084, 0x21232328}, + {0x0000b088, 0x19191c1e}, + {0x0000b08c, 0x12141417}, + {0x0000b090, 0x07070e0e}, + {0x0000b094, 0x03030305}, + {0x0000b098, 0x00000003}, + {0x0000b09c, 0x00000000}, + {0x0000b0a0, 0x00000000}, + {0x0000b0a4, 0x00000000}, + {0x0000b0a8, 0x00000000}, + {0x0000b0ac, 0x00000000}, + {0x0000b0b0, 0x00000000}, + {0x0000b0b4, 0x00000000}, + {0x0000b0b8, 0x00000000}, + {0x0000b0bc, 0x00000000}, + {0x0000b0c0, 0x003f0020}, + {0x0000b0c4, 0x00400041}, + {0x0000b0c8, 0x0140005f}, + {0x0000b0cc, 0x0160015f}, + {0x0000b0d0, 0x017e017f}, + {0x0000b0d4, 0x02410242}, + {0x0000b0d8, 0x025f0240}, + {0x0000b0dc, 0x027f0260}, + {0x0000b0e0, 0x0341027e}, + {0x0000b0e4, 0x035f0340}, + {0x0000b0e8, 0x037f0360}, + {0x0000b0ec, 0x04400441}, + {0x0000b0f0, 0x0460045f}, + {0x0000b0f4, 0x0541047f}, + {0x0000b0f8, 0x055f0540}, + {0x0000b0fc, 0x057f0560}, + {0x0000b100, 0x06400641}, + {0x0000b104, 0x0660065f}, + {0x0000b108, 0x067e067f}, + {0x0000b10c, 0x07410742}, + {0x0000b110, 0x075f0740}, + {0x0000b114, 0x077f0760}, + {0x0000b118, 0x07800781}, + {0x0000b11c, 0x07a0079f}, + {0x0000b120, 0x07c107bf}, + {0x0000b124, 0x000007c0}, + {0x0000b128, 0x00000000}, + {0x0000b12c, 0x00000000}, + {0x0000b130, 0x00000000}, + {0x0000b134, 0x00000000}, + {0x0000b138, 0x00000000}, + {0x0000b13c, 0x00000000}, + {0x0000b140, 0x003f0020}, + {0x0000b144, 0x00400041}, + {0x0000b148, 0x0140005f}, + {0x0000b14c, 0x0160015f}, + {0x0000b150, 0x017e017f}, + {0x0000b154, 0x02410242}, + {0x0000b158, 0x025f0240}, + {0x0000b15c, 0x027f0260}, + {0x0000b160, 0x0341027e}, + {0x0000b164, 0x035f0340}, + {0x0000b168, 0x037f0360}, + {0x0000b16c, 0x04400441}, + {0x0000b170, 0x0460045f}, + {0x0000b174, 0x0541047f}, + {0x0000b178, 0x055f0540}, + {0x0000b17c, 0x057f0560}, + {0x0000b180, 0x06400641}, + {0x0000b184, 0x0660065f}, + {0x0000b188, 0x067e067f}, + {0x0000b18c, 0x07410742}, + {0x0000b190, 0x075f0740}, + {0x0000b194, 0x077f0760}, + {0x0000b198, 0x07800781}, + {0x0000b19c, 0x07a0079f}, + {0x0000b1a0, 0x07c107bf}, + {0x0000b1a4, 0x000007c0}, + {0x0000b1a8, 0x00000000}, + {0x0000b1ac, 0x00000000}, + {0x0000b1b0, 0x00000000}, + {0x0000b1b4, 0x00000000}, + {0x0000b1b8, 0x00000000}, + {0x0000b1bc, 0x00000000}, + {0x0000b1c0, 0x00000000}, + {0x0000b1c4, 0x00000000}, + {0x0000b1c8, 0x00000000}, + {0x0000b1cc, 0x00000000}, + {0x0000b1d0, 0x00000000}, + {0x0000b1d4, 0x00000000}, + {0x0000b1d8, 0x00000000}, + {0x0000b1dc, 0x00000000}, + {0x0000b1e0, 0x00000000}, + {0x0000b1e4, 0x00000000}, + {0x0000b1e8, 0x00000000}, + {0x0000b1ec, 0x00000000}, + {0x0000b1f0, 0x00000396}, + {0x0000b1f4, 0x00000396}, + {0x0000b1f8, 0x00000396}, + {0x0000b1fc, 0x00000196}, +}; + +static const u32 ar9462_pcie_phy_clkreq_disable_L1_1p0[][2] = { + /* Addr allmodes */ + {0x00018c00, 0x10013e5e}, + {0x00018c04, 0x000801d8}, + {0x00018c08, 0x0000580c}, +}; + +static const u32 ar9462_1p0_baseband_core_emulation[][2] = { + /* Addr allmodes */ + {0x00009800, 0xafa68e30}, + {0x00009884, 0x00002842}, + {0x00009c04, 0xff55ff55}, + {0x00009c08, 0x0320ff55}, + {0x00009e50, 0x00000000}, + {0x00009fcc, 0x00000014}, + {0x0000a344, 0x00000010}, + {0x0000a398, 0x00000000}, + {0x0000a39c, 0x71733d01}, + {0x0000a3a0, 0xd0ad5c12}, + {0x0000a3c0, 0x22222220}, + {0x0000a3c4, 0x22222222}, + {0x0000a404, 0x00418a11}, + {0x0000a418, 0x050001ce}, + {0x0000a438, 0x00001800}, + {0x0000a458, 0x01444452}, + {0x0000a644, 0x3fad9d74}, + {0x0000a690, 0x00000038}, +}; + +static const u32 ar9462_1p0_radio_core[][2] = { + /* Addr allmodes */ + {0x00016000, 0x36db6db6}, + {0x00016004, 0x6db6db40}, + {0x00016008, 0x73f00000}, + {0x0001600c, 0x00000000}, + {0x00016010, 0x6d820001}, + {0x00016040, 0x7f80fff8}, + {0x0001604c, 0x2699e04f}, + {0x00016050, 0x6db6db6c}, + {0x00016054, 0x6db60000}, + {0x00016058, 0x6c200000}, + {0x00016080, 0x00040000}, + {0x00016084, 0x9a68048c}, + {0x00016088, 0x54214514}, + {0x0001608c, 0x12030409}, + {0x00016090, 0x24926490}, + {0x00016098, 0xd2888888}, + {0x000160a0, 0x0a108ffe}, + {0x000160a4, 0x812fc490}, + {0x000160a8, 0x423c8000}, + {0x000160b4, 0x92000000}, + {0x000160b8, 0x0285dddc}, + {0x000160bc, 0x02908888}, + {0x000160c0, 0x00adb6d0}, + {0x000160c4, 0x6db6db60}, + {0x000160c8, 0x6db6db6c}, + {0x000160cc, 0x0de6c1b0}, + {0x00016100, 0x3fffbe04}, + {0x00016104, 0xfff80000}, + {0x00016108, 0x00200400}, + {0x00016110, 0x00000000}, + {0x00016144, 0x02084080}, + {0x00016148, 0x000080c0}, + {0x00016280, 0x050a0001}, + {0x00016284, 0x3d841400}, + {0x00016288, 0x00000000}, + {0x0001628c, 0xe3000000}, + {0x00016290, 0xa1005080}, + {0x00016294, 0x00000020}, + {0x00016298, 0x50a02900}, + {0x00016340, 0x121e4276}, + {0x00016344, 0x00300000}, + {0x00016400, 0x36db6db6}, + {0x00016404, 0x6db6db40}, + {0x00016408, 0x73f00000}, + {0x0001640c, 0x00000000}, + {0x00016410, 0x6c800001}, + {0x00016440, 0x7f80fff8}, + {0x0001644c, 0x4699e04f}, + {0x00016450, 0x6db6db6c}, + {0x00016454, 0x6db60000}, + {0x00016500, 0x3fffbe04}, + {0x00016504, 0xfff80000}, + {0x00016508, 0x00200400}, + {0x00016510, 0x00000000}, + {0x00016544, 0x02084080}, + {0x00016548, 0x000080c0}, +}; + +static const u32 ar9462_1p0_soc_preamble[][2] = { + /* Addr allmodes */ + {0x00007020, 0x00000000}, + {0x00007034, 0x00000002}, + {0x00007038, 0x000004c2}, +}; + +static const u32 ar9462_1p0_sys2ant[][2] = { + /* Addr allmodes */ + {0x00063120, 0x00801980}, +}; + +#endif /* INITVALS_9462_1P0_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h index 15960024..c720042f 100644 --- a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h @@ -41,24 +41,24 @@ static const u32 ar9462_pciephy_clkreq_enable_L1_2p0[][2] = { static const u32 ar9462_2p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a800d}, - {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a01ae}, - {0x00009824, 0x5ac640de, 0x5ac640d0, 0x5ac640d0, 0x63c640da}, - {0x00009828, 0x0796be89, 0x0696b081, 0x0696b881, 0x09143e81}, + {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, + {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e}, + {0x00009824, 0x5ac640de, 0x5ac640d0, 0x5ac640d0, 0x5ac640de}, + {0x00009828, 0x0796be89, 0x0696b081, 0x0696b881, 0x0796be89}, {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, - {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000d8}, - {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec86d2e}, - {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3376605e, 0x33795d5e}, + {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, + {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x92c84d2e}, + {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003a5, 0x000003a5, 0x000003a5, 0x000003a5}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, - {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282}, - {0x00009e44, 0x62321e27, 0x62321e27, 0xfe291e27, 0xfe291e27}, + {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c782}, + {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, {0x0000a204, 0x013187c0, 0x013187c4, 0x013187c4, 0x013187c0}, @@ -81,15 +81,6 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = { {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982}, {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a3a4, 0x00000010, 0x00000010, 0x00000000, 0x00000000}, - {0x0000a3a8, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa}, - {0x0000a3ac, 0xaaaaaa00, 0xaaaaaa30, 0xaaaaaa00, 0xaaaaaa00}, - {0x0000a41c, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce}, - {0x0000a420, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce}, - {0x0000a424, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce}, - {0x0000a428, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce}, - {0x0000a42c, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce}, - {0x0000a430, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce}, {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x00100000}, {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -98,6 +89,14 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = { {0x0000b284, 0x00000000, 0x00000000, 0x00000550, 0x00000550}, }; +static const u32 ar9462_2p0_mac_core_emulation[][2] = { + /* Addr allmodes */ + {0x00000030, 0x000e0085}, + {0x00000044, 0x00000008}, + {0x0000805c, 0xffffc7ff}, + {0x00008344, 0xaa4a105b}, +}; + static const u32 ar9462_common_rx_gain_table_2p0[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, @@ -372,6 +371,349 @@ static const u32 ar9462_pciephy_pll_on_clkreq_disable_L1_2p0[][2] = { {0x00018c08, 0x0003580c}, }; +static const u32 ar9462_2p0_sys3ant[][2] = { + /* Addr allmodes */ + {0x00063280, 0x00040807}, + {0x00063284, 0x104ccccc}, +}; + +static const u32 ar9462_common_rx_gain_table_ar9280_2p0[][2] = { + /* Addr allmodes */ + {0x0000a000, 0x02000101}, + {0x0000a004, 0x02000102}, + {0x0000a008, 0x02000103}, + {0x0000a00c, 0x02000104}, + {0x0000a010, 0x02000200}, + {0x0000a014, 0x02000201}, + {0x0000a018, 0x02000202}, + {0x0000a01c, 0x02000203}, + {0x0000a020, 0x02000204}, + {0x0000a024, 0x02000205}, + {0x0000a028, 0x02000208}, + {0x0000a02c, 0x02000302}, + {0x0000a030, 0x02000303}, + {0x0000a034, 0x02000304}, + {0x0000a038, 0x02000400}, + {0x0000a03c, 0x02010300}, + {0x0000a040, 0x02010301}, + {0x0000a044, 0x02010302}, + {0x0000a048, 0x02000500}, + {0x0000a04c, 0x02010400}, + {0x0000a050, 0x02020300}, + {0x0000a054, 0x02020301}, + {0x0000a058, 0x02020302}, + {0x0000a05c, 0x02020303}, + {0x0000a060, 0x02020400}, + {0x0000a064, 0x02030300}, + {0x0000a068, 0x02030301}, + {0x0000a06c, 0x02030302}, + {0x0000a070, 0x02030303}, + {0x0000a074, 0x02030400}, + {0x0000a078, 0x02040300}, + {0x0000a07c, 0x02040301}, + {0x0000a080, 0x02040302}, + {0x0000a084, 0x02040303}, + {0x0000a088, 0x02030500}, + {0x0000a08c, 0x02040400}, + {0x0000a090, 0x02050203}, + {0x0000a094, 0x02050204}, + {0x0000a098, 0x02050205}, + {0x0000a09c, 0x02040500}, + {0x0000a0a0, 0x02050301}, + {0x0000a0a4, 0x02050302}, + {0x0000a0a8, 0x02050303}, + {0x0000a0ac, 0x02050400}, + {0x0000a0b0, 0x02050401}, + {0x0000a0b4, 0x02050402}, + {0x0000a0b8, 0x02050403}, + {0x0000a0bc, 0x02050500}, + {0x0000a0c0, 0x02050501}, + {0x0000a0c4, 0x02050502}, + {0x0000a0c8, 0x02050503}, + {0x0000a0cc, 0x02050504}, + {0x0000a0d0, 0x02050600}, + {0x0000a0d4, 0x02050601}, + {0x0000a0d8, 0x02050602}, + {0x0000a0dc, 0x02050603}, + {0x0000a0e0, 0x02050604}, + {0x0000a0e4, 0x02050700}, + {0x0000a0e8, 0x02050701}, + {0x0000a0ec, 0x02050702}, + {0x0000a0f0, 0x02050703}, + {0x0000a0f4, 0x02050704}, + {0x0000a0f8, 0x02050705}, + {0x0000a0fc, 0x02050708}, + {0x0000a100, 0x02050709}, + {0x0000a104, 0x0205070a}, + {0x0000a108, 0x0205070b}, + {0x0000a10c, 0x0205070c}, + {0x0000a110, 0x0205070d}, + {0x0000a114, 0x02050710}, + {0x0000a118, 0x02050711}, + {0x0000a11c, 0x02050712}, + {0x0000a120, 0x02050713}, + {0x0000a124, 0x02050714}, + {0x0000a128, 0x02050715}, + {0x0000a12c, 0x02050730}, + {0x0000a130, 0x02050731}, + {0x0000a134, 0x02050732}, + {0x0000a138, 0x02050733}, + {0x0000a13c, 0x02050734}, + {0x0000a140, 0x02050735}, + {0x0000a144, 0x02050750}, + {0x0000a148, 0x02050751}, + {0x0000a14c, 0x02050752}, + {0x0000a150, 0x02050753}, + {0x0000a154, 0x02050754}, + {0x0000a158, 0x02050755}, + {0x0000a15c, 0x02050770}, + {0x0000a160, 0x02050771}, + {0x0000a164, 0x02050772}, + {0x0000a168, 0x02050773}, + {0x0000a16c, 0x02050774}, + {0x0000a170, 0x02050775}, + {0x0000a174, 0x00000776}, + {0x0000a178, 0x00000776}, + {0x0000a17c, 0x00000776}, + {0x0000a180, 0x00000776}, + {0x0000a184, 0x00000776}, + {0x0000a188, 0x00000776}, + {0x0000a18c, 0x00000776}, + {0x0000a190, 0x00000776}, + {0x0000a194, 0x00000776}, + {0x0000a198, 0x00000776}, + {0x0000a19c, 0x00000776}, + {0x0000a1a0, 0x00000776}, + {0x0000a1a4, 0x00000776}, + {0x0000a1a8, 0x00000776}, + {0x0000a1ac, 0x00000776}, + {0x0000a1b0, 0x00000776}, + {0x0000a1b4, 0x00000776}, + {0x0000a1b8, 0x00000776}, + {0x0000a1bc, 0x00000776}, + {0x0000a1c0, 0x00000776}, + {0x0000a1c4, 0x00000776}, + {0x0000a1c8, 0x00000776}, + {0x0000a1cc, 0x00000776}, + {0x0000a1d0, 0x00000776}, + {0x0000a1d4, 0x00000776}, + {0x0000a1d8, 0x00000776}, + {0x0000a1dc, 0x00000776}, + {0x0000a1e0, 0x00000776}, + {0x0000a1e4, 0x00000776}, + {0x0000a1e8, 0x00000776}, + {0x0000a1ec, 0x00000776}, + {0x0000a1f0, 0x00000776}, + {0x0000a1f4, 0x00000776}, + {0x0000a1f8, 0x00000776}, + {0x0000a1fc, 0x00000776}, + {0x0000b000, 0x02000101}, + {0x0000b004, 0x02000102}, + {0x0000b008, 0x02000103}, + {0x0000b00c, 0x02000104}, + {0x0000b010, 0x02000200}, + {0x0000b014, 0x02000201}, + {0x0000b018, 0x02000202}, + {0x0000b01c, 0x02000203}, + {0x0000b020, 0x02000204}, + {0x0000b024, 0x02000205}, + {0x0000b028, 0x02000208}, + {0x0000b02c, 0x02000302}, + {0x0000b030, 0x02000303}, + {0x0000b034, 0x02000304}, + {0x0000b038, 0x02000400}, + {0x0000b03c, 0x02010300}, + {0x0000b040, 0x02010301}, + {0x0000b044, 0x02010302}, + {0x0000b048, 0x02000500}, + {0x0000b04c, 0x02010400}, + {0x0000b050, 0x02020300}, + {0x0000b054, 0x02020301}, + {0x0000b058, 0x02020302}, + {0x0000b05c, 0x02020303}, + {0x0000b060, 0x02020400}, + {0x0000b064, 0x02030300}, + {0x0000b068, 0x02030301}, + {0x0000b06c, 0x02030302}, + {0x0000b070, 0x02030303}, + {0x0000b074, 0x02030400}, + {0x0000b078, 0x02040300}, + {0x0000b07c, 0x02040301}, + {0x0000b080, 0x02040302}, + {0x0000b084, 0x02040303}, + {0x0000b088, 0x02030500}, + {0x0000b08c, 0x02040400}, + {0x0000b090, 0x02050203}, + {0x0000b094, 0x02050204}, + {0x0000b098, 0x02050205}, + {0x0000b09c, 0x02040500}, + {0x0000b0a0, 0x02050301}, + {0x0000b0a4, 0x02050302}, + {0x0000b0a8, 0x02050303}, + {0x0000b0ac, 0x02050400}, + {0x0000b0b0, 0x02050401}, + {0x0000b0b4, 0x02050402}, + {0x0000b0b8, 0x02050403}, + {0x0000b0bc, 0x02050500}, + {0x0000b0c0, 0x02050501}, + {0x0000b0c4, 0x02050502}, + {0x0000b0c8, 0x02050503}, + {0x0000b0cc, 0x02050504}, + {0x0000b0d0, 0x02050600}, + {0x0000b0d4, 0x02050601}, + {0x0000b0d8, 0x02050602}, + {0x0000b0dc, 0x02050603}, + {0x0000b0e0, 0x02050604}, + {0x0000b0e4, 0x02050700}, + {0x0000b0e8, 0x02050701}, + {0x0000b0ec, 0x02050702}, + {0x0000b0f0, 0x02050703}, + {0x0000b0f4, 0x02050704}, + {0x0000b0f8, 0x02050705}, + {0x0000b0fc, 0x02050708}, + {0x0000b100, 0x02050709}, + {0x0000b104, 0x0205070a}, + {0x0000b108, 0x0205070b}, + {0x0000b10c, 0x0205070c}, + {0x0000b110, 0x0205070d}, + {0x0000b114, 0x02050710}, + {0x0000b118, 0x02050711}, + {0x0000b11c, 0x02050712}, + {0x0000b120, 0x02050713}, + {0x0000b124, 0x02050714}, + {0x0000b128, 0x02050715}, + {0x0000b12c, 0x02050730}, + {0x0000b130, 0x02050731}, + {0x0000b134, 0x02050732}, + {0x0000b138, 0x02050733}, + {0x0000b13c, 0x02050734}, + {0x0000b140, 0x02050735}, + {0x0000b144, 0x02050750}, + {0x0000b148, 0x02050751}, + {0x0000b14c, 0x02050752}, + {0x0000b150, 0x02050753}, + {0x0000b154, 0x02050754}, + {0x0000b158, 0x02050755}, + {0x0000b15c, 0x02050770}, + {0x0000b160, 0x02050771}, + {0x0000b164, 0x02050772}, + {0x0000b168, 0x02050773}, + {0x0000b16c, 0x02050774}, + {0x0000b170, 0x02050775}, + {0x0000b174, 0x00000776}, + {0x0000b178, 0x00000776}, + {0x0000b17c, 0x00000776}, + {0x0000b180, 0x00000776}, + {0x0000b184, 0x00000776}, + {0x0000b188, 0x00000776}, + {0x0000b18c, 0x00000776}, + {0x0000b190, 0x00000776}, + {0x0000b194, 0x00000776}, + {0x0000b198, 0x00000776}, + {0x0000b19c, 0x00000776}, + {0x0000b1a0, 0x00000776}, + {0x0000b1a4, 0x00000776}, + {0x0000b1a8, 0x00000776}, + {0x0000b1ac, 0x00000776}, + {0x0000b1b0, 0x00000776}, + {0x0000b1b4, 0x00000776}, + {0x0000b1b8, 0x00000776}, + {0x0000b1bc, 0x00000776}, + {0x0000b1c0, 0x00000776}, + {0x0000b1c4, 0x00000776}, + {0x0000b1c8, 0x00000776}, + {0x0000b1cc, 0x00000776}, + {0x0000b1d0, 0x00000776}, + {0x0000b1d4, 0x00000776}, + {0x0000b1d8, 0x00000776}, + {0x0000b1dc, 0x00000776}, + {0x0000b1e0, 0x00000776}, + {0x0000b1e4, 0x00000776}, + {0x0000b1e8, 0x00000776}, + {0x0000b1ec, 0x00000776}, + {0x0000b1f0, 0x00000776}, + {0x0000b1f4, 0x00000776}, + {0x0000b1f8, 0x00000776}, + {0x0000b1fc, 0x00000776}, +}; + +static const u32 ar9200_ar9280_2p0_radio_core[][2] = { + /* Addr allmodes */ + {0x00007800, 0x00040000}, + {0x00007804, 0xdb005012}, + {0x00007808, 0x04924914}, + {0x0000780c, 0x21084210}, + {0x00007810, 0x6d801300}, + {0x00007814, 0x0019beff}, + {0x00007818, 0x07e41000}, + {0x0000781c, 0x00392000}, + {0x00007820, 0x92592480}, + {0x00007824, 0x00040000}, + {0x00007828, 0xdb005012}, + {0x0000782c, 0x04924914}, + {0x00007830, 0x21084210}, + {0x00007834, 0x6d801300}, + {0x00007838, 0x0019beff}, + {0x0000783c, 0x07e40000}, + {0x00007840, 0x00392000}, + {0x00007844, 0x92592480}, + {0x00007848, 0x00100000}, + {0x0000784c, 0x773f0567}, + {0x00007850, 0x54214514}, + {0x00007854, 0x12035828}, + {0x00007858, 0x92592692}, + {0x0000785c, 0x00000000}, + {0x00007860, 0x56400000}, + {0x00007864, 0x0a8e370e}, + {0x00007868, 0xc0102850}, + {0x0000786c, 0x812d4000}, + {0x00007870, 0x807ec400}, + {0x00007874, 0x001b6db0}, + {0x00007878, 0x00376b63}, + {0x0000787c, 0x06db6db6}, + {0x00007880, 0x006d8000}, + {0x00007884, 0xffeffffe}, + {0x00007888, 0xffeffffe}, + {0x0000788c, 0x00010000}, + {0x00007890, 0x02060aeb}, + {0x00007894, 0x5a108000}, +}; + +static const u32 ar9462_2p0_mac_postamble_emulation[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00008014, 0x10f810f8, 0x10f810f8, 0x10f810f8, 0x10f810f8}, + {0x0000801c, 0x0e8d8017, 0x0e8d8017, 0x0e8d8017, 0x0e8d8017}, +}; + +static const u32 ar9462_2p0_radio_postamble_sys3ant[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x000160ac, 0xa4646c08, 0xa4646c08, 0x24645808, 0x24645808}, + {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, + {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, +}; + +static const u32 ar9462_2p0_baseband_postamble_emulation[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00009e3c, 0xcf946221, 0xcf946221, 0xcf946221, 0xcf946221}, + {0x00009e44, 0xfc5c0000, 0xfc5c0000, 0xfc5c0000, 0xfc5c0000}, + {0x0000a258, 0x02020200, 0x02020200, 0x02020200, 0x02020200}, + {0x0000a25c, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, + {0x0000a28c, 0x00011111, 0x00011111, 0x00011111, 0x00011111}, + {0x0000a2c4, 0x00148d18, 0x00148d18, 0x00148d20, 0x00148d20}, + {0x0000a2d8, 0xf999a800, 0xf999a800, 0xf999a80c, 0xf999a80c}, + {0x0000a50c, 0x0000c00a, 0x0000c00a, 0x0000c00a, 0x0000c00a}, + {0x0000a538, 0x00038e8c, 0x00038e8c, 0x00038e8c, 0x00038e8c}, + {0x0000a53c, 0x0003cecc, 0x0003cecc, 0x0003cecc, 0x0003cecc}, + {0x0000a540, 0x00040ed4, 0x00040ed4, 0x00040ed4, 0x00040ed4}, + {0x0000a544, 0x00044edc, 0x00044edc, 0x00044edc, 0x00044edc}, + {0x0000a548, 0x00048ede, 0x00048ede, 0x00048ede, 0x00048ede}, + {0x0000a54c, 0x0004cf1e, 0x0004cf1e, 0x0004cf1e, 0x0004cf1e}, + {0x0000a550, 0x00050f5e, 0x00050f5e, 0x00050f5e, 0x00050f5e}, + {0x0000a554, 0x00054f9e, 0x00054f9e, 0x00054f9e, 0x00054f9e}, + {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, +}; + static const u32 ar9462_2p0_radio_postamble_sys2ant[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000160ac, 0xa4646c08, 0xa4646c08, 0x24645808, 0x24645808}, @@ -765,11 +1107,11 @@ static const u32 ar9462_2p0_baseband_core[][2] = { {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, - {0x00009e40, 0x15262820}, + {0x00009e40, 0x0d261820}, {0x00009e4c, 0x00001004}, {0x00009e50, 0x00ff03f1}, - {0x00009e54, 0xe4c555c2}, - {0x00009e58, 0xfd857722}, + {0x00009e54, 0xe4c355c7}, + {0x00009e58, 0xfd897735}, {0x00009e5c, 0xe9198724}, {0x00009fc0, 0x803e4788}, {0x00009fc4, 0x0001efb5}, @@ -800,6 +1142,9 @@ static const u32 ar9462_2p0_baseband_core[][2] = { {0x0000a398, 0x001f0e0f}, {0x0000a39c, 0x0075393f}, {0x0000a3a0, 0xb79f6427}, + {0x0000a3a4, 0x00000000}, + {0x0000a3a8, 0xaaaaaaaa}, + {0x0000a3ac, 0x3c466478}, {0x0000a3c0, 0x20202020}, {0x0000a3c4, 0x22222220}, {0x0000a3c8, 0x20200020}, @@ -822,6 +1167,12 @@ static const u32 ar9462_2p0_baseband_core[][2] = { {0x0000a40c, 0x00820820}, {0x0000a414, 0x1ce739ce}, {0x0000a418, 0x2d001dce}, + {0x0000a41c, 0x1ce739ce}, + {0x0000a420, 0x000001ce}, + {0x0000a424, 0x1ce739ce}, + {0x0000a428, 0x000001ce}, + {0x0000a42c, 0x1ce739ce}, + {0x0000a430, 0x1ce739ce}, {0x0000a434, 0x00000000}, {0x0000a438, 0x00001801}, {0x0000a43c, 0x00100000}, @@ -1005,6 +1356,24 @@ static const u32 ar9462_2p0_radio_core[][2] = { {0x00016548, 0x000080c0}, }; +static const u32 ar9462_2p0_tx_gain_table_baseband_postamble_emulation[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a410, 0x000000d5, 0x000000d5, 0x000000d5, 0x000000d5}, + {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a504, 0x00004002, 0x00004002, 0x00004002, 0x00004002}, + {0x0000a508, 0x00008004, 0x00008004, 0x00008004, 0x00008004}, + {0x0000a510, 0x0001000c, 0x0001000c, 0x0001000c, 0x0001000c}, + {0x0000a514, 0x0001420b, 0x0001420b, 0x0001420b, 0x0001420b}, + {0x0000a518, 0x0001824a, 0x0001824a, 0x0001824a, 0x0001824a}, + {0x0000a51c, 0x0001c44a, 0x0001c44a, 0x0001c44a, 0x0001c44a}, + {0x0000a520, 0x0002064a, 0x0002064a, 0x0002064a, 0x0002064a}, + {0x0000a524, 0x0002484a, 0x0002484a, 0x0002484a, 0x0002484a}, + {0x0000a528, 0x00028a4a, 0x00028a4a, 0x00028a4a, 0x00028a4a}, + {0x0000a52c, 0x0002cc4a, 0x0002cc4a, 0x0002cc4a, 0x0002cc4a}, + {0x0000a530, 0x00030e4a, 0x00030e4a, 0x00030e4a, 0x00030e4a}, + {0x0000a534, 0x00034e8a, 0x00034e8a, 0x00034e8a, 0x00034e8a}, +}; + static const u32 ar9462_2p0_soc_preamble[][2] = { /* Addr allmodes */ {0x00007020, 0x00000000}, @@ -1012,6 +1381,11 @@ static const u32 ar9462_2p0_soc_preamble[][2] = { {0x00007038, 0x000004c2}, }; +static const u32 ar9462_2p0_sys2ant[][2] = { + /* Addr allmodes */ + {0x00063120, 0x00801980}, +}; + static const u32 ar9462_2p0_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, @@ -1448,6 +1822,75 @@ static const u32 ar9462_common_mixed_rx_gain_table_2p0[][2] = { {0x0000b1fc, 0x00000196}, }; +static const u32 ar9462_modes_green_ob_db_tx_gain_table_2p0[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003}, + {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, + {0x0000a458, 0x80000000, 0x80000000, 0x80000000, 0x80000000}, + {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, + {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002}, + {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004}, + {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x18022622, 0x18022622, 0x11000400, 0x11000400}, + {0x0000a518, 0x1b022822, 0x1b022822, 0x15000402, 0x15000402}, + {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404}, + {0x0000a520, 0x22022c41, 0x22022c41, 0x1b000603, 0x1b000603}, + {0x0000a524, 0x28023042, 0x28023042, 0x1f000a02, 0x1f000a02}, + {0x0000a528, 0x2c023044, 0x2c023044, 0x23000a04, 0x23000a04}, + {0x0000a52c, 0x2f023644, 0x2f023644, 0x26000a20, 0x26000a20}, + {0x0000a530, 0x34025643, 0x34025643, 0x2a000e20, 0x2a000e20}, + {0x0000a534, 0x38025a44, 0x38025a44, 0x2e000e22, 0x2e000e22}, + {0x0000a538, 0x3b025e45, 0x3b025e45, 0x31000e24, 0x31000e24}, + {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x34001640, 0x34001640}, + {0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660}, + {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861}, + {0x0000a548, 0x53025eb2, 0x53025eb2, 0x3e001a81, 0x3e001a81}, + {0x0000a54c, 0x59025eb6, 0x59025eb6, 0x42001a83, 0x42001a83}, + {0x0000a550, 0x5d025ef6, 0x5d025ef6, 0x44001c84, 0x44001c84}, + {0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3}, + {0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5}, + {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9}, + {0x0000a560, 0x70049f56, 0x70049f56, 0x54001ceb, 0x54001ceb}, + {0x0000a564, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a568, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a56c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a570, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a574, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a578, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a57c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, + {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, + {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, + {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, + {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x00016044, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4}, + {0x00016048, 0x8db49060, 0x8db49060, 0x8db49060, 0x8db49060}, + {0x00016054, 0x6db60180, 0x6db60180, 0x6db60180, 0x6db60180}, + {0x00016444, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4}, + {0x00016448, 0x8db49000, 0x8db49000, 0x8db49000, 0x8db49000}, + {0x00016454, 0x6db60180, 0x6db60180, 0x6db60180, 0x6db60180}, +}; + static const u32 ar9462_2p0_BTCOEX_MAX_TXPWR_table[][2] = { /* Addr allmodes */ {0x000018c0, 0x10101010}, @@ -1460,4 +1903,26 @@ static const u32 ar9462_2p0_BTCOEX_MAX_TXPWR_table[][2] = { {0x000018dc, 0x10101010}, }; +static const u32 ar9462_2p0_baseband_core_emulation[][2] = { + /* Addr allmodes */ + {0x00009800, 0xafa68e30}, + {0x00009884, 0x00002842}, + {0x00009c04, 0xff55ff55}, + {0x00009c08, 0x0320ff55}, + {0x00009e50, 0x00000000}, + {0x00009fcc, 0x00000014}, + {0x0000a344, 0x00000010}, + {0x0000a398, 0x00000000}, + {0x0000a39c, 0x71733d01}, + {0x0000a3a0, 0xd0ad5c12}, + {0x0000a3c0, 0x22222220}, + {0x0000a3c4, 0x22222222}, + {0x0000a404, 0x00418a11}, + {0x0000a418, 0x050001ce}, + {0x0000a438, 0x00001800}, + {0x0000a458, 0x01444452}, + {0x0000a644, 0x3fad9d74}, + {0x0000a690, 0x00000038}, +}; + #endif /* INITVALS_9462_2P0_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h old mode 100644 new mode 100755 diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h old mode 100644 new mode 100755 index e2ab182d..73bd5839 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -93,7 +93,7 @@ enum buffer_type { #define bf_isampdu(bf) (bf->bf_state.bf_type & BUF_AMPDU) #define bf_isaggr(bf) (bf->bf_state.bf_type & BUF_AGGR) -#define ATH_TXSTATUS_RING_SIZE 512 +#define ATH_TXSTATUS_RING_SIZE 64 #define DS2PHYS(_dd, _ds) \ ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) @@ -155,9 +155,6 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, /* return block-ack bitmap index given sequence and starting sequence */ #define ATH_BA_INDEX(_st, _seq) (((_seq) - (_st)) & (IEEE80211_SEQ_MAX - 1)) -/* return the seqno for _start + _offset */ -#define ATH_BA_INDEX2SEQ(_seq, _offset) (((_seq) + (_offset)) & (IEEE80211_SEQ_MAX - 1)) - /* returns delimiter padding required given the packet length */ #define ATH_AGGR_GET_NDELIM(_len) \ (((_len) >= ATH_AGGR_MINPLEN) ? 0 : \ @@ -192,7 +189,6 @@ struct ath_txq { u8 txq_headidx; u8 txq_tailidx; int pending_frames; - struct sk_buff_head complete_q; }; struct ath_atx_ac { @@ -239,7 +235,6 @@ struct ath_atx_tid { struct ath_node *an; struct ath_atx_ac *ac; unsigned long tx_buf[BITS_TO_LONGS(ATH_TID_MAX_BUFS)]; - int bar_index; u16 seq_start; u16 seq_next; u16 baw_size; @@ -254,9 +249,9 @@ struct ath_atx_tid { struct ath_node { #ifdef CONFIG_ATH9K_DEBUGFS struct list_head list; /* for sc->nodes */ -#endif struct ieee80211_sta *sta; /* station struct we're part of */ struct ieee80211_vif *vif; /* interface with which we're associated */ +#endif struct ath_atx_tid tid[WME_NUM_TID]; struct ath_atx_ac ac[WME_NUM_AC]; int ps_key; @@ -278,6 +273,7 @@ struct ath_tx_control { }; #define ATH_TX_ERROR 0x01 +#define ATH_TX_BAR 0x02 /** * @txq_map: Index is mac80211 queue number. This is @@ -296,6 +292,7 @@ struct ath_tx { struct ath_rx_edma { struct sk_buff_head rx_fifo; + struct sk_buff_head rx_buffers; u32 rx_fifo_hwsize; }; @@ -451,39 +448,9 @@ struct ath_btcoex { struct ath_mci_profile mci; }; -#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT -int ath9k_init_btcoex(struct ath_softc *sc); -void ath9k_deinit_btcoex(struct ath_softc *sc); -void ath9k_start_btcoex(struct ath_softc *sc); -void ath9k_stop_btcoex(struct ath_softc *sc); +int ath_init_btcoex_timer(struct ath_softc *sc); void ath9k_btcoex_timer_resume(struct ath_softc *sc); void ath9k_btcoex_timer_pause(struct ath_softc *sc); -void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status); -u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen); -#else -static inline int ath9k_init_btcoex(struct ath_softc *sc) -{ - return 0; -} -static inline void ath9k_deinit_btcoex(struct ath_softc *sc) -{ -} -static inline void ath9k_start_btcoex(struct ath_softc *sc) -{ -} -static inline void ath9k_stop_btcoex(struct ath_softc *sc) -{ -} -static inline void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, - u32 status) -{ -} -static inline u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, - u32 max_4ms_framelen) -{ - return 0; -} -#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ /********************/ /* LED Control */ @@ -493,7 +460,7 @@ static inline u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, #define ATH_LED_PIN_9287 8 #define ATH_LED_PIN_9300 10 #define ATH_LED_PIN_9485 6 -#define ATH_LED_PIN_9462 4 +#define ATH_LED_PIN_9462 0 #ifdef CONFIG_MAC80211_LEDS void ath_init_leds(struct ath_softc *sc); @@ -573,7 +540,7 @@ struct ath_ant_comb { #define DEFAULT_CACHELINE 32 #define ATH_REGCLASSIDS_MAX 10 #define ATH_CABQ_READY_TIME 80 /* % of beacon interval */ -#define ATH_MAX_SW_RETRIES 30 +#define ATH_MAX_SW_RETRIES 10 #define ATH_CHAN_MAX 255 #define ATH_TXPOWER_MAX 100 /* .5 dBm units */ @@ -581,13 +548,19 @@ struct ath_ant_comb { #define SC_OP_INVALID BIT(0) #define SC_OP_BEACONS BIT(1) -#define SC_OP_OFFCHANNEL BIT(2) -#define SC_OP_RXFLUSH BIT(3) -#define SC_OP_TSF_RESET BIT(4) -#define SC_OP_BT_PRIORITY_DETECTED BIT(5) -#define SC_OP_BT_SCAN BIT(6) -#define SC_OP_ANI_RUN BIT(7) -#define SC_OP_PRIM_STA_VIF BIT(8) +#define SC_OP_RXAGGR BIT(2) +#define SC_OP_TXAGGR BIT(3) +#define SC_OP_OFFCHANNEL BIT(4) +#define SC_OP_PREAMBLE_SHORT BIT(5) +#define SC_OP_PROTECT_ENABLE BIT(6) +#define SC_OP_RXFLUSH BIT(7) +#define SC_OP_LED_ASSOCIATED BIT(8) +#define SC_OP_LED_ON BIT(9) +#define SC_OP_TSF_RESET BIT(11) +#define SC_OP_BT_PRIORITY_DETECTED BIT(12) +#define SC_OP_BT_SCAN BIT(13) +#define SC_OP_ANI_RUN BIT(14) +#define SC_OP_PRIM_STA_VIF BIT(15) /* Powersave flags */ #define PS_WAIT_FOR_BEACON BIT(0) @@ -609,12 +582,15 @@ struct ath9k_vif_iter_data { int nstations; /* number of station vifs */ int nwds; /* number of WDS vifs */ int nadhocs; /* number of adhoc vifs */ + int nothers; /* number of vifs not specified above. */ }; struct ath_softc { struct ieee80211_hw *hw; struct device *dev; + int chan_idx; + int chan_is_ht; struct survey_info *cur_survey; struct survey_info survey[ATH9K_NUM_CHANNELS]; @@ -668,11 +644,7 @@ struct ath_softc { struct ath_beacon_config cur_beacon_conf; struct delayed_work tx_complete_work; struct delayed_work hw_pll_work; - -#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT struct ath_btcoex btcoex; - struct ath_mci_coex mci_coex; -#endif struct ath_descdma txsdma; @@ -700,6 +672,7 @@ void ath9k_deinit_device(struct ath_softc *sc); void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); void ath9k_reload_chainmask_settings(struct ath_softc *sc); +void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw); bool ath9k_uses_beacons(int type); #ifdef CONFIG_ATH9K_PCI diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c old mode 100644 new mode 100755 index 2e4243fc..2bbc83e1 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -67,7 +67,7 @@ int ath_beaconq_config(struct ath_softc *sc) * up rate codes, and channel flags. Beacons are always sent out at the * lowest rate, and are not retried. */ -static void ath_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, +static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, struct ath_buf *bf, int rateidx) { struct sk_buff *skb = bf->bf_mpdu; @@ -82,7 +82,7 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, sband = &sc->sbands[common->hw->conf.channel->band]; rate = sband->bitrates[rateidx].hw_value; - if (vif->bss_conf.use_short_preamble) + if (sc->sc_flags & SC_OP_PREAMBLE_SHORT) rate |= sband->bitrates[rateidx].hw_value_short; memset(&info, 0, sizeof(info)); @@ -91,7 +91,7 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, info.txpower = MAX_RATE_POWER; info.keyix = ATH9K_TXKEYIX_INVALID; info.keytype = ATH9K_KEY_TYPE_CLEAR; - info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_INTREQ; + info.flags = ATH9K_TXDESC_NOACK; info.buf_addr[0] = bf->bf_buf_addr; info.buf_len[0] = roundup(skb->len, 4); @@ -117,11 +117,12 @@ static void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb) memset(&txctl, 0, sizeof(struct ath_tx_control)); txctl.txq = sc->beacon.cabq; - ath_dbg(common, XMIT, "transmitting CABQ packet, skb: %p\n", skb); + ath_dbg(common, ATH_DBG_XMIT, + "transmitting CABQ packet, skb: %p\n", skb); if (ath_tx_start(hw, skb, &txctl) != 0) { - ath_dbg(common, XMIT, "CABQ TX failed\n"); - ieee80211_free_txskb(hw, skb); + ath_dbg(common, ATH_DBG_XMIT, "CABQ TX failed\n"); + dev_kfree_skb_any(skb); } } @@ -204,13 +205,13 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, if (skb && cabq_depth) { if (sc->nvifs > 1) { - ath_dbg(common, BEACON, + ath_dbg(common, ATH_DBG_BEACON, "Flushing previous cabq traffic\n"); ath_draintxq(sc, cabq, false); } } - ath_beacon_setup(sc, vif, bf, info->control.rates[0].idx); + ath_beacon_setup(sc, avp, bf, info->control.rates[0].idx); while (skb) { ath_tx_cabq(hw, skb); @@ -297,7 +298,7 @@ int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_vif *vif) tsfadjust = TU_TO_USEC(intval * avp->av_bslot) / ATH_BCBUF; avp->tsf_adjust = cpu_to_le64(tsfadjust); - ath_dbg(common, BEACON, + ath_dbg(common, ATH_DBG_BEACON, "stagger beacons, bslot %d intval %u tsfadjust %llu\n", avp->av_bslot, intval, (unsigned long long)tsfadjust); @@ -356,7 +357,7 @@ void ath_beacon_tasklet(unsigned long data) struct ath_common *common = ath9k_hw_common(ah); struct ath_buf *bf = NULL; struct ieee80211_vif *vif; - bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); + struct ath_tx_status ts; int slot; u32 bfaddr, bc = 0; @@ -371,14 +372,15 @@ void ath_beacon_tasklet(unsigned long data) sc->beacon.bmisscnt++; if (sc->beacon.bmisscnt < BSTUCK_THRESH * sc->nbcnvifs) { - ath_dbg(common, BSTUCK, + ath_dbg(common, ATH_DBG_BSTUCK, "missed %u consecutive beacons\n", sc->beacon.bmisscnt); ath9k_hw_stop_dma_queue(ah, sc->beacon.beaconq); if (sc->beacon.bmisscnt > 3) ath9k_hw_bstuck_nfcal(ah); } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { - ath_dbg(common, BSTUCK, "beacon is officially stuck\n"); + ath_dbg(common, ATH_DBG_BSTUCK, + "beacon is officially stuck\n"); sc->sc_flags |= SC_OP_TSF_RESET; ieee80211_queue_work(sc->hw, &sc->hw_reset_work); } @@ -405,7 +407,7 @@ void ath_beacon_tasklet(unsigned long data) slot = (tsftu % (intval * ATH_BCBUF)) / intval; vif = sc->beacon.bslot[slot]; - ath_dbg(common, BEACON, + ath_dbg(common, ATH_DBG_BEACON, "slot %d [tsf %llu tsftu %u intval %u] vif %p\n", slot, tsf, tsftu / ATH_BCBUF, intval, vif); } else { @@ -423,7 +425,7 @@ void ath_beacon_tasklet(unsigned long data) } if (sc->beacon.bmisscnt != 0) { - ath_dbg(common, BSTUCK, + ath_dbg(common, ATH_DBG_BSTUCK, "resume beacon xmit after %u misses\n", sc->beacon.bmisscnt); sc->beacon.bmisscnt = 0; @@ -457,11 +459,14 @@ void ath_beacon_tasklet(unsigned long data) if (bfaddr != 0) { /* NB: cabq traffic should already be queued and primed */ ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bfaddr); - - if (!edma) - ath9k_hw_txstart(ah, sc->beacon.beaconq); + ath9k_hw_txstart(ah, sc->beacon.beaconq); sc->beacon.ast_be_xmit += bc; /* XXX per-vif? */ + if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { + spin_lock_bh(&sc->sc_pcu_lock); + ath9k_hw_txprocdesc(ah, bf->bf_desc, (void *)&ts); + spin_unlock_bh(&sc->sc_pcu_lock); + } } } @@ -537,7 +542,7 @@ static void ath_beacon_config_sta(struct ath_softc *sc, /* No need to configure beacon if we are not associated */ if (!common->curaid) { - ath_dbg(common, BEACON, + ath_dbg(common, ATH_DBG_BEACON, "STA is not yet associated..skipping beacon config\n"); return; } @@ -627,8 +632,8 @@ static void ath_beacon_config_sta(struct ath_softc *sc, /* TSF out of range threshold fixed at 1 second */ bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; - ath_dbg(common, BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); - ath_dbg(common, BEACON, + ath_dbg(common, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); + ath_dbg(common, ATH_DBG_BEACON, "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", bs.bs_bmissthreshold, bs.bs_sleepduration, bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); @@ -656,7 +661,8 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc, tsf = roundup(ath9k_hw_gettsf32(ah) + TU_TO_USEC(FUDGE), intval); nexttbtt = tsf + intval; - ath_dbg(common, BEACON, "IBSS nexttbtt %u intval %u (%u)\n", + ath_dbg(common, ATH_DBG_BEACON, + "IBSS nexttbtt %u intval %u (%u)\n", nexttbtt, intval, conf->beacon_interval); /* @@ -694,8 +700,9 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc, (sc->nbcnvifs > 1) && (vif->type == NL80211_IFTYPE_AP) && (cur_conf->beacon_interval != bss_conf->beacon_int)) { - ath_dbg(common, CONFIG, - "Changing beacon interval of multiple AP interfaces !\n"); + ath_dbg(common, ATH_DBG_CONFIG, + "Changing beacon interval of multiple \ + AP interfaces !\n"); return false; } /* @@ -704,7 +711,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc, */ if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) && (vif->type != NL80211_IFTYPE_AP)) { - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "STA vif's beacon not allowed on AP mode\n"); return false; } @@ -716,7 +723,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc, (vif->type == NL80211_IFTYPE_STATION) && (sc->sc_flags & SC_OP_BEACONS) && !avp->primary_sta_vif) { - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Beacon already configured for a station interface\n"); return false; } @@ -796,7 +803,8 @@ void ath_set_beacon(struct ath_softc *sc) ath_beacon_config_sta(sc, cur_conf); break; default: - ath_dbg(common, CONFIG, "Unsupported beaconing mode\n"); + ath_dbg(common, ATH_DBG_CONFIG, + "Unsupported beaconing mode\n"); return; } diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c index ec327199..9ac28d9d 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.c +++ b/drivers/net/wireless/ath/ath9k/btcoex.c @@ -21,7 +21,7 @@ enum ath_bt_mode { ATH_BT_COEX_MODE_LEGACY, /* legacy rx_clear mode */ ATH_BT_COEX_MODE_UNSLOTTED, /* untimed/unslotted mode */ ATH_BT_COEX_MODE_SLOTTED, /* slotted mode */ - ATH_BT_COEX_MODE_DISABLED, /* coexistence disabled */ + ATH_BT_COEX_MODE_DISALBED, /* coexistence disabled */ }; struct ath_btcoex_config { @@ -95,40 +95,6 @@ void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum) } EXPORT_SYMBOL(ath9k_hw_init_btcoex_hw); -void ath9k_hw_btcoex_init_scheme(struct ath_hw *ah) -{ - struct ath_common *common = ath9k_hw_common(ah); - struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; - - /* - * Check if BTCOEX is globally disabled. - */ - if (!common->btcoex_enabled) { - btcoex_hw->scheme = ATH_BTCOEX_CFG_NONE; - return; - } - - if (AR_SREV_9462(ah)) { - btcoex_hw->scheme = ATH_BTCOEX_CFG_MCI; - } else if (AR_SREV_9300_20_OR_LATER(ah)) { - btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE; - btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO_9300; - btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO_9300; - btcoex_hw->btpriority_gpio = ATH_BTPRIORITY_GPIO_9300; - } else if (AR_SREV_9280_20_OR_LATER(ah)) { - btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO_9280; - btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO_9280; - - if (AR_SREV_9285(ah)) { - btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE; - btcoex_hw->btpriority_gpio = ATH_BTPRIORITY_GPIO_9285; - } else { - btcoex_hw->scheme = ATH_BTCOEX_CFG_2WIRE; - } - } -} -EXPORT_SYMBOL(ath9k_hw_btcoex_init_scheme); - void ath9k_hw_btcoex_init_2wire(struct ath_hw *ah) { struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; @@ -177,30 +143,6 @@ void ath9k_hw_btcoex_init_3wire(struct ath_hw *ah) } EXPORT_SYMBOL(ath9k_hw_btcoex_init_3wire); -void ath9k_hw_btcoex_init_mci(struct ath_hw *ah) -{ - ah->btcoex_hw.mci.ready = false; - ah->btcoex_hw.mci.bt_state = 0; - ah->btcoex_hw.mci.bt_ver_major = 3; - ah->btcoex_hw.mci.bt_ver_minor = 0; - ah->btcoex_hw.mci.bt_version_known = false; - ah->btcoex_hw.mci.update_2g5g = true; - ah->btcoex_hw.mci.is_2g = true; - ah->btcoex_hw.mci.wlan_channels_update = false; - ah->btcoex_hw.mci.wlan_channels[0] = 0x00000000; - ah->btcoex_hw.mci.wlan_channels[1] = 0xffffffff; - ah->btcoex_hw.mci.wlan_channels[2] = 0xffffffff; - ah->btcoex_hw.mci.wlan_channels[3] = 0x7fffffff; - ah->btcoex_hw.mci.query_bt = true; - ah->btcoex_hw.mci.unhalt_bt_gpm = true; - ah->btcoex_hw.mci.halted_bt_gpm = false; - ah->btcoex_hw.mci.need_flush_btinfo = false; - ah->btcoex_hw.mci.wlan_cal_seq = 0; - ah->btcoex_hw.mci.wlan_cal_done = 0; - ah->btcoex_hw.mci.config = 0x2201; -} -EXPORT_SYMBOL(ath9k_hw_btcoex_init_mci); - static void ath9k_hw_btcoex_enable_2wire(struct ath_hw *ah) { struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; @@ -277,9 +219,9 @@ void ath9k_hw_btcoex_enable(struct ath_hw *ah) { struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; - switch (ath9k_hw_get_btcoex_scheme(ah)) { + switch (btcoex_hw->scheme) { case ATH_BTCOEX_CFG_NONE: - return; + break; case ATH_BTCOEX_CFG_2WIRE: ath9k_hw_btcoex_enable_2wire(ah); break; @@ -371,7 +313,8 @@ void ath9k_hw_btcoex_bt_stomp(struct ath_hw *ah, AR_STOMP_NONE_WLAN_WGHT); break; default: - ath_dbg(ath9k_hw_common(ah), BTCOEX, "Invalid Stomptype\n"); + ath_dbg(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + "Invalid Stomptype\n"); break; } } diff --git a/drivers/net/wireless/ath/ath9k/btcoex.h b/drivers/net/wireless/ath/ath9k/btcoex.h index 8f93aef4..d5e5db1f 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.h +++ b/drivers/net/wireless/ath/ath9k/btcoex.h @@ -54,38 +54,8 @@ enum ath_btcoex_scheme { ATH_BTCOEX_CFG_MCI, }; -struct ath9k_hw_mci { - u32 raw_intr; - u32 rx_msg_intr; - u32 cont_status; - u32 gpm_addr; - u32 gpm_len; - u32 gpm_idx; - u32 sched_addr; - u32 wlan_channels[4]; - u32 wlan_cal_seq; - u32 wlan_cal_done; - u32 config; - u8 *gpm_buf; - bool ready; - bool update_2g5g; - bool is_2g; - bool query_bt; - bool unhalt_bt_gpm; /* need send UNHALT */ - bool halted_bt_gpm; /* HALT sent */ - bool need_flush_btinfo; - bool bt_version_known; - bool wlan_channels_update; - u8 wlan_ver_major; - u8 wlan_ver_minor; - u8 bt_ver_major; - u8 bt_ver_minor; - u8 bt_state; -}; - struct ath_btcoex_hw { enum ath_btcoex_scheme scheme; - struct ath9k_hw_mci mci; bool enabled; u8 wlanactive_gpio; u8 btactive_gpio; @@ -97,14 +67,13 @@ struct ath_btcoex_hw { u32 wlan_weight[AR9300_NUM_WLAN_WEIGHTS]; }; -void ath9k_hw_btcoex_init_scheme(struct ath_hw *ah); void ath9k_hw_btcoex_init_2wire(struct ath_hw *ah); void ath9k_hw_btcoex_init_3wire(struct ath_hw *ah); -void ath9k_hw_btcoex_init_mci(struct ath_hw *ah); void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum); void ath9k_hw_btcoex_set_weight(struct ath_hw *ah, u32 bt_weight, u32 wlan_weight); +void ath9k_hw_btcoex_enable(struct ath_hw *ah); void ath9k_hw_btcoex_disable(struct ath_hw *ah); void ath9k_hw_btcoex_bt_stomp(struct ath_hw *ah, enum ath_stomp_type stomp_type); diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index a393e87e..7d0dba42 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -20,6 +20,7 @@ /* Common calibration code */ +#define ATH9K_NF_TOO_HIGH -60 static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) { @@ -116,7 +117,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, if (h[i].privNF > limit->max) { high_nf_mid = true; - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "NFmid[%d] (%d) > MAX (%d), %s\n", i, h[i].privNF, limit->max, (cal->nfcal_interference ? @@ -199,7 +200,8 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) return true; if (currCal->calState != CAL_DONE) { - ath_dbg(common, CALIBRATE, "Calibration state incorrect, %d\n", + ath_dbg(common, ATH_DBG_CALIBRATE, + "Calibration state incorrect, %d\n", currCal->calState); return true; } @@ -207,7 +209,8 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) if (!(ah->supp_cals & currCal->calData->calType)) return true; - ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n", + ath_dbg(common, ATH_DBG_CALIBRATE, + "Resetting Cal %d state for channel %u\n", currCal->calData->calType, conf->channel->center_freq); ah->caldata->CalValid &= ~currCal->calData->calType; @@ -300,7 +303,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) * noisefloor until the next calibration timer. */ if (j == 10000) { - ath_dbg(common, ANY, + ath_dbg(common, ATH_DBG_ANY, "Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n", REG_READ(ah, AR_PHY_AGC_CONTROL)); return; @@ -342,17 +345,17 @@ static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf) if (!nf[i]) continue; - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "NF calibrated [%s] [chain %d] is %d\n", (i >= 3 ? "ext" : "ctl"), i % 3, nf[i]); - if (nf[i] > limit->max) { - ath_dbg(common, CALIBRATE, + if (nf[i] > ATH9K_NF_TOO_HIGH) { + ath_dbg(common, ATH_DBG_CALIBRATE, "NF[%d] (%d) > MAX (%d), correcting to MAX\n", - i, nf[i], limit->max); + i, nf[i], ATH9K_NF_TOO_HIGH); nf[i] = limit->max; } else if (nf[i] < limit->min) { - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "NF[%d] (%d) < MIN (%d), correcting to NOM\n", i, nf[i], limit->min); nf[i] = limit->nominal; @@ -371,7 +374,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) chan->channelFlags &= (~CHANNEL_CW_INT); if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "NF did not complete in calibration window\n"); return false; } @@ -381,7 +384,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) nf = nfarray[0]; if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh) && nf > nfThresh) { - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "noise floor failed detected; detected %d, threshold %d\n", nf, nfThresh); chan->channelFlags |= CHANNEL_CW_INT; @@ -399,7 +402,6 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) ah->noise = ath9k_hw_getchan_noise(ah, chan); return true; } -EXPORT_SYMBOL(ath9k_hw_getnf); void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, struct ath9k_channel *chan) diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h old mode 100644 new mode 100755 index 1f2e01a9..e300a738 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -19,6 +19,7 @@ #include "hw.h" +#define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT 3 #define AR_PHY_CCA_FILTERWINDOW_LENGTH 5 /* Internal noise floor can vary by about 6db depending on the frequency */ diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h old mode 100644 new mode 100755 diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index ff47b32e..6fb719d8 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -26,6 +26,11 @@ #define REG_READ_D(_ah, _reg) \ ath9k_hw_common(_ah)->ops->read((_ah), (_reg)) +static int ath9k_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} static ssize_t ath9k_debugfs_read_buf(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -78,7 +83,7 @@ static ssize_t write_file_debug(struct file *file, const char __user *user_buf, static const struct file_operations fops_debug = { .read = read_file_debug, .write = write_file_debug, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -124,7 +129,7 @@ static ssize_t write_file_tx_chainmask(struct file *file, const char __user *use static const struct file_operations fops_tx_chainmask = { .read = read_file_tx_chainmask, .write = write_file_tx_chainmask, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -167,7 +172,7 @@ static ssize_t write_file_rx_chainmask(struct file *file, const char __user *use static const struct file_operations fops_rx_chainmask = { .read = read_file_rx_chainmask, .write = write_file_rx_chainmask, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -218,7 +223,7 @@ static ssize_t write_file_disable_ani(struct file *file, static const struct file_operations fops_disable_ani = { .read = read_file_disable_ani, .write = write_file_disable_ani, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -319,7 +324,7 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf, static const struct file_operations fops_dma = { .read = read_file_dma, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -441,7 +446,110 @@ static ssize_t read_file_interrupt(struct file *file, char __user *user_buf, static const struct file_operations fops_interrupt = { .read = read_file_interrupt, - .open = simple_open, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static const char *channel_type_str(enum nl80211_channel_type t) +{ + switch (t) { + case NL80211_CHAN_NO_HT: + return "no ht"; + case NL80211_CHAN_HT20: + return "ht20"; + case NL80211_CHAN_HT40MINUS: + return "ht40-"; + case NL80211_CHAN_HT40PLUS: + return "ht40+"; + default: + return "???"; + } +} + +static ssize_t read_file_wiphy(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ieee80211_channel *chan = sc->hw->conf.channel; + struct ieee80211_conf *conf = &(sc->hw->conf); + char buf[512]; + unsigned int len = 0; + u8 addr[ETH_ALEN]; + u32 tmp; + + len += snprintf(buf + len, sizeof(buf) - len, + "%s (chan=%d center-freq: %d MHz channel-type: %d (%s))\n", + wiphy_name(sc->hw->wiphy), + ieee80211_frequency_to_channel(chan->center_freq), + chan->center_freq, + conf->channel_type, + channel_type_str(conf->channel_type)); + + ath9k_ps_wakeup(sc); + put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_STA_ID0), addr); + put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4); + len += snprintf(buf + len, sizeof(buf) - len, + "addr: %pM\n", addr); + put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_BSSMSKL), addr); + put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4); + len += snprintf(buf + len, sizeof(buf) - len, + "addrmask: %pM\n", addr); + tmp = ath9k_hw_getrxfilter(sc->sc_ah); + ath9k_ps_restore(sc); + len += snprintf(buf + len, sizeof(buf) - len, + "rfilt: 0x%x", tmp); + if (tmp & ATH9K_RX_FILTER_UCAST) + len += snprintf(buf + len, sizeof(buf) - len, " UCAST"); + if (tmp & ATH9K_RX_FILTER_MCAST) + len += snprintf(buf + len, sizeof(buf) - len, " MCAST"); + if (tmp & ATH9K_RX_FILTER_BCAST) + len += snprintf(buf + len, sizeof(buf) - len, " BCAST"); + if (tmp & ATH9K_RX_FILTER_CONTROL) + len += snprintf(buf + len, sizeof(buf) - len, " CONTROL"); + if (tmp & ATH9K_RX_FILTER_BEACON) + len += snprintf(buf + len, sizeof(buf) - len, " BEACON"); + if (tmp & ATH9K_RX_FILTER_PROM) + len += snprintf(buf + len, sizeof(buf) - len, " PROM"); + if (tmp & ATH9K_RX_FILTER_PROBEREQ) + len += snprintf(buf + len, sizeof(buf) - len, " PROBEREQ"); + if (tmp & ATH9K_RX_FILTER_PHYERR) + len += snprintf(buf + len, sizeof(buf) - len, " PHYERR"); + if (tmp & ATH9K_RX_FILTER_MYBEACON) + len += snprintf(buf + len, sizeof(buf) - len, " MYBEACON"); + if (tmp & ATH9K_RX_FILTER_COMP_BAR) + len += snprintf(buf + len, sizeof(buf) - len, " COMP_BAR"); + if (tmp & ATH9K_RX_FILTER_PSPOLL) + len += snprintf(buf + len, sizeof(buf) - len, " PSPOLL"); + if (tmp & ATH9K_RX_FILTER_PHYRADAR) + len += snprintf(buf + len, sizeof(buf) - len, " PHYRADAR"); + if (tmp & ATH9K_RX_FILTER_MCAST_BCAST_ALL) + len += snprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL"); + + len += snprintf(buf + len, sizeof(buf) - len, + "\n\nReset causes:\n" + " baseband hang: %d\n" + " baseband watchdog: %d\n" + " fatal hardware error interrupt: %d\n" + " tx hardware error: %d\n" + " tx path hang: %d\n" + " pll rx hang: %d\n", + sc->debug.stats.reset[RESET_TYPE_BB_HANG], + sc->debug.stats.reset[RESET_TYPE_BB_WATCHDOG], + sc->debug.stats.reset[RESET_TYPE_FATAL_INT], + sc->debug.stats.reset[RESET_TYPE_TX_ERROR], + sc->debug.stats.reset[RESET_TYPE_TX_HANG], + sc->debug.stats.reset[RESET_TYPE_PLL_HANG]); + + if (len > sizeof(buf)) + len = sizeof(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_wiphy = { + .read = read_file_wiphy, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -655,126 +763,85 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf, { struct ath_softc *sc = file->private_data; struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_hw *ah = sc->sc_ah; struct ieee80211_hw *hw = sc->hw; - struct ath9k_vif_iter_data iter_data; - char buf[512]; - unsigned int len = 0; + char *buf; + unsigned int len = 0, size = 8000; ssize_t retval = 0; unsigned int reg; - u32 rxfilter; + struct ath9k_vif_iter_data iter_data; - len += snprintf(buf + len, sizeof(buf) - len, - "BSSID: %pM\n", common->curbssid); - len += snprintf(buf + len, sizeof(buf) - len, - "BSSID-MASK: %pM\n", common->bssidmask); - len += snprintf(buf + len, sizeof(buf) - len, - "OPMODE: %s\n", ath_opmode_to_string(sc->sc_ah->opmode)); + ath9k_calculate_iter_data(hw, NULL, &iter_data); + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; ath9k_ps_wakeup(sc); - rxfilter = ath9k_hw_getrxfilter(sc->sc_ah); + len += snprintf(buf + len, size - len, + "curbssid: %pM\n" + "OP-Mode: %s(%i)\n" + "Beacon-Timer-Register: 0x%x\n", + common->curbssid, + ath_opmode_to_string(sc->sc_ah->opmode), + (int)(sc->sc_ah->opmode), + REG_READ(ah, AR_BEACON_PERIOD)); + + reg = REG_READ(ah, AR_TIMER_MODE); ath9k_ps_restore(sc); - - len += snprintf(buf + len, sizeof(buf) - len, - "RXFILTER: 0x%x", rxfilter); - - if (rxfilter & ATH9K_RX_FILTER_UCAST) - len += snprintf(buf + len, sizeof(buf) - len, " UCAST"); - if (rxfilter & ATH9K_RX_FILTER_MCAST) - len += snprintf(buf + len, sizeof(buf) - len, " MCAST"); - if (rxfilter & ATH9K_RX_FILTER_BCAST) - len += snprintf(buf + len, sizeof(buf) - len, " BCAST"); - if (rxfilter & ATH9K_RX_FILTER_CONTROL) - len += snprintf(buf + len, sizeof(buf) - len, " CONTROL"); - if (rxfilter & ATH9K_RX_FILTER_BEACON) - len += snprintf(buf + len, sizeof(buf) - len, " BEACON"); - if (rxfilter & ATH9K_RX_FILTER_PROM) - len += snprintf(buf + len, sizeof(buf) - len, " PROM"); - if (rxfilter & ATH9K_RX_FILTER_PROBEREQ) - len += snprintf(buf + len, sizeof(buf) - len, " PROBEREQ"); - if (rxfilter & ATH9K_RX_FILTER_PHYERR) - len += snprintf(buf + len, sizeof(buf) - len, " PHYERR"); - if (rxfilter & ATH9K_RX_FILTER_MYBEACON) - len += snprintf(buf + len, sizeof(buf) - len, " MYBEACON"); - if (rxfilter & ATH9K_RX_FILTER_COMP_BAR) - len += snprintf(buf + len, sizeof(buf) - len, " COMP_BAR"); - if (rxfilter & ATH9K_RX_FILTER_PSPOLL) - len += snprintf(buf + len, sizeof(buf) - len, " PSPOLL"); - if (rxfilter & ATH9K_RX_FILTER_PHYRADAR) - len += snprintf(buf + len, sizeof(buf) - len, " PHYRADAR"); - if (rxfilter & ATH9K_RX_FILTER_MCAST_BCAST_ALL) - len += snprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL"); - if (rxfilter & ATH9K_RX_FILTER_CONTROL_WRAPPER) - len += snprintf(buf + len, sizeof(buf) - len, " CONTROL_WRAPPER"); - - len += snprintf(buf + len, sizeof(buf) - len, "\n"); + len += snprintf(buf + len, size - len, "Timer-Mode-Register: 0x%x (", + reg); + if (reg & AR_TBTT_TIMER_EN) + len += snprintf(buf + len, size - len, "TBTT "); + if (reg & AR_DBA_TIMER_EN) + len += snprintf(buf + len, size - len, "DBA "); + if (reg & AR_SWBA_TIMER_EN) + len += snprintf(buf + len, size - len, "SWBA "); + if (reg & AR_HCF_TIMER_EN) + len += snprintf(buf + len, size - len, "HCF "); + if (reg & AR_TIM_TIMER_EN) + len += snprintf(buf + len, size - len, "TIM "); + if (reg & AR_DTIM_TIMER_EN) + len += snprintf(buf + len, size - len, "DTIM "); + len += snprintf(buf + len, size - len, ")\n"); reg = sc->sc_ah->imask; - - len += snprintf(buf + len, sizeof(buf) - len, "INTERRUPT-MASK: 0x%x", reg); - + len += snprintf(buf + len, size - len, "imask: 0x%x (", reg); if (reg & ATH9K_INT_SWBA) - len += snprintf(buf + len, sizeof(buf) - len, " SWBA"); + len += snprintf(buf + len, size - len, "SWBA "); if (reg & ATH9K_INT_BMISS) - len += snprintf(buf + len, sizeof(buf) - len, " BMISS"); + len += snprintf(buf + len, size - len, "BMISS "); if (reg & ATH9K_INT_CST) - len += snprintf(buf + len, sizeof(buf) - len, " CST"); + len += snprintf(buf + len, size - len, "CST "); if (reg & ATH9K_INT_RX) - len += snprintf(buf + len, sizeof(buf) - len, " RX"); + len += snprintf(buf + len, size - len, "RX "); if (reg & ATH9K_INT_RXHP) - len += snprintf(buf + len, sizeof(buf) - len, " RXHP"); + len += snprintf(buf + len, size - len, "RXHP "); if (reg & ATH9K_INT_RXLP) - len += snprintf(buf + len, sizeof(buf) - len, " RXLP"); + len += snprintf(buf + len, size - len, "RXLP "); if (reg & ATH9K_INT_BB_WATCHDOG) - len += snprintf(buf + len, sizeof(buf) - len, " BB_WATCHDOG"); + len += snprintf(buf + len, size - len, "BB_WATCHDOG "); + /* there are other IRQs if one wanted to add them. */ + len += snprintf(buf + len, size - len, ")\n"); - len += snprintf(buf + len, sizeof(buf) - len, "\n"); - - ath9k_calculate_iter_data(hw, NULL, &iter_data); - - len += snprintf(buf + len, sizeof(buf) - len, - "VIF-COUNTS: AP: %i STA: %i MESH: %i WDS: %i" - " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n", + len += snprintf(buf + len, size - len, + "VIF Counts: AP: %i STA: %i MESH: %i WDS: %i" + " ADHOC: %i OTHER: %i nvifs: %hi beacon-vifs: %hi\n", iter_data.naps, iter_data.nstations, iter_data.nmeshes, - iter_data.nwds, iter_data.nadhocs, + iter_data.nwds, iter_data.nadhocs, iter_data.nothers, sc->nvifs, sc->nbcnvifs); - if (len > sizeof(buf)) - len = sizeof(buf); - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - return retval; -} - -static ssize_t read_file_reset(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char buf[512]; - unsigned int len = 0; + len += snprintf(buf + len, size - len, + "Calculated-BSSID-Mask: %pM\n", + iter_data.mask); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "Baseband Hang", - sc->debug.stats.reset[RESET_TYPE_BB_HANG]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "Baseband Watchdog", - sc->debug.stats.reset[RESET_TYPE_BB_WATCHDOG]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "Fatal HW Error", - sc->debug.stats.reset[RESET_TYPE_FATAL_INT]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "TX HW error", - sc->debug.stats.reset[RESET_TYPE_TX_ERROR]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "TX Path Hang", - sc->debug.stats.reset[RESET_TYPE_TX_HANG]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "PLL RX Hang", - sc->debug.stats.reset[RESET_TYPE_PLL_HANG]); + if (len > size) + len = size; - if (len > sizeof(buf)) - len = sizeof(buf); + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); + return retval; } void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, @@ -789,7 +856,7 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, sc->debug.stats.txstats[qnum].tx_bytes_all += bf->bf_mpdu->len; if (bf_isampdu(bf)) { - if (flags & ATH_TX_ERROR) + if (flags & ATH_TX_BAR) TX_STAT_INC(qnum, a_xretries); else TX_STAT_INC(qnum, a_completed); @@ -813,7 +880,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN) TX_STAT_INC(qnum, delim_underrun); -#ifdef CONFIG_ATH9K_MAC_DEBUG spin_lock(&sc->debug.samp_lock); TX_SAMP_DBG(jiffies) = jiffies; TX_SAMP_DBG(rssi_ctl0) = ts->ts_rssi_ctl0; @@ -840,35 +906,27 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, sc->debug.tsidx = (sc->debug.tsidx + 1) % ATH_DBG_MAX_SAMPLES; spin_unlock(&sc->debug.samp_lock); -#endif #undef TX_SAMP_DBG } static const struct file_operations fops_xmit = { .read = read_file_xmit, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static const struct file_operations fops_stations = { .read = read_file_stations, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static const struct file_operations fops_misc = { .read = read_file_misc, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static const struct file_operations fops_reset = { - .read = read_file_reset, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -877,12 +935,12 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { #define PHY_ERR(s, p) \ - len += snprintf(buf + len, size - len, "%22s : %10u\n", s, \ + len += snprintf(buf + len, size - len, "%18s : %10u\n", s, \ sc->debug.stats.rxstats.phy_err_stats[p]); struct ath_softc *sc = file->private_data; char *buf; - unsigned int len = 0, size = 1600; + unsigned int len = 0, size = 1400; ssize_t retval = 0; buf = kzalloc(size, GFP_KERNEL); @@ -890,59 +948,87 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, return -ENOMEM; len += snprintf(buf + len, size - len, - "%22s : %10u\n", "CRC ERR", + "%18s : %10u\n", "CRC ERR", sc->debug.stats.rxstats.crc_err); len += snprintf(buf + len, size - len, - "%22s : %10u\n", "DECRYPT CRC ERR", + "%18s : %10u\n", "DECRYPT CRC ERR", sc->debug.stats.rxstats.decrypt_crc_err); len += snprintf(buf + len, size - len, - "%22s : %10u\n", "PHY ERR", + "%18s : %10u\n", "PHY ERR", sc->debug.stats.rxstats.phy_err); len += snprintf(buf + len, size - len, - "%22s : %10u\n", "MIC ERR", + "%18s : %10u\n", "MIC ERR", sc->debug.stats.rxstats.mic_err); len += snprintf(buf + len, size - len, - "%22s : %10u\n", "PRE-DELIM CRC ERR", + "%18s : %10u\n", "PRE-DELIM CRC ERR", sc->debug.stats.rxstats.pre_delim_crc_err); len += snprintf(buf + len, size - len, - "%22s : %10u\n", "POST-DELIM CRC ERR", + "%18s : %10u\n", "POST-DELIM CRC ERR", sc->debug.stats.rxstats.post_delim_crc_err); len += snprintf(buf + len, size - len, - "%22s : %10u\n", "DECRYPT BUSY ERR", + "%18s : %10u\n", "DECRYPT BUSY ERR", sc->debug.stats.rxstats.decrypt_busy_err); - PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN); - PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING); - PHY_ERR("PARITY ERR", ATH9K_PHYERR_PARITY); - PHY_ERR("RATE ERR", ATH9K_PHYERR_RATE); - PHY_ERR("LENGTH ERR", ATH9K_PHYERR_LENGTH); - PHY_ERR("RADAR ERR", ATH9K_PHYERR_RADAR); - PHY_ERR("SERVICE ERR", ATH9K_PHYERR_SERVICE); - PHY_ERR("TOR ERR", ATH9K_PHYERR_TOR); - PHY_ERR("OFDM-TIMING ERR", ATH9K_PHYERR_OFDM_TIMING); - PHY_ERR("OFDM-SIGNAL-PARITY ERR", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); - PHY_ERR("OFDM-RATE ERR", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); - PHY_ERR("OFDM-LENGTH ERR", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL); - PHY_ERR("OFDM-POWER-DROP ERR", ATH9K_PHYERR_OFDM_POWER_DROP); - PHY_ERR("OFDM-SERVICE ERR", ATH9K_PHYERR_OFDM_SERVICE); - PHY_ERR("OFDM-RESTART ERR", ATH9K_PHYERR_OFDM_RESTART); - PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT); - PHY_ERR("CCK-TIMING ERR", ATH9K_PHYERR_CCK_TIMING); - PHY_ERR("CCK-HEADER-CRC ERR", ATH9K_PHYERR_CCK_HEADER_CRC); - PHY_ERR("CCK-RATE ERR", ATH9K_PHYERR_CCK_RATE_ILLEGAL); - PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE); - PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART); - PHY_ERR("CCK-LENGTH ERR", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); - PHY_ERR("CCK-POWER-DROP ERR", ATH9K_PHYERR_CCK_POWER_DROP); - PHY_ERR("HT-CRC ERR", ATH9K_PHYERR_HT_CRC_ERROR); - PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); - PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL); + len += snprintf(buf + len, size - len, + "%18s : %10d\n", "RSSI-CTL0", + sc->debug.stats.rxstats.rs_rssi_ctl0); + + len += snprintf(buf + len, size - len, + "%18s : %10d\n", "RSSI-CTL1", + sc->debug.stats.rxstats.rs_rssi_ctl1); + + len += snprintf(buf + len, size - len, + "%18s : %10d\n", "RSSI-CTL2", + sc->debug.stats.rxstats.rs_rssi_ctl2); + + len += snprintf(buf + len, size - len, + "%18s : %10d\n", "RSSI-EXT0", + sc->debug.stats.rxstats.rs_rssi_ext0); len += snprintf(buf + len, size - len, - "%22s : %10u\n", "RX-Pkts-All", + "%18s : %10d\n", "RSSI-EXT1", + sc->debug.stats.rxstats.rs_rssi_ext1); + + len += snprintf(buf + len, size - len, + "%18s : %10d\n", "RSSI-EXT2", + sc->debug.stats.rxstats.rs_rssi_ext2); + + len += snprintf(buf + len, size - len, + "%18s : %10d\n", "Rx Antenna", + sc->debug.stats.rxstats.rs_antenna); + + PHY_ERR("UNDERRUN", ATH9K_PHYERR_UNDERRUN); + PHY_ERR("TIMING", ATH9K_PHYERR_TIMING); + PHY_ERR("PARITY", ATH9K_PHYERR_PARITY); + PHY_ERR("RATE", ATH9K_PHYERR_RATE); + PHY_ERR("LENGTH", ATH9K_PHYERR_LENGTH); + PHY_ERR("RADAR", ATH9K_PHYERR_RADAR); + PHY_ERR("SERVICE", ATH9K_PHYERR_SERVICE); + PHY_ERR("TOR", ATH9K_PHYERR_TOR); + PHY_ERR("OFDM-TIMING", ATH9K_PHYERR_OFDM_TIMING); + PHY_ERR("OFDM-SIGNAL-PARITY", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); + PHY_ERR("OFDM-RATE", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); + PHY_ERR("OFDM-LENGTH", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL); + PHY_ERR("OFDM-POWER-DROP", ATH9K_PHYERR_OFDM_POWER_DROP); + PHY_ERR("OFDM-SERVICE", ATH9K_PHYERR_OFDM_SERVICE); + PHY_ERR("OFDM-RESTART", ATH9K_PHYERR_OFDM_RESTART); + PHY_ERR("FALSE-RADAR-EXT", ATH9K_PHYERR_FALSE_RADAR_EXT); + PHY_ERR("CCK-TIMING", ATH9K_PHYERR_CCK_TIMING); + PHY_ERR("CCK-HEADER-CRC", ATH9K_PHYERR_CCK_HEADER_CRC); + PHY_ERR("CCK-RATE", ATH9K_PHYERR_CCK_RATE_ILLEGAL); + PHY_ERR("CCK-SERVICE", ATH9K_PHYERR_CCK_SERVICE); + PHY_ERR("CCK-RESTART", ATH9K_PHYERR_CCK_RESTART); + PHY_ERR("CCK-LENGTH", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); + PHY_ERR("CCK-POWER-DROP", ATH9K_PHYERR_CCK_POWER_DROP); + PHY_ERR("HT-CRC", ATH9K_PHYERR_HT_CRC_ERROR); + PHY_ERR("HT-LENGTH", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); + PHY_ERR("HT-RATE", ATH9K_PHYERR_HT_RATE_ILLEGAL); + + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "RX-Pkts-All", sc->debug.stats.rxstats.rx_pkts_all); len += snprintf(buf + len, size - len, - "%22s : %10u\n", "RX-Bytes-All", + "%18s : %10u\n", "RX-Bytes-All", sc->debug.stats.rxstats.rx_bytes_all); if (len > size) @@ -963,6 +1049,8 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) #define RX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].rs\ [sc->debug.rsidx].c) + u32 phyerr; + RX_STAT_INC(rx_pkts_all); sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen; @@ -981,11 +1069,20 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) if (rs->rs_status & ATH9K_RXERR_PHY) { RX_STAT_INC(phy_err); - if (rs->rs_phyerr < ATH9K_PHYERR_MAX) - RX_PHY_ERR_INC(rs->rs_phyerr); + phyerr = rs->rs_phyerr & 0x24; + RX_PHY_ERR_INC(phyerr); } -#ifdef CONFIG_ATH9K_MAC_DEBUG + sc->debug.stats.rxstats.rs_rssi_ctl0 = rs->rs_rssi_ctl0; + sc->debug.stats.rxstats.rs_rssi_ctl1 = rs->rs_rssi_ctl1; + sc->debug.stats.rxstats.rs_rssi_ctl2 = rs->rs_rssi_ctl2; + + sc->debug.stats.rxstats.rs_rssi_ext0 = rs->rs_rssi_ext0; + sc->debug.stats.rxstats.rs_rssi_ext1 = rs->rs_rssi_ext1; + sc->debug.stats.rxstats.rs_rssi_ext2 = rs->rs_rssi_ext2; + + sc->debug.stats.rxstats.rs_antenna = rs->rs_antenna; + spin_lock(&sc->debug.samp_lock); RX_SAMP_DBG(jiffies) = jiffies; RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0; @@ -1002,8 +1099,6 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) sc->debug.rsidx = (sc->debug.rsidx + 1) % ATH_DBG_MAX_SAMPLES; spin_unlock(&sc->debug.samp_lock); -#endif - #undef RX_STAT_INC #undef RX_PHY_ERR_INC #undef RX_SAMP_DBG @@ -1011,7 +1106,7 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) static const struct file_operations fops_recv = { .read = read_file_recv, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1050,7 +1145,7 @@ static ssize_t write_file_regidx(struct file *file, const char __user *user_buf, static const struct file_operations fops_regidx = { .read = read_file_regidx, .write = write_file_regidx, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1097,7 +1192,7 @@ static ssize_t write_file_regval(struct file *file, const char __user *user_buf, static const struct file_operations fops_regval = { .read = read_file_regval, .write = write_file_regval, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1186,7 +1281,7 @@ static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf, static const struct file_operations fops_dump_nfcal = { .read = read_file_dump_nfcal, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1214,7 +1309,7 @@ static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, static const struct file_operations fops_base_eeprom = { .read = read_file_base_eeprom, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -1242,13 +1337,11 @@ static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf, static const struct file_operations fops_modal_eeprom = { .read = read_file_modal_eeprom, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; -#ifdef CONFIG_ATH9K_MAC_DEBUG - void ath9k_debug_samp_bb_mac(struct ath_softc *sc) { #define ATH_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].c) @@ -1522,7 +1615,6 @@ static const struct file_operations fops_samps = { .llseek = default_llseek, }; -#endif int ath9k_init_debug(struct ath_hw *ah) { @@ -1538,21 +1630,18 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_file("debug", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_debug); #endif - - ath9k_dfs_init_debug(sc); - debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_dma); debugfs_create_file("interrupt", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_interrupt); + debugfs_create_file("wiphy", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, + sc, &fops_wiphy); debugfs_create_file("xmit", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_xmit); debugfs_create_file("stations", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_stations); debugfs_create_file("misc", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_misc); - debugfs_create_file("reset", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_reset); debugfs_create_file("recv", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_recv); debugfs_create_file("rx_chainmask", S_IRUSR | S_IWUSR, @@ -1576,10 +1665,8 @@ int ath9k_init_debug(struct ath_hw *ah) &fops_base_eeprom); debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_modal_eeprom); -#ifdef CONFIG_ATH9K_MAC_DEBUG debugfs_create_file("samples", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_samps); -#endif debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask); @@ -1587,5 +1674,10 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->sc_ah->gpio_val); + sc->debug.regidx = 0; + memset(&sc->debug.bb_mac_samp, 0, sizeof(sc->debug.bb_mac_samp)); + sc->debug.sampidx = 0; + sc->debug.tsidx = 0; + sc->debug.rsidx = 0; return 0; } diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 64fcfad4..356352ac 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -19,7 +19,6 @@ #include "hw.h" #include "rc.h" -#include "dfs_debug.h" struct ath_txq; struct ath_buf; @@ -165,6 +164,13 @@ struct ath_rx_stats { u32 post_delim_crc_err; u32 decrypt_busy_err; u32 phy_err_stats[ATH9K_PHYERR_MAX]; + int8_t rs_rssi_ctl0; + int8_t rs_rssi_ctl1; + int8_t rs_rssi_ctl2; + int8_t rs_rssi_ext0; + int8_t rs_rssi_ext1; + int8_t rs_rssi_ext2; + u8 rs_antenna; }; enum ath_reset_type { @@ -181,7 +187,6 @@ struct ath_stats { struct ath_interrupt_stats istats; struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES]; struct ath_rx_stats rxstats; - struct ath_dfs_stats dfs_stats; u32 reset[__RESET_TYPE_MAX]; }; @@ -228,17 +233,16 @@ struct ath9k_debug { struct dentry *debugfs_phy; u32 regidx; struct ath_stats stats; -#ifdef CONFIG_ATH9K_MAC_DEBUG spinlock_t samp_lock; struct ath_dbg_bb_mac_samp bb_mac_samp[ATH_DBG_MAX_SAMPLES]; u8 sampidx; u8 tsidx; u8 rsidx; -#endif }; int ath9k_init_debug(struct ath_hw *ah); +void ath9k_debug_samp_bb_mac(struct ath_softc *sc); void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status); void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, struct ath_txq *txq, @@ -252,6 +256,10 @@ static inline int ath9k_init_debug(struct ath_hw *ah) return 0; } +static inline void ath9k_debug_samp_bb_mac(struct ath_softc *sc) +{ +} + static inline void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status) { @@ -272,17 +280,4 @@ static inline void ath_debug_stat_rx(struct ath_softc *sc, #endif /* CONFIG_ATH9K_DEBUGFS */ -#ifdef CONFIG_ATH9K_MAC_DEBUG - -void ath9k_debug_samp_bb_mac(struct ath_softc *sc); - -#else - -static inline void ath9k_debug_samp_bb_mac(struct ath_softc *sc) -{ -} - -#endif - - #endif /* DEBUG_H */ diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index c4352323..e46f751a 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -305,7 +305,8 @@ void ath9k_hw_update_regulatory_maxpower(struct ath_hw *ah) regulatory->max_power_level += INCREASE_MAXPOW_BY_THREE_CHAIN; break; default: - ath_dbg(common, EEPROM, "Invalid chainmask configuration\n"); + ath_dbg(common, ATH_DBG_EEPROM, + "Invalid chainmask configuration\n"); break; } } diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index 4322ac80..9a7520f9 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -38,7 +38,7 @@ static bool __ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) for (addr = 0; addr < SIZE_EEPROM_4K; addr++) { if (!ath9k_hw_nvram_read(common, addr + eep_start_loc, eep_data)) { - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "Unable to read eeprom region\n"); return false; } @@ -62,7 +62,8 @@ static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) struct ath_common *common = ath9k_hw_common(ah); if (!ath9k_hw_use_flash(ah)) { - ath_dbg(common, EEPROM, "Reading from EEPROM, not flash\n"); + ath_dbg(common, ATH_DBG_EEPROM, + "Reading from EEPROM, not flash\n"); } if (common->bus_ops->ath_bus_type == ATH_USB) @@ -203,7 +204,8 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) return false; } - ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic); + ath_dbg(common, ATH_DBG_EEPROM, + "Read Magic = 0x%04X\n", magic); if (magic != AR5416_EEPROM_MAGIC) { magic2 = swab16(magic); @@ -225,7 +227,7 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) } } - ath_dbg(common, EEPROM, "need_swap = %s\n", + ath_dbg(common, ATH_DBG_EEPROM, "need_swap = %s.\n", need_swap ? "True" : "False"); if (need_swap) @@ -247,7 +249,7 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) u32 integer; u16 word; - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "EEPROM Endianness is not native.. Changing\n"); word = swab16(eep->baseEepHeader.length); @@ -433,11 +435,11 @@ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah, reg32 = get_unaligned_le32(&pdadcValues[4 * j]); REG_WRITE(ah, regOffset, reg32); - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "PDADC (%d,%4x): %4.4x %8.8x\n", i, regChainOffset, regOffset, reg32); - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "PDADC: Chain %d | " "PDADC %3d Value %3d | " "PDADC %3d Value %3d | " @@ -471,7 +473,7 @@ static void ath9k_hw_set_4k_power_per_rate_table(struct ath_hw *ah, int i; u16 twiceMinEdgePower; - u16 twiceMaxEdgePower; + u16 twiceMaxEdgePower = MAX_RATE_POWER; u16 scaledPower = 0, minCtlPower; u16 numCtlModes; const u16 *pCtlMode; @@ -540,7 +542,9 @@ static void ath9k_hw_set_4k_power_per_rate_table(struct ath_hw *ah, else freq = centers.ctl_center; - twiceMaxEdgePower = MAX_RATE_POWER; + if (ah->eep_ops->get_eeprom_ver(ah) == 14 && + ah->eep_ops->get_eeprom_rev(ah) <= 2) + twiceMaxEdgePower = MAX_RATE_POWER; for (i = 0; (i < AR5416_EEP4K_NUM_CTLS) && pEepData->ctlIndex[i]; i++) { @@ -1077,7 +1081,8 @@ static u16 ath9k_hw_4k_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) u16 spur_val = AR_NO_SPUR; - ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n", + ath_dbg(common, ATH_DBG_ANI, + "Getting spur idx:%d is2Ghz:%d val:%x\n", i, is2GHz, ah->config.spurchans[i][is2GHz]); switch (ah->config.spurmode) { @@ -1085,8 +1090,8 @@ static u16 ath9k_hw_4k_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) break; case SPUR_ENABLE_IOCTL: spur_val = ah->config.spurchans[i][is2GHz]; - ath_dbg(common, ANI, "Getting spur val from new loc. %d\n", - spur_val); + ath_dbg(common, ATH_DBG_ANI, + "Getting spur val from new loc. %d\n", spur_val); break; case SPUR_ENABLE_EEPROM: spur_val = EEP_MAP4K_SPURCHAN; diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index b34e8b29..4f5c50a8 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -41,7 +41,7 @@ static bool __ath9k_hw_ar9287_fill_eeprom(struct ath_hw *ah) for (addr = 0; addr < SIZE_EEPROM_AR9287; addr++) { if (!ath9k_hw_nvram_read(common, addr + eep_start_loc, eep_data)) { - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "Unable to read eeprom region\n"); return false; } @@ -66,7 +66,8 @@ static bool ath9k_hw_ar9287_fill_eeprom(struct ath_hw *ah) struct ath_common *common = ath9k_hw_common(ah); if (!ath9k_hw_use_flash(ah)) { - ath_dbg(common, EEPROM, "Reading from EEPROM, not flash\n"); + ath_dbg(common, ATH_DBG_EEPROM, + "Reading from EEPROM, not flash\n"); } if (common->bus_ops->ath_bus_type == ATH_USB) @@ -196,7 +197,8 @@ static int ath9k_hw_ar9287_check_eeprom(struct ath_hw *ah) return false; } - ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic); + ath_dbg(common, ATH_DBG_EEPROM, + "Read Magic = 0x%04X\n", magic); if (magic != AR5416_EEPROM_MAGIC) { magic2 = swab16(magic); @@ -218,7 +220,7 @@ static int ath9k_hw_ar9287_check_eeprom(struct ath_hw *ah) } } - ath_dbg(common, EEPROM, "need_swap = %s\n", + ath_dbg(common, ATH_DBG_EEPROM, "need_swap = %s.\n", need_swap ? "True" : "False"); if (need_swap) @@ -567,7 +569,7 @@ static void ath9k_hw_set_ar9287_power_per_rate_table(struct ath_hw *ah, #define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 #define REDUCE_SCALED_POWER_BY_THREE_CHAIN 10 - u16 twiceMaxEdgePower; + u16 twiceMaxEdgePower = MAX_RATE_POWER; int i; struct cal_ctl_data_ar9287 *rep; struct cal_target_power_leg targetPowerOfdm = {0, {0, 0, 0, 0} }, @@ -667,7 +669,6 @@ static void ath9k_hw_set_ar9287_power_per_rate_table(struct ath_hw *ah, else freq = centers.ctl_center; - twiceMaxEdgePower = MAX_RATE_POWER; /* Walk through the CTL indices stored in EEPROM */ for (i = 0; (i < AR9287_NUM_CTLS) && pEepData->ctlIndex[i]; i++) { struct cal_ctl_edges *pRdEdgesPower; @@ -824,8 +825,6 @@ static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah, regulatory->max_power_level = ratesArray[i]; } - ath9k_hw_update_regulatory_maxpower(ah); - if (test) return; @@ -1041,7 +1040,8 @@ static u16 ath9k_hw_ar9287_get_spur_channel(struct ath_hw *ah, struct ath_common *common = ath9k_hw_common(ah); u16 spur_val = AR_NO_SPUR; - ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n", + ath_dbg(common, ATH_DBG_ANI, + "Getting spur idx:%d is2Ghz:%d val:%x\n", i, is2GHz, ah->config.spurchans[i][is2GHz]); switch (ah->config.spurmode) { @@ -1049,8 +1049,8 @@ static u16 ath9k_hw_ar9287_get_spur_channel(struct ath_hw *ah, break; case SPUR_ENABLE_IOCTL: spur_val = ah->config.spurchans[i][is2GHz]; - ath_dbg(common, ANI, "Getting spur val from new loc. %d\n", - spur_val); + ath_dbg(common, ATH_DBG_ANI, + "Getting spur val from new loc. %d\n", spur_val); break; case SPUR_ENABLE_EEPROM: spur_val = EEP_MAP9287_SPURCHAN; diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index 619b95d7..81e62967 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -121,7 +121,8 @@ static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah) struct ath_common *common = ath9k_hw_common(ah); if (!ath9k_hw_use_flash(ah)) { - ath_dbg(common, EEPROM, "Reading from EEPROM, not flash\n"); + ath_dbg(common, ATH_DBG_EEPROM, + "Reading from EEPROM, not flash\n"); } if (common->bus_ops->ath_bus_type == ATH_USB) @@ -278,7 +279,8 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) } if (!ath9k_hw_use_flash(ah)) { - ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic); + ath_dbg(common, ATH_DBG_EEPROM, + "Read Magic = 0x%04X\n", magic); if (magic != AR5416_EEPROM_MAGIC) { magic2 = swab16(magic); @@ -301,7 +303,7 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) } } - ath_dbg(common, EEPROM, "need_swap = %s\n", + ath_dbg(common, ATH_DBG_EEPROM, "need_swap = %s.\n", need_swap ? "True" : "False"); if (need_swap) @@ -323,7 +325,7 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) u32 integer, j; u16 word; - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "EEPROM Endianness is not native.. Changing.\n"); word = swab16(eep->baseEepHeader.length); @@ -383,7 +385,7 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) if ((ah->hw_version.devid == AR9280_DEVID_PCI) && ((eep->baseEepHeader.version & 0xff) > 0x0a) && (eep->baseEepHeader.pwdclkind == 0)) - ah->need_an_top2_fixup = true; + ah->need_an_top2_fixup = 1; if ((common->bus_ops->ath_bus_type == ATH_USB) && (AR_SREV_9280(ah))) @@ -963,12 +965,15 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah, reg32 = get_unaligned_le32(&pdadcValues[4 * j]); REG_WRITE(ah, regOffset, reg32); - ath_dbg(common, EEPROM, + ath_dbg(common, ATH_DBG_EEPROM, "PDADC (%d,%4x): %4.4x %8.8x\n", i, regChainOffset, regOffset, reg32); - ath_dbg(common, EEPROM, - "PDADC: Chain %d | PDADC %3d Value %3d | PDADC %3d Value %3d | PDADC %3d Value %3d | PDADC %3d Value %3d |\n", + ath_dbg(common, ATH_DBG_EEPROM, + "PDADC: Chain %d | PDADC %3d " + "Value %3d | PDADC %3d Value %3d | " + "PDADC %3d Value %3d | PDADC %3d " + "Value %3d |\n", i, 4 * j, pdadcValues[4 * j], 4 * j + 1, pdadcValues[4 * j + 1], 4 * j + 2, pdadcValues[4 * j + 2], @@ -995,7 +1000,7 @@ static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah, #define REDUCE_SCALED_POWER_BY_THREE_CHAIN 9 /* 10*log10(3)*2 */ struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; - u16 twiceMaxEdgePower; + u16 twiceMaxEdgePower = MAX_RATE_POWER; int i; struct cal_ctl_data *rep; struct cal_target_power_leg targetPowerOfdm, targetPowerCck = { @@ -1116,7 +1121,9 @@ static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah, else freq = centers.ctl_center; - twiceMaxEdgePower = MAX_RATE_POWER; + if (ah->eep_ops->get_eeprom_ver(ah) == 14 && + ah->eep_ops->get_eeprom_rev(ah) <= 2) + twiceMaxEdgePower = MAX_RATE_POWER; for (i = 0; (i < AR5416_NUM_CTLS) && pEepData->ctlIndex[i]; i++) { if ((((cfgCtl & ~CTL_MODE_M) | @@ -1273,7 +1280,7 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah, regulatory->max_power_level += INCREASE_MAXPOW_BY_THREE_CHAIN; break; default: - ath_dbg(ath9k_hw_common(ah), EEPROM, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_EEPROM, "Invalid chainmask configuration\n"); break; } @@ -1391,7 +1398,8 @@ static u16 ath9k_hw_def_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) u16 spur_val = AR_NO_SPUR; - ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n", + ath_dbg(common, ATH_DBG_ANI, + "Getting spur idx:%d is2Ghz:%d val:%x\n", i, is2GHz, ah->config.spurchans[i][is2GHz]); switch (ah->config.spurmode) { @@ -1399,8 +1407,8 @@ static u16 ath9k_hw_def_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) break; case SPUR_ENABLE_IOCTL: spur_val = ah->config.spurchans[i][is2GHz]; - ath_dbg(common, ANI, "Getting spur val from new loc. %d\n", - spur_val); + ath_dbg(common, ATH_DBG_ANI, + "Getting spur val from new loc. %d\n", spur_val); break; case SPUR_ENABLE_EEPROM: spur_val = EEP_DEF_SPURCHAN; diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index fbe23de1..e4ae08e0 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -110,8 +110,6 @@ void ath_start_rfkill_poll(struct ath_softc *sc) wiphy_rfkill_start_polling(sc->hw->wiphy); } -#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT - /******************/ /* BTCOEX */ /******************/ @@ -132,12 +130,12 @@ static void ath_detect_bt_priority(struct ath_softc *sc) sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN); /* Detect if colocated bt started scanning */ if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) { - ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX, + ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX, "BT scan detected\n"); sc->sc_flags |= (SC_OP_BT_SCAN | SC_OP_BT_PRIORITY_DETECTED); } else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { - ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX, + ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX, "BT priority traffic detected\n"); sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED; } @@ -232,7 +230,8 @@ static void ath_btcoex_no_stomp_timer(void *arg) struct ath_common *common = ath9k_hw_common(ah); bool is_btscan = sc->sc_flags & SC_OP_BT_SCAN; - ath_dbg(common, BTCOEX, "no stomp timer running\n"); + ath_dbg(common, ATH_DBG_BTCOEX, + "no stomp timer running\n"); ath9k_ps_wakeup(sc); spin_lock_bh(&btcoex->btcoex_lock); @@ -247,7 +246,7 @@ static void ath_btcoex_no_stomp_timer(void *arg) ath9k_ps_restore(sc); } -static int ath_init_btcoex_timer(struct ath_softc *sc) +int ath_init_btcoex_timer(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; @@ -281,7 +280,8 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc) struct ath_btcoex *btcoex = &sc->btcoex; struct ath_hw *ah = sc->sc_ah; - ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n"); + ath_dbg(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + "Starting btcoex timers\n"); /* make sure duty cycle timer is also stopped when resuming */ if (btcoex->hw_timer_enabled) @@ -310,114 +310,3 @@ void ath9k_btcoex_timer_pause(struct ath_softc *sc) btcoex->hw_timer_enabled = false; } - -u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen) -{ - struct ath_mci_profile *mci = &sc->btcoex.mci; - u16 aggr_limit = 0; - - if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && mci->aggr_limit) - aggr_limit = (max_4ms_framelen * mci->aggr_limit) >> 4; - else if (sc->sc_flags & SC_OP_BT_PRIORITY_DETECTED) - aggr_limit = min((max_4ms_framelen * 3) / 8, - (u32)ATH_AMPDU_LIMIT_MAX); - - return aggr_limit; -} - -void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status) -{ - struct ath_hw *ah = sc->sc_ah; - - if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE) - if (status & ATH9K_INT_GENTIMER) - ath_gen_timer_isr(sc->sc_ah); - - if (status & ATH9K_INT_MCI) - ath_mci_intr(sc); -} - -void ath9k_start_btcoex(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - - if ((ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) && - !ah->btcoex_hw.enabled) { - if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) - ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, - AR_STOMP_LOW_WLAN_WGHT); - ath9k_hw_btcoex_enable(ah); - - if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE) - ath9k_btcoex_timer_resume(sc); - } -} - -void ath9k_stop_btcoex(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - - if (ah->btcoex_hw.enabled && - ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) { - ath9k_hw_btcoex_disable(ah); - if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE) - ath9k_btcoex_timer_pause(sc); - if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_MCI) - ath_mci_flush_profile(&sc->btcoex.mci); - } -} - -void ath9k_deinit_btcoex(struct ath_softc *sc) -{ - if ((sc->btcoex.no_stomp_timer) && - ath9k_hw_get_btcoex_scheme(sc->sc_ah) == ATH_BTCOEX_CFG_3WIRE) - ath_gen_timer_free(sc->sc_ah, sc->btcoex.no_stomp_timer); - - if (ath9k_hw_get_btcoex_scheme(sc->sc_ah) == ATH_BTCOEX_CFG_MCI) - ath_mci_cleanup(sc); -} - -int ath9k_init_btcoex(struct ath_softc *sc) -{ - struct ath_txq *txq; - struct ath_hw *ah = sc->sc_ah; - int r; - - ath9k_hw_btcoex_init_scheme(ah); - - switch (ath9k_hw_get_btcoex_scheme(sc->sc_ah)) { - case ATH_BTCOEX_CFG_NONE: - break; - case ATH_BTCOEX_CFG_2WIRE: - ath9k_hw_btcoex_init_2wire(sc->sc_ah); - break; - case ATH_BTCOEX_CFG_3WIRE: - ath9k_hw_btcoex_init_3wire(sc->sc_ah); - r = ath_init_btcoex_timer(sc); - if (r) - return -1; - txq = sc->tx.txq_map[WME_AC_BE]; - ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum); - sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; - break; - case ATH_BTCOEX_CFG_MCI: - sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; - sc->btcoex.duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE; - INIT_LIST_HEAD(&sc->btcoex.mci.info); - - r = ath_mci_setup(sc); - if (r) - return r; - - ath9k_hw_btcoex_init_mci(ah); - - break; - default: - WARN_ON(1); - break; - } - - return 0; -} - -#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 424aabb2..04352e16 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -968,7 +968,8 @@ static void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev) ath9k_hif_usb_dealloc_rx_urbs(hif_dev); } -static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) +static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev, + u32 drv_info) { int transfer, err; const void *data = hif_dev->firmware->data; @@ -981,7 +982,7 @@ static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) return -ENOMEM; while (len) { - transfer = min_t(size_t, len, 4096); + transfer = min_t(int, len, 4096); memcpy(buf, data, transfer); err = usb_control_msg(hif_dev->udev, @@ -999,7 +1000,7 @@ static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) } kfree(buf); - if (IS_AR7010_DEVICE(hif_dev->usb_device_id->driver_info)) + if (IS_AR7010_DEVICE(drv_info)) firm_offset = AR7010_FIRMWARE_TEXT; else firm_offset = AR9271_FIRMWARE_TEXT; @@ -1020,18 +1021,28 @@ static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) return 0; } -static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev) +static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev, u32 drv_info) { + int ret, idx; struct usb_host_interface *alt = &hif_dev->interface->altsetting[0]; struct usb_endpoint_descriptor *endp; - int ret, idx; - ret = ath9k_hif_usb_download_fw(hif_dev); + /* Request firmware */ + ret = request_firmware(&hif_dev->firmware, hif_dev->fw_name, + &hif_dev->udev->dev); + if (ret) { + dev_err(&hif_dev->udev->dev, + "ath9k_htc: Firmware - %s not found\n", hif_dev->fw_name); + goto err_fw_req; + } + + /* Download firmware */ + ret = ath9k_hif_usb_download_fw(hif_dev, drv_info); if (ret) { dev_err(&hif_dev->udev->dev, "ath9k_htc: Firmware - %s download failed\n", hif_dev->fw_name); - return ret; + goto err_fw_download; } /* On downloading the firmware to the target, the USB descriptor of EP4 @@ -1053,84 +1064,23 @@ static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev) if (ret) { dev_err(&hif_dev->udev->dev, "ath9k_htc: Unable to allocate URBs\n"); - return ret; + goto err_fw_download; } return 0; + +err_fw_download: + release_firmware(hif_dev->firmware); +err_fw_req: + hif_dev->firmware = NULL; + return ret; } static void ath9k_hif_usb_dev_deinit(struct hif_device_usb *hif_dev) { ath9k_hif_usb_dealloc_urbs(hif_dev); -} - -/* - * If initialization fails or the FW cannot be retrieved, - * detach the device. - */ -static void ath9k_hif_usb_firmware_fail(struct hif_device_usb *hif_dev) -{ - struct device *parent = hif_dev->udev->dev.parent; - - complete(&hif_dev->fw_done); - - if (parent) - device_lock(parent); - - device_release_driver(&hif_dev->udev->dev); - - if (parent) - device_unlock(parent); -} - -static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context) -{ - struct hif_device_usb *hif_dev = context; - int ret; - - if (!fw) { - dev_err(&hif_dev->udev->dev, - "ath9k_htc: Failed to get firmware %s\n", - hif_dev->fw_name); - goto err_fw; - } - - hif_dev->htc_handle = ath9k_htc_hw_alloc(hif_dev, &hif_usb, - &hif_dev->udev->dev); - if (hif_dev->htc_handle == NULL) { - goto err_fw; - } - - hif_dev->firmware = fw; - - /* Proceed with initialization */ - - ret = ath9k_hif_usb_dev_init(hif_dev); - if (ret) - goto err_dev_init; - - ret = ath9k_htc_hw_init(hif_dev->htc_handle, - &hif_dev->interface->dev, - hif_dev->usb_device_id->idProduct, - hif_dev->udev->product, - hif_dev->usb_device_id->driver_info); - if (ret) { - ret = -EINVAL; - goto err_htc_hw_init; - } - - complete(&hif_dev->fw_done); - - return; - -err_htc_hw_init: - ath9k_hif_usb_dev_deinit(hif_dev); -err_dev_init: - ath9k_htc_hw_free(hif_dev->htc_handle); - release_firmware(fw); - hif_dev->firmware = NULL; -err_fw: - ath9k_hif_usb_firmware_fail(hif_dev); + if (hif_dev->firmware) + release_firmware(hif_dev->firmware); } /* @@ -1205,16 +1155,20 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface, } usb_get_dev(udev); - hif_dev->udev = udev; hif_dev->interface = interface; - hif_dev->usb_device_id = id; + hif_dev->device_id = id->idProduct; #ifdef CONFIG_PM udev->reset_resume = 1; #endif usb_set_intfdata(interface, hif_dev); - init_completion(&hif_dev->fw_done); + hif_dev->htc_handle = ath9k_htc_hw_alloc(hif_dev, &hif_usb, + &hif_dev->udev->dev); + if (hif_dev->htc_handle == NULL) { + ret = -ENOMEM; + goto err_htc_hw_alloc; + } /* Find out which firmware to load */ @@ -1223,22 +1177,29 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface, else hif_dev->fw_name = FIRMWARE_AR9271; - ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name, - &hif_dev->udev->dev, GFP_KERNEL, - hif_dev, ath9k_hif_usb_firmware_cb); + ret = ath9k_hif_usb_dev_init(hif_dev, id->driver_info); if (ret) { - dev_err(&hif_dev->udev->dev, - "ath9k_htc: Async request for firmware %s failed\n", - hif_dev->fw_name); - goto err_fw_req; + ret = -EINVAL; + goto err_hif_init_usb; + } + + ret = ath9k_htc_hw_init(hif_dev->htc_handle, + &interface->dev, hif_dev->device_id, + hif_dev->udev->product, id->driver_info); + if (ret) { + ret = -EINVAL; + goto err_htc_hw_init; } - dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n", - hif_dev->fw_name); + dev_info(&hif_dev->udev->dev, "ath9k_htc: USB layer initialized\n"); return 0; -err_fw_req: +err_htc_hw_init: + ath9k_hif_usb_dev_deinit(hif_dev); +err_hif_init_usb: + ath9k_htc_hw_free(hif_dev->htc_handle); +err_htc_hw_alloc: usb_set_intfdata(interface, NULL); kfree(hif_dev); usb_put_dev(udev); @@ -1273,15 +1234,9 @@ static void ath9k_hif_usb_disconnect(struct usb_interface *interface) if (!hif_dev) return; - wait_for_completion(&hif_dev->fw_done); - - if (hif_dev->firmware) { - ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged); - ath9k_htc_hw_free(hif_dev->htc_handle); - ath9k_hif_usb_dev_deinit(hif_dev); - release_firmware(hif_dev->firmware); - } - + ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged); + ath9k_htc_hw_free(hif_dev->htc_handle); + ath9k_hif_usb_dev_deinit(hif_dev); usb_set_intfdata(interface, NULL); if (!unplugged && (hif_dev->flags & HIF_USB_START)) @@ -1321,7 +1276,8 @@ static int ath9k_hif_usb_resume(struct usb_interface *interface) return ret; if (hif_dev->firmware) { - ret = ath9k_hif_usb_download_fw(hif_dev); + ret = ath9k_hif_usb_download_fw(hif_dev, + htc_handle->drv_priv->ah->hw_version.usbdev); if (ret) goto fail_resume; } else { @@ -1355,7 +1311,9 @@ static struct usb_driver ath9k_hif_usb_driver = { .reset_resume = ath9k_hif_usb_resume, #endif .id_table = ath9k_hif_usb_ids, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) .soft_unbind = 1, +#endif }; int ath9k_hif_usb_init(void) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h index 487ff658..794f6309 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.h +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h @@ -87,11 +87,10 @@ struct cmd_buf { #define HIF_USB_START BIT(0) struct hif_device_usb { + u16 device_id; struct usb_device *udev; struct usb_interface *interface; - const struct usb_device_id *usb_device_id; const struct firmware *firmware; - struct completion fw_done; struct htc_target *htc_handle; struct hif_usb_tx tx; struct usb_anchor regout_submitted; diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 17f687bf..09be4078 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -401,21 +401,9 @@ struct ath_btcoex { u32 btscan_no_stomp; }; -#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT -void ath9k_htc_init_btcoex(struct ath9k_htc_priv *priv, char *product); -void ath9k_htc_start_btcoex(struct ath9k_htc_priv *priv); -void ath9k_htc_stop_btcoex(struct ath9k_htc_priv *priv); -#else -static inline void ath9k_htc_init_btcoex(struct ath9k_htc_priv *priv, char *product) -{ -} -static inline void ath9k_htc_start_btcoex(struct ath9k_htc_priv *priv) -{ -} -static inline void ath9k_htc_stop_btcoex(struct ath9k_htc_priv *priv) -{ -} -#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ +void ath_htc_init_btcoex_work(struct ath9k_htc_priv *priv); +void ath_htc_resume_btcoex_work(struct ath9k_htc_priv *priv); +void ath_htc_cancel_btcoex_work(struct ath9k_htc_priv *priv); #define OP_INVALID BIT(0) #define OP_SCANNING BIT(1) @@ -496,10 +484,7 @@ struct ath9k_htc_priv { int cabq; int hwq_map[WME_NUM_AC]; -#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT struct ath_btcoex btcoex; -#endif - struct delayed_work coex_period_work; struct delayed_work duty_cycle_work; #ifdef CONFIG_ATH9K_HTC_DEBUGFS diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index 2eadffb7..57fe22b2 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -167,9 +167,9 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, /* TSF out of range threshold fixed at 1 second */ bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; - ath_dbg(common, CONFIG, "intval: %u tsf: %llu tsftu: %u\n", + ath_dbg(common, ATH_DBG_CONFIG, "intval: %u tsf: %llu tsftu: %u\n", intval, tsf, tsftu); - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", bs.bs_bmissthreshold, bs.bs_sleepduration, bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); @@ -224,8 +224,9 @@ static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv, if (priv->op_flags & OP_ENABLE_BEACON) imask |= ATH9K_INT_SWBA; - ath_dbg(common, CONFIG, - "AP Beacon config, intval: %d, nexttbtt: %u, resp_time: %d imask: 0x%x\n", + ath_dbg(common, ATH_DBG_CONFIG, + "AP Beacon config, intval: %d, nexttbtt: %u, resp_time: %d " + "imask: 0x%x\n", bss_conf->beacon_interval, nexttbtt, priv->ah->config.sw_beacon_response_time, imask); @@ -272,8 +273,9 @@ static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, if (priv->op_flags & OP_ENABLE_BEACON) imask |= ATH9K_INT_SWBA; - ath_dbg(common, CONFIG, - "IBSS Beacon config, intval: %d, nexttbtt: %u, resp_time: %d, imask: 0x%x\n", + ath_dbg(common, ATH_DBG_CONFIG, + "IBSS Beacon config, intval: %d, nexttbtt: %u, " + "resp_time: %d, imask: 0x%x\n", bss_conf->beacon_interval, nexttbtt, priv->ah->config.sw_beacon_response_time, imask); @@ -321,7 +323,7 @@ static void ath9k_htc_send_buffered(struct ath9k_htc_priv *priv, tx_slot = ath9k_htc_tx_get_slot(priv); if (tx_slot < 0) { - ath_dbg(common, XMIT, "No free CAB slot\n"); + ath_dbg(common, ATH_DBG_XMIT, "No free CAB slot\n"); dev_kfree_skb_any(skb); goto next; } @@ -331,7 +333,8 @@ static void ath9k_htc_send_buffered(struct ath9k_htc_priv *priv, ath9k_htc_tx_clear_slot(priv, tx_slot); dev_kfree_skb_any(skb); - ath_dbg(common, XMIT, "Failed to send CAB frame\n"); + ath_dbg(common, ATH_DBG_XMIT, + "Failed to send CAB frame\n"); } else { spin_lock_bh(&priv->tx.tx_lock); priv->tx.queued_cnt++; @@ -406,7 +409,7 @@ static void ath9k_htc_send_beacon(struct ath9k_htc_priv *priv, ret = htc_send(priv->htc, beacon); if (ret != 0) { if (ret == -ENOMEM) { - ath_dbg(common, BSTUCK, + ath_dbg(common, ATH_DBG_BSTUCK, "Failed to send beacon, no free TX buffer\n"); } dev_kfree_skb_any(beacon); @@ -431,7 +434,7 @@ static int ath9k_htc_choose_bslot(struct ath9k_htc_priv *priv, slot = ((tsftu % intval) * ATH9K_HTC_MAX_BCN_VIF) / intval; slot = ATH9K_HTC_MAX_BCN_VIF - slot - 1; - ath_dbg(common, BEACON, + ath_dbg(common, ATH_DBG_BEACON, "Choose slot: %d, tsf: %llu, tsftu: %u, intval: %u\n", slot, tsf, tsftu, intval); @@ -447,7 +450,8 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv, if (swba->beacon_pending != 0) { priv->cur_beacon_conf.bmiss_cnt++; if (priv->cur_beacon_conf.bmiss_cnt > BSTUCK_THRESHOLD) { - ath_dbg(common, BSTUCK, "Beacon stuck, HW reset\n"); + ath_dbg(common, ATH_DBG_BSTUCK, + "Beacon stuck, HW reset\n"); ieee80211_queue_work(priv->hw, &priv->fatal_work); } @@ -455,7 +459,7 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv, } if (priv->cur_beacon_conf.bmiss_cnt) { - ath_dbg(common, BSTUCK, + ath_dbg(common, ATH_DBG_BSTUCK, "Resuming beacon xmit after %u misses\n", priv->cur_beacon_conf.bmiss_cnt); priv->cur_beacon_conf.bmiss_cnt = 0; @@ -491,8 +495,8 @@ void ath9k_htc_assign_bslot(struct ath9k_htc_priv *priv, priv->cur_beacon_conf.bslot[avp->bslot] = vif; spin_unlock_bh(&priv->beacon_lock); - ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n", - avp->bslot); + ath_dbg(common, ATH_DBG_CONFIG, + "Added interface at beacon slot: %d\n", avp->bslot); } void ath9k_htc_remove_bslot(struct ath9k_htc_priv *priv, @@ -505,8 +509,8 @@ void ath9k_htc_remove_bslot(struct ath9k_htc_priv *priv, priv->cur_beacon_conf.bslot[avp->bslot] = NULL; spin_unlock_bh(&priv->beacon_lock); - ath_dbg(common, CONFIG, "Removed interface at beacon slot: %d\n", - avp->bslot); + ath_dbg(common, ATH_DBG_CONFIG, + "Removed interface at beacon slot: %d\n", avp->bslot); } /* @@ -532,7 +536,8 @@ void ath9k_htc_set_tsfadjust(struct ath9k_htc_priv *priv, tsfadjust = cur_conf->beacon_interval * avp->bslot / ATH9K_HTC_MAX_BCN_VIF; avp->tsfadjust = cpu_to_le64(TU_TO_USEC(tsfadjust)); - ath_dbg(common, CONFIG, "tsfadjust is: %llu for bslot: %d\n", + ath_dbg(common, ATH_DBG_CONFIG, + "tsfadjust is: %llu for bslot: %d\n", (unsigned long long)tsfadjust, avp->bslot); } @@ -563,7 +568,7 @@ static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv, (priv->num_ap_vif > 1) && (vif->type == NL80211_IFTYPE_AP) && (cur_conf->beacon_interval != bss_conf->beacon_int)) { - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Changing beacon interval of multiple AP interfaces !\n"); return false; } @@ -574,7 +579,7 @@ static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv, */ if (priv->num_ap_vif && (vif->type != NL80211_IFTYPE_AP)) { - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "HW in AP mode, cannot set STA beacon parameters\n"); return false; } @@ -592,7 +597,7 @@ static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv, &beacon_configured); if (beacon_configured) { - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Beacon already configured for a station interface\n"); return false; } @@ -632,7 +637,8 @@ void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, ath9k_htc_beacon_config_ap(priv, cur_conf); break; default: - ath_dbg(common, CONFIG, "Unsupported beaconing mode\n"); + ath_dbg(common, ATH_DBG_CONFIG, + "Unsupported beaconing mode\n"); return; } } @@ -653,7 +659,8 @@ void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv) ath9k_htc_beacon_config_ap(priv, cur_conf); break; default: - ath_dbg(common, CONFIG, "Unsupported beaconing mode\n"); + ath_dbg(common, ATH_DBG_CONFIG, + "Unsupported beaconing mode\n"); return; } } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c index 3035deb7..d3ff33c7 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c @@ -16,6 +16,12 @@ #include "htc.h" +static int ath9k_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + static ssize_t read_file_tgt_int_stats(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -69,7 +75,7 @@ static ssize_t read_file_tgt_int_stats(struct file *file, char __user *user_buf, static const struct file_operations fops_tgt_int_stats = { .read = read_file_tgt_int_stats, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -139,7 +145,7 @@ static ssize_t read_file_tgt_tx_stats(struct file *file, char __user *user_buf, static const struct file_operations fops_tgt_tx_stats = { .read = read_file_tgt_tx_stats, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -185,7 +191,7 @@ static ssize_t read_file_tgt_rx_stats(struct file *file, char __user *user_buf, static const struct file_operations fops_tgt_rx_stats = { .read = read_file_tgt_rx_stats, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -237,7 +243,7 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, static const struct file_operations fops_xmit = { .read = read_file_xmit, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -358,7 +364,7 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, static const struct file_operations fops_recv = { .read = read_file_recv, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -393,7 +399,7 @@ static ssize_t read_file_slot(struct file *file, char __user *user_buf, static const struct file_operations fops_slot = { .read = read_file_slot, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -440,7 +446,7 @@ static ssize_t read_file_queue(struct file *file, char __user *user_buf, static const struct file_operations fops_queue = { .read = read_file_queue, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -481,7 +487,7 @@ static ssize_t write_file_debug(struct file *file, const char __user *user_buf, static const struct file_operations fops_debug = { .read = read_file_debug, .write = write_file_debug, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -630,7 +636,7 @@ static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, static const struct file_operations fops_base_eeprom = { .read = read_file_base_eeprom, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; @@ -911,7 +917,7 @@ static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf, static const struct file_operations fops_modal_eeprom = { .read = read_file_modal_eeprom, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE, .llseek = default_llseek, }; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c index 1c10e2e5..ce606b61 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c @@ -20,10 +20,6 @@ /* BTCOEX */ /******************/ -#define ATH_HTC_BTCOEX_PRODUCT_ID "wb193" - -#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT - /* * Detects if there is any priority bt traffic */ @@ -40,12 +36,12 @@ static void ath_detect_bt_priority(struct ath9k_htc_priv *priv) priv->op_flags &= ~(OP_BT_PRIORITY_DETECTED | OP_BT_SCAN); /* Detect if colocated bt started scanning */ if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) { - ath_dbg(ath9k_hw_common(ah), BTCOEX, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_BTCOEX, "BT scan detected\n"); priv->op_flags |= (OP_BT_SCAN | OP_BT_PRIORITY_DETECTED); } else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { - ath_dbg(ath9k_hw_common(ah), BTCOEX, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_BTCOEX, "BT priority traffic detected\n"); priv->op_flags |= OP_BT_PRIORITY_DETECTED; } @@ -106,7 +102,8 @@ static void ath_btcoex_duty_cycle_work(struct work_struct *work) struct ath_common *common = ath9k_hw_common(ah); bool is_btscan = priv->op_flags & OP_BT_SCAN; - ath_dbg(common, BTCOEX, "time slice work for bt and wlan\n"); + ath_dbg(common, ATH_DBG_BTCOEX, + "time slice work for bt and wlan\n"); if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE); @@ -115,7 +112,7 @@ static void ath_btcoex_duty_cycle_work(struct work_struct *work) ath9k_hw_btcoex_enable(priv->ah); } -static void ath_htc_init_btcoex_work(struct ath9k_htc_priv *priv) +void ath_htc_init_btcoex_work(struct ath9k_htc_priv *priv) { struct ath_btcoex *btcoex = &priv->btcoex; @@ -132,12 +129,12 @@ static void ath_htc_init_btcoex_work(struct ath9k_htc_priv *priv) * (Re)start btcoex work */ -static void ath_htc_resume_btcoex_work(struct ath9k_htc_priv *priv) +void ath_htc_resume_btcoex_work(struct ath9k_htc_priv *priv) { struct ath_btcoex *btcoex = &priv->btcoex; struct ath_hw *ah = priv->ah; - ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex work\n"); + ath_dbg(ath9k_hw_common(ah), ATH_DBG_BTCOEX, "Starting btcoex work\n"); btcoex->bt_priority_cnt = 0; btcoex->bt_priority_time = jiffies; @@ -149,66 +146,12 @@ static void ath_htc_resume_btcoex_work(struct ath9k_htc_priv *priv) /* * Cancel btcoex and bt duty cycle work. */ -static void ath_htc_cancel_btcoex_work(struct ath9k_htc_priv *priv) +void ath_htc_cancel_btcoex_work(struct ath9k_htc_priv *priv) { cancel_delayed_work_sync(&priv->coex_period_work); cancel_delayed_work_sync(&priv->duty_cycle_work); } -void ath9k_htc_start_btcoex(struct ath9k_htc_priv *priv) -{ - struct ath_hw *ah = priv->ah; - - if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE) { - ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, - AR_STOMP_LOW_WLAN_WGHT); - ath9k_hw_btcoex_enable(ah); - ath_htc_resume_btcoex_work(priv); - } -} - -void ath9k_htc_stop_btcoex(struct ath9k_htc_priv *priv) -{ - struct ath_hw *ah = priv->ah; - - if (ah->btcoex_hw.enabled && - ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) { - ath9k_hw_btcoex_disable(ah); - if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) - ath_htc_cancel_btcoex_work(priv); - } -} - -void ath9k_htc_init_btcoex(struct ath9k_htc_priv *priv, char *product) -{ - struct ath_hw *ah = priv->ah; - int qnum; - - if (product && strncmp(product, ATH_HTC_BTCOEX_PRODUCT_ID, 5) == 0) { - ah->btcoex_hw.scheme = ATH_BTCOEX_CFG_3WIRE; - } - - switch (ath9k_hw_get_btcoex_scheme(priv->ah)) { - case ATH_BTCOEX_CFG_NONE: - break; - case ATH_BTCOEX_CFG_3WIRE: - priv->ah->btcoex_hw.btactive_gpio = 7; - priv->ah->btcoex_hw.btpriority_gpio = 6; - priv->ah->btcoex_hw.wlanactive_gpio = 8; - priv->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; - ath9k_hw_btcoex_init_3wire(priv->ah); - ath_htc_init_btcoex_work(priv); - qnum = priv->hwq_map[WME_AC_BE]; - ath9k_hw_init_btcoex_hw(priv->ah, qnum); - break; - default: - WARN_ON(1); - break; - } -} - -#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ - /*******/ /* LED */ /*******/ diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c old mode 100644 new mode 100755 index 2b8406a8..ef921e1b --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -41,6 +41,8 @@ MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); .max_power = 20, \ } +#define ATH_HTC_BTCOEX_PRODUCT_ID "wb193" + static struct ieee80211_channel ath9k_2ghz_channels[] = { CHAN2G(2412, 0), /* Channel 1 */ CHAN2G(2417, 1), /* Channel 2 */ @@ -297,7 +299,8 @@ static unsigned int ath9k_regread(void *hw_priv, u32 reg_offset) (u8 *) &val, sizeof(val), 100); if (unlikely(r)) { - ath_dbg(common, WMI, "REGISTER READ FAILED: (0x%04x, %d)\n", + ath_dbg(common, ATH_DBG_WMI, + "REGISTER READ FAILED: (0x%04x, %d)\n", reg_offset, r); return -EIO; } @@ -324,7 +327,7 @@ static void ath9k_multi_regread(void *hw_priv, u32 *addr, (u8 *)tmpval, sizeof(u32) * count, 100); if (unlikely(ret)) { - ath_dbg(common, WMI, + ath_dbg(common, ATH_DBG_WMI, "Multiple REGISTER READ FAILED (count: %d)\n", count); } @@ -349,7 +352,8 @@ static void ath9k_regwrite_single(void *hw_priv, u32 val, u32 reg_offset) (u8 *) &val, sizeof(val), 100); if (unlikely(r)) { - ath_dbg(common, WMI, "REGISTER WRITE FAILED:(0x%04x, %d)\n", + ath_dbg(common, ATH_DBG_WMI, + "REGISTER WRITE FAILED:(0x%04x, %d)\n", reg_offset, r); } } @@ -380,7 +384,7 @@ static void ath9k_regwrite_buffer(void *hw_priv, u32 val, u32 reg_offset) (u8 *) &rsp_status, sizeof(rsp_status), 100); if (unlikely(r)) { - ath_dbg(common, WMI, + ath_dbg(common, ATH_DBG_WMI, "REGISTER WRITE FAILED, multi len: %d\n", priv->wmi->multi_write_idx); } @@ -430,7 +434,7 @@ static void ath9k_regwrite_flush(void *hw_priv) (u8 *) &rsp_status, sizeof(rsp_status), 100); if (unlikely(r)) { - ath_dbg(common, WMI, + ath_dbg(common, ATH_DBG_WMI, "REGISTER WRITE FAILED, multi len: %d\n", priv->wmi->multi_write_idx); } @@ -508,7 +512,8 @@ static void setup_ht_cap(struct ath9k_htc_priv *priv, tx_streams = ath9k_cmn_count_streams(priv->ah->txchainmask, 2); rx_streams = ath9k_cmn_count_streams(priv->ah->rxchainmask, 2); - ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n", + ath_dbg(common, ATH_DBG_CONFIG, + "TX streams %d, RX streams: %d\n", tx_streams, rx_streams); if (tx_streams != rx_streams) { @@ -601,6 +606,29 @@ static void ath9k_init_misc(struct ath9k_htc_priv *priv) priv->ah->opmode = NL80211_IFTYPE_STATION; } +static void ath9k_init_btcoex(struct ath9k_htc_priv *priv) +{ + int qnum; + + switch (priv->ah->btcoex_hw.scheme) { + case ATH_BTCOEX_CFG_NONE: + break; + case ATH_BTCOEX_CFG_3WIRE: + priv->ah->btcoex_hw.btactive_gpio = 7; + priv->ah->btcoex_hw.btpriority_gpio = 6; + priv->ah->btcoex_hw.wlanactive_gpio = 8; + priv->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; + ath9k_hw_btcoex_init_3wire(priv->ah); + ath_htc_init_btcoex_work(priv); + qnum = priv->hwq_map[WME_AC_BE]; + ath9k_hw_init_btcoex_hw(priv->ah, qnum); + break; + default: + WARN_ON(1); + break; + } +} + static int ath9k_init_priv(struct ath9k_htc_priv *priv, u16 devid, char *product, u32 drv_info) @@ -673,7 +701,11 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, ath9k_cmn_init_crypto(ah); ath9k_init_channels_rates(priv); ath9k_init_misc(priv); - ath9k_htc_init_btcoex(priv, product); + + if (product && strncmp(product, ATH_HTC_BTCOEX_PRODUCT_ID, 5) == 0) { + ah->btcoex_hw.scheme = ATH_BTCOEX_CFG_3WIRE; + ath9k_init_btcoex(priv); + } return 0; @@ -711,8 +743,6 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - hw->queues = 4; hw->channel_change_time = 5000; hw->max_listen_interval = 10; @@ -847,8 +877,9 @@ static int ath9k_init_device(struct ath9k_htc_priv *priv, goto err_world; } - ath_dbg(common, CONFIG, - "WMI:%d, BCN:%d, CAB:%d, UAPSD:%d, MGMT:%d, BE:%d, BK:%d, VI:%d, VO:%d\n", + ath_dbg(common, ATH_DBG_CONFIG, + "WMI:%d, BCN:%d, CAB:%d, UAPSD:%d, MGMT:%d, " + "BE:%d, BK:%d, VI:%d, VO:%d\n", priv->wmi_cmd_ep, priv->beacon_ep, priv->cab_ep, diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c old mode 100644 new mode 100755 index 204619e2..99bbf069 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -275,7 +275,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, ath9k_wmi_event_drain(priv); - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n", priv->ah->curchan->channel, channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf), @@ -424,7 +424,7 @@ static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv) priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx; priv->ah->is_monitoring = true; - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Attached a monitor interface at idx: %d, sta idx: %d\n", priv->mon_vif_idx, sta_idx); @@ -436,7 +436,7 @@ static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv) */ __ath9k_htc_remove_monitor_interface(priv); err_vif: - ath_dbg(common, FATAL, "Unable to attach a monitor interface\n"); + ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n"); return ret; } @@ -461,7 +461,7 @@ static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) priv->nstations--; priv->ah->is_monitoring = false; - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Removed a monitor interface at idx: %d, sta idx: %d\n", priv->mon_vif_idx, sta_idx); @@ -521,11 +521,11 @@ static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, } if (sta) { - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Added a station entry for: %pM (idx: %d)\n", sta->addr, tsta.sta_index); } else { - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Added a station entry for VIF %d (idx: %d)\n", avp->index, tsta.sta_index); } @@ -565,11 +565,11 @@ static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv, } if (sta) { - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Removed a station entry for: %pM (idx: %d)\n", sta->addr, sta_idx); } else { - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Removed a station entry for VIF %d (idx: %d)\n", avp->index, sta_idx); } @@ -674,7 +674,7 @@ static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv, ath9k_htc_setup_rate(priv, sta, &trate); ret = ath9k_htc_send_rate_cmd(priv, &trate); if (!ret) - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Updated target sta: %pM, rate caps: 0x%X\n", sta->addr, be32_to_cpu(trate.capflags)); } @@ -701,7 +701,7 @@ static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv, ret = ath9k_htc_send_rate_cmd(priv, &trate); if (!ret) - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Updated target sta: %pM, rate caps: 0x%X\n", bss_conf->bssid, be32_to_cpu(trate.capflags)); } @@ -730,11 +730,11 @@ static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv, WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr); if (ret) - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Unable to %s TX aggregation for (%pM, %d)\n", (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid); else - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "%s TX aggregation for (%pM, %d)\n", (aggr.aggr_enable) ? "Starting" : "Stopping", sta->addr, tid); @@ -793,7 +793,7 @@ void ath9k_htc_ani_work(struct work_struct *work) /* Long calibration runs independently of short calibration. */ if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { longcal = true; - ath_dbg(common, ANI, "longcal @%lu\n", jiffies); + ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies); common->ani.longcal_timer = timestamp; } @@ -802,7 +802,8 @@ void ath9k_htc_ani_work(struct work_struct *work) if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) { shortcal = true; - ath_dbg(common, ANI, "shortcal @%lu\n", jiffies); + ath_dbg(common, ATH_DBG_ANI, + "shortcal @%lu\n", jiffies); common->ani.shortcal_timer = timestamp; common->ani.resetcal_timer = timestamp; } @@ -816,8 +817,7 @@ void ath9k_htc_ani_work(struct work_struct *work) } /* Verify whether we must check ANI */ - if (ah->config.enable_ani && - (timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) { + if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) { aniflag = true; common->ani.checkani_timer = timestamp; } @@ -847,7 +847,7 @@ void ath9k_htc_ani_work(struct work_struct *work) * short calibration and long calibration. */ cal_interval = ATH_LONG_CALINTERVAL; - if (ah->config.enable_ani) + if (priv->ah->config.enable_ani) cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL); if (!common->ani.caldone) cal_interval = min(cal_interval, (u32)short_cal_interval); @@ -874,7 +874,7 @@ static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) padsize = padpos & 3; if (padsize && skb->len > padpos) { if (skb_headroom(skb) < padsize) { - ath_dbg(common, XMIT, "No room for padding\n"); + ath_dbg(common, ATH_DBG_XMIT, "No room for padding\n"); goto fail_tx; } skb_push(skb, padsize); @@ -883,13 +883,13 @@ static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) slot = ath9k_htc_tx_get_slot(priv); if (slot < 0) { - ath_dbg(common, XMIT, "No free TX slot\n"); + ath_dbg(common, ATH_DBG_XMIT, "No free TX slot\n"); goto fail_tx; } ret = ath9k_htc_tx_start(priv, skb, slot, false); if (ret != 0) { - ath_dbg(common, XMIT, "Tx failed\n"); + ath_dbg(common, ATH_DBG_XMIT, "Tx failed\n"); goto clear_slot; } @@ -917,7 +917,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) mutex_lock(&priv->mutex); - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Starting driver with initial channel: %d MHz\n", curchan->center_freq); @@ -928,6 +928,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) /* setup initial channel */ init_channel = ath9k_cmn_get_curchannel(hw, ah); + ath9k_hw_htc_resetinit(ah); ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false); if (ret) { ath_err(common, @@ -950,7 +951,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) ret = ath9k_htc_update_cap_target(priv, 0); if (ret) - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Failed to update capability in target\n"); priv->op_flags &= ~OP_INVALID; @@ -965,8 +966,12 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) mod_timer(&priv->tx.cleanup_timer, jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL)); - ath9k_htc_start_btcoex(priv); - + if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) { + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_LOW_WLAN_WGHT); + ath9k_hw_btcoex_enable(ah); + ath_htc_resume_btcoex_work(priv); + } mutex_unlock(&priv->mutex); return ret; @@ -983,7 +988,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) mutex_lock(&priv->mutex); if (priv->op_flags & OP_INVALID) { - ath_dbg(common, ANY, "Device not present\n"); + ath_dbg(common, ATH_DBG_ANY, "Device not present\n"); mutex_unlock(&priv->mutex); return; } @@ -1013,7 +1018,11 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) mutex_lock(&priv->mutex); - ath9k_htc_stop_btcoex(priv); + if (ah->btcoex_hw.enabled) { + ath9k_hw_btcoex_disable(ah); + if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) + ath_htc_cancel_btcoex_work(priv); + } /* Remove a monitor interface if it's present. */ if (priv->ah->is_monitoring) @@ -1026,7 +1035,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) priv->op_flags |= OP_INVALID; - ath_dbg(common, CONFIG, "Driver halt\n"); + ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n"); mutex_unlock(&priv->mutex); } @@ -1119,8 +1128,8 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, ath9k_htc_start_ani(priv); } - ath_dbg(common, CONFIG, "Attach a VIF of type: %d at idx: %d\n", - vif->type, avp->index); + ath_dbg(common, ATH_DBG_CONFIG, + "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index); out: ath9k_htc_ps_restore(priv); @@ -1176,7 +1185,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, ath9k_htc_stop_ani(priv); } - ath_dbg(common, CONFIG, "Detach Interface at idx: %d\n", avp->index); + ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); @@ -1201,7 +1210,8 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) mutex_unlock(&priv->htc_pm_lock); if (enable_radio) { - ath_dbg(common, CONFIG, "not-idle: enabling radio\n"); + ath_dbg(common, ATH_DBG_CONFIG, + "not-idle: enabling radio\n"); ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); ath9k_htc_radio_enable(hw); } @@ -1223,7 +1233,7 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) struct ieee80211_channel *curchan = hw->conf.channel; int pos = curchan->hw_value; - ath_dbg(common, CONFIG, "Set channel: %d MHz\n", + ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n", curchan->center_freq); ath9k_cmn_update_ichannel(&priv->ah->channels[pos], @@ -1263,7 +1273,8 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) } mutex_unlock(&priv->htc_pm_lock); - ath_dbg(common, CONFIG, "idle: disabling radio\n"); + ath_dbg(common, ATH_DBG_CONFIG, + "idle: disabling radio\n"); ath9k_htc_radio_disable(hw); } @@ -1295,7 +1306,7 @@ static void ath9k_htc_configure_filter(struct ieee80211_hw *hw, *total_flags &= SUPPORTED_FILTERS; if (priv->op_flags & OP_INVALID) { - ath_dbg(ath9k_hw_common(priv->ah), ANY, + ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_ANY, "Unable to configure filter on invalid state\n"); mutex_unlock(&priv->mutex); return; @@ -1306,8 +1317,8 @@ static void ath9k_htc_configure_filter(struct ieee80211_hw *hw, rfilt = ath9k_htc_calcrxfilter(priv); ath9k_hw_setrxfilter(priv->ah, rfilt); - ath_dbg(ath9k_hw_common(priv->ah), CONFIG, "Set HW RX filter: 0x%x\n", - rfilt); + ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG, + "Set HW RX filter: 0x%x\n", rfilt); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); @@ -1374,7 +1385,7 @@ static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, qnum = get_hw_qnum(queue, priv->hwq_map); - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", queue, qnum, params->aifs, params->cw_min, params->cw_max, params->txop); @@ -1408,23 +1419,8 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw, if (htc_modparam_nohwcrypt) return -ENOSPC; - if ((vif->type == NL80211_IFTYPE_ADHOC || - vif->type == NL80211_IFTYPE_MESH_POINT) && - (key->cipher == WLAN_CIPHER_SUITE_TKIP || - key->cipher == WLAN_CIPHER_SUITE_CCMP) && - !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { - /* - * For now, disable hw crypto for the RSN IBSS group keys. This - * could be optimized in the future to use a modified key cache - * design to support per-STA RX GTK, but until that gets - * implemented, use of software crypto for group addressed - * frames is a acceptable to allow RSN IBSS to be used. - */ - return -EOPNOTSUPP; - } - mutex_lock(&priv->mutex); - ath_dbg(common, CONFIG, "Set HW Key\n"); + ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n"); ath9k_htc_ps_wakeup(priv); switch (cmd) { @@ -1460,7 +1456,8 @@ static void ath9k_htc_set_bssid(struct ath9k_htc_priv *priv) struct ath_common *common = ath9k_hw_common(priv->ah); ath9k_hw_write_associd(priv->ah); - ath_dbg(common, CONFIG, "BSSID: %pM aid: 0x%x\n", + ath_dbg(common, ATH_DBG_CONFIG, + "BSSID: %pM aid: 0x%x\n", common->curbssid, common->curaid); } @@ -1498,7 +1495,7 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, ath9k_htc_ps_wakeup(priv); if (changed & BSS_CHANGED_ASSOC) { - ath_dbg(common, CONFIG, "BSS Changed ASSOC %d\n", + ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n", bss_conf->assoc); bss_conf->assoc ? @@ -1522,8 +1519,8 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, } if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) { - ath_dbg(common, CONFIG, "Beacon enabled for BSS: %pM\n", - bss_conf->bssid); + ath_dbg(common, ATH_DBG_CONFIG, + "Beacon enabled for BSS: %pM\n", bss_conf->bssid); ath9k_htc_set_tsfadjust(priv, vif); priv->op_flags |= OP_ENABLE_BEACON; ath9k_htc_beacon_config(priv, vif); @@ -1535,7 +1532,7 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, * AP/IBSS interfaces. */ if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) { - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Beacon disabled for BSS: %pM\n", bss_conf->bssid); priv->op_flags &= ~OP_ENABLE_BEACON; @@ -1553,7 +1550,7 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, (vif->type == NL80211_IFTYPE_AP)) { priv->op_flags |= OP_TSF_RESET; } - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Beacon interval changed for BSS: %pM\n", bss_conf->bssid); ath9k_htc_beacon_config(priv, vif); @@ -1743,7 +1740,8 @@ static int ath9k_htc_set_bitrate_mask(struct ieee80211_hw *hw, goto out; } - ath_dbg(common, CONFIG, "Set bitrate masks: 0x%x, 0x%x\n", + ath_dbg(common, ATH_DBG_CONFIG, + "Set bitrate masks: 0x%x, 0x%x\n", mask->control[IEEE80211_BAND_2GHZ].legacy, mask->control[IEEE80211_BAND_5GHZ].legacy); out: diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 6596a23e..9a571496 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -355,7 +355,7 @@ int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, vif_idx = avp->index; } else { if (!priv->ah->is_monitoring) { - ath_dbg(ath9k_hw_common(priv->ah), XMIT, + ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT, "VIF is null, but no monitor interface !\n"); return -EINVAL; } @@ -630,7 +630,8 @@ static struct sk_buff* ath9k_htc_tx_get_packet(struct ath9k_htc_priv *priv, } spin_unlock_irqrestore(&epid_queue->lock, flags); - ath_dbg(common, XMIT, "No matching packet for cookie: %d, epid: %d\n", + ath_dbg(common, ATH_DBG_XMIT, + "No matching packet for cookie: %d, epid: %d\n", txs->cookie, epid); return NULL; @@ -714,7 +715,8 @@ static inline bool check_packet(struct ath9k_htc_priv *priv, struct sk_buff *skb if (time_after(jiffies, tx_ctl->timestamp + msecs_to_jiffies(ATH9K_HTC_TX_TIMEOUT_INTERVAL))) { - ath_dbg(common, XMIT, "Dropping a packet due to TX timeout\n"); + ath_dbg(common, ATH_DBG_XMIT, + "Dropping a packet due to TX timeout\n"); return true; } @@ -761,7 +763,7 @@ void ath9k_htc_tx_cleanup_timer(unsigned long data) skb = ath9k_htc_tx_get_packet(priv, &event->txs); if (skb) { - ath_dbg(common, XMIT, + ath_dbg(common, ATH_DBG_XMIT, "Found packet for cookie: %d, epid: %d\n", event->txs.cookie, MS(event->txs.ts_rate, ATH9K_HTC_TXSTAT_EPID)); @@ -1179,7 +1181,8 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb, spin_unlock(&priv->rx.rxbuflock); if (rxbuf == NULL) { - ath_dbg(common, ANY, "No free RX buffer\n"); + ath_dbg(common, ATH_DBG_ANY, + "No free RX buffer\n"); goto err; } diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c old mode 100644 new mode 100755 index ec86d981..4f7843ae --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -433,8 +433,11 @@ struct htc_target *ath9k_htc_hw_alloc(void *hif_handle, struct htc_target *target; target = kzalloc(sizeof(struct htc_target), GFP_KERNEL); - if (!target) + if (!target) { + printk(KERN_ERR "Unable to allocate memory for" + "target device\n"); return NULL; + } init_completion(&target->target_wait); init_completion(&target->cmd_wait); diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index 265bf775..e74c2337 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -24,7 +24,7 @@ static inline void ath9k_hw_configpcipowersave(struct ath_hw *ah, bool power_off) { - if (!ah->aspm_enabled) + if (ah->aspm_enabled != true) return; ath9k_hw_ops(ah)->config_pci_powersave(ah, power_off); @@ -212,13 +212,4 @@ static inline int ath9k_hw_fast_chan_change(struct ath_hw *ah, return ath9k_hw_private_ops(ah)->fast_chan_change(ah, chan, ini_reloaded); } - -static inline void ath9k_hw_set_radar_params(struct ath_hw *ah) -{ - if (!ath9k_hw_private_ops(ah)->set_radar_params) - return; - - ath9k_hw_private_ops(ah)->set_radar_params(ah, &ah->radar_conf); -} - #endif /* ATH9K_HW_OPS_H */ diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 4b870110..7c7b377f 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -23,7 +23,6 @@ #include "hw-ops.h" #include "rc.h" #include "ar9003_mac.h" -#include "ar9003_mci.h" static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type); @@ -134,7 +133,7 @@ bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout) udelay(AH_TIME_QUANTUM); } - ath_dbg(ath9k_hw_common(ah), ANY, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_ANY, "timeout (%d us) on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n", timeout, reg, REG_READ(ah, reg), mask, val); @@ -449,7 +448,6 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah) ah->slottime = ATH9K_SLOT_TIME_9; ah->globaltxtimeout = (u32) -1; ah->power_mode = ATH9K_PM_UNDEFINED; - ah->htc_reset_init = true; } static int ath9k_hw_init_macaddr(struct ath_hw *ah) @@ -493,7 +491,8 @@ static int ath9k_hw_post_init(struct ath_hw *ah) if (ecode != 0) return ecode; - ath_dbg(ath9k_hw_common(ah), CONFIG, "Eeprom VER: %d, REV: %d\n", + ath_dbg(ath9k_hw_common(ah), ATH_DBG_CONFIG, + "Eeprom VER: %d, REV: %d\n", ah->eep_ops->get_eeprom_ver(ah), ah->eep_ops->get_eeprom_rev(ah)); @@ -505,7 +504,7 @@ static int ath9k_hw_post_init(struct ath_hw *ah) return ecode; } - if (ah->config.enable_ani) { + if (!AR_SREV_9100(ah) && !AR_SREV_9340(ah)) { ath9k_hw_ani_setup(ah); ath9k_hw_ani_init(ah); } @@ -538,17 +537,6 @@ static int __ath9k_hw_init(struct ath_hw *ah) ah->WARegVal |= (AR_WA_D3_L1_DISABLE | AR_WA_ASPM_TIMER_BASED_DISABLE); - /* - * Read back AR_WA into a permanent copy and set bits 14 and 17. - * We need to do this to avoid RMW of this register. We cannot - * read the reg when chip is asleep. - */ - ah->WARegVal = REG_READ(ah, AR_WA); - ah->WARegVal |= (AR_WA_D3_L1_DISABLE | - AR_WA_ASPM_TIMER_BASED_DISABLE); - - ath9k_hw_read_revisions(ah); - if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { ath_err(common, "Couldn't reset chip\n"); return -EIO; @@ -567,7 +555,7 @@ static int __ath9k_hw_init(struct ath_hw *ah) return -EIO; } - if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_AUTO) { + if (ah->config.serialize_regmode == SER_REG_MODE_AUTO) { if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI || ((AR_SREV_9160(ah) || AR_SREV_9280(ah) || AR_SREV_9287(ah)) && !ah->is_pciexpress)) { @@ -579,7 +567,7 @@ static int __ath9k_hw_init(struct ath_hw *ah) } } - ath_dbg(common, RESET, "serialize_regmode is %d\n", + ath_dbg(common, ATH_DBG_RESET, "serialize_regmode is %d\n", ah->config.serialize_regmode); if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) @@ -622,15 +610,14 @@ static int __ath9k_hw_init(struct ath_hw *ah) if (!AR_SREV_9300_20_OR_LATER(ah)) ah->ani_function &= ~ATH9K_ANI_MRC_CCK; - /* disable ANI for 9340 */ - if (AR_SREV_9340(ah)) - ah->config.enable_ani = false; - ath9k_hw_init_mode_regs(ah); if (!ah->is_pciexpress) ath9k_hw_disablepcie(ah); + if (!AR_SREV_9300_20_OR_LATER(ah)) + ar9002_hw_cck_chan14_spread(ah); + r = ath9k_hw_post_init(ah); if (r) return r; @@ -980,8 +967,8 @@ static void ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us) static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu) { if (tu > 0xFFFF) { - ath_dbg(ath9k_hw_common(ah), XMIT, "bad global tx timeout %u\n", - tu); + ath_dbg(ath9k_hw_common(ah), ATH_DBG_XMIT, + "bad global tx timeout %u\n", tu); ah->globaltxtimeout = (u32) -1; return false; } else { @@ -1002,7 +989,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) int rx_lat = 0, tx_lat = 0, eifs = 0; u32 reg; - ath_dbg(ath9k_hw_common(ah), RESET, "ah->misc_mode 0x%x\n", + ath_dbg(ath9k_hw_common(ah), ATH_DBG_RESET, "ah->misc_mode 0x%x\n", ah->misc_mode); if (!chan) @@ -1060,16 +1047,13 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) /* * Workaround for early ACK timeouts, add an offset to match the - * initval's 64us ack timeout value. Use 48us for the CTS timeout. + * initval's 64us ack timeout value. * This was initially only meant to work around an issue with delayed * BA frames in some implementations, but it has been found to fix ACK * timeout issues in other cases as well. */ - if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ) { + if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ) acktimeout += 64 - sifstime - ah->slottime; - ctstimeout += 48 - sifstime - ah->slottime; - } - ath9k_hw_set_sifs_time(ah, sifstime); ath9k_hw_setslottime(ah, slottime); @@ -1300,7 +1284,7 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type) (npend || type == ATH9K_RESET_COLD)) { int reset_err = 0; - ath_dbg(ath9k_hw_common(ah), RESET, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_RESET, "reset MAC via external reset\n"); reset_err = ah->external_reset(); @@ -1323,7 +1307,8 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type) REG_WRITE(ah, AR_RTC_RC, 0); if (!ath9k_hw_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0, AH_WAIT_TIMEOUT)) { - ath_dbg(ath9k_hw_common(ah), RESET, "RTC stuck in MAC reset\n"); + ath_dbg(ath9k_hw_common(ah), ATH_DBG_RESET, + "RTC stuck in MAC reset\n"); return false; } @@ -1368,7 +1353,8 @@ static bool ath9k_hw_set_reset_power_on(struct ath_hw *ah) AR_RTC_STATUS_M, AR_RTC_STATUS_ON, AH_WAIT_TIMEOUT)) { - ath_dbg(ath9k_hw_common(ah), RESET, "RTC not waking up\n"); + ath_dbg(ath9k_hw_common(ah), ATH_DBG_RESET, + "RTC not waking up\n"); return false; } @@ -1377,7 +1363,6 @@ static bool ath9k_hw_set_reset_power_on(struct ath_hw *ah) static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type) { - bool ret = false; if (AR_SREV_9300_20_OR_LATER(ah)) { REG_WRITE(ah, AR_WA, ah->WARegVal); @@ -1389,37 +1374,22 @@ static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type) switch (type) { case ATH9K_RESET_POWER_ON: - ret = ath9k_hw_set_reset_power_on(ah); - break; + return ath9k_hw_set_reset_power_on(ah); case ATH9K_RESET_WARM: case ATH9K_RESET_COLD: - ret = ath9k_hw_set_reset(ah, type); - break; + return ath9k_hw_set_reset(ah, type); default: - break; + return false; } - - if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) - REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); - - return ret; } static bool ath9k_hw_chip_reset(struct ath_hw *ah, struct ath9k_channel *chan) { - int reset_type = ATH9K_RESET_WARM; - - if (AR_SREV_9280(ah)) { - if (ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) - reset_type = ATH9K_RESET_POWER_ON; - else - reset_type = ATH9K_RESET_COLD; - } else if (ah->chip_fullsleep || REG_READ(ah, AR_Q_TXE) || - (REG_READ(ah, AR_CR) & AR_CR_RXE)) - reset_type = ATH9K_RESET_COLD; - - if (!ath9k_hw_set_reset_reg(ah, reset_type)) + if (AR_SREV_9280(ah) && ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) { + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) + return false; + } else if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM)) return false; if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) @@ -1449,7 +1419,7 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, for (qnum = 0; qnum < AR_NUM_QCU; qnum++) { if (ath9k_hw_numtxpending(ah, qnum)) { - ath_dbg(common, QUEUE, + ath_dbg(common, ATH_DBG_QUEUE, "Transmit frames pending on queue %d\n", qnum); return false; } @@ -1480,7 +1450,7 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, return false; } ath9k_hw_set_clockrate(ah); - ath9k_hw_apply_txpower(ah, chan, false); + ath9k_hw_apply_txpower(ah, chan); ath9k_hw_rfbus_done(ah); if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) @@ -1545,95 +1515,23 @@ bool ath9k_hw_check_alive(struct ath_hw *ah) } EXPORT_SYMBOL(ath9k_hw_check_alive); -/* - * Fast channel change: - * (Change synthesizer based on channel freq without resetting chip) - * - * Don't do FCC when - * - Flag is not set - * - Chip is just coming out of full sleep - * - Channel to be set is same as current channel - * - Channel flags are different, (eg.,moving from 2GHz to 5GHz channel) - */ -static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan) -{ - struct ath_common *common = ath9k_hw_common(ah); - int ret; - - if (AR_SREV_9280(ah) && common->bus_ops->ath_bus_type == ATH_PCI) - goto fail; - - if (ah->chip_fullsleep) - goto fail; - - if (!ah->curchan) - goto fail; - - if (chan->channel == ah->curchan->channel) - goto fail; - - if ((chan->channelFlags & CHANNEL_ALL) != - (ah->curchan->channelFlags & CHANNEL_ALL)) - goto fail; - - if (!ath9k_hw_check_alive(ah)) - goto fail; - - /* - * For AR9462, make sure that calibration data for - * re-using are present. - */ - if (AR_SREV_9462(ah) && (!ah->caldata || - !ah->caldata->done_txiqcal_once || - !ah->caldata->done_txclcal_once || - !ah->caldata->rtt_hist.num_readings)) - goto fail; - - ath_dbg(common, RESET, "FastChannelChange for %d -> %d\n", - ah->curchan->channel, chan->channel); - - ret = ath9k_hw_channel_change(ah, chan); - if (!ret) - goto fail; - - ath9k_hw_loadnf(ah, ah->curchan); - ath9k_hw_start_nfcal(ah, true); - - if ((ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && ar9003_mci_is_ready(ah)) - ar9003_mci_2g5g_switch(ah, true); - - if (AR_SREV_9271(ah)) - ar9002_hw_load_ani_reg(ah, chan); - - return 0; -fail: - return -EINVAL; -} - int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, - struct ath9k_hw_cal_data *caldata, bool fastcc) + struct ath9k_hw_cal_data *caldata, bool bChannelChange) { struct ath_common *common = ath9k_hw_common(ah); u32 saveLedState; + struct ath9k_channel *curchan = ah->curchan; u32 saveDefAntenna; u32 macStaId1; u64 tsf = 0; int i, r; - bool start_mci_reset = false; - bool mci = !!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI); - bool save_fullsleep = ah->chip_fullsleep; - - if (mci) { - start_mci_reset = ar9003_mci_start_reset(ah, chan); - if (start_mci_reset) - return 0; - } + bool allow_fbs = false; if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) return -EIO; - if (ah->curchan && !ah->chip_fullsleep) - ath9k_hw_getnf(ah, ah->curchan); + if (curchan && !ah->chip_fullsleep) + ath9k_hw_getnf(ah, curchan); ah->caldata = caldata; if (caldata && @@ -1647,14 +1545,30 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, } ah->noise = ath9k_hw_getchan_noise(ah, chan); - if (fastcc) { - r = ath9k_hw_do_fastcc(ah, chan); - if (!r) - return r; - } + if (AR_SREV_9280(ah) && common->bus_ops->ath_bus_type == ATH_PCI) + bChannelChange = false; - if (mci) - ar9003_mci_stop_bt(ah, save_fullsleep); + if (caldata && + caldata->done_txiqcal_once && + caldata->done_txclcal_once && + caldata->rtt_hist.num_readings) + allow_fbs = true; + + if (bChannelChange && + (ah->chip_fullsleep != true) && + (ah->curchan != NULL) && + (chan->channel != ah->curchan->channel) && + (allow_fbs || + ((chan->channelFlags & CHANNEL_ALL) == + (ah->curchan->channelFlags & CHANNEL_ALL)))) { + if (ath9k_hw_channel_change(ah, chan)) { + ath9k_hw_loadnf(ah, ah->curchan); + ath9k_hw_start_nfcal(ah, true); + if (AR_SREV_9271(ah)) + ar9002_hw_load_ani_reg(ah, chan); + return 0; + } + } saveDefAntenna = REG_READ(ah, AR_DEF_ANTENNA); if (saveDefAntenna == 0) @@ -1711,9 +1625,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (r) return r; - if (mci) - ar9003_mci_reset(ah, false, IS_CHAN_2GHZ(chan), save_fullsleep); - /* * Some AR91xx SoC devices frequently fail to accept TSF writes * right after the chip reset. When that happens, write a new @@ -1831,9 +1742,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_loadnf(ah, chan); ath9k_hw_start_nfcal(ah, true); - if (mci && ar9003_mci_end_reset(ah, chan, caldata)) - return -EIO; - ENABLE_REGWRITE_BUFFER(ah); ath9k_hw_restore_chainmask(ah); @@ -1848,14 +1756,14 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, u32 mask; mask = REG_READ(ah, AR_CFG); if (mask & (AR_CFG_SWRB | AR_CFG_SWTB | AR_CFG_SWRG)) { - ath_dbg(common, RESET, "CFG Byte Swap Set 0x%x\n", - mask); + ath_dbg(common, ATH_DBG_RESET, + "CFG Byte Swap Set 0x%x\n", mask); } else { mask = INIT_CONFIG_STATUS | AR_CFG_SWRB | AR_CFG_SWTB; REG_WRITE(ah, AR_CFG, mask); - ath_dbg(common, RESET, "Setting CFG 0x%x\n", - REG_READ(ah, AR_CFG)); + ath_dbg(common, ATH_DBG_RESET, + "Setting CFG 0x%x\n", REG_READ(ah, AR_CFG)); } } else { if (common->bus_ops->ath_bus_type == ATH_USB) { @@ -1873,12 +1781,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, #endif } - if (ath9k_hw_btcoex_is_enabled(ah)) + if (ah->btcoex_hw.enabled) ath9k_hw_btcoex_enable(ah); - if (mci) - ar9003_mci_check_bt(ah); - if (AR_SREV_9300_20_OR_LATER(ah)) { ar9003_hw_bb_watchdog_config(ah); @@ -1928,15 +1833,15 @@ static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip) REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF); /* Shutdown chip. Active low */ - if (!AR_SREV_5416(ah) && !AR_SREV_9271(ah)) { + if (!AR_SREV_5416(ah) && + !AR_SREV_9271(ah) && !AR_SREV_9462_10(ah)) { REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN); udelay(2); } } /* Clear Bit 14 of AR_WA after putting chip into Full Sleep mode. */ - if (AR_SREV_9300_20_OR_LATER(ah)) - REG_WRITE(ah, AR_WA, ah->WARegVal & ~AR_WA_D3_L1_DISABLE); + REG_WRITE(ah, AR_WA, ah->WARegVal & ~AR_WA_D3_L1_DISABLE); } /* @@ -2003,7 +1908,8 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip) if (setChip) { if ((REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) == AR_RTC_STATUS_SHUTDOWN) { - if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { + if (ath9k_hw_set_reset_reg(ah, + ATH9K_RESET_POWER_ON) != true) { return false; } if (!AR_SREV_9300_20_OR_LATER(ah)) @@ -2052,29 +1958,18 @@ bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode) if (ah->power_mode == mode) return status; - ath_dbg(common, RESET, "%s -> %s\n", + ath_dbg(common, ATH_DBG_RESET, "%s -> %s\n", modes[ah->power_mode], modes[mode]); switch (mode) { case ATH9K_PM_AWAKE: status = ath9k_hw_set_power_awake(ah, setChip); - - if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) - REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); - break; case ATH9K_PM_FULL_SLEEP: - if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) - ar9003_mci_set_full_sleep(ah); - ath9k_set_power_sleep(ah, setChip); ah->chip_fullsleep = true; break; case ATH9K_PM_NETWORK_SLEEP: - - if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) - REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); - ath9k_set_power_network_sleep(ah, setChip); break; default: @@ -2124,8 +2019,9 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN; break; default: - ath_dbg(ath9k_hw_common(ah), BEACON, - "%s: unsupported opmode: %d\n", __func__, ah->opmode); + ath_dbg(ath9k_hw_common(ah), ATH_DBG_BEACON, + "%s: unsupported opmode: %d\n", + __func__, ah->opmode); return; break; } @@ -2176,10 +2072,10 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, else nextTbtt = bs->bs_nexttbtt; - ath_dbg(common, BEACON, "next DTIM %d\n", bs->bs_nextdtim); - ath_dbg(common, BEACON, "next beacon %d\n", nextTbtt); - ath_dbg(common, BEACON, "beacon period %d\n", beaconintval); - ath_dbg(common, BEACON, "DTIM period %d\n", dtimperiod); + ath_dbg(common, ATH_DBG_BEACON, "next DTIM %d\n", bs->bs_nextdtim); + ath_dbg(common, ATH_DBG_BEACON, "next beacon %d\n", nextTbtt); + ath_dbg(common, ATH_DBG_BEACON, "beacon period %d\n", beaconintval); + ath_dbg(common, ATH_DBG_BEACON, "DTIM period %d\n", dtimperiod); ENABLE_REGWRITE_BUFFER(ah); @@ -2226,35 +2122,12 @@ static u8 fixup_chainmask(u8 chip_chainmask, u8 eeprom_chainmask) return chip_chainmask; } -/** - * ath9k_hw_dfs_tested - checks if DFS has been tested with used chipset - * @ah: the atheros hardware data structure - * - * We enable DFS support upstream on chipsets which have passed a series - * of tests. The testing requirements are going to be documented. Desired - * test requirements are documented at: - * - * http://wireless.kernel.org/en/users/Drivers/ath9k/dfs - * - * Once a new chipset gets properly tested an individual commit can be used - * to document the testing for DFS for that chipset. - */ -static bool ath9k_hw_dfs_tested(struct ath_hw *ah) -{ - - switch (ah->hw_version.macVersion) { - /* AR9580 will likely be our first target to get testing on */ - case AR_SREV_VERSION_9580: - default: - return false; - } -} - int ath9k_hw_fill_cap_info(struct ath_hw *ah) { struct ath9k_hw_capabilities *pCap = &ah->caps; struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); struct ath_common *common = ath9k_hw_common(ah); + struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; unsigned int chip_chainmask; u16 eeval; @@ -2270,8 +2143,8 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) regulatory->current_rd += 5; else if (regulatory->current_rd == 0x41) regulatory->current_rd = 0x43; - ath_dbg(common, REGULATORY, "regdomain mapped to 0x%x\n", - regulatory->current_rd); + ath_dbg(common, ATH_DBG_REGULATORY, + "regdomain mapped to 0x%x\n", regulatory->current_rd); } eeval = ah->eep_ops->get_eeprom(ah, EEP_OP_MODE); @@ -2289,8 +2162,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) if (AR_SREV_9485(ah) || AR_SREV_9285(ah) || AR_SREV_9330(ah)) chip_chainmask = 1; - else if (AR_SREV_9462(ah)) - chip_chainmask = 3; else if (!AR_SREV_9280_20_OR_LATER(ah)) chip_chainmask = 7; else if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9340(ah)) @@ -2347,10 +2218,12 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) else pCap->num_gpio_pins = AR_NUM_GPIO; - if (AR_SREV_9160_10_OR_LATER(ah) || AR_SREV_9100(ah)) + if (AR_SREV_9160_10_OR_LATER(ah) || AR_SREV_9100(ah)) { + pCap->hw_caps |= ATH9K_HW_CAP_CST; pCap->rts_aggr_limit = ATH_AMPDU_LIMIT_MAX; - else + } else { pCap->rts_aggr_limit = (8 * 1024); + } #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) ah->rfsilent = ah->eep_ops->get_eeprom(ah, EEP_RF_SILENT); @@ -2373,6 +2246,28 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) else pCap->hw_caps |= ATH9K_HW_CAP_4KB_SPLITTRANS; + if (common->btcoex_enabled) { + if (AR_SREV_9300_20_OR_LATER(ah)) { + btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE; + btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO_9300; + btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO_9300; + btcoex_hw->btpriority_gpio = ATH_BTPRIORITY_GPIO_9300; + } else if (AR_SREV_9280_20_OR_LATER(ah)) { + btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO_9280; + btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO_9280; + + if (AR_SREV_9285(ah)) { + btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE; + btcoex_hw->btpriority_gpio = + ATH_BTPRIORITY_GPIO_9285; + } else { + btcoex_hw->scheme = ATH_BTCOEX_CFG_2WIRE; + } + } + } else { + btcoex_hw->scheme = ATH_BTCOEX_CFG_NONE; + } + if (AR_SREV_9300_20_OR_LATER(ah)) { pCap->hw_caps |= ATH9K_HW_CAP_EDMA | ATH9K_HW_CAP_FASTCLOCK; if (!AR_SREV_9330(ah) && !AR_SREV_9485(ah)) @@ -2436,9 +2331,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) pCap->pcie_lcr_offset = 0x80; } - if (ath9k_hw_dfs_tested(ah)) - pCap->hw_caps |= ATH9K_HW_CAP_DFS; - tx_chainmask = pCap->tx_chainmask; rx_chainmask = pCap->rx_chainmask; while (tx_chainmask || rx_chainmask) { @@ -2453,20 +2345,11 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) if (AR_SREV_9300_20_OR_LATER(ah)) { ah->enabled_cals |= TX_IQ_CAL; - if (AR_SREV_9485_OR_LATER(ah)) + if (!AR_SREV_9330(ah)) ah->enabled_cals |= TX_IQ_ON_AGC_CAL; } - - if (AR_SREV_9462(ah)) { - - if (!(ah->ent_mode & AR_ENT_OTP_49GHZ_DISABLE)) - pCap->hw_caps |= ATH9K_HW_CAP_MCI; - - if (AR_SREV_9462_20(ah)) - pCap->hw_caps |= ATH9K_HW_CAP_RTT; - - } - + if (AR_SREV_9462(ah)) + pCap->hw_caps |= ATH9K_HW_CAP_RTT | ATH9K_HW_CAP_MCI; return 0; } @@ -2592,6 +2475,12 @@ void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val) } EXPORT_SYMBOL(ath9k_hw_set_gpio); +u32 ath9k_hw_getdefantenna(struct ath_hw *ah) +{ + return REG_READ(ah, AR_DEF_ANTENNA) & 0x7; +} +EXPORT_SYMBOL(ath9k_hw_getdefantenna); + void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna) { REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7)); @@ -2649,7 +2538,6 @@ bool ath9k_hw_phy_disable(struct ath_hw *ah) return false; ath9k_hw_init_pll(ah, NULL); - ah->htc_reset_init = true; return true; } EXPORT_SYMBOL(ath9k_hw_phy_disable); @@ -2679,8 +2567,7 @@ static int get_antenna_gain(struct ath_hw *ah, struct ath9k_channel *chan) return ah->eep_ops->get_eeprom(ah, gain_param); } -void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan, - bool test) +void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_regulatory *reg = ath9k_hw_regulatory(ah); struct ieee80211_channel *channel; @@ -2701,7 +2588,7 @@ void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan, ah->eep_ops->set_txpower(ah, chan, ath9k_regd_get_ctl(reg, chan), - ant_reduction, new_pwr, test); + ant_reduction, new_pwr, false); } void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test) @@ -2714,7 +2601,7 @@ void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test) if (test) channel->max_power = MAX_RATE_POWER / 2; - ath9k_hw_apply_txpower(ah, chan, test); + ath9k_hw_apply_txpower(ah, chan); if (test) channel->max_power = DIV_ROUND_UP(reg->max_power_level, 2); @@ -2777,7 +2664,7 @@ void ath9k_hw_reset_tsf(struct ath_hw *ah) { if (!ath9k_hw_wait(ah, AR_SLP32_MODE, AR_SLP32_TSF_WRITE_STATUS, 0, AH_TSF_WRITE_TIMEOUT)) - ath_dbg(ath9k_hw_common(ah), RESET, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_RESET, "AR_SLP32_TSF_WRITE_STATUS limit exceeded\n"); REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE); @@ -2902,7 +2789,7 @@ void ath9k_hw_gen_timer_start(struct ath_hw *ah, timer_next = tsf + trig_timeout; - ath_dbg(ath9k_hw_common(ah), HWTIMER, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_HWTIMER, "current tsf %x period %x timer_next %x\n", tsf, timer_period, timer_next); @@ -2991,8 +2878,8 @@ void ath_gen_timer_isr(struct ath_hw *ah) index = rightmost_index(timer_table, &thresh_mask); timer = timer_table->timers[index]; BUG_ON(!timer); - ath_dbg(common, HWTIMER, "TSF overflow for Gen timer %d\n", - index); + ath_dbg(common, ATH_DBG_HWTIMER, + "TSF overflow for Gen timer %d\n", index); timer->overflow(timer->arg); } @@ -3000,7 +2887,7 @@ void ath_gen_timer_isr(struct ath_hw *ah) index = rightmost_index(timer_table, &trigger_mask); timer = timer_table->timers[index]; BUG_ON(!timer); - ath_dbg(common, HWTIMER, + ath_dbg(common, ATH_DBG_HWTIMER, "Gen timer[%d] trigger\n", index); timer->trigger(timer->arg); } @@ -3011,6 +2898,12 @@ EXPORT_SYMBOL(ath_gen_timer_isr); /* HTC */ /********/ +void ath9k_hw_htc_resetinit(struct ath_hw *ah) +{ + ah->htc_reset_init = true; +} +EXPORT_SYMBOL(ath9k_hw_htc_resetinit); + static struct { u32 version; const char * name; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index f49be96a..947895c4 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -127,16 +127,6 @@ #define AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL 4 #define AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED 5 #define AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED 6 -#define AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_DATA 0x16 -#define AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_CLK 0x17 -#define AR_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA 0x18 -#define AR_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK 0x19 -#define AR_GPIO_OUTPUT_MUX_AS_WL_IN_TX 0x14 -#define AR_GPIO_OUTPUT_MUX_AS_WL_IN_RX 0x13 -#define AR_GPIO_OUTPUT_MUX_AS_BT_IN_TX 9 -#define AR_GPIO_OUTPUT_MUX_AS_BT_IN_RX 8 -#define AR_GPIO_OUTPUT_MUX_AS_RUCKUS_STROBE 0x1d -#define AR_GPIO_OUTPUT_MUX_AS_RUCKUS_DATA 0x1e #define AR_GPIOD_MASK 0x00001FFF #define AR_GPIO_BIT(_gpio) (1 << (_gpio)) @@ -181,8 +171,8 @@ #define PAPRD_IDEAL_AGC2_PWR_RANGE 0xe0 enum ath_hw_txq_subtype { - ATH_TXQ_AC_BE = 0, - ATH_TXQ_AC_BK = 1, + ATH_TXQ_AC_BK = 0, + ATH_TXQ_AC_BE = 1, ATH_TXQ_AC_VI = 2, ATH_TXQ_AC_VO = 3, }; @@ -197,21 +187,21 @@ enum ath_ini_subsys { enum ath9k_hw_caps { ATH9K_HW_CAP_HT = BIT(0), ATH9K_HW_CAP_RFSILENT = BIT(1), - ATH9K_HW_CAP_AUTOSLEEP = BIT(2), - ATH9K_HW_CAP_4KB_SPLITTRANS = BIT(3), - ATH9K_HW_CAP_EDMA = BIT(4), - ATH9K_HW_CAP_RAC_SUPPORTED = BIT(5), - ATH9K_HW_CAP_LDPC = BIT(6), - ATH9K_HW_CAP_FASTCLOCK = BIT(7), - ATH9K_HW_CAP_SGI_20 = BIT(8), - ATH9K_HW_CAP_PAPRD = BIT(9), - ATH9K_HW_CAP_ANT_DIV_COMB = BIT(10), - ATH9K_HW_CAP_2GHZ = BIT(11), - ATH9K_HW_CAP_5GHZ = BIT(12), - ATH9K_HW_CAP_APM = BIT(13), - ATH9K_HW_CAP_RTT = BIT(14), - ATH9K_HW_CAP_MCI = BIT(15), - ATH9K_HW_CAP_DFS = BIT(16), + ATH9K_HW_CAP_CST = BIT(2), + ATH9K_HW_CAP_AUTOSLEEP = BIT(4), + ATH9K_HW_CAP_4KB_SPLITTRANS = BIT(5), + ATH9K_HW_CAP_EDMA = BIT(6), + ATH9K_HW_CAP_RAC_SUPPORTED = BIT(7), + ATH9K_HW_CAP_LDPC = BIT(8), + ATH9K_HW_CAP_FASTCLOCK = BIT(9), + ATH9K_HW_CAP_SGI_20 = BIT(10), + ATH9K_HW_CAP_PAPRD = BIT(11), + ATH9K_HW_CAP_ANT_DIV_COMB = BIT(12), + ATH9K_HW_CAP_2GHZ = BIT(13), + ATH9K_HW_CAP_5GHZ = BIT(14), + ATH9K_HW_CAP_APM = BIT(15), + ATH9K_HW_CAP_RTT = BIT(16), + ATH9K_HW_CAP_MCI = BIT(17), }; struct ath9k_hw_capabilities { @@ -277,7 +267,6 @@ enum ath9k_int { ATH9K_INT_TX = 0x00000040, ATH9K_INT_TXDESC = 0x00000080, ATH9K_INT_TIM_TIMER = 0x00000100, - ATH9K_INT_MCI = 0x00000200, ATH9K_INT_BB_WATCHDOG = 0x00000400, ATH9K_INT_TXURN = 0x00000800, ATH9K_INT_MIB = 0x00001000, @@ -430,6 +419,16 @@ enum ath9k_rx_qtype { ATH9K_RX_QUEUE_MAX, }; +enum ath_mci_gpm_coex_profile_type { + MCI_GPM_COEX_PROFILE_UNKNOWN, + MCI_GPM_COEX_PROFILE_RFCOMM, + MCI_GPM_COEX_PROFILE_A2DP, + MCI_GPM_COEX_PROFILE_HID, + MCI_GPM_COEX_PROFILE_BNEP, + MCI_GPM_COEX_PROFILE_VOICE, + MCI_GPM_COEX_PROFILE_MAX +}; + struct ath9k_beacon_state { u32 bs_nexttbtt; u32 bs_nextdtim; @@ -783,6 +782,7 @@ struct ath_hw { u32 *analogBank6Data; u32 *analogBank6TPCData; u32 *analogBank7Data; + u32 *addac5416_21; u32 *bank6Temp; u8 txpower_limit; @@ -799,9 +799,8 @@ struct ath_hw { int firpwr[5]; enum ath9k_ani_cmd ani_function; -#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT + /* Bluetooth coexistance */ struct ath_btcoex_hw btcoex_hw; -#endif u32 intr_txqs; u8 txchainmask; @@ -829,14 +828,19 @@ struct ath_hw { struct ar5416IniArray iniAddac; struct ar5416IniArray iniPcieSerdes; struct ar5416IniArray iniPcieSerdesLowPower; - struct ar5416IniArray iniModesFastClock; - struct ar5416IniArray iniAdditional; + struct ar5416IniArray iniModesAdditional; + struct ar5416IniArray iniModesAdditional_40M; struct ar5416IniArray iniModesRxGain; struct ar5416IniArray iniModesTxGain; + struct ar5416IniArray iniModes_9271_1_0_only; struct ar5416IniArray iniCckfirNormal; struct ar5416IniArray iniCckfirJapan2484; struct ar5416IniArray ini_japan2484; + struct ar5416IniArray iniCommon_normal_cck_fir_coeff_9271; + struct ar5416IniArray iniCommon_japan_2484_cck_fir_coeff_9271; struct ar5416IniArray iniModes_9271_ANI_reg; + struct ar5416IniArray iniModes_high_power_tx_gain_9271; + struct ar5416IniArray iniModes_normal_power_tx_gain_9271; struct ar5416IniArray ini_radio_post_sys2ant; struct ar5416IniArray ini_BTCOEX_MAX_TXPWR; @@ -854,7 +858,7 @@ struct ath_hw { u32 ts_paddr_start; u32 ts_paddr_end; u16 ts_tail; - u16 ts_size; + u8 ts_size; u32 bb_watchdog_last_status; u32 bb_watchdog_timeout_ms; /* in ms, 0 to disable */ @@ -921,7 +925,7 @@ const char *ath9k_hw_probe(u16 vendorid, u16 devid); void ath9k_hw_deinit(struct ath_hw *ah); int ath9k_hw_init(struct ath_hw *ah); int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, - struct ath9k_hw_cal_data *caldata, bool fastcc); + struct ath9k_hw_cal_data *caldata, bool bChannelChange); int ath9k_hw_fill_cap_info(struct ath_hw *ah); u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan); @@ -931,6 +935,7 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio); void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio, u32 ah_signal_type); void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val); +u32 ath9k_hw_getdefantenna(struct ath_hw *ah); void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna); /* General Operation */ @@ -951,6 +956,7 @@ bool ath9k_hw_disable(struct ath_hw *ah); void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test); void ath9k_hw_setopmode(struct ath_hw *ah); void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1); +void ath9k_hw_setbssidmask(struct ath_hw *ah); void ath9k_hw_write_associd(struct ath_hw *ah); u32 ath9k_hw_gettsf32(struct ath_hw *ah); u64 ath9k_hw_gettsf64(struct ath_hw *ah); @@ -984,16 +990,19 @@ void ath_gen_timer_isr(struct ath_hw *hw); void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len); +/* HTC */ +void ath9k_hw_htc_resetinit(struct ath_hw *ah); + /* PHY */ void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled, u32 *coef_mantissa, u32 *coef_exponent); -void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan, - bool test); +void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan); /* * Code Specific to AR5008, AR9001 or AR9002, * we stuff these here to avoid callbacks for AR9003. */ +void ar9002_hw_cck_chan14_spread(struct ath_hw *ah); int ar9002_hw_rf_claim(struct ath_hw *ah); void ar9002_hw_enable_async_fifo(struct ath_hw *ah); @@ -1041,32 +1050,6 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning); void ath9k_hw_proc_mib_event(struct ath_hw *ah); void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan); -#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT -static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah) -{ - return ah->btcoex_hw.enabled; -} -void ath9k_hw_btcoex_enable(struct ath_hw *ah); -static inline enum ath_btcoex_scheme -ath9k_hw_get_btcoex_scheme(struct ath_hw *ah) -{ - return ah->btcoex_hw.scheme; -} -#else -static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah) -{ - return false; -} -static inline void ath9k_hw_btcoex_enable(struct ath_hw *ah) -{ -} -static inline enum ath_btcoex_scheme -ath9k_hw_get_btcoex_scheme(struct ath_hw *ah) -{ - return ATH_BTCOEX_CFG_NONE; -} -#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ - #define ATH9K_CLOCK_RATE_CCK 22 #define ATH9K_CLOCK_RATE_5GHZ_OFDM 40 #define ATH9K_CLOCK_RATE_2GHZ_OFDM 44 diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c old mode 100644 new mode 100755 index 30b039fb..39b5faf5 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -172,7 +172,7 @@ static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset) struct ath_common *common = ath9k_hw_common(ah); struct ath_softc *sc = (struct ath_softc *) common->priv; - if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_ON) { + if (ah->config.serialize_regmode == SER_REG_MODE_ON) { unsigned long flags; spin_lock_irqsave(&sc->sc_serial_rw, flags); iowrite32(val, sc->mem + reg_offset); @@ -188,7 +188,7 @@ static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset) struct ath_softc *sc = (struct ath_softc *) common->priv; u32 val; - if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_ON) { + if (ah->config.serialize_regmode == SER_REG_MODE_ON) { unsigned long flags; spin_lock_irqsave(&sc->sc_serial_rw, flags); val = ioread32(sc->mem + reg_offset); @@ -219,7 +219,7 @@ static unsigned int ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 cl unsigned long uninitialized_var(flags); u32 val; - if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_ON) { + if (ah->config.serialize_regmode == SER_REG_MODE_ON) { spin_lock_irqsave(&sc->sc_serial_rw, flags); val = __ath9k_reg_rmw(sc, reg_offset, set, clr); spin_unlock_irqrestore(&sc->sc_serial_rw, flags); @@ -258,8 +258,6 @@ static void setup_ht_cap(struct ath_softc *sc, if (AR_SREV_9330(ah) || AR_SREV_9485(ah)) max_streams = 1; - else if (AR_SREV_9462(ah)) - max_streams = 2; else if (AR_SREV_9300_20_OR_LATER(ah)) max_streams = 3; else @@ -276,7 +274,8 @@ static void setup_ht_cap(struct ath_softc *sc, tx_streams = ath9k_cmn_count_streams(ah->txchainmask, max_streams); rx_streams = ath9k_cmn_count_streams(ah->rxchainmask, max_streams); - ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n", + ath_dbg(common, ATH_DBG_CONFIG, + "TX streams %d, RX streams: %d\n", tx_streams, rx_streams); if (tx_streams != rx_streams) { @@ -296,22 +295,9 @@ static int ath9k_reg_notifier(struct wiphy *wiphy, { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath_softc *sc = hw->priv; - struct ath_hw *ah = sc->sc_ah; - struct ath_regulatory *reg = ath9k_hw_regulatory(ah); - int ret; - - ret = ath_reg_notifier_apply(wiphy, request, reg); - - /* Set tx power */ - if (ah->curchan) { - sc->config.txpowlimit = 2 * ah->curchan->chan->max_power; - ath9k_ps_wakeup(sc); - ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false); - sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit; - ath9k_ps_restore(sc); - } + struct ath_regulatory *reg = ath9k_hw_regulatory(sc->sc_ah); - return ret; + return ath_reg_notifier_apply(wiphy, request, reg); } /* @@ -328,7 +314,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, struct ath_buf *bf; int i, bsize, error, desc_len; - ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n", + ath_dbg(common, ATH_DBG_CONFIG, "%s DMA: %u buffers %u desc/buf\n", name, nbuf, ndesc); INIT_LIST_HEAD(head); @@ -374,7 +360,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, goto fail; } ds = (u8 *) dd->dd_desc; - ath_dbg(common, CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n", + ath_dbg(common, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n", name, ds, (u32) dd->dd_desc_len, ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); @@ -419,6 +405,36 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, return error; } +static int ath9k_init_btcoex(struct ath_softc *sc) +{ + struct ath_txq *txq; + int r; + + switch (sc->sc_ah->btcoex_hw.scheme) { + case ATH_BTCOEX_CFG_NONE: + break; + case ATH_BTCOEX_CFG_2WIRE: + ath9k_hw_btcoex_init_2wire(sc->sc_ah); + break; + case ATH_BTCOEX_CFG_3WIRE: + ath9k_hw_btcoex_init_3wire(sc->sc_ah); + r = ath_init_btcoex_timer(sc); + if (r) + return -1; + txq = sc->tx.txq_map[WME_AC_BE]; + ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum); + sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; + sc->btcoex.duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE; + INIT_LIST_HEAD(&sc->btcoex.mci.info); + break; + default: + WARN_ON(1); + break; + } + + return 0; +} + static int ath9k_init_queues(struct ath_softc *sc) { int i = 0; @@ -484,11 +500,19 @@ static void ath9k_init_misc(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); int i = 0; - setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc); sc->config.txpowlimit = ATH_TXPOWER_MAX; + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { + sc->sc_flags |= SC_OP_TXAGGR; + sc->sc_flags |= SC_OP_RXAGGR; + } + + sc->rx.defant = ath9k_hw_getdefantenna(sc->sc_ah); + memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); + sc->beacon.slottime = ATH9K_SLOT_TIME_9; for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) @@ -547,10 +571,8 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, mutex_init(&sc->mutex); #ifdef CONFIG_ATH9K_DEBUGFS spin_lock_init(&sc->nodes_lock); - INIT_LIST_HEAD(&sc->nodes); -#endif -#ifdef CONFIG_ATH9K_MAC_DEBUG spin_lock_init(&sc->debug.samp_lock); + INIT_LIST_HEAD(&sc->nodes); #endif tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet, @@ -621,10 +643,8 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band) static void ath9k_init_txpower_limits(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath9k_channel *curchan = ah->curchan; - ah->txchainmask = common->tx_chainmask; if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) ath9k_init_band_txpower(sc, IEEE80211_BAND_2GHZ); if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) @@ -681,10 +701,11 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->queues = 4; hw->max_rates = 4; hw->channel_change_time = 5000; - hw->max_listen_interval = 1; + hw->max_listen_interval = 10; hw->max_rate_tries = 10; hw->sta_data_size = sizeof(struct ath_node); hw->vif_data_size = sizeof(struct ath_vif); + hw->extra_tx_headroom = 4; hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1; hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1; @@ -753,11 +774,6 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, ARRAY_SIZE(ath9k_tpt_blink)); #endif - INIT_WORK(&sc->hw_reset_work, ath_reset_work); - INIT_WORK(&sc->hw_check_work, ath_hw_check); - INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); - INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); - /* Register with mac80211 */ error = ieee80211_register_hw(hw); if (error) @@ -776,6 +792,10 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, goto error_world; } + INIT_WORK(&sc->hw_reset_work, ath_reset_work); + INIT_WORK(&sc->hw_check_work, ath_hw_check); + INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); + INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); sc->last_rssi = ATH_RSSI_DUMMY_MARKER; ath_init_leds(sc); @@ -811,7 +831,9 @@ static void ath9k_deinit_softc(struct ath_softc *sc) if (sc->sbands[IEEE80211_BAND_5GHZ].channels) kfree(sc->sbands[IEEE80211_BAND_5GHZ].channels); - ath9k_deinit_btcoex(sc); + if ((sc->btcoex.no_stomp_timer) && + sc->sc_ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) + ath_gen_timer_free(sc->sc_ah, sc->btcoex.no_stomp_timer); for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index f7bd2532..21333058 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -21,7 +21,7 @@ static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah, struct ath9k_tx_queue_info *qi) { - ath_dbg(ath9k_hw_common(ah), INTERRUPT, + ath_dbg(ath9k_hw_common(ah), ATH_DBG_INTERRUPT, "tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n", ah->txok_interrupt_mask, ah->txerr_interrupt_mask, ah->txdesc_interrupt_mask, ah->txeol_interrupt_mask, @@ -57,7 +57,8 @@ EXPORT_SYMBOL(ath9k_hw_puttxbuf); void ath9k_hw_txstart(struct ath_hw *ah, u32 q) { - ath_dbg(ath9k_hw_common(ah), QUEUE, "Enable TXE on queue: %u\n", q); + ath_dbg(ath9k_hw_common(ah), ATH_DBG_QUEUE, + "Enable TXE on queue: %u\n", q); REG_WRITE(ah, AR_Q_TXE, 1 << q); } EXPORT_SYMBOL(ath9k_hw_txstart); @@ -185,6 +186,13 @@ bool ath9k_hw_stop_dma_queue(struct ath_hw *ah, u32 q) } EXPORT_SYMBOL(ath9k_hw_stop_dma_queue); +void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs) +{ + *txqs &= ah->intr_txqs; + ah->intr_txqs &= ~(*txqs); +} +EXPORT_SYMBOL(ath9k_hw_gettxintrtxqs); + bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q, const struct ath9k_tx_queue_info *qinfo) { @@ -194,12 +202,12 @@ bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q, qi = &ah->txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { - ath_dbg(common, QUEUE, + ath_dbg(common, ATH_DBG_QUEUE, "Set TXQ properties, inactive queue: %u\n", q); return false; } - ath_dbg(common, QUEUE, "Set queue properties for: %u\n", q); + ath_dbg(common, ATH_DBG_QUEUE, "Set queue properties for: %u\n", q); qi->tqi_ver = qinfo->tqi_ver; qi->tqi_subtype = qinfo->tqi_subtype; @@ -258,7 +266,7 @@ bool ath9k_hw_get_txq_props(struct ath_hw *ah, int q, qi = &ah->txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { - ath_dbg(common, QUEUE, + ath_dbg(common, ATH_DBG_QUEUE, "Get TXQ properties, inactive queue: %u\n", q); return false; } @@ -303,21 +311,14 @@ int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type, q = ATH9K_NUM_TX_QUEUES - 3; break; case ATH9K_TX_QUEUE_DATA: - for (q = 0; q < ATH9K_NUM_TX_QUEUES; q++) - if (ah->txq[q].tqi_type == - ATH9K_TX_QUEUE_INACTIVE) - break; - if (q == ATH9K_NUM_TX_QUEUES) { - ath_err(common, "No available TX queue\n"); - return -1; - } + q = qinfo->tqi_subtype; break; default: ath_err(common, "Invalid TX queue type: %u\n", type); return -1; } - ath_dbg(common, QUEUE, "Setup TX queue: %u\n", q); + ath_dbg(common, ATH_DBG_QUEUE, "Setup TX queue: %u\n", q); qi = &ah->txq[q]; if (qi->tqi_type != ATH9K_TX_QUEUE_INACTIVE) { @@ -333,15 +334,6 @@ int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type, } EXPORT_SYMBOL(ath9k_hw_setuptxqueue); -static void ath9k_hw_clear_queue_interrupts(struct ath_hw *ah, u32 q) -{ - ah->txok_interrupt_mask &= ~(1 << q); - ah->txerr_interrupt_mask &= ~(1 << q); - ah->txdesc_interrupt_mask &= ~(1 << q); - ah->txeol_interrupt_mask &= ~(1 << q); - ah->txurn_interrupt_mask &= ~(1 << q); -} - bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q) { struct ath_common *common = ath9k_hw_common(ah); @@ -349,14 +341,19 @@ bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q) qi = &ah->txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { - ath_dbg(common, QUEUE, "Release TXQ, inactive queue: %u\n", q); + ath_dbg(common, ATH_DBG_QUEUE, + "Release TXQ, inactive queue: %u\n", q); return false; } - ath_dbg(common, QUEUE, "Release TX queue: %u\n", q); + ath_dbg(common, ATH_DBG_QUEUE, "Release TX queue: %u\n", q); qi->tqi_type = ATH9K_TX_QUEUE_INACTIVE; - ath9k_hw_clear_queue_interrupts(ah, q); + ah->txok_interrupt_mask &= ~(1 << q); + ah->txerr_interrupt_mask &= ~(1 << q); + ah->txdesc_interrupt_mask &= ~(1 << q); + ah->txeol_interrupt_mask &= ~(1 << q); + ah->txurn_interrupt_mask &= ~(1 << q); ath9k_hw_set_txq_interrupts(ah, qi); return true; @@ -372,11 +369,12 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) qi = &ah->txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { - ath_dbg(common, QUEUE, "Reset TXQ, inactive queue: %u\n", q); + ath_dbg(common, ATH_DBG_QUEUE, + "Reset TXQ, inactive queue: %u\n", q); return true; } - ath_dbg(common, QUEUE, "Reset TX queue: %u\n", q); + ath_dbg(common, ATH_DBG_QUEUE, "Reset TX queue: %u\n", q); if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) { if (chan && IS_CHAN_B(chan)) @@ -508,17 +506,26 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) if (AR_SREV_9300_20_OR_LATER(ah)) REG_WRITE(ah, AR_Q_DESC_CRCCHK, AR_Q_DESC_CRCCHK_EN); - ath9k_hw_clear_queue_interrupts(ah, q); - if (qi->tqi_qflags & TXQ_FLAG_TXINT_ENABLE) { + if (qi->tqi_qflags & TXQ_FLAG_TXOKINT_ENABLE) ah->txok_interrupt_mask |= 1 << q; + else + ah->txok_interrupt_mask &= ~(1 << q); + if (qi->tqi_qflags & TXQ_FLAG_TXERRINT_ENABLE) ah->txerr_interrupt_mask |= 1 << q; - } + else + ah->txerr_interrupt_mask &= ~(1 << q); if (qi->tqi_qflags & TXQ_FLAG_TXDESCINT_ENABLE) ah->txdesc_interrupt_mask |= 1 << q; + else + ah->txdesc_interrupt_mask &= ~(1 << q); if (qi->tqi_qflags & TXQ_FLAG_TXEOLINT_ENABLE) ah->txeol_interrupt_mask |= 1 << q; + else + ah->txeol_interrupt_mask &= ~(1 << q); if (qi->tqi_qflags & TXQ_FLAG_TXURNINT_ENABLE) ah->txurn_interrupt_mask |= 1 << q; + else + ah->txurn_interrupt_mask &= ~(1 << q); ath9k_hw_set_txq_interrupts(ah, qi); return true; @@ -607,11 +614,10 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, rs->rs_status |= ATH9K_RXERR_DECRYPT; else if (ads.ds_rxstatus8 & AR_MichaelErr) rs->rs_status |= ATH9K_RXERR_MIC; + if (ads.ds_rxstatus8 & AR_KeyMiss) + rs->rs_status |= ATH9K_RXERR_KEYMISS; } - if (ads.ds_rxstatus8 & AR_KeyMiss) - rs->rs_status |= ATH9K_RXERR_KEYMISS; - return 0; } EXPORT_SYMBOL(ath9k_hw_rxprocdesc); @@ -734,10 +740,7 @@ int ath9k_hw_beaconq_setup(struct ath_hw *ah) qi.tqi_aifs = 1; qi.tqi_cwmin = 0; qi.tqi_cwmax = 0; - - if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) - qi.tqi_qflags = TXQ_FLAG_TXINT_ENABLE; - + /* NB: don't enable any interrupts */ return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi); } EXPORT_SYMBOL(ath9k_hw_beaconq_setup); @@ -750,10 +753,7 @@ bool ath9k_hw_intrpend(struct ath_hw *ah) return true; host_isr = REG_READ(ah, AR_INTR_ASYNC_CAUSE); - - if (((host_isr & AR_INTR_MAC_IRQ) || - (host_isr & AR_INTR_ASYNC_MASK_MCI)) && - (host_isr != AR_INTR_SPURIOUS)) + if ((host_isr & AR_INTR_MAC_IRQ) && (host_isr != AR_INTR_SPURIOUS)) return true; host_isr = REG_READ(ah, AR_INTR_SYNC_CAUSE); @@ -774,7 +774,7 @@ void ath9k_hw_disable_interrupts(struct ath_hw *ah) else atomic_dec(&ah->intr_ref_cnt); - ath_dbg(common, INTERRUPT, "disable IER\n"); + ath_dbg(common, ATH_DBG_INTERRUPT, "disable IER\n"); REG_WRITE(ah, AR_IER, AR_IER_DISABLE); (void) REG_READ(ah, AR_IER); if (!AR_SREV_9100(ah)) { @@ -791,13 +791,13 @@ void ath9k_hw_enable_interrupts(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); u32 sync_default = AR_INTR_SYNC_DEFAULT; - u32 async_mask; if (!(ah->imask & ATH9K_INT_GLOBAL)) return; if (!atomic_inc_and_test(&ah->intr_ref_cnt)) { - ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n", + ath_dbg(common, ATH_DBG_INTERRUPT, + "Do not enable IER ref count %d\n", atomic_read(&ah->intr_ref_cnt)); return; } @@ -805,21 +805,18 @@ void ath9k_hw_enable_interrupts(struct ath_hw *ah) if (AR_SREV_9340(ah)) sync_default &= ~AR_INTR_SYNC_HOST1_FATAL; - async_mask = AR_INTR_MAC_IRQ; - - if (ah->imask & ATH9K_INT_MCI) - async_mask |= AR_INTR_ASYNC_MASK_MCI; - - ath_dbg(common, INTERRUPT, "enable IER\n"); + ath_dbg(common, ATH_DBG_INTERRUPT, "enable IER\n"); REG_WRITE(ah, AR_IER, AR_IER_ENABLE); if (!AR_SREV_9100(ah)) { - REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, async_mask); - REG_WRITE(ah, AR_INTR_ASYNC_MASK, async_mask); + REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, + AR_INTR_MAC_IRQ); + REG_WRITE(ah, AR_INTR_ASYNC_MASK, AR_INTR_MAC_IRQ); + REG_WRITE(ah, AR_INTR_SYNC_ENABLE, sync_default); REG_WRITE(ah, AR_INTR_SYNC_MASK, sync_default); } - ath_dbg(common, INTERRUPT, "AR_IMR 0x%x IER 0x%x\n", + ath_dbg(common, ATH_DBG_INTERRUPT, "AR_IMR 0x%x IER 0x%x\n", REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER)); } EXPORT_SYMBOL(ath9k_hw_enable_interrupts); @@ -834,7 +831,7 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah) if (!(ints & ATH9K_INT_GLOBAL)) ath9k_hw_disable_interrupts(ah); - ath_dbg(common, INTERRUPT, "New interrupt mask 0x%x\n", ints); + ath_dbg(common, ATH_DBG_INTERRUPT, "New interrupt mask 0x%x\n", ints); mask = ints & ATH9K_INT_COMMON; mask2 = 0; @@ -897,7 +894,7 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah) mask2 |= AR_IMR_S2_CST; } - ath_dbg(common, INTERRUPT, "new IMR 0x%x\n", mask); + ath_dbg(common, ATH_DBG_INTERRUPT, "new IMR 0x%x\n", mask); REG_WRITE(ah, AR_IMR, mask); ah->imrs2_reg &= ~(AR_IMR_S2_TIM | AR_IMR_S2_DTIM | AR_IMR_S2_DTIMSYNC | AR_IMR_S2_CABEND | AR_IMR_S2_CABTO | diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 21c95560..11dbd147 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -583,7 +583,8 @@ enum ath9k_tx_queue { #define ATH9K_WME_UPSD 4 enum ath9k_tx_queue_flags { - TXQ_FLAG_TXINT_ENABLE = 0x0001, + TXQ_FLAG_TXOKINT_ENABLE = 0x0001, + TXQ_FLAG_TXERRINT_ENABLE = 0x0001, TXQ_FLAG_TXDESCINT_ENABLE = 0x0002, TXQ_FLAG_TXEOLINT_ENABLE = 0x0004, TXQ_FLAG_TXURNINT_ENABLE = 0x0008, @@ -713,6 +714,7 @@ u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q); bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel); bool ath9k_hw_stop_dma_queue(struct ath_hw *ah, u32 q); void ath9k_hw_abort_tx_dma(struct ath_hw *ah); +void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs); bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q, const struct ath9k_tx_queue_info *qinfo); bool ath9k_hw_get_txq_props(struct ath_hw *ah, int q, diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c old mode 100644 new mode 100755 index 7e7bd157..608f8bf4 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -118,7 +118,7 @@ void ath9k_ps_restore(struct ath_softc *sc) if (--sc->ps_usecount != 0) goto unlock; - if (sc->ps_idle && (sc->ps_flags & PS_WAIT_FOR_TX_ACK)) + if (sc->ps_idle) mode = ATH9K_PM_FULL_SLEEP; else if (sc->ps_enabled && !(sc->ps_flags & (PS_WAIT_FOR_BEACON | @@ -289,7 +289,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) ath_start_ani(common); } - if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3) { + if (ath9k_hw_ops(ah)->antdiv_comb_conf_get && sc->ant_rx != 3) { struct ath_hw_antcomb_conf div_ant_conf; u8 lna_conf; @@ -335,11 +335,17 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, hchan = ah->curchan; } + if (fastcc && !ath9k_hw_check_alive(ah)) + fastcc = false; + if (!ath_prepare_reset(sc, retry_tx, flush)) fastcc = false; - ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n", - hchan->channel, IS_CHAN_HT40(hchan), fastcc); + ath_dbg(common, ATH_DBG_CONFIG, + "Reset to %u MHz, HT40: %d fastcc: %d\n", + hchan->channel, !!(hchan->channelFlags & (CHANNEL_HT40MINUS | + CHANNEL_HT40PLUS)), + fastcc); r = ath9k_hw_reset(ah, hchan, caldata, fastcc); if (r) { @@ -370,8 +376,12 @@ static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, if (sc->sc_flags & SC_OP_INVALID) return -EIO; + ath9k_ps_wakeup(sc); + r = ath_reset_internal(sc, hchan, false); + ath9k_ps_restore(sc); + return r; } @@ -421,7 +431,7 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int txctl.paprd = BIT(chain); if (ath_tx_start(hw, skb, &txctl) != 0) { - ath_dbg(common, CALIBRATE, "PAPRD TX failed\n"); + ath_dbg(common, ATH_DBG_CALIBRATE, "PAPRD TX failed\n"); dev_kfree_skb_any(skb); return false; } @@ -430,7 +440,7 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int msecs_to_jiffies(ATH_PAPRD_TIMEOUT)); if (!time_left) - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "Timeout waiting for paprd training on TX chain %d\n", chain); @@ -479,27 +489,27 @@ void ath_paprd_calibrate(struct work_struct *work) chain_ok = 0; - ath_dbg(common, CALIBRATE, - "Sending PAPRD frame for thermal measurement on chain %d\n", - chain); + ath_dbg(common, ATH_DBG_CALIBRATE, + "Sending PAPRD frame for thermal measurement " + "on chain %d\n", chain); if (!ath_paprd_send_frame(sc, skb, chain)) goto fail_paprd; ar9003_paprd_setup_gain_table(ah, chain); - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "Sending PAPRD training frame on chain %d\n", chain); if (!ath_paprd_send_frame(sc, skb, chain)) goto fail_paprd; if (!ar9003_paprd_is_done(ah)) { - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "PAPRD not yet done on chain %d\n", chain); break; } if (ar9003_paprd_create_curve(ah, caldata, chain)) { - ath_dbg(common, CALIBRATE, + ath_dbg(common, ATH_DBG_CALIBRATE, "PAPRD create curve failed on chain %d\n", chain); break; @@ -554,6 +564,7 @@ void ath_ani_calibrate(unsigned long data) /* Long calibration runs independently of short calibration. */ if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) { longcal = true; + ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies); common->ani.longcal_timer = timestamp; } @@ -561,6 +572,8 @@ void ath_ani_calibrate(unsigned long data) if (!common->ani.caldone) { if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) { shortcal = true; + ath_dbg(common, ATH_DBG_ANI, + "shortcal @%lu\n", jiffies); common->ani.shortcal_timer = timestamp; common->ani.resetcal_timer = timestamp; } @@ -574,9 +587,8 @@ void ath_ani_calibrate(unsigned long data) } /* Verify whether we must check ANI */ - if (sc->sc_ah->config.enable_ani - && (timestamp - common->ani.checkani_timer) >= - ah->config.ani_poll_interval) { + if ((timestamp - common->ani.checkani_timer) >= + ah->config.ani_poll_interval) { aniflag = true; common->ani.checkani_timer = timestamp; } @@ -596,12 +608,6 @@ void ath_ani_calibrate(unsigned long data) ah->rxchainmask, longcal); } - ath_dbg(common, ANI, - "Calibration @%lu finished: %s %s %s, caldone: %s\n", - jiffies, - longcal ? "long" : "", shortcal ? "short" : "", - aniflag ? "ani" : "", common->ani.caldone ? "true" : "false"); - ath9k_ps_restore(sc); set_timer: @@ -637,11 +643,10 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, spin_lock(&sc->nodes_lock); list_add(&an->list, &sc->nodes); spin_unlock(&sc->nodes_lock); -#endif an->sta = sta; an->vif = vif; - - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { +#endif + if (sc->sc_flags & SC_OP_TXAGGR) { ath_tx_node_init(sc, an); an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + sta->ht_cap.ampdu_factor); @@ -660,7 +665,7 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) an->sta = NULL; #endif - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) + if (sc->sc_flags & SC_OP_TXAGGR) ath_tx_node_cleanup(sc, an); } @@ -709,7 +714,8 @@ void ath9k_tasklet(unsigned long data) * TSF sync does not look correct; remain awake to sync with * the next Beacon. */ - ath_dbg(common, PS, "TSFOOR - Sync with next Beacon\n"); + ath_dbg(common, ATH_DBG_PS, + "TSFOOR - Sync with next Beacon\n"); sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC; } @@ -735,7 +741,9 @@ void ath9k_tasklet(unsigned long data) ath_tx_tasklet(sc); } - ath9k_btcoex_handle_interrupt(sc, status); + if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) + if (status & ATH9K_INT_GENTIMER) + ath_gen_timer_isr(sc->sc_ah); out: /* re-enable hardware interrupt */ @@ -759,8 +767,7 @@ irqreturn_t ath_isr(int irq, void *dev) ATH9K_INT_BMISS | \ ATH9K_INT_CST | \ ATH9K_INT_TSFOOR | \ - ATH9K_INT_GENTIMER | \ - ATH9K_INT_MCI) + ATH9K_INT_GENTIMER) struct ath_softc *sc = dev; struct ath_hw *ah = sc->sc_ah; @@ -878,6 +885,82 @@ irqreturn_t ath_isr(int irq, void *dev) #undef SCHED_INTR } +static void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_channel *channel = hw->conf.channel; + int r; + + ath9k_ps_wakeup(sc); + spin_lock_bh(&sc->sc_pcu_lock); + atomic_set(&ah->intr_ref_cnt, -1); + + ath9k_hw_configpcipowersave(ah, false); + + if (!ah->curchan) + ah->curchan = ath9k_cmn_get_curchannel(sc->hw, ah); + + r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); + if (r) { + ath_err(common, + "Unable to reset channel (%u MHz), reset status %d\n", + channel->center_freq, r); + } + + ath_complete_reset(sc, true); + + /* Enable LED */ + ath9k_hw_cfg_output(ah, ah->led_pin, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + ath9k_hw_set_gpio(ah, ah->led_pin, 0); + + spin_unlock_bh(&sc->sc_pcu_lock); + + ath9k_ps_restore(sc); +} + +void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw) +{ + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_channel *channel = hw->conf.channel; + int r; + + ath9k_ps_wakeup(sc); + + ath_cancel_work(sc); + + spin_lock_bh(&sc->sc_pcu_lock); + + /* + * Keep the LED on when the radio is disabled + * during idle unassociated state. + */ + if (!sc->ps_idle) { + ath9k_hw_set_gpio(ah, ah->led_pin, 1); + ath9k_hw_cfg_gpio_input(ah, ah->led_pin); + } + + ath_prepare_reset(sc, false, true); + + if (!ah->curchan) + ah->curchan = ath9k_cmn_get_curchannel(hw, ah); + + r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); + if (r) { + ath_err(ath9k_hw_common(sc->sc_ah), + "Unable to reset channel (%u MHz), reset status %d\n", + channel->center_freq, r); + } + + ath9k_hw_phy_disable(ah); + + ath9k_hw_configpcipowersave(ah, true); + + spin_unlock_bh(&sc->sc_pcu_lock); + ath9k_ps_restore(sc); +} + static int ath_reset(struct ath_softc *sc, bool retry_tx) { int r; @@ -924,8 +1007,8 @@ void ath_hw_check(struct work_struct *work) busy = ath_update_survey_stats(sc); spin_unlock_irqrestore(&common->cc_lock, flags); - ath_dbg(common, RESET, "Possible baseband hang, busy=%d (try %d)\n", - busy, sc->hw_busy_count + 1); + ath_dbg(common, ATH_DBG_RESET, "Possible baseband hang, " + "busy=%d (try %d)\n", busy, sc->hw_busy_count + 1); if (busy >= 99) { if (++sc->hw_busy_count >= 3) { RESET_STAT_INC(sc, RESET_TYPE_BB_HANG); @@ -948,7 +1031,8 @@ static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum) count++; if (count == 3) { /* Rx is hung for more than 500ms. Reset it */ - ath_dbg(common, RESET, "Possible RX hang, resetting\n"); + ath_dbg(common, ATH_DBG_RESET, + "Possible RX hang, resetting"); RESET_STAT_INC(sc, RESET_TYPE_PLL_HANG); ieee80211_queue_work(sc->hw, &sc->hw_reset_work); count = 0; @@ -997,13 +1081,17 @@ static int ath9k_start(struct ieee80211_hw *hw) struct ath9k_channel *init_channel; int r; - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Starting driver with initial channel: %d MHz\n", curchan->center_freq); ath9k_ps_wakeup(sc); + mutex_lock(&sc->mutex); + /* setup initial channel */ + sc->chan_idx = curchan->hw_value; + init_channel = ath9k_cmn_get_curchannel(hw, ah); /* Reset SERDES registers */ @@ -1017,9 +1105,6 @@ static int ath9k_start(struct ieee80211_hw *hw) * and then setup of the interrupt mask. */ spin_lock_bh(&sc->sc_pcu_lock); - - atomic_set(&ah->intr_ref_cnt, -1); - r = ath9k_hw_reset(ah, init_channel, ah->caldata, false); if (r) { ath_err(common, @@ -1046,33 +1131,30 @@ static int ath9k_start(struct ieee80211_hw *hw) if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) ah->imask |= ATH9K_INT_CST; - if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) - ah->imask |= ATH9K_INT_MCI; - sc->sc_flags &= ~SC_OP_INVALID; sc->sc_ah->is_monitoring = false; + /* Disable BMISS interrupt when we're not associated */ + ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); + if (!ath_complete_reset(sc, false)) { r = -EIO; spin_unlock_bh(&sc->sc_pcu_lock); goto mutex_unlock; } - if (ah->led_pin >= 0) { - ath9k_hw_cfg_output(ah, ah->led_pin, - AR_GPIO_OUTPUT_MUX_AS_OUTPUT); - ath9k_hw_set_gpio(ah, ah->led_pin, 0); - } - - /* - * Reset key cache to sane defaults (all entries cleared) instead of - * semi-random values after suspend/resume. - */ - ath9k_cmn_init_crypto(sc->sc_ah); - spin_unlock_bh(&sc->sc_pcu_lock); - ath9k_start_btcoex(sc); + if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) && + !ah->btcoex_hw.enabled) { + if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_LOW_WLAN_WGHT); + ath9k_hw_btcoex_enable(ah); + + if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) + ath9k_btcoex_timer_resume(sc); + } if (ah->caps.pcie_lcr_extsync_en && common->bus_ops->extn_synch_en) common->bus_ops->extn_synch_en(common); @@ -1100,19 +1182,12 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (ieee80211_is_data(hdr->frame_control) && !ieee80211_is_nullfunc(hdr->frame_control) && !ieee80211_has_pm(hdr->frame_control)) { - ath_dbg(common, PS, + ath_dbg(common, ATH_DBG_PS, "Add PM=1 for a TX frame while in PS mode\n"); hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); } } - /* - * Cannot tx while the hardware is in full sleep, it first needs a full - * chip reset to recover from that - */ - if (unlikely(sc->sc_ah->power_mode == ATH9K_PM_FULL_SLEEP)) - goto exit; - if (unlikely(sc->sc_ah->power_mode != ATH9K_PM_AWAKE)) { /* * We are using PS-Poll and mac80211 can request TX while in @@ -1123,11 +1198,12 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) ath9k_hw_setrxabort(sc->sc_ah, 0); if (ieee80211_is_pspoll(hdr->frame_control)) { - ath_dbg(common, PS, + ath_dbg(common, ATH_DBG_PS, "Sending PS-Poll to pick a buffered frame\n"); sc->ps_flags |= PS_WAIT_FOR_PSPOLL_DATA; } else { - ath_dbg(common, PS, "Wake up to complete TX\n"); + ath_dbg(common, ATH_DBG_PS, + "Wake up to complete TX\n"); sc->ps_flags |= PS_WAIT_FOR_TX_ACK; } /* @@ -1141,10 +1217,10 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) memset(&txctl, 0, sizeof(struct ath_tx_control)); txctl.txq = sc->tx.txq_map[skb_get_queue_mapping(skb)]; - ath_dbg(common, XMIT, "transmitting packet, skb: %p\n", skb); + ath_dbg(common, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb); if (ath_tx_start(hw, skb, &txctl) != 0) { - ath_dbg(common, XMIT, "TX failed\n"); + ath_dbg(common, ATH_DBG_XMIT, "TX failed\n"); goto exit; } @@ -1158,14 +1234,13 @@ static void ath9k_stop(struct ieee80211_hw *hw) struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - bool prev_idle; mutex_lock(&sc->mutex); ath_cancel_work(sc); if (sc->sc_flags & SC_OP_INVALID) { - ath_dbg(common, ANY, "Device not present\n"); + ath_dbg(common, ATH_DBG_ANY, "Device not present\n"); mutex_unlock(&sc->mutex); return; } @@ -1173,7 +1248,12 @@ static void ath9k_stop(struct ieee80211_hw *hw) /* Ensure HW is awake when we try to shut it down. */ ath9k_ps_wakeup(sc); - ath9k_stop_btcoex(sc); + if (ah->btcoex_hw.enabled) { + ath9k_hw_btcoex_disable(ah); + if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) + ath9k_btcoex_timer_pause(sc); + ath_mci_flush_profile(&sc->btcoex.mci); + } spin_lock_bh(&sc->sc_pcu_lock); @@ -1184,49 +1264,39 @@ static void ath9k_stop(struct ieee80211_hw *hw) * before setting the invalid flag. */ ath9k_hw_disable_interrupts(ah); - spin_unlock_bh(&sc->sc_pcu_lock); - - /* we can now sync irq and kill any running tasklets, since we already - * disabled interrupts and not holding a spin lock */ - synchronize_irq(sc->irq); - tasklet_kill(&sc->intr_tq); - tasklet_kill(&sc->bcon_tasklet); - - prev_idle = sc->ps_idle; - sc->ps_idle = true; - - spin_lock_bh(&sc->sc_pcu_lock); - - if (ah->led_pin >= 0) { - ath9k_hw_set_gpio(ah, ah->led_pin, 1); - ath9k_hw_cfg_gpio_input(ah, ah->led_pin); - } - - ath_prepare_reset(sc, false, true); + if (!(sc->sc_flags & SC_OP_INVALID)) { + ath_drain_all_txq(sc, false); + ath_stoprecv(sc); + ath9k_hw_phy_disable(ah); + } else + sc->rx.rxlink = NULL; if (sc->rx.frag) { dev_kfree_skb_any(sc->rx.frag); sc->rx.frag = NULL; } - if (!ah->curchan) - ah->curchan = ath9k_cmn_get_curchannel(hw, ah); - - ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); - ath9k_hw_phy_disable(ah); - - ath9k_hw_configpcipowersave(ah, true); + /* disable HAL and put h/w to sleep */ + ath9k_hw_disable(ah); spin_unlock_bh(&sc->sc_pcu_lock); + /* we can now sync irq and kill any running tasklets, since we already + * disabled interrupts and not holding a spin lock */ + synchronize_irq(sc->irq); + tasklet_kill(&sc->intr_tq); + tasklet_kill(&sc->bcon_tasklet); + ath9k_ps_restore(sc); + sc->ps_idle = true; + ath_radio_disable(sc, hw); + sc->sc_flags |= SC_OP_INVALID; - sc->ps_idle = prev_idle; mutex_unlock(&sc->mutex); - ath_dbg(common, CONFIG, "Driver halt\n"); + ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n"); } bool ath9k_uses_beacons(int type) @@ -1279,6 +1349,7 @@ static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) iter_data->nwds++; break; default: + iter_data->nothers++; break; } } @@ -1432,7 +1503,17 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, } } - ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type); + if ((ah->opmode == NL80211_IFTYPE_ADHOC) || + ((vif->type == NL80211_IFTYPE_ADHOC) && + sc->nvifs > 0)) { + ath_err(common, "Cannot create ADHOC interface when other" + " interfaces already exist.\n"); + ret = -EINVAL; + goto out; + } + + ath_dbg(common, ATH_DBG_CONFIG, + "Attach a VIF of type: %d\n", vif->type); sc->nvifs++; @@ -1452,7 +1533,7 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, struct ath_common *common = ath9k_hw_common(sc->sc_ah); int ret = 0; - ath_dbg(common, CONFIG, "Change Interface\n"); + ath_dbg(common, ATH_DBG_CONFIG, "Change Interface\n"); mutex_lock(&sc->mutex); ath9k_ps_wakeup(sc); @@ -1495,7 +1576,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - ath_dbg(common, CONFIG, "Detach Interface\n"); + ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface\n"); ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); @@ -1552,21 +1633,25 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &hw->conf; - bool reset_channel = false; + bool disable_radio = false; - ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); + /* + * Leave this as the first check because we need to turn on the + * radio if it was disabled before prior to processing the rest + * of the changes. Likewise we must only disable the radio towards + * the end. + */ if (changed & IEEE80211_CONF_CHANGE_IDLE) { sc->ps_idle = !!(conf->flags & IEEE80211_CONF_IDLE); - if (sc->ps_idle) - ath_cancel_work(sc); - else - /* - * The chip needs a reset to properly wake up from - * full sleep - */ - reset_channel = ah->chip_fullsleep; + if (!sc->ps_idle) { + ath_radio_enable(sc, hw); + ath_dbg(common, ATH_DBG_CONFIG, + "not-idle: enabling radio\n"); + } else { + disable_radio = true; + } } /* @@ -1587,16 +1672,19 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) if (changed & IEEE80211_CONF_CHANGE_MONITOR) { if (conf->flags & IEEE80211_CONF_MONITOR) { - ath_dbg(common, CONFIG, "Monitor mode is enabled\n"); + ath_dbg(common, ATH_DBG_CONFIG, + "Monitor mode is enabled\n"); sc->sc_ah->is_monitoring = true; } else { - ath_dbg(common, CONFIG, "Monitor mode is disabled\n"); + ath_dbg(common, ATH_DBG_CONFIG, + "Monitor mode is disabled\n"); sc->sc_ah->is_monitoring = false; } } - if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) { + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { struct ieee80211_channel *curchan = hw->conf.channel; + struct ath9k_channel old_chan; int pos = curchan->hw_value; int old_pos = -1; unsigned long flags; @@ -1609,7 +1697,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) else sc->sc_flags &= ~SC_OP_OFFCHANNEL; - ath_dbg(common, CONFIG, "Set channel: %d MHz type: %d\n", + ath_dbg(common, ATH_DBG_CONFIG, + "Set channel: %d MHz type: %d\n", curchan->center_freq, conf->channel_type); /* update survey stats for the old channel before switching */ @@ -1617,6 +1706,16 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) ath_update_survey_stats(sc); spin_unlock_irqrestore(&common->cc_lock, flags); + /* + * Preserve the current channel values, before updating + * the same channel + */ + if (old_pos == pos) { + memcpy(&old_chan, &sc->sc_ah->channels[pos], + sizeof(struct ath9k_channel)); + ah->curchan = &old_chan; + } + ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos], curchan, conf->channel_type); @@ -1656,14 +1755,21 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) } if (changed & IEEE80211_CONF_CHANGE_POWER) { - ath_dbg(common, CONFIG, "Set power: %d\n", conf->power_level); + ath_dbg(common, ATH_DBG_CONFIG, + "Set power: %d\n", conf->power_level); sc->config.txpowlimit = 2 * conf->power_level; + ath9k_ps_wakeup(sc); ath9k_cmn_update_txpow(ah, sc->curtxpow, sc->config.txpowlimit, &sc->curtxpow); + ath9k_ps_restore(sc); + } + + if (disable_radio) { + ath_dbg(common, ATH_DBG_CONFIG, "idle: disabling radio\n"); + ath_radio_disable(sc, hw); } mutex_unlock(&sc->mutex); - ath9k_ps_restore(sc); return 0; } @@ -1696,8 +1802,8 @@ static void ath9k_configure_filter(struct ieee80211_hw *hw, ath9k_hw_setrxfilter(sc->sc_ah, rfilt); ath9k_ps_restore(sc); - ath_dbg(ath9k_hw_common(sc->sc_ah), CONFIG, "Set HW RX filter: 0x%x\n", - rfilt); + ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG, + "Set HW RX filter: 0x%x\n", rfilt); } static int ath9k_sta_add(struct ieee80211_hw *hw, @@ -1758,9 +1864,6 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; struct ath_node *an = (struct ath_node *) sta->drv_priv; - if (!sta->ht_cap.ht_supported) - return; - switch (cmd) { case STA_NOTIFY_SLEEP: an->sleeping = true; @@ -1798,7 +1901,7 @@ static int ath9k_conf_tx(struct ieee80211_hw *hw, qi.tqi_cwmax = params->cw_max; qi.tqi_burstTime = params->txop; - ath_dbg(common, CONFIG, + ath_dbg(common, ATH_DBG_CONFIG, "Configure tx [queue/halq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", queue, txq->axq_qnum, params->aifs, params->cw_min, params->cw_max, params->txop); @@ -1830,8 +1933,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw, if (ath9k_modparam_nohwcrypt) return -ENOSPC; - if ((vif->type == NL80211_IFTYPE_ADHOC || - vif->type == NL80211_IFTYPE_MESH_POINT) && + if (vif->type == NL80211_IFTYPE_ADHOC && (key->cipher == WLAN_CIPHER_SUITE_TKIP || key->cipher == WLAN_CIPHER_SUITE_CCMP) && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { @@ -1847,7 +1949,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); ath9k_ps_wakeup(sc); - ath_dbg(common, CONFIG, "Set HW Key\n"); + ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n"); switch (cmd) { case SET_KEY: @@ -1899,8 +2001,9 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif) memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); common->curaid = bss_conf->aid; ath9k_hw_write_associd(sc->sc_ah); - ath_dbg(common, CONFIG, "Bss Info ASSOC %d, bssid: %pM\n", - bss_conf->aid, common->curbssid); + ath_dbg(common, ATH_DBG_CONFIG, + "Bss Info ASSOC %d, bssid: %pM\n", + bss_conf->aid, common->curbssid); ath_beacon_config(sc, vif); /* * Request a re-configuration of Beacon related timers @@ -1931,7 +2034,8 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif) /* Reconfigure bss info */ if (avp->primary_sta_vif && !bss_conf->assoc) { - ath_dbg(common, CONFIG, "Bss Info DISASSOC %d, bssid %pM\n", + ath_dbg(common, ATH_DBG_CONFIG, + "Bss Info DISASSOC %d, bssid %pM\n", common->curaid, common->curbssid); sc->sc_flags &= ~(SC_OP_PRIM_STA_VIF | SC_OP_BEACONS); avp->primary_sta_vif = false; @@ -1970,10 +2074,10 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); - if (changed & BSS_CHANGED_ASSOC) { + if (changed & BSS_CHANGED_BSSID) { ath9k_config_bss(sc, vif); - ath_dbg(common, CONFIG, "BSSID: %pM aid: 0x%x\n", + ath_dbg(common, ATH_DBG_CONFIG, "BSSID: %pM aid: 0x%x\n", common->curbssid, common->curaid); } @@ -2050,6 +2154,25 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, ath_beacon_config(sc, vif); } + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n", + bss_conf->use_short_preamble); + if (bss_conf->use_short_preamble) + sc->sc_flags |= SC_OP_PREAMBLE_SHORT; + else + sc->sc_flags &= ~SC_OP_PREAMBLE_SHORT; + } + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n", + bss_conf->use_cts_prot); + if (bss_conf->use_cts_prot && + hw->conf.channel->band != IEEE80211_BAND_5GHZ) + sc->sc_flags |= SC_OP_PROTECT_ENABLE; + else + sc->sc_flags &= ~SC_OP_PROTECT_ENABLE; + } + mutex_unlock(&sc->mutex); ath9k_ps_restore(sc); } @@ -2107,10 +2230,15 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, switch (action) { case IEEE80211_AMPDU_RX_START: + if (!(sc->sc_flags & SC_OP_RXAGGR)) + ret = -ENOTSUPP; break; case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_START: + if (!(sc->sc_flags & SC_OP_TXAGGR)) + return -EOPNOTSUPP; + ath9k_ps_wakeup(sc); ret = ath_tx_aggr_start(sc, sta, tid, ssn); if (!ret) @@ -2202,17 +2330,20 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop) cancel_delayed_work_sync(&sc->tx_complete_work); if (ah->ah_flags & AH_UNPLUGGED) { - ath_dbg(common, ANY, "Device has been unplugged!\n"); + ath_dbg(common, ATH_DBG_ANY, "Device has been unplugged!\n"); mutex_unlock(&sc->mutex); return; } if (sc->sc_flags & SC_OP_INVALID) { - ath_dbg(common, ANY, "Device not present\n"); + ath_dbg(common, ATH_DBG_ANY, "Device not present\n"); mutex_unlock(&sc->mutex); return; } + if (drop) + timeout = 1; + for (j = 0; j < timeout; j++) { bool npend = false; @@ -2230,22 +2361,21 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop) } if (!npend) - break; + goto out; } - if (drop) { - ath9k_ps_wakeup(sc); - spin_lock_bh(&sc->sc_pcu_lock); - drain_txq = ath_drain_all_txq(sc, false); - spin_unlock_bh(&sc->sc_pcu_lock); + ath9k_ps_wakeup(sc); + spin_lock_bh(&sc->sc_pcu_lock); + drain_txq = ath_drain_all_txq(sc, false); + spin_unlock_bh(&sc->sc_pcu_lock); - if (!drain_txq) - ath_reset(sc, false); + if (!drain_txq) + ath_reset(sc, false); - ath9k_ps_restore(sc); - ieee80211_wake_queues(hw); - } + ath9k_ps_restore(sc); + ieee80211_wake_queues(hw); +out: ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0); mutex_unlock(&sc->mutex); } @@ -2273,7 +2403,6 @@ static int ath9k_tx_last_beacon(struct ieee80211_hw *hw) struct ath_vif *avp; struct ath_buf *bf; struct ath_tx_status ts; - bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); int status; vif = sc->beacon.bslot[0]; @@ -2284,7 +2413,7 @@ static int ath9k_tx_last_beacon(struct ieee80211_hw *hw) if (!avp->is_bslot_active) return 0; - if (!sc->beacon.tx_processed && !edma) { + if (!sc->beacon.tx_processed) { tasklet_disable(&sc->bcon_tasklet); bf = avp->av_bcbuf; diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 29fe52d6..0fbb141b 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -14,13 +14,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include - #include "ath9k.h" #include "mci.h" -static const u8 ath_mci_duty_cycle[] = { 0, 50, 60, 70, 80, 85, 90, 95, 98 }; +u8 ath_mci_duty_cycle[] = { 0, 50, 60, 70, 80, 85, 90, 95, 98 }; static struct ath_mci_profile_info* ath_mci_find_profile(struct ath_mci_profile *mci, @@ -42,18 +39,24 @@ static bool ath_mci_add_profile(struct ath_common *common, struct ath_mci_profile_info *entry; if ((mci->num_sco == ATH_MCI_MAX_SCO_PROFILE) && - (info->type == MCI_GPM_COEX_PROFILE_VOICE)) + (info->type == MCI_GPM_COEX_PROFILE_VOICE)) { + ath_dbg(common, ATH_DBG_MCI, + "Too many SCO profile, failed to add new profile\n"); return false; + } if (((NUM_PROF(mci) - mci->num_sco) == ATH_MCI_MAX_ACL_PROFILE) && - (info->type != MCI_GPM_COEX_PROFILE_VOICE)) + (info->type != MCI_GPM_COEX_PROFILE_VOICE)) { + ath_dbg(common, ATH_DBG_MCI, + "Too many ACL profile, failed to add new profile\n"); return false; + } entry = ath_mci_find_profile(mci, info); - if (entry) { + if (entry) memcpy(entry, info, 10); - } else { + else { entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return false; @@ -62,7 +65,6 @@ static bool ath_mci_add_profile(struct ath_common *common, INC_PROF(mci, info); list_add_tail(&info->list, &mci->info); } - return true; } @@ -74,9 +76,11 @@ static void ath_mci_del_profile(struct ath_common *common, entry = ath_mci_find_profile(mci, info); - if (!entry) + if (!entry) { + ath_dbg(common, ATH_DBG_MCI, + "Profile to be deleted not found\n"); return; - + } DEC_PROF(mci, entry); list_del(&entry->list); kfree(entry); @@ -125,30 +129,30 @@ static void ath_mci_update_scheme(struct ath_softc *sc) list); if (mci->num_sco && info->T == 12) { mci->aggr_limit = 8; - ath_dbg(common, MCI, + ath_dbg(common, ATH_DBG_MCI, "Single SCO, aggregation limit 2 ms\n"); } else if ((info->type == MCI_GPM_COEX_PROFILE_BNEP) && !info->master) { btcoex->btcoex_period = 60; - ath_dbg(common, MCI, + ath_dbg(common, ATH_DBG_MCI, "Single slave PAN/FTP, bt period 60 ms\n"); } else if ((info->type == MCI_GPM_COEX_PROFILE_HID) && (info->T > 0 && info->T < 50) && (info->A > 1 || info->W > 1)) { btcoex->duty_cycle = 30; mci->aggr_limit = 8; - ath_dbg(common, MCI, + ath_dbg(common, ATH_DBG_MCI, "Multiple attempt/timeout single HID " "aggregation limit 2 ms dutycycle 30%%\n"); } } else if ((num_profile == 2) && (mci->num_hid == 2)) { btcoex->duty_cycle = 30; mci->aggr_limit = 8; - ath_dbg(common, MCI, + ath_dbg(common, ATH_DBG_MCI, "Two HIDs aggregation limit 2 ms dutycycle 30%%\n"); } else if (num_profile > 3) { mci->aggr_limit = 6; - ath_dbg(common, MCI, + ath_dbg(common, ATH_DBG_MCI, "Three or more profiles aggregation limit 1.5 ms\n"); } @@ -171,44 +175,14 @@ static void ath_mci_update_scheme(struct ath_softc *sc) btcoex->btcoex_period *= 1000; btcoex->btcoex_no_stomp = btcoex->btcoex_period * - (100 - btcoex->duty_cycle) / 100; + (100 - btcoex->duty_cycle) / 100; ath9k_hw_btcoex_enable(sc->sc_ah); ath9k_btcoex_timer_resume(sc); } -static void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - u32 payload[4] = {0, 0, 0, 0}; - - switch (opcode) { - case MCI_GPM_BT_CAL_REQ: - if (ar9003_mci_state(ah, MCI_STATE_BT, NULL) == MCI_BT_AWAKE) { - ar9003_mci_state(ah, MCI_STATE_SET_BT_CAL_START, NULL); - ieee80211_queue_work(sc->hw, &sc->hw_reset_work); - } else { - ath_dbg(common, MCI, "MCI State mismatch: %d\n", - ar9003_mci_state(ah, MCI_STATE_BT, NULL)); - } - break; - case MCI_GPM_BT_CAL_DONE: - ar9003_mci_state(ah, MCI_STATE_BT, NULL); - break; - case MCI_GPM_BT_CAL_GRANT: - MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE); - ar9003_mci_send_message(sc->sc_ah, MCI_GPM, 0, payload, - 16, false, true); - break; - default: - ath_dbg(common, MCI, "Unknown GPM CAL message\n"); - break; - } -} - -static void ath_mci_process_profile(struct ath_softc *sc, - struct ath_mci_profile_info *info) +void ath_mci_process_profile(struct ath_softc *sc, + struct ath_mci_profile_info *info) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_btcoex *btcoex = &sc->btcoex; @@ -222,7 +196,6 @@ static void ath_mci_process_profile(struct ath_softc *sc, btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD; mci->aggr_limit = mci->num_sco ? 6 : 0; - if (NUM_PROF(mci)) { btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW; btcoex->duty_cycle = ath_mci_duty_cycle[NUM_PROF(mci)]; @@ -235,27 +208,36 @@ static void ath_mci_process_profile(struct ath_softc *sc, ath_mci_update_scheme(sc); } -static void ath_mci_process_status(struct ath_softc *sc, - struct ath_mci_profile_status *status) +void ath_mci_process_status(struct ath_softc *sc, + struct ath_mci_profile_status *status) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &btcoex->mci; struct ath_mci_profile_info info; int i = 0, old_num_mgmt = mci->num_mgmt; /* Link status type are not handled */ - if (status->is_link) + if (status->is_link) { + ath_dbg(common, ATH_DBG_MCI, + "Skip link type status update\n"); return; + } memset(&info, 0, sizeof(struct ath_mci_profile_info)); info.conn_handle = status->conn_handle; - if (ath_mci_find_profile(mci, &info)) + if (ath_mci_find_profile(mci, &info)) { + ath_dbg(common, ATH_DBG_MCI, + "Skip non link state update for existing profile %d\n", + status->conn_handle); return; - - if (status->conn_handle >= ATH_MCI_MAX_PROFILE) + } + if (status->conn_handle >= ATH_MCI_MAX_PROFILE) { + ath_dbg(common, ATH_DBG_MCI, + "Ignore too many non-link update\n"); return; - + } if (status->is_critical) __set_bit(status->conn_handle, mci->status); else @@ -270,271 +252,3 @@ static void ath_mci_process_status(struct ath_softc *sc, if (old_num_mgmt != mci->num_mgmt) ath_mci_update_scheme(sc); } - -static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath_mci_profile_info profile_info; - struct ath_mci_profile_status profile_status; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - u32 version; - u8 major; - u8 minor; - u32 seq_num; - - switch (opcode) { - case MCI_GPM_COEX_VERSION_QUERY: - version = ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_COEX_VERSION, - NULL); - break; - case MCI_GPM_COEX_VERSION_RESPONSE: - major = *(rx_payload + MCI_GPM_COEX_B_MAJOR_VERSION); - minor = *(rx_payload + MCI_GPM_COEX_B_MINOR_VERSION); - version = (major << 8) + minor; - version = ar9003_mci_state(ah, MCI_STATE_SET_BT_COEX_VERSION, - &version); - break; - case MCI_GPM_COEX_STATUS_QUERY: - ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_CHANNELS, NULL); - break; - case MCI_GPM_COEX_BT_PROFILE_INFO: - memcpy(&profile_info, - (rx_payload + MCI_GPM_COEX_B_PROFILE_TYPE), 10); - - if ((profile_info.type == MCI_GPM_COEX_PROFILE_UNKNOWN) || - (profile_info.type >= MCI_GPM_COEX_PROFILE_MAX)) { - ath_dbg(common, MCI, - "Illegal profile type = %d, state = %d\n", - profile_info.type, - profile_info.start); - break; - } - - ath_mci_process_profile(sc, &profile_info); - break; - case MCI_GPM_COEX_BT_STATUS_UPDATE: - profile_status.is_link = *(rx_payload + - MCI_GPM_COEX_B_STATUS_TYPE); - profile_status.conn_handle = *(rx_payload + - MCI_GPM_COEX_B_STATUS_LINKID); - profile_status.is_critical = *(rx_payload + - MCI_GPM_COEX_B_STATUS_STATE); - - seq_num = *((u32 *)(rx_payload + 12)); - ath_dbg(common, MCI, - "BT_Status_Update: is_link=%d, linkId=%d, state=%d, SEQ=%d\n", - profile_status.is_link, profile_status.conn_handle, - profile_status.is_critical, seq_num); - - ath_mci_process_status(sc, &profile_status); - break; - default: - ath_dbg(common, MCI, "Unknown GPM COEX message = 0x%02x\n", opcode); - break; - } -} - -int ath_mci_setup(struct ath_softc *sc) -{ - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_mci_coex *mci = &sc->mci_coex; - struct ath_mci_buf *buf = &mci->sched_buf; - - buf->bf_addr = dma_alloc_coherent(sc->dev, - ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE, - &buf->bf_paddr, GFP_KERNEL); - - if (buf->bf_addr == NULL) { - ath_dbg(common, FATAL, "MCI buffer alloc failed\n"); - return -ENOMEM; - } - - memset(buf->bf_addr, MCI_GPM_RSVD_PATTERN, - ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE); - - mci->sched_buf.bf_len = ATH_MCI_SCHED_BUF_SIZE; - - mci->gpm_buf.bf_len = ATH_MCI_GPM_BUF_SIZE; - mci->gpm_buf.bf_addr = (u8 *)mci->sched_buf.bf_addr + mci->sched_buf.bf_len; - mci->gpm_buf.bf_paddr = mci->sched_buf.bf_paddr + mci->sched_buf.bf_len; - - ar9003_mci_setup(sc->sc_ah, mci->gpm_buf.bf_paddr, - mci->gpm_buf.bf_addr, (mci->gpm_buf.bf_len >> 4), - mci->sched_buf.bf_paddr); - - ath_dbg(common, MCI, "MCI Initialized\n"); - - return 0; -} - -void ath_mci_cleanup(struct ath_softc *sc) -{ - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_hw *ah = sc->sc_ah; - struct ath_mci_coex *mci = &sc->mci_coex; - struct ath_mci_buf *buf = &mci->sched_buf; - - if (buf->bf_addr) - dma_free_coherent(sc->dev, - ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE, - buf->bf_addr, buf->bf_paddr); - - ar9003_mci_cleanup(ah); - - ath_dbg(common, MCI, "MCI De-Initialized\n"); -} - -void ath_mci_intr(struct ath_softc *sc) -{ - struct ath_mci_coex *mci = &sc->mci_coex; - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - u32 mci_int, mci_int_rxmsg; - u32 offset, subtype, opcode; - u32 *pgpm; - u32 more_data = MCI_GPM_MORE; - bool skip_gpm = false; - - ar9003_mci_get_interrupt(sc->sc_ah, &mci_int, &mci_int_rxmsg); - - if (ar9003_mci_state(ah, MCI_STATE_ENABLE, NULL) == 0) { - ar9003_mci_state(ah, MCI_STATE_INIT_GPM_OFFSET, NULL); - return; - } - - if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE) { - u32 payload[4] = { 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffff00}; - - /* - * The following REMOTE_RESET and SYS_WAKING used to sent - * only when BT wake up. Now they are always sent, as a - * recovery method to reset BT MCI's RX alignment. - */ - ar9003_mci_send_message(ah, MCI_REMOTE_RESET, 0, - payload, 16, true, false); - ar9003_mci_send_message(ah, MCI_SYS_WAKING, 0, - NULL, 0, true, false); - - mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE; - ar9003_mci_state(ah, MCI_STATE_RESET_REQ_WAKE, NULL); - - /* - * always do this for recovery and 2G/5G toggling and LNA_TRANS - */ - ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE, NULL); - } - - if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING) { - mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING; - - if (ar9003_mci_state(ah, MCI_STATE_BT, NULL) == MCI_BT_SLEEP) { - if (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP, NULL) != - MCI_BT_SLEEP) - ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE, - NULL); - } - } - - if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) { - mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING; - - if (ar9003_mci_state(ah, MCI_STATE_BT, NULL) == MCI_BT_AWAKE) { - if (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP, NULL) != - MCI_BT_AWAKE) - ar9003_mci_state(ah, MCI_STATE_SET_BT_SLEEP, - NULL); - } - } - - if ((mci_int & AR_MCI_INTERRUPT_RX_INVALID_HDR) || - (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) { - ar9003_mci_state(ah, MCI_STATE_RECOVER_RX, NULL); - skip_gpm = true; - } - - if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO) { - mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO; - offset = ar9003_mci_state(ah, MCI_STATE_LAST_SCHD_MSG_OFFSET, - NULL); - } - - if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_GPM) { - mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_GPM; - - while (more_data == MCI_GPM_MORE) { - - pgpm = mci->gpm_buf.bf_addr; - offset = ar9003_mci_state(ah, MCI_STATE_NEXT_GPM_OFFSET, - &more_data); - - if (offset == MCI_GPM_INVALID) - break; - - pgpm += (offset >> 2); - - /* - * The first dword is timer. - * The real data starts from 2nd dword. - */ - subtype = MCI_GPM_TYPE(pgpm); - opcode = MCI_GPM_OPCODE(pgpm); - - if (skip_gpm) - goto recycle; - - if (MCI_GPM_IS_CAL_TYPE(subtype)) { - ath_mci_cal_msg(sc, subtype, (u8 *)pgpm); - } else { - switch (subtype) { - case MCI_GPM_COEX_AGENT: - ath_mci_msg(sc, opcode, (u8 *)pgpm); - break; - default: - break; - } - } - recycle: - MCI_GPM_RECYCLE(pgpm); - } - } - - if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_HW_MSG_MASK) { - if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL) - mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL; - - if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_LNA_INFO) - mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_LNA_INFO; - - if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_INFO) { - int value_dbm = ar9003_mci_state(ah, - MCI_STATE_CONT_RSSI_POWER, NULL); - - mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_INFO; - - if (ar9003_mci_state(ah, MCI_STATE_CONT_TXRX, NULL)) - ath_dbg(common, MCI, - "MCI CONT_INFO: (tx) pri = %d, pwr = %d dBm\n", - ar9003_mci_state(ah, - MCI_STATE_CONT_PRIORITY, NULL), - value_dbm); - else - ath_dbg(common, MCI, - "MCI CONT_INFO: (rx) pri = %d,pwr = %d dBm\n", - ar9003_mci_state(ah, - MCI_STATE_CONT_PRIORITY, NULL), - value_dbm); - } - - if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_NACK) - mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_NACK; - - if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_RST) - mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_RST; - } - - if ((mci_int & AR_MCI_INTERRUPT_RX_INVALID_HDR) || - (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) - mci_int &= ~(AR_MCI_INTERRUPT_RX_INVALID_HDR | - AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT); -} diff --git a/drivers/net/wireless/ath/ath9k/mci.h b/drivers/net/wireless/ath/ath9k/mci.h index c841444f..9590c618 100644 --- a/drivers/net/wireless/ath/ath9k/mci.h +++ b/drivers/net/wireless/ath/ath9k/mci.h @@ -17,11 +17,6 @@ #ifndef MCI_H #define MCI_H -#include "ar9003_mci.h" - -#define ATH_MCI_SCHED_BUF_SIZE (16 * 16) /* 16 entries, 4 dword each */ -#define ATH_MCI_GPM_MAX_ENTRY 16 -#define ATH_MCI_GPM_BUF_SIZE (ATH_MCI_GPM_MAX_ENTRY * 16) #define ATH_MCI_DEF_BT_PERIOD 40 #define ATH_MCI_BDR_DUTY_CYCLE 20 #define ATH_MCI_MAX_DUTY_CYCLE 90 @@ -115,19 +110,9 @@ struct ath_mci_profile { u8 num_bdr; }; -struct ath_mci_buf { - void *bf_addr; /* virtual addr of desc */ - dma_addr_t bf_paddr; /* physical addr of buffer */ - u32 bf_len; /* len of data */ -}; - -struct ath_mci_coex { - struct ath_mci_buf sched_buf; - struct ath_mci_buf gpm_buf; -}; - void ath_mci_flush_profile(struct ath_mci_profile *mci); -int ath_mci_setup(struct ath_softc *sc); -void ath_mci_cleanup(struct ath_softc *sc); -void ath_mci_intr(struct ath_softc *sc); +void ath_mci_process_profile(struct ath_softc *sc, + struct ath_mci_profile_info *info); +void ath_mci_process_status(struct ath_softc *sc, + struct ath_mci_profile_status *status); #endif diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c old mode 100644 new mode 100755 index e8a13b8a..3374d09c --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -122,9 +122,8 @@ static void ath_pci_aspm_init(struct ath_common *common) if (!parent) return; - if ((ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) && - (AR_SREV_9285(ah))) { - /* Bluetooth coexistance requires disabling ASPM for AR9285. */ + if (ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) { + /* Bluetooth coexistance requires disabling ASPM. */ pci_read_config_byte(pdev, pos + PCI_EXP_LNKCTL, &aspm); aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); pci_write_config_byte(pdev, pos + PCI_EXP_LNKCTL, aspm); @@ -150,31 +149,6 @@ static void ath_pci_aspm_init(struct ath_common *common) } } -static void ath_pci_aspm_init(struct ath_common *common) -{ - struct ath_softc *sc = (struct ath_softc *) common->priv; - struct ath_hw *ah = sc->sc_ah; - struct pci_dev *pdev = to_pci_dev(sc->dev); - struct pci_dev *parent; - int pos; - u8 aspm; - - if (!pci_is_pcie(pdev)) - return; - - parent = pdev->bus->self; - if (WARN_ON(!parent)) - return; - - pos = pci_pcie_cap(parent); - pci_read_config_byte(parent, pos + PCI_EXP_LNKCTL, &aspm); - if (aspm & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1)) { - ah->aspm_enabled = true; - /* Initialize PCIe PM and SERDES registers. */ - ath9k_hw_configpcipowersave(ah, 0, 0); - } -} - static const struct ath_bus_ops ath_pci_bus_ops = { .ath_bus_type = ATH_PCI, .read_cachesize = ath_pci_read_cachesize, @@ -334,11 +308,12 @@ static int ath_pci_suspend(struct device *device) struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath_softc *sc = hw->priv; + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); + /* The device has to be moved to FULLSLEEP forcibly. * Otherwise the chip never moved to full sleep, * when no interface is up. */ - ath9k_hw_disable(sc->sc_ah); ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); return 0; @@ -347,6 +322,8 @@ static int ath_pci_suspend(struct device *device) static int ath_pci_resume(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct ath_softc *sc = hw->priv; u32 val; /* @@ -358,17 +335,29 @@ static int ath_pci_resume(struct device *device) if ((val & 0x0000ff00) != 0) pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + ath9k_ps_wakeup(sc); + /* Enable LED */ + ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); + + /* + * Reset key cache to sane defaults (all entries cleared) instead of + * semi-random values after suspend/resume. + */ + ath9k_cmn_init_crypto(sc->sc_ah); + ath9k_ps_restore(sc); + + sc->ps_idle = true; + ath_radio_disable(sc, hw); + return 0; } -static const struct dev_pm_ops ath9k_pm_ops = { - .suspend = ath_pci_suspend, - .resume = ath_pci_resume, - .freeze = ath_pci_suspend, - .thaw = ath_pci_resume, - .poweroff = ath_pci_suspend, - .restore = ath_pci_resume, -}; +compat_pci_suspend(ath_pci_suspend) +compat_pci_resume(ath_pci_resume) + +static SIMPLE_DEV_PM_OPS(ath9k_pm_ops, ath_pci_suspend, ath_pci_resume); #define ATH9K_PM_OPS (&ath9k_pm_ops) @@ -386,7 +375,12 @@ static struct pci_driver ath_pci_driver = { .id_table = ath_pci_id_table, .probe = ath_pci_probe, .remove = ath_pci_remove, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = ATH9K_PM_OPS, +#elif defined(CONFIG_PM) + .suspend = ath_pci_suspend_compat, + .resume = ath_pci_resume_compat, +#endif }; int ath_pci_init(void) diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 08bb4553..888abc2b 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -567,8 +567,10 @@ static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv, static u8 ath_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv, const struct ath_rate_table *rate_table, - struct ath_rateset *rateset, u32 capflag) + u8 *mcs_set, u32 capflag) { + struct ath_rateset *rateset = (struct ath_rateset *)mcs_set; + u8 i, j, hi = 0; /* Use intersection of working rates and valid rates */ @@ -692,7 +694,7 @@ static u8 ath_rc_get_highest_rix(struct ath_softc *sc, return rate; /* This should not happen */ - WARN_ON_ONCE(1); + WARN_ON(1); rate = ath_rc_priv->valid_rate_index[0]; @@ -748,8 +750,7 @@ static void ath_rc_rate_set_rtscts(struct ath_softc *sc, * If 802.11g protection is enabled, determine whether to use RTS/CTS or * just CTS. Note that this is only done for OFDM/HT unicast frames. */ - if ((tx_info->control.vif && - tx_info->control.vif->bss_conf.use_cts_prot) && + if ((sc->sc_flags & SC_OP_PROTECT_ENABLE) && (rate_table->info[rix].phy == WLAN_RC_PHY_OFDM || WLAN_RC_PHY_HT(rate_table->info[rix].phy))) { rates[0].flags |= IEEE80211_TX_RC_USE_CTS_PROTECT; @@ -1198,7 +1199,7 @@ struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc, return &ar5416_11na_ratetable; return &ar5416_11a_ratetable; default: - ath_dbg(common, CONFIG, "Invalid band\n"); + ath_dbg(common, ATH_DBG_CONFIG, "Invalid band\n"); return NULL; } } @@ -1211,7 +1212,7 @@ static void ath_rc_init(struct ath_softc *sc, { struct ath_rateset *rateset = &ath_rc_priv->neg_rates; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_rateset *ht_mcs = &ath_rc_priv->neg_ht_rates; + u8 *ht_mcs = (u8 *)&ath_rc_priv->neg_ht_rates; u8 i, j, k, hi = 0, hthi = 0; /* Initial rate table size. Will change depending @@ -1227,7 +1228,7 @@ static void ath_rc_init(struct ath_softc *sc, ath_rc_init_valid_rate_idx(ath_rc_priv); for (i = 0; i < WLAN_RC_PHY_MAX; i++) { - for (j = 0; j < RATE_TABLE_SIZE; j++) + for (j = 0; j < MAX_TX_RATE_PHY; j++) ath_rc_priv->valid_phy_rateidx[i][j] = 0; ath_rc_priv->valid_phy_ratecnt[i] = 0; } @@ -1270,12 +1271,11 @@ static void ath_rc_init(struct ath_softc *sc, ath_rc_priv->max_valid_rate = k; ath_rc_sort_validrates(rate_table, ath_rc_priv); - ath_rc_priv->rate_max_phy = (k > 4) ? - ath_rc_priv->valid_rate_index[k-4] : - ath_rc_priv->valid_rate_index[k-1]; + ath_rc_priv->rate_max_phy = ath_rc_priv->valid_rate_index[k-4]; ath_rc_priv->rate_table = rate_table; - ath_dbg(common, CONFIG, "RC Initialized with capabilities: 0x%x\n", + ath_dbg(common, ATH_DBG_CONFIG, + "RC Initialized with capabilities: 0x%x\n", ath_rc_priv->ht_cap); } @@ -1299,13 +1299,12 @@ static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta, return caps; } -static bool ath_tx_aggr_check(struct ath_softc *sc, struct ieee80211_sta *sta, +static bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno) { - struct ath_node *an = (struct ath_node *)sta->drv_priv; struct ath_atx_tid *txtid; - if (!sta->ht_cap.ht_supported) + if (!(sc->sc_flags & SC_OP_TXAGGR)) return false; txtid = ATH_AN_2_TID(an, tidno); @@ -1346,7 +1345,7 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, fc = hdr->frame_control; for (i = 0; i < sc->hw->max_rates; i++) { struct ieee80211_tx_rate *rate = &tx_info->status.rates[i]; - if (rate->idx < 0 || !rate->count) + if (!rate->count) break; final_ts_idx = i; @@ -1376,11 +1375,13 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, if (ieee80211_is_data_qos(fc) && skb_get_queue_mapping(skb) != IEEE80211_AC_VO) { u8 *qc, tid; + struct ath_node *an; qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; + an = (struct ath_node *)sta->drv_priv; - if(ath_tx_aggr_check(sc, sta, tid)) + if(ath_tx_aggr_check(sc, an, tid)) ieee80211_start_tx_ba_session(sta, tid, 0); } } @@ -1471,7 +1472,7 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, oper_cw40, oper_sgi); ath_rc_init(sc, priv_sta, sband, sta, rate_table); - ath_dbg(ath9k_hw_common(sc->sc_ah), CONFIG, + ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG, "Operating HT Bandwidth changed to: %d\n", sc->hw->conf.channel_type); } @@ -1480,6 +1481,12 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, #ifdef CONFIG_ATH9K_DEBUGFS +static int ath9k_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1547,7 +1554,7 @@ static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, static const struct file_operations fops_rcstat = { .read = read_file_rcstat, - .open = simple_open, + .open = ath9k_debugfs_open, .owner = THIS_MODULE }; diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h old mode 100644 new mode 100755 index 12cf1225..e8e1901b --- a/drivers/net/wireless/ath/ath9k/rc.h +++ b/drivers/net/wireless/ath/ath9k/rc.h @@ -25,6 +25,8 @@ struct ath_softc; #define ATH_RATE_MAX 30 #define RATE_TABLE_SIZE 72 +#define MAX_TX_RATE_PHY 48 + #define RC_INVALID 0x0000 #define RC_LEGACY 0x0001 diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c old mode 100644 new mode 100755 index 2e6583d3..64546771 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -174,17 +174,22 @@ static void ath_rx_addbuffer_edma(struct ath_softc *sc, enum ath9k_rx_qtype qtype, int size) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_buf *bf, *tbf; + u32 nbuf = 0; if (list_empty(&sc->rx.rxbuf)) { - ath_dbg(common, QUEUE, "No free rx buf available\n"); + ath_dbg(common, ATH_DBG_QUEUE, "No free rx buf available\n"); return; } - list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) + while (!list_empty(&sc->rx.rxbuf)) { + nbuf++; + if (!ath_rx_edma_buf_link(sc, qtype)) break; + if (nbuf >= size) + break; + } } static void ath_rx_remove_buffer(struct ath_softc *sc, @@ -232,6 +237,7 @@ static void ath_rx_edma_cleanup(struct ath_softc *sc) static void ath_rx_edma_init_queue(struct ath_rx_edma *rx_edma, int size) { skb_queue_head_init(&rx_edma->rx_fifo); + skb_queue_head_init(&rx_edma->rx_buffers); rx_edma->rx_fifo_hwsize = size; } @@ -336,7 +342,7 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { return ath_rx_edma_init(sc, nbufs); } else { - ath_dbg(common, CONFIG, "cachelsz %u rxbufsize %u\n", + ath_dbg(common, ATH_DBG_CONFIG, "cachelsz %u rxbufsize %u\n", common->cachelsz, common->rx_bufsize); /* Initialize rx descriptors */ @@ -474,6 +480,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc) return rfilt; +#undef RX_FILTER_PRESERVE } int ath_startrecv(struct ath_softc *sc) @@ -591,7 +598,7 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) if (sc->ps_flags & PS_BEACON_SYNC) { sc->ps_flags &= ~PS_BEACON_SYNC; - ath_dbg(common, PS, + ath_dbg(common, ATH_DBG_PS, "Reconfigure Beacon timers based on timestamp from the AP\n"); ath_set_beacon(sc); } @@ -604,7 +611,7 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) * a backup trigger for returning into NETWORK SLEEP state, * so we are waiting for it as well. */ - ath_dbg(common, PS, + ath_dbg(common, ATH_DBG_PS, "Received DTIM beacon indicating buffered broadcast/multicast frame(s)\n"); sc->ps_flags |= PS_WAIT_FOR_CAB | PS_WAIT_FOR_BEACON; return; @@ -617,7 +624,8 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) * been delivered. */ sc->ps_flags &= ~PS_WAIT_FOR_CAB; - ath_dbg(common, PS, "PS wait for CAB frames timed out\n"); + ath_dbg(common, ATH_DBG_PS, + "PS wait for CAB frames timed out\n"); } } @@ -642,13 +650,13 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon) * point. */ sc->ps_flags &= ~(PS_WAIT_FOR_CAB | PS_WAIT_FOR_BEACON); - ath_dbg(common, PS, + ath_dbg(common, ATH_DBG_PS, "All PS CAB frames received, back to sleep\n"); } else if ((sc->ps_flags & PS_WAIT_FOR_PSPOLL_DATA) && !is_multicast_ether_addr(hdr->addr1) && !ieee80211_has_morefrags(hdr->frame_control)) { sc->ps_flags &= ~PS_WAIT_FOR_PSPOLL_DATA; - ath_dbg(common, PS, + ath_dbg(common, ATH_DBG_PS, "Going back to sleep after having received PS-Poll data (0x%lx)\n", sc->ps_flags & (PS_WAIT_FOR_BEACON | PS_WAIT_FOR_CAB | @@ -658,9 +666,7 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon) } static bool ath_edma_get_buffers(struct ath_softc *sc, - enum ath9k_rx_qtype qtype, - struct ath_rx_status *rs, - struct ath_buf **dest) + enum ath9k_rx_qtype qtype) { struct ath_rx_edma *rx_edma = &sc->rx.rx_edma[qtype]; struct ath_hw *ah = sc->sc_ah; @@ -679,7 +685,7 @@ static bool ath_edma_get_buffers(struct ath_softc *sc, dma_sync_single_for_cpu(sc->dev, bf->bf_buf_addr, common->rx_bufsize, DMA_FROM_DEVICE); - ret = ath9k_hw_process_rxdesc_edma(ah, rs, skb->data); + ret = ath9k_hw_process_rxdesc_edma(ah, NULL, skb->data); if (ret == -EINPROGRESS) { /*let device gain the buffer again*/ dma_sync_single_for_device(sc->dev, bf->bf_buf_addr, @@ -692,21 +698,20 @@ static bool ath_edma_get_buffers(struct ath_softc *sc, /* corrupt descriptor, skip this one and the following one */ list_add_tail(&bf->list, &sc->rx.rxbuf); ath_rx_edma_buf_link(sc, qtype); - skb = skb_peek(&rx_edma->rx_fifo); - if (skb) { - bf = SKB_CB_ATHBUF(skb); - BUG_ON(!bf); + if (!skb) + return true; - __skb_unlink(skb, &rx_edma->rx_fifo); - list_add_tail(&bf->list, &sc->rx.rxbuf); - ath_rx_edma_buf_link(sc, qtype); - } + bf = SKB_CB_ATHBUF(skb); + BUG_ON(!bf); - bf = NULL; + __skb_unlink(skb, &rx_edma->rx_fifo); + list_add_tail(&bf->list, &sc->rx.rxbuf); + ath_rx_edma_buf_link(sc, qtype); + return true; } + skb_queue_tail(&rx_edma->rx_buffers, skb); - *dest = bf; return true; } @@ -714,15 +719,18 @@ static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc, struct ath_rx_status *rs, enum ath9k_rx_qtype qtype) { - struct ath_buf *bf = NULL; + struct ath_rx_edma *rx_edma = &sc->rx.rx_edma[qtype]; + struct sk_buff *skb; + struct ath_buf *bf; - while (ath_edma_get_buffers(sc, qtype, rs, &bf)) { - if (!bf) - continue; + while (ath_edma_get_buffers(sc, qtype)); + skb = __skb_dequeue(&rx_edma->rx_buffers); + if (!skb) + return NULL; - return bf; - } - return NULL; + bf = SKB_CB_ATHBUF(skb); + ath9k_hw_process_rxdesc_edma(sc->sc_ah, rs, skb->data); + return bf; } static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, @@ -826,15 +834,6 @@ static bool ath9k_rx_accept(struct ath_common *common, (ATH9K_RXERR_DECRYPT | ATH9K_RXERR_CRC | ATH9K_RXERR_MIC | ATH9K_RXERR_KEYMISS)); - /* - * Key miss events are only relevant for pairwise keys where the - * descriptor does contain a valid key index. This has been observed - * mostly with CCMP encryption. - */ - if (rx_stats->rs_keyix == ATH9K_RXKEYIX_INVALID || - !test_bit(rx_stats->rs_keyix, common->ccmp_keymap)) - rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS; - if (!rx_stats->rs_datalen) return false; /* @@ -944,7 +943,7 @@ static int ath9k_process_rate(struct ath_common *common, * No valid hardware bitrate found -- we should not get here * because hardware has already validated this frame as OK. */ - ath_dbg(common, ANY, + ath_dbg(common, ATH_DBG_ANY, "unsupported hw bitrate detected 0x%02x using 1 Mbit\n", rx_stats->rs_rate); @@ -959,7 +958,6 @@ static void ath9k_process_rssi(struct ath_common *common, struct ath_softc *sc = hw->priv; struct ath_hw *ah = common->ah; int last_rssi; - int rssi = rx_stats->rs_rssi; if (!rx_stats->is_mybeacon || ((ah->opmode != NL80211_IFTYPE_STATION) && @@ -971,12 +969,13 @@ static void ath9k_process_rssi(struct ath_common *common, last_rssi = sc->last_rssi; if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) - rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER); - if (rssi < 0) - rssi = 0; + rx_stats->rs_rssi = ATH_EP_RND(last_rssi, + ATH_RSSI_EP_MULTIPLIER); + if (rx_stats->rs_rssi < 0) + rx_stats->rs_rssi = 0; /* Update Beacon RSSI, this is used by ANI. */ - ah->stats.avgbrssi = rssi; + ah->stats.avgbrssi = rx_stats->rs_rssi; } /* @@ -993,6 +992,8 @@ static int ath9k_rx_skb_preprocess(struct ath_common *common, { struct ath_hw *ah = common->ah; + memset(rx_status, 0, sizeof(struct ieee80211_rx_status)); + /* * everything but the rate is checked here, the rate check is done * separately to avoid doing two lookups for a rate for each frame. @@ -1014,8 +1015,6 @@ static int ath9k_rx_skb_preprocess(struct ath_common *common, rx_status->signal = ah->noise + rx_stats->rs_rssi; rx_status->antenna = rx_stats->rs_antenna; rx_status->flag |= RX_FLAG_MACTIME_MPDU; - if (rx_stats->rs_moreaggr) - rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; return 0; } @@ -1835,7 +1834,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) hdr = (struct ieee80211_hdr *) (hdr_skb->data + rx_status_len); rxs = IEEE80211_SKB_RXCB(hdr_skb); if (ieee80211_is_beacon(hdr->frame_control) && - !is_zero_ether_addr(common->curbssid) && !compare_ether_addr(hdr->addr3, common->curbssid)) rs.is_mybeacon = true; else @@ -1850,7 +1848,10 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) if (sc->sc_flags & SC_OP_RXFLUSH) goto requeue_drop_frag; - memset(rxs, 0, sizeof(struct ieee80211_rx_status)); + retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs, + rxs, &decrypt_error); + if (retval) + goto requeue_drop_frag; rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp; if (rs.rs_tstamp > tsf_lower && @@ -1861,11 +1862,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) unlikely(tsf_lower - rs.rs_tstamp > 0x10000000)) rxs->mactime += 0x100000000ULL; - retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs, - rxs, &decrypt_error); - if (retval) - goto requeue_drop_frag; - /* Ensure we always have an skb to requeue once we are done * processing the current buffer's skb */ requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC); @@ -1924,33 +1920,28 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) if (sc->rx.frag) { int space = skb->len - skb_tailroom(hdr_skb); + sc->rx.frag = NULL; + if (pskb_expand_head(hdr_skb, 0, space, GFP_ATOMIC) < 0) { dev_kfree_skb(skb); goto requeue_drop_frag; } - sc->rx.frag = NULL; - skb_copy_from_linear_data(skb, skb_put(hdr_skb, skb->len), skb->len); dev_kfree_skb_any(skb); skb = hdr_skb; } - - if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) { - - /* - * change the default rx antenna if rx diversity - * chooses the other antenna 3 times in a row. - */ - if (sc->rx.defant != rs.rs_antenna) { - if (++sc->rx.rxotherant >= 3) - ath_setdefantenna(sc, rs.rs_antenna); - } else { - sc->rx.rxotherant = 0; - } - + /* + * change the default rx antenna if rx diversity chooses the + * other antenna 3 times in a row. + */ + if (sc->rx.defant != rs.rs_antenna) { + if (++sc->rx.rxotherant >= 3) + ath_setdefantenna(sc, rs.rs_antenna); + } else { + sc->rx.rxotherant = 0; } if (rxs->flag & RX_FLAG_MMIC_STRIPPED) diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 458f81b4..45910975 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -797,6 +797,7 @@ #define AR_SREV_VERSION_9580 0x1C0 #define AR_SREV_REVISION_9580_10 4 /* AR9580 1.0 */ #define AR_SREV_VERSION_9462 0x280 +#define AR_SREV_REVISION_9462_10 0 #define AR_SREV_REVISION_9462_20 2 #define AR_SREV_5416(_ah) \ @@ -897,6 +898,10 @@ #define AR_SREV_9462(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462)) +#define AR_SREV_9462_10(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_10)) + #define AR_SREV_9462_20(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_20)) @@ -1001,8 +1006,6 @@ enum { #define AR_INTR_ASYNC_MASK (AR_SREV_9340(ah) ? 0x4018 : 0x4030) #define AR_INTR_ASYNC_MASK_GPIO 0xFFFC0000 #define AR_INTR_ASYNC_MASK_GPIO_S 18 -#define AR_INTR_ASYNC_MASK_MCI 0x00000080 -#define AR_INTR_ASYNC_MASK_MCI_S 7 #define AR_INTR_SYNC_MASK (AR_SREV_9340(ah) ? 0x401c : 0x4034) #define AR_INTR_SYNC_MASK_GPIO 0xFFFC0000 @@ -1010,14 +1013,6 @@ enum { #define AR_INTR_ASYNC_CAUSE_CLR (AR_SREV_9340(ah) ? 0x4020 : 0x4038) #define AR_INTR_ASYNC_CAUSE (AR_SREV_9340(ah) ? 0x4020 : 0x4038) -#define AR_INTR_ASYNC_CAUSE_MCI 0x00000080 -#define AR_INTR_ASYNC_USED (AR_INTR_MAC_IRQ | \ - AR_INTR_ASYNC_CAUSE_MCI) - -/* Asynchronous Interrupt Enable Register */ -#define AR_INTR_ASYNC_ENABLE_MCI 0x00000080 -#define AR_INTR_ASYNC_ENABLE_MCI_S 7 - #define AR_INTR_ASYNC_ENABLE (AR_SREV_9340(ah) ? 0x4024 : 0x403c) #define AR_INTR_ASYNC_ENABLE_GPIO 0xFFFC0000 @@ -1151,7 +1146,6 @@ enum { #define AR_INTR_PRIO_ASYNC_ENABLE (AR_SREV_9340(ah) ? 0x4094 : 0x40d4) #define AR_ENT_OTP 0x40d8 #define AR_ENT_OTP_CHAIN2_DISABLE 0x00020000 -#define AR_ENT_OTP_49GHZ_DISABLE 0x00100000 #define AR_ENT_OTP_MIN_PKT_SIZE_DISABLE 0x00800000 #define AR_CH0_BB_DPLL1 0x16180 @@ -1275,8 +1269,6 @@ enum { #define AR_RTC_INTR_MASK \ ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0058) : 0x7058) -#define AR_RTC_KEEP_AWAKE 0x7034 - /* RTC_DERIVED_* - only for AR9100 */ #define AR_RTC_DERIVED_CLK \ @@ -1563,8 +1555,6 @@ enum { #define AR_DIAG_FRAME_NV0 0x00020000 #define AR_DIAG_OBS_PT_SEL1 0x000C0000 #define AR_DIAG_OBS_PT_SEL1_S 18 -#define AR_DIAG_OBS_PT_SEL2 0x08000000 -#define AR_DIAG_OBS_PT_SEL2_S 27 #define AR_DIAG_FORCE_RX_CLEAR 0x00100000 /* force rx_clear high */ #define AR_DIAG_IGNORE_VIRT_CS 0x00200000 #define AR_DIAG_FORCE_CH_IDLE_HIGH 0x00400000 @@ -1939,277 +1929,37 @@ enum { #define AR_PHY_AGC_CONTROL_YCOK_MAX_S 6 /* MCI Registers */ - -#define AR_MCI_COMMAND0 0x1800 -#define AR_MCI_COMMAND0_HEADER 0xFF -#define AR_MCI_COMMAND0_HEADER_S 0 -#define AR_MCI_COMMAND0_LEN 0x1f00 -#define AR_MCI_COMMAND0_LEN_S 8 -#define AR_MCI_COMMAND0_DISABLE_TIMESTAMP 0x2000 -#define AR_MCI_COMMAND0_DISABLE_TIMESTAMP_S 13 - -#define AR_MCI_COMMAND1 0x1804 - -#define AR_MCI_COMMAND2 0x1808 -#define AR_MCI_COMMAND2_RESET_TX 0x01 -#define AR_MCI_COMMAND2_RESET_TX_S 0 -#define AR_MCI_COMMAND2_RESET_RX 0x02 -#define AR_MCI_COMMAND2_RESET_RX_S 1 -#define AR_MCI_COMMAND2_RESET_RX_NUM_CYCLES 0x3FC -#define AR_MCI_COMMAND2_RESET_RX_NUM_CYCLES_S 2 -#define AR_MCI_COMMAND2_RESET_REQ_WAKEUP 0x400 -#define AR_MCI_COMMAND2_RESET_REQ_WAKEUP_S 10 - -#define AR_MCI_RX_CTRL 0x180c - -#define AR_MCI_TX_CTRL 0x1810 -/* 0 = no division, 1 = divide by 2, 2 = divide by 4, 3 = divide by 8 */ -#define AR_MCI_TX_CTRL_CLK_DIV 0x03 -#define AR_MCI_TX_CTRL_CLK_DIV_S 0 -#define AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE 0x04 -#define AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE_S 2 -#define AR_MCI_TX_CTRL_GAIN_UPDATE_FREQ 0xFFFFF8 -#define AR_MCI_TX_CTRL_GAIN_UPDATE_FREQ_S 3 -#define AR_MCI_TX_CTRL_GAIN_UPDATE_NUM 0xF000000 -#define AR_MCI_TX_CTRL_GAIN_UPDATE_NUM_S 24 - -#define AR_MCI_MSG_ATTRIBUTES_TABLE 0x1814 -#define AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM 0xFFFF -#define AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM_S 0 -#define AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR 0xFFFF0000 -#define AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR_S 16 - -#define AR_MCI_SCHD_TABLE_0 0x1818 -#define AR_MCI_SCHD_TABLE_1 0x181c -#define AR_MCI_GPM_0 0x1820 -#define AR_MCI_GPM_1 0x1824 -#define AR_MCI_GPM_WRITE_PTR 0xFFFF0000 -#define AR_MCI_GPM_WRITE_PTR_S 16 -#define AR_MCI_GPM_BUF_LEN 0x0000FFFF -#define AR_MCI_GPM_BUF_LEN_S 0 - -#define AR_MCI_INTERRUPT_RAW 0x1828 -#define AR_MCI_INTERRUPT_EN 0x182c -#define AR_MCI_INTERRUPT_SW_MSG_DONE 0x00000001 -#define AR_MCI_INTERRUPT_SW_MSG_DONE_S 0 -#define AR_MCI_INTERRUPT_CPU_INT_MSG 0x00000002 -#define AR_MCI_INTERRUPT_CPU_INT_MSG_S 1 -#define AR_MCI_INTERRUPT_RX_CKSUM_FAIL 0x00000004 -#define AR_MCI_INTERRUPT_RX_CKSUM_FAIL_S 2 -#define AR_MCI_INTERRUPT_RX_INVALID_HDR 0x00000008 -#define AR_MCI_INTERRUPT_RX_INVALID_HDR_S 3 -#define AR_MCI_INTERRUPT_RX_HW_MSG_FAIL 0x00000010 -#define AR_MCI_INTERRUPT_RX_HW_MSG_FAIL_S 4 -#define AR_MCI_INTERRUPT_RX_SW_MSG_FAIL 0x00000020 -#define AR_MCI_INTERRUPT_RX_SW_MSG_FAIL_S 5 -#define AR_MCI_INTERRUPT_TX_HW_MSG_FAIL 0x00000080 -#define AR_MCI_INTERRUPT_TX_HW_MSG_FAIL_S 7 -#define AR_MCI_INTERRUPT_TX_SW_MSG_FAIL 0x00000100 -#define AR_MCI_INTERRUPT_TX_SW_MSG_FAIL_S 8 -#define AR_MCI_INTERRUPT_RX_MSG 0x00000200 -#define AR_MCI_INTERRUPT_RX_MSG_S 9 -#define AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE 0x00000400 -#define AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE_S 10 -#define AR_MCI_INTERRUPT_BT_PRI 0x07fff800 -#define AR_MCI_INTERRUPT_BT_PRI_S 11 -#define AR_MCI_INTERRUPT_BT_PRI_THRESH 0x08000000 -#define AR_MCI_INTERRUPT_BT_PRI_THRESH_S 27 -#define AR_MCI_INTERRUPT_BT_FREQ 0x10000000 -#define AR_MCI_INTERRUPT_BT_FREQ_S 28 -#define AR_MCI_INTERRUPT_BT_STOMP 0x20000000 -#define AR_MCI_INTERRUPT_BT_STOMP_S 29 -#define AR_MCI_INTERRUPT_BB_AIC_IRQ 0x40000000 -#define AR_MCI_INTERRUPT_BB_AIC_IRQ_S 30 -#define AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT 0x80000000 -#define AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT_S 31 - -#define AR_MCI_INTERRUPT_DEFAULT (AR_MCI_INTERRUPT_SW_MSG_DONE | \ - AR_MCI_INTERRUPT_RX_INVALID_HDR | \ - AR_MCI_INTERRUPT_RX_HW_MSG_FAIL | \ - AR_MCI_INTERRUPT_RX_SW_MSG_FAIL | \ - AR_MCI_INTERRUPT_TX_HW_MSG_FAIL | \ - AR_MCI_INTERRUPT_TX_SW_MSG_FAIL | \ - AR_MCI_INTERRUPT_RX_MSG | \ - AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE | \ - AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT) - -#define AR_MCI_INTERRUPT_MSG_FAIL_MASK (AR_MCI_INTERRUPT_RX_HW_MSG_FAIL | \ - AR_MCI_INTERRUPT_RX_SW_MSG_FAIL | \ - AR_MCI_INTERRUPT_TX_HW_MSG_FAIL | \ - AR_MCI_INTERRUPT_TX_SW_MSG_FAIL) - -#define AR_MCI_REMOTE_CPU_INT 0x1830 -#define AR_MCI_REMOTE_CPU_INT_EN 0x1834 -#define AR_MCI_INTERRUPT_RX_MSG_RAW 0x1838 -#define AR_MCI_INTERRUPT_RX_MSG_EN 0x183c -#define AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET 0x00000001 -#define AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET_S 0 -#define AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL 0x00000002 -#define AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL_S 1 -#define AR_MCI_INTERRUPT_RX_MSG_CONT_NACK 0x00000004 -#define AR_MCI_INTERRUPT_RX_MSG_CONT_NACK_S 2 -#define AR_MCI_INTERRUPT_RX_MSG_CONT_INFO 0x00000008 -#define AR_MCI_INTERRUPT_RX_MSG_CONT_INFO_S 3 -#define AR_MCI_INTERRUPT_RX_MSG_CONT_RST 0x00000010 -#define AR_MCI_INTERRUPT_RX_MSG_CONT_RST_S 4 -#define AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO 0x00000020 -#define AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO_S 5 -#define AR_MCI_INTERRUPT_RX_MSG_CPU_INT 0x00000040 -#define AR_MCI_INTERRUPT_RX_MSG_CPU_INT_S 6 -#define AR_MCI_INTERRUPT_RX_MSG_GPM 0x00000100 -#define AR_MCI_INTERRUPT_RX_MSG_GPM_S 8 -#define AR_MCI_INTERRUPT_RX_MSG_LNA_INFO 0x00000200 -#define AR_MCI_INTERRUPT_RX_MSG_LNA_INFO_S 9 -#define AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING 0x00000400 -#define AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING_S 10 -#define AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING 0x00000800 -#define AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING_S 11 -#define AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE 0x00001000 -#define AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE_S 12 -#define AR_MCI_INTERRUPT_RX_HW_MSG_MASK (AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO | \ +#define AR_MCI_INTERRUPT_RX_MSG_EN 0x183c +#define AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET 0x00000001 +#define AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET_S 0 +#define AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL 0x00000002 +#define AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL_S 1 +#define AR_MCI_INTERRUPT_RX_MSG_CONT_NACK 0x00000004 +#define AR_MCI_INTERRUPT_RX_MSG_CONT_NACK_S 2 +#define AR_MCI_INTERRUPT_RX_MSG_CONT_INFO 0x00000008 +#define AR_MCI_INTERRUPT_RX_MSG_CONT_INFO_S 3 +#define AR_MCI_INTERRUPT_RX_MSG_CONT_RST 0x00000010 +#define AR_MCI_INTERRUPT_RX_MSG_CONT_RST_S 4 +#define AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO 0x00000020 +#define AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO_S 5 +#define AR_MCI_INTERRUPT_RX_MSG_CPU_INT 0x00000040 +#define AR_MCI_INTERRUPT_RX_MSG_CPU_INT_S 6 +#define AR_MCI_INTERRUPT_RX_MSG_GPM 0x00000100 +#define AR_MCI_INTERRUPT_RX_MSG_GPM_S 8 +#define AR_MCI_INTERRUPT_RX_MSG_LNA_INFO 0x00000200 +#define AR_MCI_INTERRUPT_RX_MSG_LNA_INFO_S 9 +#define AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING 0x00000400 +#define AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING_S 10 +#define AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING 0x00000800 +#define AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING_S 11 +#define AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE 0x00001000 +#define AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE_S 12 +#define AR_MCI_INTERRUPT_RX_HW_MSG_MASK (AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO | \ AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL| \ AR_MCI_INTERRUPT_RX_MSG_LNA_INFO | \ AR_MCI_INTERRUPT_RX_MSG_CONT_NACK | \ AR_MCI_INTERRUPT_RX_MSG_CONT_INFO | \ AR_MCI_INTERRUPT_RX_MSG_CONT_RST) -#define AR_MCI_INTERRUPT_RX_MSG_DEFAULT (AR_MCI_INTERRUPT_RX_MSG_GPM | \ - AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET| \ - AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING | \ - AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING| \ - AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO | \ - AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL | \ - AR_MCI_INTERRUPT_RX_MSG_LNA_INFO | \ - AR_MCI_INTERRUPT_RX_MSG_CONT_NACK | \ - AR_MCI_INTERRUPT_RX_MSG_CONT_INFO | \ - AR_MCI_INTERRUPT_RX_MSG_CONT_RST | \ - AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE) - -#define AR_MCI_CPU_INT 0x1840 - -#define AR_MCI_RX_STATUS 0x1844 -#define AR_MCI_RX_LAST_SCHD_MSG_INDEX 0x00000F00 -#define AR_MCI_RX_LAST_SCHD_MSG_INDEX_S 8 -#define AR_MCI_RX_REMOTE_SLEEP 0x00001000 -#define AR_MCI_RX_REMOTE_SLEEP_S 12 -#define AR_MCI_RX_MCI_CLK_REQ 0x00002000 -#define AR_MCI_RX_MCI_CLK_REQ_S 13 - -#define AR_MCI_CONT_STATUS 0x1848 -#define AR_MCI_CONT_RSSI_POWER 0x000000FF -#define AR_MCI_CONT_RSSI_POWER_S 0 -#define AR_MCI_CONT_RRIORITY 0x0000FF00 -#define AR_MCI_CONT_RRIORITY_S 8 -#define AR_MCI_CONT_TXRX 0x00010000 -#define AR_MCI_CONT_TXRX_S 16 - -#define AR_MCI_BT_PRI0 0x184c -#define AR_MCI_BT_PRI1 0x1850 -#define AR_MCI_BT_PRI2 0x1854 -#define AR_MCI_BT_PRI3 0x1858 -#define AR_MCI_BT_PRI 0x185c -#define AR_MCI_WL_FREQ0 0x1860 -#define AR_MCI_WL_FREQ1 0x1864 -#define AR_MCI_WL_FREQ2 0x1868 -#define AR_MCI_GAIN 0x186c -#define AR_MCI_WBTIMER1 0x1870 -#define AR_MCI_WBTIMER2 0x1874 -#define AR_MCI_WBTIMER3 0x1878 -#define AR_MCI_WBTIMER4 0x187c -#define AR_MCI_MAXGAIN 0x1880 -#define AR_MCI_HW_SCHD_TBL_CTL 0x1884 -#define AR_MCI_HW_SCHD_TBL_D0 0x1888 -#define AR_MCI_HW_SCHD_TBL_D1 0x188c -#define AR_MCI_HW_SCHD_TBL_D2 0x1890 -#define AR_MCI_HW_SCHD_TBL_D3 0x1894 -#define AR_MCI_TX_PAYLOAD0 0x1898 -#define AR_MCI_TX_PAYLOAD1 0x189c -#define AR_MCI_TX_PAYLOAD2 0x18a0 -#define AR_MCI_TX_PAYLOAD3 0x18a4 -#define AR_BTCOEX_WBTIMER 0x18a8 - -#define AR_BTCOEX_CTRL 0x18ac -#define AR_BTCOEX_CTRL_AR9462_MODE 0x00000001 -#define AR_BTCOEX_CTRL_AR9462_MODE_S 0 -#define AR_BTCOEX_CTRL_WBTIMER_EN 0x00000002 -#define AR_BTCOEX_CTRL_WBTIMER_EN_S 1 -#define AR_BTCOEX_CTRL_MCI_MODE_EN 0x00000004 -#define AR_BTCOEX_CTRL_MCI_MODE_EN_S 2 -#define AR_BTCOEX_CTRL_LNA_SHARED 0x00000008 -#define AR_BTCOEX_CTRL_LNA_SHARED_S 3 -#define AR_BTCOEX_CTRL_PA_SHARED 0x00000010 -#define AR_BTCOEX_CTRL_PA_SHARED_S 4 -#define AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN 0x00000020 -#define AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN_S 5 -#define AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN 0x00000040 -#define AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN_S 6 -#define AR_BTCOEX_CTRL_NUM_ANTENNAS 0x00000180 -#define AR_BTCOEX_CTRL_NUM_ANTENNAS_S 7 -#define AR_BTCOEX_CTRL_RX_CHAIN_MASK 0x00000E00 -#define AR_BTCOEX_CTRL_RX_CHAIN_MASK_S 9 -#define AR_BTCOEX_CTRL_AGGR_THRESH 0x00007000 -#define AR_BTCOEX_CTRL_AGGR_THRESH_S 12 -#define AR_BTCOEX_CTRL_1_CHAIN_BCN 0x00080000 -#define AR_BTCOEX_CTRL_1_CHAIN_BCN_S 19 -#define AR_BTCOEX_CTRL_1_CHAIN_ACK 0x00100000 -#define AR_BTCOEX_CTRL_1_CHAIN_ACK_S 20 -#define AR_BTCOEX_CTRL_WAIT_BA_MARGIN 0x1FE00000 -#define AR_BTCOEX_CTRL_WAIT_BA_MARGIN_S 28 -#define AR_BTCOEX_CTRL_REDUCE_TXPWR 0x20000000 -#define AR_BTCOEX_CTRL_REDUCE_TXPWR_S 29 -#define AR_BTCOEX_CTRL_SPDT_ENABLE_10 0x40000000 -#define AR_BTCOEX_CTRL_SPDT_ENABLE_10_S 30 -#define AR_BTCOEX_CTRL_SPDT_POLARITY 0x80000000 -#define AR_BTCOEX_CTRL_SPDT_POLARITY_S 31 - -#define AR_BTCOEX_WL_WEIGHTS0 0x18b0 -#define AR_BTCOEX_WL_WEIGHTS1 0x18b4 -#define AR_BTCOEX_WL_WEIGHTS2 0x18b8 -#define AR_BTCOEX_WL_WEIGHTS3 0x18bc -#define AR_BTCOEX_MAX_TXPWR(_x) (0x18c0 + ((_x) << 2)) -#define AR_BTCOEX_WL_LNA 0x1940 -#define AR_BTCOEX_RFGAIN_CTRL 0x1944 - -#define AR_BTCOEX_CTRL2 0x1948 -#define AR_BTCOEX_CTRL2_TXPWR_THRESH 0x0007F800 -#define AR_BTCOEX_CTRL2_TXPWR_THRESH_S 11 -#define AR_BTCOEX_CTRL2_TX_CHAIN_MASK 0x00380000 -#define AR_BTCOEX_CTRL2_TX_CHAIN_MASK_S 19 -#define AR_BTCOEX_CTRL2_RX_DEWEIGHT 0x00400000 -#define AR_BTCOEX_CTRL2_RX_DEWEIGHT_S 22 -#define AR_BTCOEX_CTRL2_GPIO_OBS_SEL 0x00800000 -#define AR_BTCOEX_CTRL2_GPIO_OBS_SEL_S 23 -#define AR_BTCOEX_CTRL2_MAC_BB_OBS_SEL 0x01000000 -#define AR_BTCOEX_CTRL2_MAC_BB_OBS_SEL_S 24 -#define AR_BTCOEX_CTRL2_DESC_BASED_TXPWR_ENABLE 0x02000000 -#define AR_BTCOEX_CTRL2_DESC_BASED_TXPWR_ENABLE_S 25 - -#define AR_BTCOEX_CTRL_SPDT_ENABLE 0x00000001 -#define AR_BTCOEX_CTRL_SPDT_ENABLE_S 0 -#define AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL 0x00000002 -#define AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL_S 1 -#define AR_BTCOEX_CTRL_USE_LATCHED_BT_ANT 0x00000004 -#define AR_BTCOEX_CTRL_USE_LATCHED_BT_ANT_S 2 -#define AR_GLB_WLAN_UART_INTF_EN 0x00020000 -#define AR_GLB_WLAN_UART_INTF_EN_S 17 -#define AR_GLB_DS_JTAG_DISABLE 0x00040000 -#define AR_GLB_DS_JTAG_DISABLE_S 18 - -#define AR_BTCOEX_RC 0x194c -#define AR_BTCOEX_MAX_RFGAIN(_x) (0x1950 + ((_x) << 2)) -#define AR_BTCOEX_DBG 0x1a50 -#define AR_MCI_LAST_HW_MSG_HDR 0x1a54 -#define AR_MCI_LAST_HW_MSG_BDY 0x1a58 - -#define AR_MCI_SCHD_TABLE_2 0x1a5c -#define AR_MCI_SCHD_TABLE_2_MEM_BASED 0x00000001 -#define AR_MCI_SCHD_TABLE_2_MEM_BASED_S 0 -#define AR_MCI_SCHD_TABLE_2_HW_BASED 0x00000002 -#define AR_MCI_SCHD_TABLE_2_HW_BASED_S 1 - -#define AR_BTCOEX_CTRL3 0x1a60 -#define AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT 0x00000fff -#define AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT_S 0 - #endif diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c index 65c8894c..35422fc1 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.c +++ b/drivers/net/wireless/ath/ath9k/wmi.c @@ -187,7 +187,7 @@ void ath9k_fatal_work(struct work_struct *work) fatal_work); struct ath_common *common = ath9k_hw_common(priv->ah); - ath_dbg(common, FATAL, "FATAL Event received, resetting device\n"); + ath_dbg(common, ATH_DBG_FATAL, "FATAL Event received, resetting device\n"); ath9k_htc_reset(priv); } @@ -330,7 +330,8 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id, time_left = wait_for_completion_timeout(&wmi->cmd_wait, timeout); if (!time_left) { - ath_dbg(common, WMI, "Timeout waiting for WMI command: %s\n", + ath_dbg(common, ATH_DBG_WMI, + "Timeout waiting for WMI command: %s\n", wmi_cmd_to_name(cmd_id)); mutex_unlock(&wmi->op_mutex); return -ETIMEDOUT; @@ -341,7 +342,8 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id, return 0; out: - ath_dbg(common, WMI, "WMI failure for: %s\n", wmi_cmd_to_name(cmd_id)); + ath_dbg(common, ATH_DBG_WMI, + "WMI failure for: %s\n", wmi_cmd_to_name(cmd_id)); mutex_unlock(&wmi->op_mutex); kfree_skb(skb); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c old mode 100644 new mode 100755 index 1a5f275f..8f68e760 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -53,7 +53,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, int tx_flags, struct ath_txq *txq); static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, struct ath_txq *txq, struct list_head *bf_q, - struct ath_tx_status *ts, int txok); + struct ath_tx_status *ts, int txok, int sendbar); static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, struct list_head *head, bool internal); static void ath_tx_rc_status(struct ath_softc *sc, struct ath_buf *bf, @@ -104,32 +104,6 @@ static int ath_max_4ms_framelen[4][32] = { /* Aggregation logic */ /*********************/ -static void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq) - __acquires(&txq->axq_lock) -{ - spin_lock_bh(&txq->axq_lock); -} - -static void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq) - __releases(&txq->axq_lock) -{ - spin_unlock_bh(&txq->axq_lock); -} - -static void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq) - __releases(&txq->axq_lock) -{ - struct sk_buff_head q; - struct sk_buff *skb; - - __skb_queue_head_init(&q); - skb_queue_splice_init(&txq->complete_q, &q); - spin_unlock_bh(&txq->axq_lock); - - while ((skb = __skb_dequeue(&q))) - ieee80211_tx_status(sc->hw, skb); -} - static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid) { struct ath_atx_ac *ac = tid->ac; @@ -156,7 +130,7 @@ static void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid) WARN_ON(!tid->paused); - ath_txq_lock(sc, txq); + spin_lock_bh(&txq->axq_lock); tid->paused = false; if (skb_queue_empty(&tid->buf_q)) @@ -165,7 +139,7 @@ static void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid) ath_tx_queue_tid(txq, tid); ath_txq_schedule(sc, txq); unlock: - ath_txq_unlock_complete(sc, txq); + spin_unlock_bh(&txq->axq_lock); } static struct ath_frame_info *get_frame_info(struct sk_buff *skb) @@ -176,12 +150,6 @@ static struct ath_frame_info *get_frame_info(struct sk_buff *skb) return (struct ath_frame_info *) &tx_info->rate_driver_data[0]; } -static void ath_send_bar(struct ath_atx_tid *tid, u16 seqno) -{ - ieee80211_send_bar(tid->an->vif, tid->an->sta->addr, tid->tidno, - seqno << IEEE80211_SEQ_SEQ_SHIFT); -} - static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) { struct ath_txq *txq = tid->ac->txq; @@ -190,44 +158,28 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) struct list_head bf_head; struct ath_tx_status ts; struct ath_frame_info *fi; - bool sendbar = false; INIT_LIST_HEAD(&bf_head); memset(&ts, 0, sizeof(ts)); + spin_lock_bh(&txq->axq_lock); while ((skb = __skb_dequeue(&tid->buf_q))) { fi = get_frame_info(skb); bf = fi->bf; - if (!bf) { - bf = ath_tx_setup_buffer(sc, txq, tid, skb); - if (!bf) { - ieee80211_free_txskb(sc->hw, skb); - continue; - } - } - - if (fi->retries) { + spin_unlock_bh(&txq->axq_lock); + if (bf && fi->retries) { list_add_tail(&bf->list, &bf_head); ath_tx_update_baw(sc, tid, bf->bf_state.seqno); - ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); - sendbar = true; + ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 1); } else { ath_tx_send_normal(sc, txq, NULL, skb); } + spin_lock_bh(&txq->axq_lock); } - if (tid->baw_head == tid->baw_tail) { - tid->state &= ~AGGR_ADDBA_COMPLETE; - tid->state &= ~AGGR_CLEANUP; - } - - if (sendbar) { - ath_txq_unlock(sc, txq); - ath_send_bar(tid, tid->seq_start); - ath_txq_lock(sc, txq); - } + spin_unlock_bh(&txq->axq_lock); } static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, @@ -243,8 +195,6 @@ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, while (tid->baw_head != tid->baw_tail && !test_bit(tid->baw_head, tid->tx_buf)) { INCR(tid->seq_start, IEEE80211_SEQ_MAX); INCR(tid->baw_head, ATH_TID_MAX_BUFS); - if (tid->bar_index >= 0) - tid->bar_index--; } } @@ -288,7 +238,9 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq, bf = fi->bf; if (!bf) { + spin_unlock(&txq->axq_lock); ath_tx_complete(sc, skb, ATH_TX_ERROR, txq); + spin_lock(&txq->axq_lock); continue; } @@ -297,26 +249,24 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq, if (fi->retries) ath_tx_update_baw(sc, tid, bf->bf_state.seqno); - ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); + spin_unlock(&txq->axq_lock); + ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0); + spin_lock(&txq->axq_lock); } tid->seq_next = tid->seq_start; tid->baw_tail = tid->baw_head; - tid->bar_index = -1; } static void ath_tx_set_retry(struct ath_softc *sc, struct ath_txq *txq, - struct sk_buff *skb, int count) + struct sk_buff *skb) { struct ath_frame_info *fi = get_frame_info(skb); struct ath_buf *bf = fi->bf; struct ieee80211_hdr *hdr; - int prev = fi->retries; TX_STAT_INC(txq->axq_qnum, a_retries); - fi->retries += count; - - if (prev > 0) + if (fi->retries++ > 0) return; hdr = (struct ieee80211_hdr *)skb->data; @@ -416,7 +366,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf_next, *bf_last = bf->bf_lastbf; struct list_head bf_head; struct sk_buff_head bf_pending; - u16 seq_st = 0, acked_cnt = 0, txfail_cnt = 0, seq_first; + u16 seq_st = 0, acked_cnt = 0, txfail_cnt = 0; u32 ba[WME_BA_BMP_SIZE >> 5]; int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0; bool rc_update = true, isba; @@ -425,8 +375,6 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, int nframes; u8 tidno; bool flush = !!(ts->ts_status & ATH9K_TX_FLUSH); - int i, retries; - int bar_index = -1; skb = bf->bf_mpdu; hdr = (struct ieee80211_hdr *)skb->data; @@ -435,10 +383,6 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, memcpy(rates, tx_info->control.rates, sizeof(rates)); - retries = ts->ts_longretry + 1; - for (i = 0; i < ts->ts_rateindex; i++) - retries += rates[i].count; - rcu_read_lock(); sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr1, hdr->addr2); @@ -452,7 +396,8 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, if (!bf->bf_stale || bf_next != NULL) list_move_tail(&bf->list, &bf_head); - ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, 0); + ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, + 0, 0); bf = bf_next; } @@ -462,18 +407,13 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, an = (struct ath_node *)sta->drv_priv; tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; tid = ATH_AN_2_TID(an, tidno); - seq_first = tid->seq_start; - isba = ts->ts_flags & ATH9K_TX_BA; /* * The hardware occasionally sends a tx status for the wrong TID. * In this case, the BA status cannot be considered valid and all * subframes need to be retransmitted - * - * Only BlockAcks have a TID and therefore normal Acks cannot be - * checked */ - if (isba && tidno != ts->tid) + if (tidno != ts->tid) txok = false; isaggr = bf_isaggr(bf); @@ -516,25 +456,25 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, } else if (!isaggr && txok) { /* transmit completion */ acked_cnt++; - } else if ((tid->state & AGGR_CLEANUP) || !retry) { - /* - * cleanup in progress, just fail - * the un-acked sub-frames - */ - txfail = 1; - } else if (flush) { - txpending = 1; - } else if (fi->retries < ATH_MAX_SW_RETRIES) { - if (txok || !an->sleeping) - ath_tx_set_retry(sc, txq, bf->bf_mpdu, - retries); - - txpending = 1; } else { - txfail = 1; - txfail_cnt++; - bar_index = max_t(int, bar_index, - ATH_BA_INDEX(seq_first, seqno)); + if ((tid->state & AGGR_CLEANUP) || !retry) { + /* + * cleanup in progress, just fail + * the un-acked sub-frames + */ + txfail = 1; + } else if (flush) { + txpending = 1; + } else if (fi->retries < ATH_MAX_SW_RETRIES) { + if (txok || !an->sleeping) + ath_tx_set_retry(sc, txq, bf->bf_mpdu); + + txpending = 1; + } else { + txfail = 1; + sendbar = 1; + txfail_cnt++; + } } /* @@ -551,7 +491,9 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, * complete the acked-ones/xretried ones; update * block-ack window */ + spin_lock_bh(&txq->axq_lock); ath_tx_update_baw(sc, tid, seqno); + spin_unlock_bh(&txq->axq_lock); if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) { memcpy(tx_info->control.rates, rates, sizeof(rates)); @@ -560,30 +502,33 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, } ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, - !txfail); + !txfail, sendbar); } else { /* retry the un-acked ones */ - if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && - bf->bf_next == NULL && bf_last->bf_stale) { - struct ath_buf *tbf; - - tbf = ath_clone_txbuf(sc, bf_last); - /* - * Update tx baw and complete the - * frame with failed status if we - * run out of tx buf. - */ - if (!tbf) { - ath_tx_update_baw(sc, tid, seqno); - - ath_tx_complete_buf(sc, bf, txq, - &bf_head, ts, 0); - bar_index = max_t(int, bar_index, - ATH_BA_INDEX(seq_first, seqno)); - break; + if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)) { + if (bf->bf_next == NULL && bf_last->bf_stale) { + struct ath_buf *tbf; + + tbf = ath_clone_txbuf(sc, bf_last); + /* + * Update tx baw and complete the + * frame with failed status if we + * run out of tx buf. + */ + if (!tbf) { + spin_lock_bh(&txq->axq_lock); + ath_tx_update_baw(sc, tid, seqno); + spin_unlock_bh(&txq->axq_lock); + + ath_tx_complete_buf(sc, bf, txq, + &bf_head, + ts, 0, + !flush); + break; + } + + fi->bf = tbf; } - - fi->bf = tbf; } /* @@ -601,6 +546,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, if (an->sleeping) ieee80211_sta_set_buffered(sta, tid->tidno, true); + spin_lock_bh(&txq->axq_lock); skb_queue_splice(&bf_pending, &tid->buf_q); if (!an->sleeping) { ath_tx_queue_tid(txq, tid); @@ -608,22 +554,18 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, if (ts->ts_status & ATH9K_TXERR_FILT) tid->ac->clear_ps_filter = true; } + spin_unlock_bh(&txq->axq_lock); } - if (bar_index >= 0) { - u16 bar_seq = ATH_BA_INDEX2SEQ(seq_first, bar_index); - - if (BAW_WITHIN(tid->seq_start, tid->baw_size, bar_seq)) - tid->bar_index = ATH_BA_INDEX(tid->seq_start, bar_seq); + if (tid->state & AGGR_CLEANUP) { + ath_tx_flush_tid(sc, tid); - ath_txq_unlock(sc, txq); - ath_send_bar(tid, ATH_BA_INDEX2SEQ(seq_first, bar_index + 1)); - ath_txq_lock(sc, txq); + if (tid->baw_head == tid->baw_tail) { + tid->state &= ~AGGR_ADDBA_COMPLETE; + tid->state &= ~AGGR_CLEANUP; + } } - if (tid->state & AGGR_CLEANUP) - ath_tx_flush_tid(sc, tid); - rcu_read_unlock(); if (needreset) { @@ -660,8 +602,9 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, struct sk_buff *skb; struct ieee80211_tx_info *tx_info; struct ieee80211_tx_rate *rates; + struct ath_mci_profile *mci = &sc->btcoex.mci; u32 max_4ms_framelen, frmlen; - u16 aggr_limit, bt_aggr_limit, legacy = 0; + u16 aggr_limit, legacy = 0; int i; skb = bf->bf_mpdu; @@ -676,26 +619,24 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, max_4ms_framelen = ATH_AMPDU_LIMIT_MAX; for (i = 0; i < 4; i++) { - int modeidx; - - if (!rates[i].count) - continue; - - if (!(rates[i].flags & IEEE80211_TX_RC_MCS)) { - legacy = 1; - break; - } + if (rates[i].count) { + int modeidx; + if (!(rates[i].flags & IEEE80211_TX_RC_MCS)) { + legacy = 1; + break; + } - if (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - modeidx = MCS_HT40; - else - modeidx = MCS_HT20; + if (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + modeidx = MCS_HT40; + else + modeidx = MCS_HT20; - if (rates[i].flags & IEEE80211_TX_RC_SHORT_GI) - modeidx++; + if (rates[i].flags & IEEE80211_TX_RC_SHORT_GI) + modeidx++; - frmlen = ath_max_4ms_framelen[modeidx][rates[i].idx]; - max_4ms_framelen = min(max_4ms_framelen, frmlen); + frmlen = ath_max_4ms_framelen[modeidx][rates[i].idx]; + max_4ms_framelen = min(max_4ms_framelen, frmlen); + } } /* @@ -706,14 +647,14 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, if (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE || legacy) return 0; - aggr_limit = min(max_4ms_framelen, (u32)ATH_AMPDU_LIMIT_MAX); - - /* - * Override the default aggregation limit for BTCOEX. - */ - bt_aggr_limit = ath9k_btcoex_aggr_limit(sc, max_4ms_framelen); - if (bt_aggr_limit) - aggr_limit = bt_aggr_limit; + if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && mci->aggr_limit) + aggr_limit = (max_4ms_framelen * mci->aggr_limit) >> 4; + else if (sc->sc_flags & SC_OP_BT_PRIORITY_DETECTED) + aggr_limit = min((max_4ms_framelen * 3) / 8, + (u32)ATH_AMPDU_LIMIT_MAX); + else + aggr_limit = min(max_4ms_framelen, + (u32)ATH_AMPDU_LIMIT_MAX); /* * h/w can accept aggregates up to 16 bit lengths (65535). @@ -834,6 +775,8 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, bf->bf_state.bf_type = BUF_AMPDU | BUF_AGGR; seqno = bf->bf_state.seqno; + if (!bf_first) + bf_first = bf; /* do not step over block-ack window */ if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) { @@ -841,21 +784,6 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, break; } - if (tid->bar_index > ATH_BA_INDEX(tid->seq_start, seqno)) { - struct ath_tx_status ts = {}; - struct list_head bf_head; - - INIT_LIST_HEAD(&bf_head); - list_add(&bf->list, &bf_head); - __skb_unlink(skb, &tid->buf_q); - ath_tx_update_baw(sc, tid, seqno); - ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); - continue; - } - - if (!bf_first) - bf_first = bf; - if (!rl) { aggr_limit = ath_lookup_rate(sc, bf, tid); rl = 1; @@ -964,7 +892,16 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, /* set dur_update_en for l-sig computation except for PS-Poll frames */ info->dur_update = !ieee80211_is_pspoll(hdr->frame_control); - info->rtscts_rate = fi->rtscts_rate; + + /* + * We check if Short Preamble is needed for the CTS rate by + * checking the BSS's global flag. + * But for the rate series, IEEE80211_TX_RC_USE_SHORT_PREAMBLE is used. + */ + rate = ieee80211_get_rts_cts_rate(sc->hw, tx_info); + info->rtscts_rate = rate->hw_value; + if (sc->sc_flags & SC_OP_PREAMBLE_SHORT) + info->rtscts_rate |= rate->hw_value_short; for (i = 0; i < 4; i++) { bool is_40, is_sgi, is_sp; @@ -1190,7 +1127,6 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, txtid->state |= AGGR_ADDBA_PROGRESS; txtid->paused = true; *ssn = txtid->seq_start = txtid->seq_next; - txtid->bar_index = -1; memset(txtid->tx_buf, 0, sizeof(txtid->tx_buf)); txtid->baw_head = txtid->baw_tail = 0; @@ -1212,7 +1148,7 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) return; } - ath_txq_lock(sc, txq); + spin_lock_bh(&txq->axq_lock); txtid->paused = true; /* @@ -1225,9 +1161,9 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) txtid->state |= AGGR_CLEANUP; else txtid->state &= ~AGGR_ADDBA_COMPLETE; + spin_unlock_bh(&txq->axq_lock); ath_tx_flush_tid(sc, txtid); - ath_txq_unlock_complete(sc, txq); } void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, @@ -1242,15 +1178,13 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, for (tidno = 0, tid = &an->tid[tidno]; tidno < WME_NUM_TID; tidno++, tid++) { + if (!tid->sched) + continue; + ac = tid->ac; txq = ac->txq; - ath_txq_lock(sc, txq); - - if (!tid->sched) { - ath_txq_unlock(sc, txq); - continue; - } + spin_lock_bh(&txq->axq_lock); buffered = !skb_queue_empty(&tid->buf_q); @@ -1262,7 +1196,7 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, list_del(&ac->list); } - ath_txq_unlock(sc, txq); + spin_unlock_bh(&txq->axq_lock); ieee80211_sta_set_buffered(sta, tidno, buffered); } @@ -1281,7 +1215,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) ac = tid->ac; txq = ac->txq; - ath_txq_lock(sc, txq); + spin_lock_bh(&txq->axq_lock); ac->clear_ps_filter = true; if (!skb_queue_empty(&tid->buf_q) && !tid->paused) { @@ -1289,7 +1223,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) ath_txq_schedule(sc, txq); } - ath_txq_unlock_complete(sc, txq); + spin_unlock_bh(&txq->axq_lock); } } @@ -1300,11 +1234,14 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid an = (struct ath_node *)sta->drv_priv; - txtid = ATH_AN_2_TID(an, tid); - txtid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor; - txtid->state |= AGGR_ADDBA_COMPLETE; - txtid->state &= ~AGGR_ADDBA_PROGRESS; - ath_tx_resume_tid(sc, txtid); + if (sc->sc_flags & SC_OP_TXAGGR) { + txtid = ATH_AN_2_TID(an, tid); + txtid->baw_size = + IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor; + txtid->state |= AGGR_ADDBA_COMPLETE; + txtid->state &= ~AGGR_ADDBA_PROGRESS; + ath_tx_resume_tid(sc, txtid); + } } /********************/ @@ -1363,7 +1300,8 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) * based intr on the EOSP frames. */ if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { - qi.tqi_qflags = TXQ_FLAG_TXINT_ENABLE; + qi.tqi_qflags = TXQ_FLAG_TXOKINT_ENABLE | + TXQ_FLAG_TXERRINT_ENABLE; } else { if (qtype == ATH9K_TX_QUEUE_UAPSD) qi.tqi_qflags = TXQ_FLAG_TXDESCINT_ENABLE; @@ -1385,7 +1323,6 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) txq->axq_qnum = axq_qnum; txq->mac80211_qnum = -1; txq->axq_link = NULL; - __skb_queue_head_init(&txq->complete_q); INIT_LIST_HEAD(&txq->axq_q); INIT_LIST_HEAD(&txq->axq_acq); spin_lock_init(&txq->axq_lock); @@ -1468,6 +1405,8 @@ static bool bf_is_ampdu_not_probing(struct ath_buf *bf) static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq, struct list_head *list, bool retry_tx) + __releases(txq->axq_lock) + __acquires(txq->axq_lock) { struct ath_buf *bf, *lastbf; struct list_head bf_head; @@ -1494,11 +1433,13 @@ static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq, if (bf_is_ampdu_not_probing(bf)) txq->axq_ampdu_depth--; + spin_unlock_bh(&txq->axq_lock); if (bf_isampdu(bf)) ath_tx_complete_aggr(sc, txq, bf, &bf_head, &ts, 0, retry_tx); else - ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); + ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0); + spin_lock_bh(&txq->axq_lock); } } @@ -1510,8 +1451,7 @@ static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq, */ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx) { - ath_txq_lock(sc, txq); - + spin_lock_bh(&txq->axq_lock); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { int idx = txq->txq_tailidx; @@ -1529,10 +1469,10 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx) ath_drain_txq_list(sc, txq, &txq->axq_q, retry_tx); /* flush any pending frames if aggregation is enabled */ - if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) && !retry_tx) + if ((sc->sc_flags & SC_OP_TXAGGR) && !retry_tx) ath_txq_drain_pending_buffers(sc, txq); - ath_txq_unlock_complete(sc, txq); + spin_unlock_bh(&txq->axq_lock); } bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) @@ -1626,9 +1566,11 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) break; } - if (!list_empty(&ac->tid_q) && !ac->sched) { - ac->sched = true; - list_add_tail(&ac->list, &txq->axq_acq); + if (!list_empty(&ac->tid_q)) { + if (!ac->sched) { + ac->sched = true; + list_add_tail(&ac->list, &txq->axq_acq); + } } if (ac == last_ac || @@ -1666,8 +1608,8 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, bf = list_first_entry(head, struct ath_buf, list); bf_last = list_entry(head->prev, struct ath_buf, list); - ath_dbg(common, QUEUE, "qnum: %d, txq depth: %d\n", - txq->axq_qnum, txq->axq_depth); + ath_dbg(common, ATH_DBG_QUEUE, + "qnum: %d, txq depth: %d\n", txq->axq_qnum, txq->axq_depth); if (edma && list_empty(&txq->txq_fifo[txq->txq_headidx])) { list_splice_tail_init(head, &txq->txq_fifo[txq->txq_headidx]); @@ -1678,7 +1620,8 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, if (txq->axq_link) { ath9k_hw_set_desc_link(ah, txq->axq_link, bf->bf_daddr); - ath_dbg(common, XMIT, "link[%u] (%p)=%llx (%p)\n", + ath_dbg(common, ATH_DBG_XMIT, + "link[%u] (%p)=%llx (%p)\n", txq->axq_qnum, txq->axq_link, ito64(bf->bf_daddr), bf->bf_desc); } else if (!edma) @@ -1690,7 +1633,7 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, if (puttxbuf) { TX_STAT_INC(txq->axq_qnum, puttxbuf); ath9k_hw_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); - ath_dbg(common, XMIT, "TXDP[%u] = %llx (%p)\n", + ath_dbg(common, ATH_DBG_XMIT, "TXDP[%u] = %llx (%p)\n", txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc); } @@ -1762,12 +1705,20 @@ static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf; bf = fi->bf; + if (!bf) + bf = ath_tx_setup_buffer(sc, txq, tid, skb); + + if (!bf) + return; INIT_LIST_HEAD(&bf_head); list_add_tail(&bf->list, &bf_head); bf->bf_state.bf_type = 0; - bf->bf_next = NULL; + /* update starting sequence number for subsequent ADDBA request */ + if (tid) + INCR(tid->seq_start, IEEE80211_SEQ_MAX); + bf->bf_lastbf = bf; ath_tx_fill_desc(sc, bf, txq, fi->framelen); ath_tx_txqaddbuf(sc, txq, &bf_head, false); @@ -1841,28 +1792,20 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, struct ath_frame_info *fi = get_frame_info(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ath_buf *bf; - int fragno; u16 seqno; bf = ath_tx_get_buffer(sc); if (!bf) { - ath_dbg(common, XMIT, "TX buffers are full\n"); - return NULL; + ath_dbg(common, ATH_DBG_XMIT, "TX buffers are full\n"); + goto error; } ATH_TXBUF_RESET(bf); if (tid) { - fragno = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; seqno = tid->seq_next; hdr->seq_ctrl = cpu_to_le16(tid->seq_next << IEEE80211_SEQ_SEQ_SHIFT); - - if (fragno) - hdr->seq_ctrl |= cpu_to_le16(fragno); - - if (!ieee80211_has_morefrags(hdr->frame_control)) - INCR(tid->seq_next, IEEE80211_SEQ_MAX); - + INCR(tid->seq_next, IEEE80211_SEQ_MAX); bf->bf_state.seqno = seqno; } @@ -1894,7 +1837,8 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct sk_buff *skb, struct ath_buf *bf; u8 tidno; - if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) && txctl->an && + spin_lock_bh(&txctl->txq->axq_lock); + if ((sc->sc_flags & SC_OP_TXAGGR) && txctl->an && ieee80211_is_data_qos(hdr->frame_control)) { tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; @@ -1911,13 +1855,8 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct sk_buff *skb, ath_tx_send_ampdu(sc, tid, skb, txctl); } else { bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb); - if (!bf) { - if (txctl->paprd) - dev_kfree_skb_any(skb); - else - ieee80211_free_txskb(sc->hw, skb); - return; - } + if (!bf) + goto out; bf->bf_state.bfs_paprd = txctl->paprd; @@ -1926,6 +1865,9 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct sk_buff *skb, ath_tx_send_normal(sc, txctl->txq, tid, skb); } + +out: + spin_unlock_bh(&txctl->txq->axq_lock); } /* Upon failure caller should free skb */ @@ -1986,18 +1928,15 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, */ q = skb_get_queue_mapping(skb); - - ath_txq_lock(sc, txq); + spin_lock_bh(&txq->axq_lock); if (txq == sc->tx.txq_map[q] && ++txq->pending_frames > ATH_MAX_QDEPTH && !txq->stopped) { ieee80211_stop_queue(sc->hw, q); - txq->stopped = true; + txq->stopped = 1; } + spin_unlock_bh(&txq->axq_lock); ath_tx_start_dma(sc, skb, txctl); - - ath_txq_unlock(sc, txq); - return 0; } @@ -2008,12 +1947,16 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, int tx_flags, struct ath_txq *txq) { + struct ieee80211_hw *hw = sc->hw; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data; int q, padpos, padsize; - ath_dbg(common, XMIT, "TX complete: skb: %p\n", skb); + ath_dbg(common, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb); + + if (tx_flags & ATH_TX_BAR) + tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; if (!(tx_flags & ATH_TX_ERROR)) /* Frame was ACKed */ @@ -2030,9 +1973,9 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, skb_pull(skb, padsize); } - if ((sc->ps_flags & PS_WAIT_FOR_TX_ACK) && !txq->axq_depth) { + if (sc->ps_flags & PS_WAIT_FOR_TX_ACK) { sc->ps_flags &= ~PS_WAIT_FOR_TX_ACK; - ath_dbg(common, PS, + ath_dbg(common, ATH_DBG_PS, "Going back to sleep after having received TX status (0x%lx)\n", sc->ps_flags & (PS_WAIT_FOR_BEACON | PS_WAIT_FOR_CAB | @@ -2042,27 +1985,32 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, q = skb_get_queue_mapping(skb); if (txq == sc->tx.txq_map[q]) { + spin_lock_bh(&txq->axq_lock); if (WARN_ON(--txq->pending_frames < 0)) txq->pending_frames = 0; if (txq->stopped && txq->pending_frames < ATH_MAX_QDEPTH) { ieee80211_wake_queue(sc->hw, q); - txq->stopped = false; + txq->stopped = 0; } + spin_unlock_bh(&txq->axq_lock); } - __skb_queue_tail(&txq->complete_q, skb); + ieee80211_tx_status(hw, skb); } static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, struct ath_txq *txq, struct list_head *bf_q, - struct ath_tx_status *ts, int txok) + struct ath_tx_status *ts, int txok, int sendbar) { struct sk_buff *skb = bf->bf_mpdu; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); unsigned long flags; int tx_flags = 0; + if (sendbar) + tx_flags = ATH_TX_BAR; + if (!txok) tx_flags |= ATH_TX_ERROR; @@ -2154,6 +2102,8 @@ static void ath_tx_rc_status(struct ath_softc *sc, struct ath_buf *bf, static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq, struct ath_tx_status *ts, struct ath_buf *bf, struct list_head *bf_head) + __releases(txq->axq_lock) + __acquires(txq->axq_lock) { int txok; @@ -2163,13 +2113,17 @@ static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq, if (bf_is_ampdu_not_probing(bf)) txq->axq_ampdu_depth--; + spin_unlock_bh(&txq->axq_lock); + if (!bf_isampdu(bf)) { ath_tx_rc_status(sc, bf, ts, 1, txok ? 0 : 1, txok); - ath_tx_complete_buf(sc, bf, txq, bf_head, ts, txok); + ath_tx_complete_buf(sc, bf, txq, bf_head, ts, txok, 0); } else ath_tx_complete_aggr(sc, txq, bf, bf_head, ts, txok, true); - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) + spin_lock_bh(&txq->axq_lock); + + if (sc->sc_flags & SC_OP_TXAGGR) ath_txq_schedule(sc, txq); } @@ -2183,18 +2137,18 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) struct ath_tx_status ts; int status; - ath_dbg(common, QUEUE, "tx queue %d (%x), link %p\n", + ath_dbg(common, ATH_DBG_QUEUE, "tx queue %d (%x), link %p\n", txq->axq_qnum, ath9k_hw_gettxbuf(sc->sc_ah, txq->axq_qnum), txq->axq_link); - ath_txq_lock(sc, txq); + spin_lock_bh(&txq->axq_lock); for (;;) { if (work_pending(&sc->hw_reset_work)) break; if (list_empty(&txq->axq_q)) { txq->axq_link = NULL; - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) + if (sc->sc_flags & SC_OP_TXAGGR) ath_txq_schedule(sc, txq); break; } @@ -2246,7 +2200,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head); } - ath_txq_unlock_complete(sc, txq); + spin_unlock_bh(&txq->axq_lock); } static void ath_tx_complete_poll_work(struct work_struct *work) @@ -2263,21 +2217,21 @@ static void ath_tx_complete_poll_work(struct work_struct *work) for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) { txq = &sc->tx.txq[i]; - ath_txq_lock(sc, txq); + spin_lock_bh(&txq->axq_lock); if (txq->axq_depth) { if (txq->axq_tx_inprogress) { needreset = true; - ath_txq_unlock(sc, txq); + spin_unlock_bh(&txq->axq_lock); break; } else { txq->axq_tx_inprogress = true; } } - ath_txq_unlock_complete(sc, txq); + spin_unlock_bh(&txq->axq_lock); } if (needreset) { - ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, + ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_RESET, "tx hung, resetting the chip\n"); RESET_STAT_INC(sc, RESET_TYPE_TX_HANG); ieee80211_queue_work(sc->hw, &sc->hw_reset_work); @@ -2291,9 +2245,10 @@ static void ath_tx_complete_poll_work(struct work_struct *work) void ath_tx_tasklet(struct ath_softc *sc) { - struct ath_hw *ah = sc->sc_ah; - u32 qcumask = ((1 << ATH9K_NUM_TX_QUEUES) - 1) & ah->intr_txqs; int i; + u32 qcumask = ((1 << ATH9K_NUM_TX_QUEUES) - 1); + + ath9k_hw_gettxintrtxqs(sc->sc_ah, &qcumask); for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { if (ATH_TXQ_SETUP(sc, i) && (qcumask & (1 << i))) @@ -2319,23 +2274,21 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) if (status == -EINPROGRESS) break; if (status == -EIO) { - ath_dbg(common, XMIT, "Error processing tx status\n"); + ath_dbg(common, ATH_DBG_XMIT, + "Error processing tx status\n"); break; } - /* Process beacon completions separately */ - if (ts.qid == sc->beacon.beaconq) { - sc->beacon.tx_processed = true; - sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK); + /* Skip beacon completions */ + if (ts.qid == sc->beacon.beaconq) continue; - } txq = &sc->tx.txq[ts.qid]; - ath_txq_lock(sc, txq); + spin_lock_bh(&txq->axq_lock); if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) { - ath_txq_unlock(sc, txq); + spin_unlock_bh(&txq->axq_lock); return; } @@ -2361,7 +2314,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) } ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head); - ath_txq_unlock_complete(sc, txq); + spin_unlock_bh(&txq->axq_lock); } } @@ -2500,7 +2453,7 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) ac = tid->ac; txq = ac->txq; - ath_txq_lock(sc, txq); + spin_lock_bh(&txq->axq_lock); if (tid->sched) { list_del(&tid->list); @@ -2516,6 +2469,6 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) tid->state &= ~AGGR_ADDBA_COMPLETE; tid->state &= ~AGGR_CLEANUP; - ath_txq_unlock(sc, txq); + spin_unlock_bh(&txq->axq_lock); } } diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h index 0cea20e3..6cfbb419 100644 --- a/drivers/net/wireless/ath/carl9170/carl9170.h +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -559,7 +559,6 @@ int carl9170_set_hwretry_limit(struct ar9170 *ar, const u32 max_retry); int carl9170_upload_key(struct ar9170 *ar, const u8 id, const u8 *mac, const u8 ktype, const u8 keyidx, const u8 *keydata, const int keylen); int carl9170_disable_key(struct ar9170 *ar, const u8 id); -int carl9170_set_mac_tpc(struct ar9170 *ar, struct ieee80211_channel *channel); /* RX */ void carl9170_rx(struct ar9170 *ar, void *buf, unsigned int len); @@ -594,6 +593,7 @@ int carl9170_get_noisefloor(struct ar9170 *ar); /* FW */ int carl9170_parse_firmware(struct ar9170 *ar); +int carl9170_fw_fix_eeprom(struct ar9170 *ar); extern struct ieee80211_rate __carl9170_ratetable[]; extern int modparam_noht; diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c index 93fe6003..de57f90e 100644 --- a/drivers/net/wireless/ath/carl9170/debug.c +++ b/drivers/net/wireless/ath/carl9170/debug.c @@ -48,10 +48,15 @@ #define ADD(buf, off, max, fmt, args...) \ off += snprintf(&buf[off], max - off, fmt, ##args); +static int carl9170_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} struct carl9170_debugfs_fops { unsigned int read_bufsize; - umode_t attr; + mode_t attr; char *(*read)(struct ar9170 *ar, char *buf, size_t bufsize, ssize_t *len); ssize_t (*write)(struct ar9170 *aru, const char *buf, size_t size); @@ -173,7 +178,7 @@ static const struct carl9170_debugfs_fops carl_debugfs_##name ##_ops = {\ .attr = _attr, \ .req_dev_state = _dstate, \ .fops = { \ - .open = simple_open, \ + .open = carl9170_debugfs_open, \ .read = carl9170_debugfs_read, \ .write = carl9170_debugfs_write, \ .owner = THIS_MODULE \ diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c index cffde8d9..cba9d043 100644 --- a/drivers/net/wireless/ath/carl9170/fw.c +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -146,15 +146,13 @@ static bool valid_cpu_addr(const u32 address) return false; } -static int carl9170_fw_checksum(struct ar9170 *ar, const __u8 *data, - size_t len) +static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) { const struct carl9170fw_otus_desc *otus_desc; - const struct carl9170fw_last_desc *last_desc; const struct carl9170fw_chk_desc *chk_desc; - unsigned long fin, diff; - unsigned int dsc_len; - u32 crc32; + const struct carl9170fw_last_desc *last_desc; + const struct carl9170fw_txsq_desc *txsq_desc; + u16 if_comb_types; last_desc = carl9170_fw_find_desc(ar, LAST_MAGIC, sizeof(*last_desc), CARL9170FW_LAST_DESC_CUR_VER); @@ -172,68 +170,36 @@ static int carl9170_fw_checksum(struct ar9170 *ar, const __u8 *data, chk_desc = carl9170_fw_find_desc(ar, CHK_MAGIC, sizeof(*chk_desc), CARL9170FW_CHK_DESC_CUR_VER); - if (!chk_desc) { - dev_warn(&ar->udev->dev, "Unprotected firmware image.\n"); - return 0; - } + if (chk_desc) { + unsigned long fin, diff; + unsigned int dsc_len; + u32 crc32; - dsc_len = min_t(unsigned int, len, + dsc_len = min_t(unsigned int, len, (unsigned long)chk_desc - (unsigned long)otus_desc); - fin = (unsigned long) last_desc + sizeof(*last_desc); - diff = fin - (unsigned long) otus_desc; + fin = (unsigned long) last_desc + sizeof(*last_desc); + diff = fin - (unsigned long) otus_desc; - if (diff < len) - len -= diff; + if (diff < len) + len -= diff; - if (len < 256) - return -EIO; + if (len < 256) + return -EIO; - crc32 = crc32_le(~0, data, len); - if (cpu_to_le32(crc32) != chk_desc->fw_crc32) { - dev_err(&ar->udev->dev, "fw checksum test failed.\n"); - return -ENOEXEC; - } - - crc32 = crc32_le(crc32, (void *)otus_desc, dsc_len); - if (cpu_to_le32(crc32) != chk_desc->hdr_crc32) { - dev_err(&ar->udev->dev, "descriptor check failed.\n"); - return -EINVAL; - } - return 0; -} - -static int carl9170_fw_tx_sequence(struct ar9170 *ar) -{ - const struct carl9170fw_txsq_desc *txsq_desc; + crc32 = crc32_le(~0, data, len); + if (cpu_to_le32(crc32) != chk_desc->fw_crc32) { + dev_err(&ar->udev->dev, "fw checksum test failed.\n"); + return -ENOEXEC; + } - txsq_desc = carl9170_fw_find_desc(ar, TXSQ_MAGIC, sizeof(*txsq_desc), - CARL9170FW_TXSQ_DESC_CUR_VER); - if (txsq_desc) { - ar->fw.tx_seq_table = le32_to_cpu(txsq_desc->seq_table_addr); - if (!valid_cpu_addr(ar->fw.tx_seq_table)) + crc32 = crc32_le(crc32, (void *)otus_desc, dsc_len); + if (cpu_to_le32(crc32) != chk_desc->hdr_crc32) { + dev_err(&ar->udev->dev, "descriptor check failed.\n"); return -EINVAL; + } } else { - ar->fw.tx_seq_table = 0; - } - - return 0; -} - -static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) -{ - const struct carl9170fw_otus_desc *otus_desc; - int err; - u16 if_comb_types; - - err = carl9170_fw_checksum(ar, data, len); - if (err) - return err; - - otus_desc = carl9170_fw_find_desc(ar, OTUS_MAGIC, - sizeof(*otus_desc), CARL9170FW_OTUS_DESC_CUR_VER); - if (!otus_desc) { - return -ENODATA; + dev_warn(&ar->udev->dev, "Unprotected firmware image.\n"); } #define SUPP(feat) \ @@ -355,8 +321,19 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) ar->hw->wiphy->interface_modes |= if_comb_types; + txsq_desc = carl9170_fw_find_desc(ar, TXSQ_MAGIC, + sizeof(*txsq_desc), CARL9170FW_TXSQ_DESC_CUR_VER); + + if (txsq_desc) { + ar->fw.tx_seq_table = le32_to_cpu(txsq_desc->seq_table_addr); + if (!valid_cpu_addr(ar->fw.tx_seq_table)) + return -EINVAL; + } else { + ar->fw.tx_seq_table = 0; + } + #undef SUPPORTED - return carl9170_fw_tx_sequence(ar); + return 0; } static struct carl9170fw_desc_head * @@ -389,6 +366,39 @@ carl9170_find_fw_desc(struct ar9170 *ar, const __u8 *fw_data, const size_t len) return (void *)&fw_data[scan - found]; } +int carl9170_fw_fix_eeprom(struct ar9170 *ar) +{ + const struct carl9170fw_fix_desc *fix_desc = NULL; + unsigned int i, n, off; + u32 *data = (void *)&ar->eeprom; + + fix_desc = carl9170_fw_find_desc(ar, FIX_MAGIC, + sizeof(*fix_desc), CARL9170FW_FIX_DESC_CUR_VER); + + if (!fix_desc) + return 0; + + n = (le16_to_cpu(fix_desc->head.length) - sizeof(*fix_desc)) / + sizeof(struct carl9170fw_fix_entry); + + for (i = 0; i < n; i++) { + off = le32_to_cpu(fix_desc->data[i].address) - + AR9170_EEPROM_START; + + if (off >= sizeof(struct ar9170_eeprom) || (off & 3)) { + dev_err(&ar->udev->dev, "Skip invalid entry %d\n", i); + continue; + } + + data[off / sizeof(*data)] &= + le32_to_cpu(fix_desc->data[i].mask); + data[off / sizeof(*data)] |= + le32_to_cpu(fix_desc->data[i].value); + } + + return 0; +} + int carl9170_parse_firmware(struct ar9170 *ar) { const struct carl9170fw_desc_head *fw_desc = NULL; diff --git a/drivers/net/wireless/ath/carl9170/mac.c b/drivers/net/wireless/ath/carl9170/mac.c index 53415bfd..dfda9197 100644 --- a/drivers/net/wireless/ath/carl9170/mac.c +++ b/drivers/net/wireless/ath/carl9170/mac.c @@ -485,38 +485,3 @@ int carl9170_disable_key(struct ar9170 *ar, const u8 id) return carl9170_exec_cmd(ar, CARL9170_CMD_DKEY, sizeof(key), (u8 *)&key, 0, NULL); } - -int carl9170_set_mac_tpc(struct ar9170 *ar, struct ieee80211_channel *channel) -{ - unsigned int power, chains; - - if (ar->eeprom.tx_mask != 1) - chains = AR9170_TX_PHY_TXCHAIN_2; - else - chains = AR9170_TX_PHY_TXCHAIN_1; - - switch (channel->band) { - case IEEE80211_BAND_2GHZ: - power = ar->power_2G_ofdm[0] & 0x3f; - break; - case IEEE80211_BAND_5GHZ: - power = ar->power_5G_leg[0] & 0x3f; - break; - default: - BUG_ON(1); - } - - power = min_t(unsigned int, power, ar->hw->conf.power_level * 2); - - carl9170_regwrite_begin(ar); - carl9170_regwrite(AR9170_MAC_REG_ACK_TPC, - 0x3c1e | power << 20 | chains << 26); - carl9170_regwrite(AR9170_MAC_REG_RTS_CTS_TPC, - power << 5 | chains << 11 | - power << 21 | chains << 27); - carl9170_regwrite(AR9170_MAC_REG_CFEND_QOSNULL_TPC, - power << 5 | chains << 11 | - power << 21 | chains << 27); - carl9170_regwrite_finish(); - return carl9170_regwrite_result(); -} diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 8d2523b3..e24a4b7b 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -48,7 +48,7 @@ #include "carl9170.h" #include "cmd.h" -static bool modparam_nohwcrypt; +static int modparam_nohwcrypt; module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); MODULE_PARM_DESC(nohwcrypt, "Disable hardware crypto offload."); @@ -446,7 +446,7 @@ static void carl9170_op_stop(struct ieee80211_hw *hw) mutex_lock(&ar->mutex); if (IS_ACCEPTING_CMD(ar)) { - RCU_INIT_POINTER(ar->beacon_iter, NULL); + rcu_assign_pointer(ar->beacon_iter, NULL); carl9170_led_set_state(ar, 0); @@ -678,7 +678,7 @@ static int carl9170_op_add_interface(struct ieee80211_hw *hw, vif_priv->active = false; bitmap_release_region(&ar->vif_bitmap, vif_id, 0); ar->vifs--; - RCU_INIT_POINTER(ar->vif_priv[vif_id].vif, NULL); + rcu_assign_pointer(ar->vif_priv[vif_id].vif, NULL); list_del_rcu(&vif_priv->list); mutex_unlock(&ar->mutex); synchronize_rcu(); @@ -716,7 +716,7 @@ static void carl9170_op_remove_interface(struct ieee80211_hw *hw, WARN_ON(vif_priv->enable_beacon); vif_priv->enable_beacon = false; list_del_rcu(&vif_priv->list); - RCU_INIT_POINTER(ar->vif_priv[id].vif, NULL); + rcu_assign_pointer(ar->vif_priv[id].vif, NULL); if (vif == main_vif) { rcu_read_unlock(); @@ -853,6 +853,11 @@ static int carl9170_op_config(struct ieee80211_hw *hw, u32 changed) goto out; } + if (changed & IEEE80211_CONF_CHANGE_POWER) { + /* TODO */ + err = 0; + } + if (changed & IEEE80211_CONF_CHANGE_SMPS) { /* TODO */ err = 0; @@ -886,28 +891,40 @@ static int carl9170_op_config(struct ieee80211_hw *hw, u32 changed) goto out; } - if (changed & IEEE80211_CONF_CHANGE_POWER) { - err = carl9170_set_mac_tpc(ar, ar->hw->conf.channel); - if (err) - goto out; - } - out: mutex_unlock(&ar->mutex); return err; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) static u64 carl9170_op_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list) +#else +static u64 carl9170_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, + struct dev_addr_list *ha) +#endif { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) struct netdev_hw_addr *ha; +#else + int i; +#endif u64 mchash; /* always get broadcast frames */ mchash = 1ULL << (0xff >> 2); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) netdev_hw_addr_list_for_each(ha, mc_list) mchash |= 1ULL << (ha->addr[5] >> 2); +#else + for (i = 0; i < mc_count; i++) { + if (WARN_ON(!ha)) + break; + mchash |= 1ULL << (ha->dmi_addr[5] >> 2); + ha = ha->next; + } +#endif return mchash; } @@ -1259,7 +1276,7 @@ static int carl9170_op_sta_add(struct ieee80211_hw *hw, } for (i = 0; i < CARL9170_NUM_TID; i++) - RCU_INIT_POINTER(sta_info->agg[i], NULL); + rcu_assign_pointer(sta_info->agg[i], NULL); sta_info->ampdu_max_len = 1 << (3 + sta->ht_cap.ampdu_factor); sta_info->ht_sta = true; @@ -1286,7 +1303,7 @@ static int carl9170_op_sta_remove(struct ieee80211_hw *hw, struct carl9170_sta_tid *tid_info; tid_info = rcu_dereference(sta_info->agg[i]); - RCU_INIT_POINTER(sta_info->agg[i], NULL); + rcu_assign_pointer(sta_info->agg[i], NULL); if (!tid_info) continue; @@ -1399,7 +1416,7 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, spin_unlock_bh(&ar->tx_ampdu_list_lock); } - RCU_INIT_POINTER(sta_info->agg[tid], NULL); + rcu_assign_pointer(sta_info->agg[tid], NULL); rcu_read_unlock(); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); @@ -1797,9 +1814,6 @@ void *carl9170_alloc(size_t priv_size) ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */ hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - /* As IBSS Encryption is software-based, IBSS RSN is supported. */ - hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; return ar; err_nomem: @@ -1935,6 +1949,10 @@ int carl9170_register(struct ar9170 *ar) if (err) return err; + err = carl9170_fw_fix_eeprom(ar); + if (err) + return err; + err = carl9170_parse_eeprom(ar); if (err) return err; diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c index b72c09cf..472efc7e 100644 --- a/drivers/net/wireless/ath/carl9170/phy.c +++ b/drivers/net/wireless/ath/carl9170/phy.c @@ -1426,15 +1426,15 @@ static void carl9170_calc_ctl(struct ar9170 *ar, u32 freq, enum carl9170_bw bw) #undef EDGES } -static void carl9170_set_power_cal(struct ar9170 *ar, u32 freq, - enum carl9170_bw bw) +static int carl9170_set_power_cal(struct ar9170 *ar, u32 freq, + enum carl9170_bw bw) { struct ar9170_calibration_target_power_legacy *ctpl; struct ar9170_calibration_target_power_ht *ctph; u8 *ctpres; int ntargets; int idx, i, n; - u8 f; + u8 ackpower, ackchains, f; u8 pwr_freqs[AR5416_MAX_NUM_TGT_PWRS]; if (freq < 3000) @@ -1523,6 +1523,32 @@ static void carl9170_set_power_cal(struct ar9170 *ar, u32 freq, /* calc. conformance test limits and apply to ar->power*[] */ carl9170_calc_ctl(ar, freq, bw); + + /* set ACK/CTS TX power */ + carl9170_regwrite_begin(ar); + + if (ar->eeprom.tx_mask != 1) + ackchains = AR9170_TX_PHY_TXCHAIN_2; + else + ackchains = AR9170_TX_PHY_TXCHAIN_1; + + if (freq < 3000) + ackpower = ar->power_2G_ofdm[0] & 0x3f; + else + ackpower = ar->power_5G_leg[0] & 0x3f; + + carl9170_regwrite(AR9170_MAC_REG_ACK_TPC, + 0x3c1e | ackpower << 20 | ackchains << 26); + carl9170_regwrite(AR9170_MAC_REG_RTS_CTS_TPC, + ackpower << 5 | ackchains << 11 | + ackpower << 21 | ackchains << 27); + + carl9170_regwrite(AR9170_MAC_REG_CFEND_QOSNULL_TPC, + ackpower << 5 | ackchains << 11 | + ackpower << 21 | ackchains << 27); + + carl9170_regwrite_finish(); + return carl9170_regwrite_result(); } int carl9170_get_noisefloor(struct ar9170 *ar) @@ -1686,9 +1712,7 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, if (err) return err; - carl9170_set_power_cal(ar, channel->center_freq, bw); - - err = carl9170_set_mac_tpc(ar, channel); + err = carl9170_set_power_cal(ar, channel->center_freq, bw); if (err) return err; diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index aed30517..59472e16 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -314,7 +314,7 @@ static void carl9170_tx_release(struct kref *ref) * feedback either [CTL_REQ_TX_STATUS not set] */ - ieee80211_free_txskb(ar->hw, skb); + dev_kfree_skb_any(skb); return; } else { /* @@ -719,8 +719,6 @@ static void carl9170_tx_rate_tpc_chains(struct ar9170 *ar, else *chains = AR9170_TX_PHY_TXCHAIN_2; } - - *tpc = min_t(unsigned int, *tpc, ar->hw->conf.power_level * 2); } static __le32 carl9170_tx_physet(struct ar9170 *ar, @@ -1236,7 +1234,6 @@ static bool carl9170_tx_ps_drop(struct ar9170 *ar, struct sk_buff *skb) { struct ieee80211_sta *sta; struct carl9170_sta_info *sta_info; - struct ieee80211_tx_info *tx_info; rcu_read_lock(); sta = __carl9170_get_tx_sta(ar, skb); @@ -1244,18 +1241,16 @@ static bool carl9170_tx_ps_drop(struct ar9170 *ar, struct sk_buff *skb) goto out_rcu; sta_info = (void *) sta->drv_priv; - tx_info = IEEE80211_SKB_CB(skb); + if (unlikely(sta_info->sleeping)) { + struct ieee80211_tx_info *tx_info; - if (unlikely(sta_info->sleeping) && - !(tx_info->flags & (IEEE80211_TX_CTL_NO_PS_BUFFER | - IEEE80211_TX_CTL_CLEAR_PS_FILT))) { rcu_read_unlock(); + tx_info = IEEE80211_SKB_CB(skb); if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) atomic_dec(&ar->tx_ampdu_upload); tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; - carl9170_release_dev_space(ar, skb); carl9170_tx_status(ar, skb, false); return true; } @@ -1437,7 +1432,7 @@ void carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) err_free: ar->tx_dropped++; - ieee80211_free_txskb(ar->hw, skb); + dev_kfree_skb_any(skb); } void carl9170_tx_scheduler(struct ar9170 *ar) diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c index 89821e48..333b69ef 100644 --- a/drivers/net/wireless/ath/carl9170/usb.c +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -1161,4 +1161,15 @@ static struct usb_driver carl9170_driver = { #endif /* CONFIG_PM */ }; -module_usb_driver(carl9170_driver); +static int __init carl9170_usb_init(void) +{ + return usb_register(&carl9170_driver); +} + +static void __exit carl9170_usb_exit(void) +{ + usb_deregister(&carl9170_driver); +} + +module_init(carl9170_usb_init); +module_exit(carl9170_usb_exit); diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c old mode 100644 new mode 100755 diff --git a/drivers/power/msm_battery.c b/drivers/power/msm_battery.c index 801d01e0..df0297cd 100644 --- a/drivers/power/msm_battery.c +++ b/drivers/power/msm_battery.c @@ -68,9 +68,9 @@ #define MSM_BATT_POLLING_TIME (10 * HZ) -#define BATTERY_HIGH 4200 -#define BATTERY_LOW 3500 -#define BATTERY_ULTRA_LOW 3400 +#define BATTERY_HIGH 4300 +#define BATTERY_LOW 3400 +#define BATTERY_ULTRA_LOW 3200 #define TEMPERATURE_HOT 350 #define TEMPERATURE_COLD 50 diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index d7be4960..2fca70ce 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -82,14 +82,6 @@ source "drivers/staging/iio/Kconfig" source "drivers/staging/snappy/Kconfig" -source "drivers/staging/zram/Kconfig" - -source "drivers/staging/zcache/Kconfig" - -source "drivers/staging/qcache/Kconfig" - -source "drivers/staging/zsmalloc/Kconfig" - source "drivers/staging/wlags49_h2/Kconfig" source "drivers/staging/wlags49_h25/Kconfig" @@ -100,6 +92,8 @@ source "drivers/staging/crystalhd/Kconfig" source "drivers/staging/cxt1e1/Kconfig" +source "drivers/staging/zcache/Kconfig" + source "drivers/staging/xgifb/Kconfig" source "drivers/staging/tidspbridge/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 150a7ce8..ab64895f 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -34,10 +34,7 @@ obj-$(CONFIG_DX_SEP) += sep/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_SNAPPY_COMPRESS) += snappy/ obj-$(CONFIG_SNAPPY_DECOMPRESS) += snappy/ -obj-$(CONFIG_ZRAM) += zram/ obj-$(CONFIG_ZCACHE) += zcache/ -obj-$(CONFIG_QCACHE) += qcache/ -obj-$(CONFIG_ZSMALLOC) += zsmalloc/ obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/ obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/ obj-$(CONFIG_FB_SM7XX) += sm7xx/ diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 901066bc..a38faeda 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -34,10 +34,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); @@ -500,6 +502,19 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd) 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; @@ -626,6 +641,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 @@ -1211,8 +1228,9 @@ static int binder_inc_ref(struct binder_ref *ref, int strong, } -static int binder_dec_ref(struct binder_ref *ref, int strong) +static int binder_dec_ref(struct binder_ref **ptr_to_ref, int strong) { + struct binder_ref *ref = *ptr_to_ref; if (strong) { if (ref->strong == 0) { binder_user_error("binder: %d invalid dec strong, " @@ -1238,8 +1256,10 @@ static int binder_dec_ref(struct binder_ref *ref, int strong) } ref->weak--; } - if (ref->strong == 0 && ref->weak == 0) + if (ref->strong == 0 && ref->weak == 0) { binder_delete_ref(ref); + *ptr_to_ref = NULL; + } return 0; } @@ -1378,7 +1398,7 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, binder_debug(BINDER_DEBUG_TRANSACTION, " ref %d desc %d (node %d)\n", ref->debug_id, ref->desc, ref->node->debug_id); - binder_dec_ref(ref, fp->type == BINDER_TYPE_HANDLE); + binder_dec_ref(&ref, fp->type == BINDER_TYPE_HANDLE); } break; case BINDER_TYPE_FD: @@ -1404,6 +1424,7 @@ static void binder_transaction(struct binder_proc *proc, struct binder_transaction *t; struct binder_work *tcomplete; size_t *offp, *off_end; + size_t off_min; struct binder_proc *target_proc; struct binder_thread *target_thread = NULL; struct binder_node *target_node = NULL; @@ -1489,6 +1510,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; @@ -1565,6 +1590,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) { @@ -1575,6 +1603,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); @@ -1600,18 +1629,23 @@ static void binder_transaction(struct binder_proc *proc, goto err_bad_offset; } off_end = (void *)offp + tr->offsets_size; + off_min = 0; for (; offp < off_end; offp++) { struct flat_binder_object *fp; if (*offp > t->buffer->data_size - sizeof(*fp) || + *offp < off_min || t->buffer->data_size < sizeof(*fp) || - !IS_ALIGNED(*offp, sizeof(void *))) { - binder_user_error("binder: %d:%d got transaction with " - "invalid offset, %zd\n", - proc->pid, thread->pid, *offp); + !IS_ALIGNED(*offp, sizeof(u32))) { + binder_user_error("%d:%d got transaction with invalid offset, %lld (min %lld, max %lld)\n", + proc->pid, thread->pid, (u64)*offp, + (u64)off_min, + (u64)(t->buffer->data_size - + sizeof(*fp))); return_error = BR_FAILED_REPLY; goto err_bad_offset; } fp = (struct flat_binder_object *)(t->buffer->data + *offp); + off_min = *offp + sizeof(struct flat_binder_object); switch (fp->type) { case BINDER_TYPE_BINDER: case BINDER_TYPE_WEAK_BINDER: { @@ -1634,6 +1668,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; @@ -1647,6 +1685,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, @@ -1663,6 +1702,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; @@ -1671,6 +1714,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, @@ -1684,6 +1728,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, @@ -1716,6 +1762,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); @@ -1723,6 +1774,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? */ @@ -1771,6 +1823,7 @@ static void binder_transaction(struct binder_proc *proc, 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); @@ -1816,6 +1869,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)]++; @@ -1864,18 +1918,24 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, break; case BC_RELEASE: debug_string = "Release"; - binder_dec_ref(ref, 1); + binder_dec_ref(&ref, 1); break; case BC_DECREFS: default: debug_string = "DecRefs"; - binder_dec_ref(ref, 0); + binder_dec_ref(&ref, 0); break; } - binder_debug(BINDER_DEBUG_USER_REFS, - "binder: %d:%d %s ref %d desc %d s %d w %d for node %d\n", - proc->pid, thread->pid, debug_string, ref->debug_id, - ref->desc, ref->strong, ref->weak, ref->node->debug_id); + if (ref == NULL) { + binder_debug(BINDER_DEBUG_USER_REFS, + "binder: %d:%d %s ref deleted", + proc->pid, thread->pid, debug_string); + } else { + binder_debug(BINDER_DEBUG_USER_REFS, + "binder: %d:%d %s ref %d desc %d s %d w %d for node %d\n", + proc->pid, thread->pid, debug_string, ref->debug_id, + ref->desc, ref->strong, ref->weak, ref->node->debug_id); + } break; } case BC_INCREFS_DONE: @@ -1987,6 +2047,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; @@ -2196,6 +2257,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)]++; @@ -2257,7 +2319,12 @@ static int binder_thread_read(struct binder_proc *proc, 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))) { @@ -2281,7 +2348,9 @@ static int binder_thread_read(struct binder_proc *proc, } else ret = wait_event_freezable(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; @@ -2471,6 +2540,7 @@ static int binder_thread_read(struct binder_proc *proc, 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" @@ -2647,12 +2717,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)) @@ -2680,11 +2752,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; @@ -2709,6 +2783,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))) @@ -2718,6 +2793,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) { @@ -2749,6 +2825,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) { binder_debug(BINDER_DEBUG_TOP_ERRORS, @@ -2795,12 +2874,14 @@ 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) binder_debug(BINDER_DEBUG_TOP_ERRORS, "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; } @@ -2946,13 +3027,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]; @@ -3135,7 +3219,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, @@ -3162,7 +3246,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); @@ -3503,7 +3587,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"); @@ -3515,7 +3599,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; } @@ -3526,7 +3610,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"); @@ -3535,7 +3619,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; } @@ -3546,27 +3630,39 @@ 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; } static int binder_proc_show(struct seq_file *m, void *unused) { - struct binder_proc *proc = m->private; - int do_lock = !binder_debug_no_lock; + struct binder_proc *itr; + struct binder_proc *proc = m->private; + struct hlist_node *pos; + int do_lock = !binder_debug_no_lock; + bool valid_proc = false; if (do_lock) - mutex_lock(&binder_lock); - seq_puts(m, "binder proc state:\n"); - print_binder_proc(m, proc, 1); + binder_lock(__func__); + + hlist_for_each_entry(itr, pos, &binder_procs, proc_node) { + if (itr == proc) { + valid_proc = true; + break; + } + } + if (valid_proc) { + 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; } @@ -3661,4 +3757,7 @@ static int __init binder_init(void) device_initcall(binder_init); +#define CREATE_TRACE_POINTS +#include + 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..12ef1e93 --- /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 "trace/define_trace.h" diff --git a/drivers/staging/qcache/Kconfig b/drivers/staging/qcache/Kconfig deleted file mode 100644 index 389341cf..00000000 --- a/drivers/staging/qcache/Kconfig +++ /dev/null @@ -1,8 +0,0 @@ -config QCACHE - tristate "Dynamic compression of clean pagecache pages" - depends on CLEANCACHE - select LZO_COMPRESS - select LZO_DECOMPRESS - default n - help - Qcache is the backend for fmem diff --git a/drivers/staging/qcache/Makefile b/drivers/staging/qcache/Makefile deleted file mode 100644 index 4fdf05c1..00000000 --- a/drivers/staging/qcache/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -qcache-y := qcache-main.o tmem.o fmem.o - -obj-$(CONFIG_QCACHE) += qcache.o diff --git a/drivers/staging/qcache/fmem.c b/drivers/staging/qcache/fmem.c deleted file mode 100644 index 0609f4af..00000000 --- a/drivers/staging/qcache/fmem.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * - * Copyright (c) 2011-2012, The Linux Foundation. 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 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#ifdef CONFIG_MEMORY_HOTPLUG -#include -#include -#endif -#include "tmem.h" -#include - -struct fmem_data fmem_data; -enum fmem_state fmem_state; -static spinlock_t fmem_state_lock; - -#ifdef CONFIG_MEMORY_HOTPLUG -static unsigned int section_powered_off[NR_MEM_SECTIONS]; -static unsigned int fmem_section_start, fmem_section_end; -#endif - -void *fmem_map_virtual_area(int cacheability) -{ - unsigned long addr; - const struct mem_type *type; - int ret; - - addr = (unsigned long) fmem_data.area->addr; - type = get_mem_type(cacheability); - ret = ioremap_pages(addr, fmem_data.phys, fmem_data.size, type); - if (ret) - return ERR_PTR(ret); - - fmem_data.virt = fmem_data.area->addr; - - return fmem_data.virt; -} - -void fmem_unmap_virtual_area(void) -{ - unmap_kernel_range((unsigned long)fmem_data.virt, fmem_data.size); - fmem_data.virt = NULL; -} - -static int fmem_probe(struct platform_device *pdev) -{ - struct fmem_platform_data *pdata = pdev->dev.platform_data; - - if (!pdata->phys) - pdata->phys = allocate_contiguous_ebi_nomap(pdata->size, - pdata->align); - -#ifdef CONFIG_MEMORY_HOTPLUG - fmem_section_start = pdata->phys >> PA_SECTION_SHIFT; - fmem_section_end = (pdata->phys - 1 + pdata->size) >> PA_SECTION_SHIFT; -#endif - fmem_data.phys = pdata->phys + pdata->reserved_size_low; - fmem_data.size = pdata->size - pdata->reserved_size_low - - pdata->reserved_size_high; - fmem_data.reserved_size_low = pdata->reserved_size_low; - fmem_data.reserved_size_high = pdata->reserved_size_high; - - if (!fmem_data.size) - return -ENODEV; - - fmem_data.area = get_vm_area(fmem_data.size, VM_IOREMAP); - if (!fmem_data.area) - return -ENOMEM; - - if (!fmem_map_virtual_area(MT_DEVICE_CACHED)) { - remove_vm_area(fmem_data.area->addr); - return -ENOMEM; - } - pr_info("fmem phys %lx virt %p size %lx\n", - fmem_data.phys, fmem_data.virt, fmem_data.size); - - spin_lock_init(&fmem_state_lock); - - return 0; -} - -static int fmem_remove(struct platform_device *pdev) -{ - return 0; -} - -static struct platform_driver fmem_driver = { - .probe = fmem_probe, - .remove = fmem_remove, - .driver = { .name = "fmem" } -}; - -#ifdef CONFIG_SYSFS -static ssize_t fmem_state_show(struct kobject *kobj, - struct kobj_attribute *attr, - char *buf) -{ - if (fmem_state == FMEM_T_STATE) - return snprintf(buf, 3, "t\n"); - else if (fmem_state == FMEM_C_STATE) - return snprintf(buf, 3, "c\n"); -#ifdef CONFIG_MEMORY_HOTPLUG - else if (fmem_state == FMEM_O_STATE) - return snprintf(buf, 3, "o\n"); -#endif - else if (fmem_state == FMEM_UNINITIALIZED) - return snprintf(buf, 15, "uninitialized\n"); - return snprintf(buf, 3, "?\n"); -} - -static ssize_t fmem_state_store(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, size_t count) -{ - int ret = -EINVAL; - - if (!strncmp(buf, "t", 1)) - ret = fmem_set_state(FMEM_T_STATE); - else if (!strncmp(buf, "c", 1)) - ret = fmem_set_state(FMEM_C_STATE); -#ifdef CONFIG_MEMORY_HOTPLUG - else if (!strncmp(buf, "o", 1)) - ret = fmem_set_state(FMEM_O_STATE); -#endif - if (ret) - return ret; - return 1; -} - -static struct kobj_attribute fmem_state_attr = { - .attr = { .name = "state", .mode = 0644 }, - .show = fmem_state_show, - .store = fmem_state_store, -}; - -static struct attribute *fmem_attrs[] = { - &fmem_state_attr.attr, - NULL, -}; - -static struct attribute_group fmem_attr_group = { - .attrs = fmem_attrs, - .name = "fmem", -}; - -static int fmem_create_sysfs(void) -{ - int ret = 0; - - ret = sysfs_create_group(mm_kobj, &fmem_attr_group); - if (ret) - pr_err("fmem: can't create sysfs\n"); - return ret; -} - -#endif - -#ifdef CONFIG_MEMORY_HOTPLUG -bool fmem_is_disjoint(unsigned long start_pfn, unsigned long nr_pages) -{ - unsigned long fmem_start_pfn, fmem_end_pfn; - unsigned long unstable_end_pfn; - unsigned long highest_start_pfn, lowest_end_pfn; - - fmem_start_pfn = (fmem_data.phys - fmem_data.reserved_size_low) - >> PAGE_SHIFT; - fmem_end_pfn = (fmem_data.phys + fmem_data.size + - fmem_data.reserved_size_high - 1) >> PAGE_SHIFT; - unstable_end_pfn = start_pfn + nr_pages - 1; - - highest_start_pfn = max(fmem_start_pfn, start_pfn); - lowest_end_pfn = min(fmem_end_pfn, unstable_end_pfn); - - return lowest_end_pfn < highest_start_pfn; -} - -static int fmem_mem_going_offline_callback(void *arg) -{ - struct memory_notify *marg = arg; - - if (fmem_is_disjoint(marg->start_pfn, marg->nr_pages)) - return 0; - return fmem_set_state(FMEM_O_STATE); -} - -static void fmem_mem_online_callback(void *arg) -{ - struct memory_notify *marg = arg; - int i; - - section_powered_off[marg->start_pfn >> PFN_SECTION_SHIFT] = 0; - - if (fmem_state != FMEM_O_STATE) - return; - - for (i = fmem_section_start; i <= fmem_section_end; i++) { - if (section_powered_off[i]) - return; - } - - fmem_set_state(FMEM_T_STATE); -} - -static void fmem_mem_offline_callback(void *arg) -{ - struct memory_notify *marg = arg; - - section_powered_off[marg->start_pfn >> PFN_SECTION_SHIFT] = 1; -} - -static int fmem_memory_callback(struct notifier_block *self, - unsigned long action, void *arg) -{ - int ret = 0; - - if (fmem_state == FMEM_UNINITIALIZED) - return NOTIFY_OK; - - switch (action) { - case MEM_ONLINE: - fmem_mem_online_callback(arg); - break; - case MEM_GOING_OFFLINE: - ret = fmem_mem_going_offline_callback(arg); - break; - case MEM_OFFLINE: - fmem_mem_offline_callback(arg); - break; - case MEM_GOING_ONLINE: - case MEM_CANCEL_ONLINE: - case MEM_CANCEL_OFFLINE: - break; - } - if (ret) - ret = notifier_from_errno(ret); - else - ret = NOTIFY_OK; - return ret; -} -#endif - -static int __init fmem_init(void) -{ -#ifdef CONFIG_MEMORY_HOTPLUG - hotplug_memory_notifier(fmem_memory_callback, 0); -#endif - return platform_driver_register(&fmem_driver); -} - -static void __exit fmem_exit(void) -{ - platform_driver_unregister(&fmem_driver); -} - -struct fmem_data *fmem_get_info(void) -{ - return &fmem_data; -} -EXPORT_SYMBOL(fmem_get_info); - -void lock_fmem_state(void) -{ - spin_lock(&fmem_state_lock); -} - -void unlock_fmem_state(void) -{ - spin_unlock(&fmem_state_lock); -} - -int fmem_set_state(enum fmem_state new_state) -{ - int ret = 0; - int create_sysfs = 0; - - lock_fmem_state(); - if (fmem_state == new_state) - goto out; - - if (fmem_state == FMEM_UNINITIALIZED) { - if (new_state == FMEM_T_STATE) { - tmem_enable(); - create_sysfs = 1; - goto out_set; - } else { - ret = -EINVAL; - goto out; - } - } - -#ifdef CONFIG_MEMORY_HOTPLUG - if (fmem_state == FMEM_C_STATE && new_state == FMEM_O_STATE) { - ret = -EAGAIN; - goto out; - } - - if (fmem_state == FMEM_O_STATE && new_state == FMEM_C_STATE) { - pr_warn("attempting to use powered off memory as fmem\n"); - ret = -EAGAIN; - goto out; - } -#endif - - if (new_state == FMEM_T_STATE) { - void *v; - v = fmem_map_virtual_area(MT_DEVICE_CACHED); - if (IS_ERR_OR_NULL(v)) { - ret = PTR_ERR(v); - goto out; - } - tmem_enable(); - } else { - tmem_disable(); - fmem_unmap_virtual_area(); - } - -out_set: - fmem_state = new_state; -out: - unlock_fmem_state(); -#ifdef CONFIG_SYSFS - if (create_sysfs) - fmem_create_sysfs(); -#endif - return ret; -} -EXPORT_SYMBOL(fmem_set_state); - -arch_initcall(fmem_init); -module_exit(fmem_exit); diff --git a/drivers/staging/qcache/qcache-main.c b/drivers/staging/qcache/qcache-main.c deleted file mode 100644 index f416cfcb..00000000 --- a/drivers/staging/qcache/qcache-main.c +++ /dev/null @@ -1,1358 +0,0 @@ -/* - * Copyright (c) 2010,2011, Dan Magenheimer, Oracle Corp. - * Copyright (c) 2010,2011, Nitin Gupta - * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. - * - * Qcache provides an in-kernel "host implementation" for transcendent memory - * and, thus indirectly, for cleancache and frontswap. Qcache includes a - * page-accessible memory [1] interface, utilizing lzo1x compression: - * 1) "compression buddies" ("zbud") is used for ephemeral pages - * Zbud allows pairs (and potentially, - * in the future, more than a pair of) compressed pages to be closely linked - * so that reclaiming can be done via the kernel's physical-page-oriented - * "shrinker" interface. - * - * [1] For a definition of page-accessible memory (aka PAM), see: - * http://marc.info/?l=linux-mm&m=127811271605009 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "tmem.h" - -#if !defined(CONFIG_CLEANCACHE) -#error "qcache is useless without CONFIG_CLEANCACHE" -#endif -#include - -#define ZCACHE_GFP_MASK \ - (__GFP_FS | __GFP_NORETRY | __GFP_NOWARN | __GFP_NOMEMALLOC) - -#define MAX_POOLS_PER_CLIENT 16 - -#define MAX_CLIENTS 16 -#define LOCAL_CLIENT ((uint16_t)-1) - -MODULE_LICENSE("GPL"); - -struct zcache_client { - struct tmem_pool *tmem_pools[MAX_POOLS_PER_CLIENT]; - struct xv_pool *xvpool; - bool allocated; - atomic_t refcount; -}; - -struct qcache_info { - void *addr; - unsigned long *bitmap; - spinlock_t lock; - unsigned pages; -}; -static struct qcache_info qcache_info; -static unsigned long zcache_qc_allocated; -static unsigned long zcache_qc_freed; -static unsigned long zcache_qc_used; -static unsigned long zcache_qc_max_used; - -static struct zcache_client zcache_host; -static struct zcache_client zcache_clients[MAX_CLIENTS]; - -static inline uint16_t get_client_id_from_client(struct zcache_client *cli) -{ - BUG_ON(cli == NULL); - if (cli == &zcache_host) - return LOCAL_CLIENT; - return cli - &zcache_clients[0]; -} - -static inline bool is_local_client(struct zcache_client *cli) -{ - return cli == &zcache_host; -} - -/********** - * Compression buddies ("zbud") provides for packing two (or, possibly - * in the future, more) compressed ephemeral pages into a single "raw" - * (physical) page and tracking them with data structures so that - * the raw pages can be easily reclaimed. - * - * A zbud page ("zbpg") is an aligned page containing a list_head, - * a lock, and two "zbud headers". The remainder of the physical - * page is divided up into aligned 64-byte "chunks" which contain - * the compressed data for zero, one, or two zbuds. Each zbpg - * resides on: (1) an "unused list" if it has no zbuds; (2) a - * "buddied" list if it is fully populated with two zbuds; or - * (3) one of PAGE_SIZE/64 "unbuddied" lists indexed by how many chunks - * the one unbuddied zbud uses. The data inside a zbpg cannot be - * read or written unless the zbpg's lock is held. - */ - -#define ZBH_SENTINEL 0x43214321 -#define ZBPG_SENTINEL 0xdeadbeef - -#define ZBUD_MAX_BUDS 2 - -struct zbud_hdr { - uint16_t client_id; - uint16_t pool_id; - struct tmem_oid oid; - uint32_t index; - uint16_t size; /* compressed size in bytes, zero means unused */ - DECL_SENTINEL -}; - -struct zbud_page { - struct list_head bud_list; - spinlock_t lock; - struct zbud_hdr buddy[ZBUD_MAX_BUDS]; - DECL_SENTINEL - /* followed by NUM_CHUNK aligned CHUNK_SIZE-byte chunks */ -}; - -#define CHUNK_SHIFT 6 -#define CHUNK_SIZE (1 << CHUNK_SHIFT) -#define CHUNK_MASK (~(CHUNK_SIZE-1)) -#define NCHUNKS (((PAGE_SIZE - sizeof(struct zbud_page)) & \ - CHUNK_MASK) >> CHUNK_SHIFT) -#define MAX_CHUNK (NCHUNKS-1) - -static struct { - struct list_head list; - unsigned count; -} zbud_unbuddied[NCHUNKS]; -/* list N contains pages with N chunks USED and NCHUNKS-N unused */ -/* element 0 is never used but optimizing that isn't worth it */ -static unsigned long zbud_cumul_chunk_counts[NCHUNKS]; - -struct list_head zbud_buddied_list; -static unsigned long zcache_zbud_buddied_count; - -/* protects the buddied list and all unbuddied lists */ -static DEFINE_SPINLOCK(zbud_budlists_spinlock); - -static atomic_t zcache_zbud_curr_raw_pages; -static atomic_t zcache_zbud_curr_zpages; -static unsigned long zcache_zbud_curr_zbytes; -static unsigned long zcache_zbud_cumul_zpages; -static unsigned long zcache_zbud_cumul_zbytes; -static unsigned long zcache_compress_poor; -static unsigned long zcache_mean_compress_poor; - -/* forward references */ -static void *zcache_get_free_page(void); - -static void *qcache_alloc(void) -{ - void *addr; - unsigned long flags; - int offset; - struct qcache_info *qc = &qcache_info; - - spin_lock_irqsave(&qc->lock, flags); - offset = bitmap_find_free_region(qc->bitmap, qc->pages, 0); - - if (offset < 0) { - spin_unlock_irqrestore(&qc->lock, flags); - return NULL; - } - - zcache_qc_allocated++; - zcache_qc_used++; - zcache_qc_max_used = max(zcache_qc_max_used, zcache_qc_used); - spin_unlock_irqrestore(&qc->lock, flags); - - addr = qc->addr + offset * PAGE_SIZE; - - return addr; -} - -static void qcache_free(void *addr) -{ - unsigned long flags; - int offset; - struct qcache_info *qc = &qcache_info; - - offset = (addr - qc->addr) / PAGE_SIZE; - - spin_lock_irqsave(&qc->lock, flags); - bitmap_release_region(qc->bitmap, offset, 0); - - zcache_qc_freed++; - zcache_qc_used--; - spin_unlock_irqrestore(&qc->lock, flags); -} - -/* - * zbud helper functions - */ - -static inline unsigned zbud_max_buddy_size(void) -{ - return MAX_CHUNK << CHUNK_SHIFT; -} - -static inline unsigned zbud_size_to_chunks(unsigned size) -{ - BUG_ON(size == 0 || size > zbud_max_buddy_size()); - return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT; -} - -static inline int zbud_budnum(struct zbud_hdr *zh) -{ - unsigned offset = (unsigned long)zh & (PAGE_SIZE - 1); - struct zbud_page *zbpg = NULL; - unsigned budnum = -1U; - int i; - - for (i = 0; i < ZBUD_MAX_BUDS; i++) - if (offset == offsetof(typeof(*zbpg), buddy[i])) { - budnum = i; - break; - } - BUG_ON(budnum == -1U); - return budnum; -} - -static char *zbud_data(struct zbud_hdr *zh, unsigned size) -{ - struct zbud_page *zbpg; - char *p; - unsigned budnum; - - ASSERT_SENTINEL(zh, ZBH); - budnum = zbud_budnum(zh); - BUG_ON(size == 0 || size > zbud_max_buddy_size()); - zbpg = container_of(zh, struct zbud_page, buddy[budnum]); - p = (char *)zbpg; - if (budnum == 0) - p += ((sizeof(struct zbud_page) + CHUNK_SIZE - 1) & - CHUNK_MASK); - else if (budnum == 1) - p += PAGE_SIZE - ((size + CHUNK_SIZE - 1) & CHUNK_MASK); - return p; -} - -/* - * zbud raw page management - */ - -static struct zbud_page *zbud_alloc_raw_page(void) -{ - struct zbud_page *zbpg = NULL; - struct zbud_hdr *zh0, *zh1; - - zbpg = zcache_get_free_page(); - if (likely(zbpg != NULL)) { - INIT_LIST_HEAD(&zbpg->bud_list); - zh0 = &zbpg->buddy[0]; zh1 = &zbpg->buddy[1]; - spin_lock_init(&zbpg->lock); - atomic_inc(&zcache_zbud_curr_raw_pages); - INIT_LIST_HEAD(&zbpg->bud_list); - SET_SENTINEL(zbpg, ZBPG); - zh0->size = 0; zh1->size = 0; - tmem_oid_set_invalid(&zh0->oid); - tmem_oid_set_invalid(&zh1->oid); - } - return zbpg; -} - -static void zbud_free_raw_page(struct zbud_page *zbpg) -{ - struct zbud_hdr *zh0 = &zbpg->buddy[0], *zh1 = &zbpg->buddy[1]; - - ASSERT_SENTINEL(zbpg, ZBPG); - BUG_ON(!list_empty(&zbpg->bud_list)); - BUG_ON(zh0->size != 0 || tmem_oid_valid(&zh0->oid)); - BUG_ON(zh1->size != 0 || tmem_oid_valid(&zh1->oid)); - INVERT_SENTINEL(zbpg, ZBPG); - spin_unlock(&zbpg->lock); - qcache_free(zbpg); -} - -/* - * core zbud handling routines - */ - -static unsigned zbud_free(struct zbud_hdr *zh) -{ - unsigned size; - - ASSERT_SENTINEL(zh, ZBH); - BUG_ON(!tmem_oid_valid(&zh->oid)); - size = zh->size; - BUG_ON(zh->size == 0 || zh->size > zbud_max_buddy_size()); - zh->size = 0; - tmem_oid_set_invalid(&zh->oid); - INVERT_SENTINEL(zh, ZBH); - zcache_zbud_curr_zbytes -= size; - atomic_dec(&zcache_zbud_curr_zpages); - return size; -} - -static void zbud_free_and_delist(struct zbud_hdr *zh) -{ - unsigned chunks; - struct zbud_hdr *zh_other; - unsigned budnum = zbud_budnum(zh), size; - struct zbud_page *zbpg = - container_of(zh, struct zbud_page, buddy[budnum]); - - spin_lock(&zbpg->lock); - if (list_empty(&zbpg->bud_list)) { - spin_unlock(&zbpg->lock); - return; - } - size = zbud_free(zh); - zh_other = &zbpg->buddy[(budnum == 0) ? 1 : 0]; - if (zh_other->size == 0) { /* was unbuddied: unlist and free */ - chunks = zbud_size_to_chunks(size) ; - spin_lock(&zbud_budlists_spinlock); - BUG_ON(list_empty(&zbud_unbuddied[chunks].list)); - list_del_init(&zbpg->bud_list); - zbud_unbuddied[chunks].count--; - spin_unlock(&zbud_budlists_spinlock); - zbud_free_raw_page(zbpg); - } else { /* was buddied: move remaining buddy to unbuddied list */ - chunks = zbud_size_to_chunks(zh_other->size) ; - spin_lock(&zbud_budlists_spinlock); - list_del_init(&zbpg->bud_list); - zcache_zbud_buddied_count--; - list_add_tail(&zbpg->bud_list, &zbud_unbuddied[chunks].list); - zbud_unbuddied[chunks].count++; - spin_unlock(&zbud_budlists_spinlock); - spin_unlock(&zbpg->lock); - } -} - -static struct zbud_hdr *zbud_create(uint16_t client_id, uint16_t pool_id, - struct tmem_oid *oid, - uint32_t index, struct page *page, - void *cdata, unsigned size) -{ - struct zbud_hdr *zh0, *zh1, *zh = NULL; - struct zbud_page *zbpg = NULL, *ztmp; - unsigned nchunks; - char *to; - int i, found_good_buddy = 0; - - nchunks = zbud_size_to_chunks(size) ; - for (i = MAX_CHUNK - nchunks + 1; i > 0; i--) { - spin_lock(&zbud_budlists_spinlock); - if (!list_empty(&zbud_unbuddied[i].list)) { - list_for_each_entry_safe(zbpg, ztmp, - &zbud_unbuddied[i].list, bud_list) { - if (spin_trylock(&zbpg->lock)) { - found_good_buddy = i; - goto found_unbuddied; - } - } - } - spin_unlock(&zbud_budlists_spinlock); - } - /* didn't find a good buddy, try allocating a new page */ - zbpg = zbud_alloc_raw_page(); - if (unlikely(zbpg == NULL)) - goto out; - /* ok, have a page, now compress the data before taking locks */ - spin_lock(&zbpg->lock); - spin_lock(&zbud_budlists_spinlock); - list_add_tail(&zbpg->bud_list, &zbud_unbuddied[nchunks].list); - zbud_unbuddied[nchunks].count++; - zh = &zbpg->buddy[0]; - goto init_zh; - -found_unbuddied: - zh0 = &zbpg->buddy[0]; zh1 = &zbpg->buddy[1]; - BUG_ON(!((zh0->size == 0) ^ (zh1->size == 0))); - if (zh0->size != 0) { /* buddy0 in use, buddy1 is vacant */ - ASSERT_SENTINEL(zh0, ZBH); - zh = zh1; - } else if (zh1->size != 0) { /* buddy1 in use, buddy0 is vacant */ - ASSERT_SENTINEL(zh1, ZBH); - zh = zh0; - } else - BUG(); - list_del_init(&zbpg->bud_list); - zbud_unbuddied[found_good_buddy].count--; - list_add_tail(&zbpg->bud_list, &zbud_buddied_list); - zcache_zbud_buddied_count++; - -init_zh: - SET_SENTINEL(zh, ZBH); - zh->size = size; - zh->index = index; - zh->oid = *oid; - zh->pool_id = pool_id; - zh->client_id = client_id; - /* can wait to copy the data until the list locks are dropped */ - spin_unlock(&zbud_budlists_spinlock); - - to = zbud_data(zh, size); - memcpy(to, cdata, size); - spin_unlock(&zbpg->lock); - zbud_cumul_chunk_counts[nchunks]++; - atomic_inc(&zcache_zbud_curr_zpages); - zcache_zbud_cumul_zpages++; - zcache_zbud_curr_zbytes += size; - zcache_zbud_cumul_zbytes += size; -out: - return zh; -} - -static int zbud_decompress(struct page *page, struct zbud_hdr *zh) -{ - struct zbud_page *zbpg; - unsigned budnum = zbud_budnum(zh); - size_t out_len = PAGE_SIZE; - char *to_va, *from_va; - unsigned size; - int ret = 0; - - zbpg = container_of(zh, struct zbud_page, buddy[budnum]); - spin_lock(&zbpg->lock); - if (list_empty(&zbpg->bud_list)) { - ret = -EINVAL; - goto out; - } - ASSERT_SENTINEL(zh, ZBH); - BUG_ON(zh->size == 0 || zh->size > zbud_max_buddy_size()); - to_va = kmap_atomic(page); - size = zh->size; - from_va = zbud_data(zh, size); - ret = lzo1x_decompress_safe(from_va, size, to_va, &out_len); - BUG_ON(ret != LZO_E_OK); - BUG_ON(out_len != PAGE_SIZE); - kunmap_atomic(to_va); -out: - spin_unlock(&zbpg->lock); - return ret; -} - -static struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id, - uint16_t poolid); -static void zcache_put_pool(struct tmem_pool *pool); - -static void zbud_init(void) -{ - int i; - - INIT_LIST_HEAD(&zbud_buddied_list); - zcache_zbud_buddied_count = 0; - for (i = 0; i < NCHUNKS; i++) { - INIT_LIST_HEAD(&zbud_unbuddied[i].list); - zbud_unbuddied[i].count = 0; - } -} - -#ifdef CONFIG_SYSFS -/* - * These sysfs routines show a nice distribution of how many zbpg's are - * currently (and have ever been placed) in each unbuddied list. It's fun - * to watch but can probably go away before final merge. - */ -static int zbud_show_unbuddied_list_counts(char *buf) -{ - int i; - char *p = buf; - - for (i = 0; i < NCHUNKS; i++) - p += sprintf(p, "%u ", zbud_unbuddied[i].count); - return p - buf; -} - -static int zbud_show_cumul_chunk_counts(char *buf) -{ - unsigned long i, chunks = 0, total_chunks = 0, sum_total_chunks = 0; - unsigned long total_chunks_lte_21 = 0, total_chunks_lte_32 = 0; - unsigned long total_chunks_lte_42 = 0; - char *p = buf; - - for (i = 0; i < NCHUNKS; i++) { - p += sprintf(p, "%lu ", zbud_cumul_chunk_counts[i]); - chunks += zbud_cumul_chunk_counts[i]; - total_chunks += zbud_cumul_chunk_counts[i]; - sum_total_chunks += i * zbud_cumul_chunk_counts[i]; - if (i == 21) - total_chunks_lte_21 = total_chunks; - if (i == 32) - total_chunks_lte_32 = total_chunks; - if (i == 42) - total_chunks_lte_42 = total_chunks; - } - p += sprintf(p, "<=21:%lu <=32:%lu <=42:%lu, mean:%lu\n", - total_chunks_lte_21, total_chunks_lte_32, total_chunks_lte_42, - chunks == 0 ? 0 : sum_total_chunks / chunks); - return p - buf; -} -#endif - -/* - * zcache core code starts here - */ - -/* useful stats not collected by cleancache or frontswap */ -static unsigned long zcache_flush_total; -static unsigned long zcache_flush_found; -static unsigned long zcache_flobj_total; -static unsigned long zcache_flobj_found; -static unsigned long zcache_failed_eph_puts; - -/* - * Tmem operations assume the poolid implies the invoking client. - * Zcache only has one client (the kernel itself): LOCAL_CLIENT. - * RAMster has each client numbered by cluster node, and a KVM version - * of zcache would have one client per guest and each client might - * have a poolid==N. - */ -static struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id, uint16_t poolid) -{ - struct tmem_pool *pool = NULL; - struct zcache_client *cli = NULL; - - if (cli_id == LOCAL_CLIENT) - cli = &zcache_host; - else { - if (cli_id >= MAX_CLIENTS) - goto out; - cli = &zcache_clients[cli_id]; - if (cli == NULL) - goto out; - atomic_inc(&cli->refcount); - } - if (poolid < MAX_POOLS_PER_CLIENT) { - pool = cli->tmem_pools[poolid]; - if (pool != NULL) - atomic_inc(&pool->refcount); - } -out: - return pool; -} - -static void zcache_put_pool(struct tmem_pool *pool) -{ - struct zcache_client *cli = NULL; - - if (pool == NULL) - BUG(); - cli = pool->client; - atomic_dec(&pool->refcount); - atomic_dec(&cli->refcount); -} - -int zcache_new_client(uint16_t cli_id) -{ - struct zcache_client *cli = NULL; - int ret = -1; - - if (cli_id == LOCAL_CLIENT) - cli = &zcache_host; - else if ((unsigned int)cli_id < MAX_CLIENTS) - cli = &zcache_clients[cli_id]; - if (cli == NULL) - goto out; - if (cli->allocated) - goto out; - cli->allocated = 1; - ret = 0; -out: - return ret; -} - -/* counters for debugging */ -static unsigned long zcache_failed_get_free_pages; -static unsigned long zcache_failed_alloc; -static unsigned long zcache_put_to_flush; -static unsigned long zcache_aborted_preload; -static unsigned long zcache_aborted_shrink; - -/* - * Ensure that memory allocation requests in zcache don't result - * in direct reclaim requests via the shrinker, which would cause - * an infinite loop. Maybe a GFP flag would be better? - */ -static DEFINE_SPINLOCK(zcache_direct_reclaim_lock); - -/* - * for now, used named slabs so can easily track usage; later can - * either just use kmalloc, or perhaps add a slab-like allocator - * to more carefully manage total memory utilization - */ -static struct kmem_cache *zcache_objnode_cache; -static struct kmem_cache *zcache_obj_cache; -static atomic_t zcache_curr_obj_count = ATOMIC_INIT(0); -static unsigned long zcache_curr_obj_count_max; -static atomic_t zcache_curr_objnode_count = ATOMIC_INIT(0); -static unsigned long zcache_curr_objnode_count_max; - -/* - * to avoid memory allocation recursion (e.g. due to direct reclaim), we - * preload all necessary data structures so the hostops callbacks never - * actually do a malloc - */ -struct zcache_preload { - void *page; - struct tmem_obj *obj; - int nr; - struct tmem_objnode *objnodes[OBJNODE_TREE_MAX_PATH]; -}; -static DEFINE_PER_CPU(struct zcache_preload, zcache_preloads) = { 0, }; - -static int zcache_do_preload(struct tmem_pool *pool) -{ - struct zcache_preload *kp; - struct tmem_objnode *objnode; - struct tmem_obj *obj; - void *page; - int ret = -ENOMEM; - - if (unlikely(zcache_objnode_cache == NULL)) - goto out; - if (unlikely(zcache_obj_cache == NULL)) - goto out; - if (!spin_trylock(&zcache_direct_reclaim_lock)) { - zcache_aborted_preload++; - goto out; - } - preempt_disable(); - kp = &__get_cpu_var(zcache_preloads); - while (kp->nr < ARRAY_SIZE(kp->objnodes)) { - preempt_enable_no_resched(); - objnode = kmem_cache_alloc(zcache_objnode_cache, - ZCACHE_GFP_MASK); - if (unlikely(objnode == NULL)) { - zcache_failed_alloc++; - goto unlock_out; - } - preempt_disable(); - kp = &__get_cpu_var(zcache_preloads); - if (kp->nr < ARRAY_SIZE(kp->objnodes)) - kp->objnodes[kp->nr++] = objnode; - else - kmem_cache_free(zcache_objnode_cache, objnode); - } - preempt_enable_no_resched(); - obj = kmem_cache_alloc(zcache_obj_cache, ZCACHE_GFP_MASK); - if (unlikely(obj == NULL)) { - zcache_failed_alloc++; - goto unlock_out; - } - page = qcache_alloc(); - if (unlikely(page == NULL)) { - zcache_failed_get_free_pages++; - kmem_cache_free(zcache_obj_cache, obj); - goto unlock_out; - } - preempt_disable(); - kp = &__get_cpu_var(zcache_preloads); - if (kp->obj == NULL) - kp->obj = obj; - else - kmem_cache_free(zcache_obj_cache, obj); - if (kp->page == NULL) - kp->page = page; - else - qcache_free(page); - ret = 0; -unlock_out: - spin_unlock(&zcache_direct_reclaim_lock); -out: - return ret; -} - -static void *zcache_get_free_page(void) -{ - struct zcache_preload *kp; - void *page; - - kp = &__get_cpu_var(zcache_preloads); - page = kp->page; - BUG_ON(page == NULL); - kp->page = NULL; - return page; -} - -/* - * zcache implementation for tmem host ops - */ - -static struct tmem_objnode *zcache_objnode_alloc(struct tmem_pool *pool) -{ - struct tmem_objnode *objnode = NULL; - unsigned long count; - struct zcache_preload *kp; - - kp = &__get_cpu_var(zcache_preloads); - if (kp->nr <= 0) - goto out; - objnode = kp->objnodes[kp->nr - 1]; - BUG_ON(objnode == NULL); - kp->objnodes[kp->nr - 1] = NULL; - kp->nr--; - count = atomic_inc_return(&zcache_curr_objnode_count); - if (count > zcache_curr_objnode_count_max) - zcache_curr_objnode_count_max = count; -out: - return objnode; -} - -static void zcache_objnode_free(struct tmem_objnode *objnode, - struct tmem_pool *pool) -{ - atomic_dec(&zcache_curr_objnode_count); - BUG_ON(atomic_read(&zcache_curr_objnode_count) < 0); - kmem_cache_free(zcache_objnode_cache, objnode); -} - -static struct tmem_obj *zcache_obj_alloc(struct tmem_pool *pool) -{ - struct tmem_obj *obj = NULL; - unsigned long count; - struct zcache_preload *kp; - - kp = &__get_cpu_var(zcache_preloads); - obj = kp->obj; - BUG_ON(obj == NULL); - kp->obj = NULL; - count = atomic_inc_return(&zcache_curr_obj_count); - if (count > zcache_curr_obj_count_max) - zcache_curr_obj_count_max = count; - return obj; -} - -static void zcache_obj_free(struct tmem_obj *obj, struct tmem_pool *pool) -{ - atomic_dec(&zcache_curr_obj_count); - BUG_ON(atomic_read(&zcache_curr_obj_count) < 0); - kmem_cache_free(zcache_obj_cache, obj); -} - -static void zcache_flush_all_obj(void) -{ - struct tmem_pool *pool; - int pool_id; - struct zcache_preload *kp; - - kp = &__get_cpu_var(zcache_preloads); - - for (pool_id = 0; pool_id < MAX_POOLS_PER_CLIENT; pool_id++) { - pool = zcache_get_pool_by_id(LOCAL_CLIENT, pool_id); - tmem_flush_pool(pool); - if (pool) - zcache_put_pool(pool); - } - if (kp->page) { - qcache_free(kp->page); - kp->page = NULL; - } - if (zcache_qc_used) - pr_warn("pages used not 0 after qcache flush all, is %ld\n", - zcache_qc_used); -} - -/* - * When zcache is disabled ("frozen"), pools can be created and destroyed, - * but all puts (and thus all other operations that require memory allocation) - * must fail. If zcache is unfrozen, accepts puts, then frozen again, - * data consistency requires all puts while frozen to be converted into - * flushes. - */ -static bool zcache_freeze; - -static void zcache_control(bool freeze) -{ - zcache_freeze = freeze; -} - -static struct tmem_hostops zcache_hostops = { - .obj_alloc = zcache_obj_alloc, - .obj_free = zcache_obj_free, - .objnode_alloc = zcache_objnode_alloc, - .objnode_free = zcache_objnode_free, - .flush_all_obj = zcache_flush_all_obj, - .control = zcache_control, -}; - -/* - * zcache implementations for PAM page descriptor ops - */ - -static atomic_t zcache_curr_eph_pampd_count = ATOMIC_INIT(0); -static unsigned long zcache_curr_eph_pampd_count_max; - -/* forward reference */ -static int zcache_compress(struct page *from, void **out_va, size_t *out_len); - -static void *zcache_pampd_create(char *data, size_t size, bool raw, int eph, - struct tmem_pool *pool, struct tmem_oid *oid, - uint32_t index) -{ - void *pampd = NULL, *cdata; - size_t clen; - int ret; - unsigned long count; - struct page *page = (struct page *)(data); - struct zcache_client *cli = pool->client; - uint16_t client_id = get_client_id_from_client(cli); - - ret = zcache_compress(page, &cdata, &clen); - if (ret == 0) - goto out; - if (clen == 0 || clen > zbud_max_buddy_size()) { - zcache_compress_poor++; - goto out; - } - pampd = (void *)zbud_create(client_id, pool->pool_id, oid, - index, page, cdata, clen); - if (pampd != NULL) { - count = atomic_inc_return(&zcache_curr_eph_pampd_count); - if (count > zcache_curr_eph_pampd_count_max) - zcache_curr_eph_pampd_count_max = count; - } -out: - return pampd; -} - -/* - * fill the pageframe corresponding to the struct page with the data - * from the passed pampd - */ -static int zcache_pampd_get_data(char *data, size_t *bufsize, bool raw, - void *pampd, struct tmem_pool *pool, - struct tmem_oid *oid, uint32_t index) -{ - BUG(); - return 0; -} - -/* - * fill the pageframe corresponding to the struct page with the data - * from the passed pampd - */ -static int zcache_pampd_get_data_and_free(char *data, size_t *bufsize, bool raw, - void *pampd, struct tmem_pool *pool, - struct tmem_oid *oid, uint32_t index) -{ - int ret = 0; - - zbud_decompress((struct page *)(data), pampd); - zbud_free_and_delist((struct zbud_hdr *)pampd); - atomic_dec(&zcache_curr_eph_pampd_count); - return ret; -} - -/* - * free the pampd and remove it from any zcache lists - * pampd must no longer be pointed to from any tmem data structures! - */ -static void zcache_pampd_free(void *pampd, struct tmem_pool *pool, - struct tmem_oid *oid, uint32_t index) -{ - zbud_free_and_delist((struct zbud_hdr *)pampd); - atomic_dec(&zcache_curr_eph_pampd_count); - BUG_ON(atomic_read(&zcache_curr_eph_pampd_count) < 0); -} - -static void zcache_pampd_free_obj(struct tmem_pool *pool, struct tmem_obj *obj) -{ -} - -static void zcache_pampd_new_obj(struct tmem_obj *obj) -{ -} - -static int zcache_pampd_replace_in_obj(void *pampd, struct tmem_obj *obj) -{ - return -1; -} - -static bool zcache_pampd_is_remote(void *pampd) -{ - return 0; -} - -static struct tmem_pamops zcache_pamops = { - .create = zcache_pampd_create, - .get_data = zcache_pampd_get_data, - .get_data_and_free = zcache_pampd_get_data_and_free, - .free = zcache_pampd_free, - .free_obj = zcache_pampd_free_obj, - .new_obj = zcache_pampd_new_obj, - .replace_in_obj = zcache_pampd_replace_in_obj, - .is_remote = zcache_pampd_is_remote, -}; - -/* - * zcache compression/decompression and related per-cpu stuff - */ - -#define LZO_WORKMEM_BYTES LZO1X_1_MEM_COMPRESS -#define LZO_DSTMEM_PAGE_ORDER 1 -static DEFINE_PER_CPU(unsigned char *, zcache_workmem); -static DEFINE_PER_CPU(unsigned char *, zcache_dstmem); - -static int zcache_compress(struct page *from, void **out_va, size_t *out_len) -{ - int ret = 0; - unsigned char *dmem = __get_cpu_var(zcache_dstmem); - unsigned char *wmem = __get_cpu_var(zcache_workmem); - char *from_va; - - BUG_ON(!irqs_disabled()); - if (unlikely(dmem == NULL || wmem == NULL)) - goto out; /* no buffer, so can't compress */ - from_va = kmap_atomic(from); - mb(); - ret = lzo1x_1_compress(from_va, PAGE_SIZE, dmem, out_len, wmem); - BUG_ON(ret != LZO_E_OK); - *out_va = dmem; - kunmap_atomic(from_va); - ret = 1; -out: - return ret; -} - -#ifdef CONFIG_SYSFS -#define ZCACHE_SYSFS_RO(_name) \ - static ssize_t zcache_##_name##_show(struct kobject *kobj, \ - struct kobj_attribute *attr, char *buf) \ - { \ - return sprintf(buf, "%lu\n", zcache_##_name); \ - } \ - static struct kobj_attribute zcache_##_name##_attr = { \ - .attr = { .name = __stringify(_name), .mode = 0444 }, \ - .show = zcache_##_name##_show, \ - } - -#define ZCACHE_SYSFS_RO_ATOMIC(_name) \ - static ssize_t zcache_##_name##_show(struct kobject *kobj, \ - struct kobj_attribute *attr, char *buf) \ - { \ - return sprintf(buf, "%d\n", atomic_read(&zcache_##_name)); \ - } \ - static struct kobj_attribute zcache_##_name##_attr = { \ - .attr = { .name = __stringify(_name), .mode = 0444 }, \ - .show = zcache_##_name##_show, \ - } - -#define ZCACHE_SYSFS_RO_CUSTOM(_name, _func) \ - static ssize_t zcache_##_name##_show(struct kobject *kobj, \ - struct kobj_attribute *attr, char *buf) \ - { \ - return _func(buf); \ - } \ - static struct kobj_attribute zcache_##_name##_attr = { \ - .attr = { .name = __stringify(_name), .mode = 0444 }, \ - .show = zcache_##_name##_show, \ - } - -ZCACHE_SYSFS_RO(curr_obj_count_max); -ZCACHE_SYSFS_RO(curr_objnode_count_max); -ZCACHE_SYSFS_RO(flush_total); -ZCACHE_SYSFS_RO(flush_found); -ZCACHE_SYSFS_RO(flobj_total); -ZCACHE_SYSFS_RO(flobj_found); -ZCACHE_SYSFS_RO(failed_eph_puts); -ZCACHE_SYSFS_RO(zbud_curr_zbytes); -ZCACHE_SYSFS_RO(zbud_cumul_zpages); -ZCACHE_SYSFS_RO(zbud_cumul_zbytes); -ZCACHE_SYSFS_RO(zbud_buddied_count); -ZCACHE_SYSFS_RO(failed_get_free_pages); -ZCACHE_SYSFS_RO(failed_alloc); -ZCACHE_SYSFS_RO(put_to_flush); -ZCACHE_SYSFS_RO(aborted_preload); -ZCACHE_SYSFS_RO(aborted_shrink); -ZCACHE_SYSFS_RO(compress_poor); -ZCACHE_SYSFS_RO(mean_compress_poor); -ZCACHE_SYSFS_RO(qc_allocated); -ZCACHE_SYSFS_RO(qc_freed); -ZCACHE_SYSFS_RO(qc_used); -ZCACHE_SYSFS_RO(qc_max_used); -ZCACHE_SYSFS_RO_ATOMIC(zbud_curr_raw_pages); -ZCACHE_SYSFS_RO_ATOMIC(zbud_curr_zpages); -ZCACHE_SYSFS_RO_ATOMIC(curr_obj_count); -ZCACHE_SYSFS_RO_ATOMIC(curr_objnode_count); -ZCACHE_SYSFS_RO_CUSTOM(zbud_unbuddied_list_counts, - zbud_show_unbuddied_list_counts); -ZCACHE_SYSFS_RO_CUSTOM(zbud_cumul_chunk_counts, - zbud_show_cumul_chunk_counts); - -static struct attribute *qcache_attrs[] = { - &zcache_curr_obj_count_attr.attr, - &zcache_curr_obj_count_max_attr.attr, - &zcache_curr_objnode_count_attr.attr, - &zcache_curr_objnode_count_max_attr.attr, - &zcache_flush_total_attr.attr, - &zcache_flobj_total_attr.attr, - &zcache_flush_found_attr.attr, - &zcache_flobj_found_attr.attr, - &zcache_failed_eph_puts_attr.attr, - &zcache_compress_poor_attr.attr, - &zcache_mean_compress_poor_attr.attr, - &zcache_zbud_curr_raw_pages_attr.attr, - &zcache_zbud_curr_zpages_attr.attr, - &zcache_zbud_curr_zbytes_attr.attr, - &zcache_zbud_cumul_zpages_attr.attr, - &zcache_zbud_cumul_zbytes_attr.attr, - &zcache_zbud_buddied_count_attr.attr, - &zcache_failed_get_free_pages_attr.attr, - &zcache_failed_alloc_attr.attr, - &zcache_put_to_flush_attr.attr, - &zcache_aborted_preload_attr.attr, - &zcache_aborted_shrink_attr.attr, - &zcache_zbud_unbuddied_list_counts_attr.attr, - &zcache_zbud_cumul_chunk_counts_attr.attr, - &zcache_qc_allocated_attr.attr, - &zcache_qc_freed_attr.attr, - &zcache_qc_used_attr.attr, - &zcache_qc_max_used_attr.attr, - NULL, -}; - -static struct attribute_group qcache_attr_group = { - .attrs = qcache_attrs, - .name = "qcache", -}; - -#endif /* CONFIG_SYSFS */ - -/* - * zcache shims between cleancache ops and tmem - */ - -static int zcache_put_page(int cli_id, int pool_id, struct tmem_oid *oidp, - uint32_t index, struct page *page) -{ - struct tmem_pool *pool; - int ret = -1; - - BUG_ON(!irqs_disabled()); - pool = zcache_get_pool_by_id(cli_id, pool_id); - if (unlikely(pool == NULL)) - goto out; - if (!zcache_freeze && zcache_do_preload(pool) == 0) { - /* preload does preempt_disable on success */ - ret = tmem_put(pool, oidp, index, (char *)(page), - PAGE_SIZE, 0, is_ephemeral(pool)); - if (ret < 0) { - zcache_failed_eph_puts++; - } - zcache_put_pool(pool); - preempt_enable_no_resched(); - } else { - zcache_put_to_flush++; - if (atomic_read(&pool->obj_count) > 0) - /* the put fails whether the flush succeeds or not */ - (void)tmem_flush_page(pool, oidp, index); - zcache_put_pool(pool); - } -out: - return ret; -} - -static int zcache_get_page(int cli_id, int pool_id, struct tmem_oid *oidp, - uint32_t index, struct page *page) -{ - struct tmem_pool *pool; - int ret = -1; - unsigned long flags; - size_t size = PAGE_SIZE; - - local_irq_save(flags); - pool = zcache_get_pool_by_id(cli_id, pool_id); - if (likely(pool != NULL)) { - if (atomic_read(&pool->obj_count) > 0) - ret = tmem_get(pool, oidp, index, (char *)(page), - &size, 0, is_ephemeral(pool)); - zcache_put_pool(pool); - } - local_irq_restore(flags); - return ret; -} - -static int zcache_flush_page(int cli_id, int pool_id, - struct tmem_oid *oidp, uint32_t index) -{ - struct tmem_pool *pool; - int ret = -1; - unsigned long flags; - - local_irq_save(flags); - zcache_flush_total++; - pool = zcache_get_pool_by_id(cli_id, pool_id); - if (likely(pool != NULL)) { - if (atomic_read(&pool->obj_count) > 0) - ret = tmem_flush_page(pool, oidp, index); - zcache_put_pool(pool); - } - if (ret >= 0) - zcache_flush_found++; - local_irq_restore(flags); - return ret; -} - -static int zcache_flush_object(int cli_id, int pool_id, - struct tmem_oid *oidp) -{ - struct tmem_pool *pool; - int ret = -1; - unsigned long flags; - - local_irq_save(flags); - zcache_flobj_total++; - pool = zcache_get_pool_by_id(cli_id, pool_id); - if (likely(pool != NULL)) { - if (atomic_read(&pool->obj_count) > 0) - ret = tmem_flush_object(pool, oidp); - zcache_put_pool(pool); - } - if (ret >= 0) - zcache_flobj_found++; - local_irq_restore(flags); - return ret; -} - -static int zcache_destroy_pool(int cli_id, int pool_id) -{ - struct tmem_pool *pool = NULL; - struct zcache_client *cli = NULL; - int ret = -1; - - if (pool_id < 0) - goto out; - if (cli_id == LOCAL_CLIENT) - cli = &zcache_host; - else if ((unsigned int)cli_id < MAX_CLIENTS) - cli = &zcache_clients[cli_id]; - if (cli == NULL) - goto out; - atomic_inc(&cli->refcount); - pool = cli->tmem_pools[pool_id]; - if (pool == NULL) - goto out; - cli->tmem_pools[pool_id] = NULL; - /* wait for pool activity on other cpus to quiesce */ - while (atomic_read(&pool->refcount) != 0) - ; - atomic_dec(&cli->refcount); - local_bh_disable(); - ret = tmem_destroy_pool(pool); - local_bh_enable(); - kfree(pool); - pr_info("qcache: destroyed pool id=%d, cli_id=%d\n", - pool_id, cli_id); -out: - return ret; -} - -static int zcache_new_pool(uint16_t cli_id, uint32_t flags) -{ - int poolid = -1; - struct tmem_pool *pool; - struct zcache_client *cli = NULL; - - if (cli_id == LOCAL_CLIENT) - cli = &zcache_host; - else if ((unsigned int)cli_id < MAX_CLIENTS) - cli = &zcache_clients[cli_id]; - if (cli == NULL) - goto out; - atomic_inc(&cli->refcount); - pool = kmalloc(sizeof(struct tmem_pool), GFP_KERNEL); - if (pool == NULL) { - pr_info("qcache: pool creation failed: out of memory\n"); - goto out; - } - - for (poolid = 0; poolid < MAX_POOLS_PER_CLIENT; poolid++) - if (cli->tmem_pools[poolid] == NULL) - break; - if (poolid >= MAX_POOLS_PER_CLIENT) { - pr_info("qcache: pool creation failed: max exceeded\n"); - kfree(pool); - poolid = -1; - goto out; - } - atomic_set(&pool->refcount, 0); - pool->client = cli; - pool->pool_id = poolid; - tmem_new_pool(pool, flags); - cli->tmem_pools[poolid] = pool; - pr_info("qcache: created %s tmem pool, id=%d, client=%d\n", - flags & TMEM_POOL_PERSIST ? "persistent" : "ephemeral", - poolid, cli_id); -out: - if (cli != NULL) - atomic_dec(&cli->refcount); - return poolid; -} - -/********** - * Two kernel functionalities currently can be layered on top of tmem. - * These are "cleancache" which is used as a second-chance cache for clean - * page cache pages; and "frontswap" which is used for swap pages - * to avoid writes to disk. A generic "shim" is provided here for each - * to translate in-kernel semantics to zcache semantics. - */ - -static void zcache_cleancache_put_page(int pool_id, - struct cleancache_filekey key, - pgoff_t index, struct page *page) -{ - u32 ind = (u32) index; - struct tmem_oid oid = *(struct tmem_oid *)&key; - - if (likely(ind == index)) - (void)zcache_put_page(LOCAL_CLIENT, pool_id, &oid, index, page); -} - -static int zcache_cleancache_get_page(int pool_id, - struct cleancache_filekey key, - pgoff_t index, struct page *page) -{ - u32 ind = (u32) index; - struct tmem_oid oid = *(struct tmem_oid *)&key; - int ret = -1; - - if (likely(ind == index)) - ret = zcache_get_page(LOCAL_CLIENT, pool_id, &oid, index, page); - return ret; -} - -static void zcache_cleancache_flush_page(int pool_id, - struct cleancache_filekey key, - pgoff_t index) -{ - u32 ind = (u32) index; - struct tmem_oid oid = *(struct tmem_oid *)&key; - - if (likely(ind == index)) - (void)zcache_flush_page(LOCAL_CLIENT, pool_id, &oid, ind); -} - -static void zcache_cleancache_flush_inode(int pool_id, - struct cleancache_filekey key) -{ - struct tmem_oid oid = *(struct tmem_oid *)&key; - - (void)zcache_flush_object(LOCAL_CLIENT, pool_id, &oid); -} - -static void zcache_cleancache_flush_fs(int pool_id) -{ - if (pool_id >= 0) - (void)zcache_destroy_pool(LOCAL_CLIENT, pool_id); -} - -static int zcache_cleancache_init_fs(size_t pagesize) -{ - BUG_ON(sizeof(struct cleancache_filekey) != - sizeof(struct tmem_oid)); - BUG_ON(pagesize != PAGE_SIZE); - return zcache_new_pool(LOCAL_CLIENT, 0); -} - -static int zcache_cleancache_init_shared_fs(char *uuid, size_t pagesize) -{ - /* shared pools are unsupported and map to private */ - BUG_ON(sizeof(struct cleancache_filekey) != - sizeof(struct tmem_oid)); - BUG_ON(pagesize != PAGE_SIZE); - return zcache_new_pool(LOCAL_CLIENT, 0); -} - -static struct cleancache_ops zcache_cleancache_ops = { - .put_page = zcache_cleancache_put_page, - .get_page = zcache_cleancache_get_page, - .invalidate_page = zcache_cleancache_flush_page, - .invalidate_inode = zcache_cleancache_flush_inode, - .invalidate_fs = zcache_cleancache_flush_fs, - .init_shared_fs = zcache_cleancache_init_shared_fs, - .init_fs = zcache_cleancache_init_fs -}; - -struct cleancache_ops zcache_cleancache_register_ops(void) -{ - struct cleancache_ops old_ops = - cleancache_register_ops(&zcache_cleancache_ops); - - return old_ops; -} - -static int __init qcache_init(void) -{ - int ret = 0; - struct qcache_info *qc = &qcache_info; - struct fmem_data *fdp; - int bitmap_size; - unsigned int cpu; - struct cleancache_ops old_ops; - -#ifdef CONFIG_SYSFS - ret = sysfs_create_group(mm_kobj, &qcache_attr_group); - if (ret) { - pr_err("qcache: can't create sysfs\n"); - goto out; - } -#endif /* CONFIG_SYSFS */ - - fdp = fmem_get_info(); - qc->addr = fdp->virt; - qc->pages = fdp->size >> PAGE_SHIFT; - if (!qc->pages) - goto out; - - tmem_register_hostops(&zcache_hostops); - tmem_register_pamops(&zcache_pamops); - for_each_online_cpu(cpu) { - per_cpu(zcache_dstmem, cpu) = (void *)__get_free_pages( - GFP_KERNEL | __GFP_REPEAT, - LZO_DSTMEM_PAGE_ORDER), - per_cpu(zcache_workmem, cpu) = - kzalloc(LZO1X_MEM_COMPRESS, - GFP_KERNEL | __GFP_REPEAT); - } - zcache_objnode_cache = kmem_cache_create("zcache_objnode", - sizeof(struct tmem_objnode), 0, 0, NULL); - zcache_obj_cache = kmem_cache_create("zcache_obj", - sizeof(struct tmem_obj), 0, 0, NULL); - ret = zcache_new_client(LOCAL_CLIENT); - if (ret) { - pr_err("qcache: can't create client\n"); - goto out; - } - - zbud_init(); - old_ops = zcache_cleancache_register_ops(); - pr_info("qcache: cleancache enabled using kernel " - "transcendent memory and compression buddies\n"); - if (old_ops.init_fs != NULL) - pr_warning("qcache: cleancache_ops overridden"); - - - bitmap_size = BITS_TO_LONGS(qc->pages) * sizeof(long); - - qc->bitmap = kzalloc(bitmap_size, GFP_KERNEL); - if (!qc->bitmap) { - pr_info("can't allocate qcache bitmap!\n"); - ret = -ENOMEM; - goto out; - } - spin_lock_init(&qc->lock); - - fmem_set_state(FMEM_T_STATE); - -out: - return ret; -} - -module_init(qcache_init) diff --git a/drivers/staging/qcache/tmem.c b/drivers/staging/qcache/tmem.c deleted file mode 100644 index 40f22460..00000000 --- a/drivers/staging/qcache/tmem.c +++ /dev/null @@ -1,833 +0,0 @@ -/* - * In-kernel transcendent memory (generic implementation) - * - * Copyright (c) 2009-2011, Dan Magenheimer, Oracle Corp. - * Copyright (c) 2011, The Linux Foundation. All rights reserved. - * - * The primary purpose of Transcedent Memory ("tmem") is to map object-oriented - * "handles" (triples containing a pool id, and object id, and an index), to - * pages in a page-accessible memory (PAM). Tmem references the PAM pages via - * an abstract "pampd" (PAM page-descriptor), which can be operated on by a - * set of functions (pamops). Each pampd contains some representation of - * PAGE_SIZE bytes worth of data. Tmem must support potentially millions of - * pages and must be able to insert, find, and delete these pages at a - * potential frequency of thousands per second concurrently across many CPUs, - * (and, if used with KVM, across many vcpus across many guests). - * Tmem is tracked with a hierarchy of data structures, organized by - * the elements in a handle-tuple: pool_id, object_id, and page index. - * One or more "clients" (e.g. guests) each provide one or more tmem_pools. - * Each pool, contains a hash table of rb_trees of tmem_objs. Each - * tmem_obj contains a radix-tree-like tree of pointers, with intermediate - * nodes called tmem_objnodes. Each leaf pointer in this tree points to - * a pampd, which is accessible only through a small set of callbacks - * registered by the PAM implementation (see tmem_register_pamops). Tmem - * does all memory allocation via a set of callbacks registered by the tmem - * host implementation (e.g. see tmem_register_hostops). - */ - -#include -#include -#include - -#include "tmem.h" - -/* data structure sentinels used for debugging... see tmem.h */ -#define POOL_SENTINEL 0x87658765 -#define OBJ_SENTINEL 0x12345678 -#define OBJNODE_SENTINEL 0xfedcba09 - -static bool tmem_enabled; - -static void lock_tmem_state(void) -{ - lock_fmem_state(); -} - -static void unlock_tmem_state(void) -{ - unlock_fmem_state(); -} - -/* - * A tmem host implementation must use this function to register callbacks - * for memory allocation. - */ -static struct tmem_hostops tmem_hostops; - -static void tmem_objnode_tree_init(void); - -void tmem_register_hostops(struct tmem_hostops *m) -{ - tmem_objnode_tree_init(); - tmem_hostops = *m; -} - -/* - * A tmem host implementation must use this function to register - * callbacks for a page-accessible memory (PAM) implementation - */ -static struct tmem_pamops tmem_pamops; - -void tmem_register_pamops(struct tmem_pamops *m) -{ - tmem_pamops = *m; -} - -/* - * Oid's are potentially very sparse and tmem_objs may have an indeterminately - * short life, being added and deleted at a relatively high frequency. - * So an rb_tree is an ideal data structure to manage tmem_objs. But because - * of the potentially huge number of tmem_objs, each pool manages a hashtable - * of rb_trees to reduce search, insert, delete, and rebalancing time. - * Each hashbucket also has a lock to manage concurrent access. - * - * The following routines manage tmem_objs. When any tmem_obj is accessed, - * the hashbucket lock must be held. - */ - -/* searches for object==oid in pool, returns locked object if found */ -static struct tmem_obj *tmem_obj_find(struct tmem_hashbucket *hb, - struct tmem_oid *oidp) -{ - struct rb_node *rbnode; - struct tmem_obj *obj; - - rbnode = hb->obj_rb_root.rb_node; - while (rbnode) { - BUG_ON(RB_EMPTY_NODE(rbnode)); - obj = rb_entry(rbnode, struct tmem_obj, rb_tree_node); - switch (tmem_oid_compare(oidp, &obj->oid)) { - case 0: /* equal */ - goto out; - case -1: - rbnode = rbnode->rb_left; - break; - case 1: - rbnode = rbnode->rb_right; - break; - } - } - obj = NULL; -out: - return obj; -} - -static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *); - -/* free an object that has no more pampds in it */ -static void tmem_obj_free(struct tmem_obj *obj, struct tmem_hashbucket *hb) -{ - struct tmem_pool *pool; - - BUG_ON(obj == NULL); - ASSERT_SENTINEL(obj, OBJ); - BUG_ON(obj->pampd_count > 0); - pool = obj->pool; - BUG_ON(pool == NULL); - if (obj->objnode_tree_root != NULL) /* may be "stump" with no leaves */ - tmem_pampd_destroy_all_in_obj(obj); - BUG_ON(obj->objnode_tree_root != NULL); - BUG_ON((long)obj->objnode_count != 0); - atomic_dec(&pool->obj_count); - BUG_ON(atomic_read(&pool->obj_count) < 0); - INVERT_SENTINEL(obj, OBJ); - obj->pool = NULL; - tmem_oid_set_invalid(&obj->oid); - rb_erase(&obj->rb_tree_node, &hb->obj_rb_root); -} - -/* - * initialize, and insert an tmem_object_root (called only if find failed) - */ -static void tmem_obj_init(struct tmem_obj *obj, struct tmem_hashbucket *hb, - struct tmem_pool *pool, - struct tmem_oid *oidp) -{ - struct rb_root *root = &hb->obj_rb_root; - struct rb_node **new = &(root->rb_node), *parent = NULL; - struct tmem_obj *this; - - BUG_ON(pool == NULL); - atomic_inc(&pool->obj_count); - obj->objnode_tree_height = 0; - obj->objnode_tree_root = NULL; - obj->pool = pool; - obj->oid = *oidp; - obj->objnode_count = 0; - obj->pampd_count = 0; - (*tmem_pamops.new_obj)(obj); - SET_SENTINEL(obj, OBJ); - while (*new) { - BUG_ON(RB_EMPTY_NODE(*new)); - this = rb_entry(*new, struct tmem_obj, rb_tree_node); - parent = *new; - switch (tmem_oid_compare(oidp, &this->oid)) { - case 0: - BUG(); /* already present; should never happen! */ - break; - case -1: - new = &(*new)->rb_left; - break; - case 1: - new = &(*new)->rb_right; - break; - } - } - rb_link_node(&obj->rb_tree_node, parent, new); - rb_insert_color(&obj->rb_tree_node, root); -} - -/* - * Tmem is managed as a set of tmem_pools with certain attributes, such as - * "ephemeral" vs "persistent". These attributes apply to all tmem_objs - * and all pampds that belong to a tmem_pool. A tmem_pool is created - * or deleted relatively rarely (for example, when a filesystem is - * mounted or unmounted. - */ - -/* flush all data from a pool and, optionally, free it */ -static void tmem_pool_flush(struct tmem_pool *pool, bool destroy) -{ - struct rb_node *rbnode; - struct tmem_obj *obj; - struct tmem_hashbucket *hb = &pool->hashbucket[0]; - int i; - - BUG_ON(pool == NULL); - for (i = 0; i < TMEM_HASH_BUCKETS; i++, hb++) { - spin_lock(&hb->lock); - rbnode = rb_first(&hb->obj_rb_root); - while (rbnode != NULL) { - obj = rb_entry(rbnode, struct tmem_obj, rb_tree_node); - rbnode = rb_next(rbnode); - tmem_pampd_destroy_all_in_obj(obj); - tmem_obj_free(obj, hb); - (*tmem_hostops.obj_free)(obj, pool); - } - spin_unlock(&hb->lock); - } - if (destroy) - list_del(&pool->pool_list); -} - -/* - * A tmem_obj contains a radix-tree-like tree in which the intermediate - * nodes are called tmem_objnodes. (The kernel lib/radix-tree.c implementation - * is very specialized and tuned for specific uses and is not particularly - * suited for use from this code, though some code from the core algorithms has - * been reused, thus the copyright notices below). Each tmem_objnode contains - * a set of pointers which point to either a set of intermediate tmem_objnodes - * or a set of of pampds. - * - * Portions Copyright (C) 2001 Momchil Velikov - * Portions Copyright (C) 2001 Christoph Hellwig - * Portions Copyright (C) 2005 SGI, Christoph Lameter - */ - -struct tmem_objnode_tree_path { - struct tmem_objnode *objnode; - int offset; -}; - -/* objnode height_to_maxindex translation */ -static unsigned long tmem_objnode_tree_h2max[OBJNODE_TREE_MAX_PATH + 1]; - -static void tmem_objnode_tree_init(void) -{ - unsigned int ht, tmp; - - for (ht = 0; ht < ARRAY_SIZE(tmem_objnode_tree_h2max); ht++) { - tmp = ht * OBJNODE_TREE_MAP_SHIFT; - if (tmp >= OBJNODE_TREE_INDEX_BITS) - tmem_objnode_tree_h2max[ht] = ~0UL; - else - tmem_objnode_tree_h2max[ht] = - (~0UL >> (OBJNODE_TREE_INDEX_BITS - tmp - 1)) >> 1; - } -} - -static struct tmem_objnode *tmem_objnode_alloc(struct tmem_obj *obj) -{ - struct tmem_objnode *objnode; - - ASSERT_SENTINEL(obj, OBJ); - BUG_ON(obj->pool == NULL); - ASSERT_SENTINEL(obj->pool, POOL); - objnode = (*tmem_hostops.objnode_alloc)(obj->pool); - if (unlikely(objnode == NULL)) - goto out; - objnode->obj = obj; - SET_SENTINEL(objnode, OBJNODE); - memset(&objnode->slots, 0, sizeof(objnode->slots)); - objnode->slots_in_use = 0; - obj->objnode_count++; -out: - return objnode; -} - -static void tmem_objnode_free(struct tmem_objnode *objnode) -{ - struct tmem_pool *pool; - int i; - - BUG_ON(objnode == NULL); - for (i = 0; i < OBJNODE_TREE_MAP_SIZE; i++) - BUG_ON(objnode->slots[i] != NULL); - ASSERT_SENTINEL(objnode, OBJNODE); - INVERT_SENTINEL(objnode, OBJNODE); - BUG_ON(objnode->obj == NULL); - ASSERT_SENTINEL(objnode->obj, OBJ); - pool = objnode->obj->pool; - BUG_ON(pool == NULL); - ASSERT_SENTINEL(pool, POOL); - objnode->obj->objnode_count--; - objnode->obj = NULL; - (*tmem_hostops.objnode_free)(objnode, pool); -} - -/* - * lookup index in object and return associated pampd (or NULL if not found) - */ -static void **__tmem_pampd_lookup_in_obj(struct tmem_obj *obj, uint32_t index) -{ - unsigned int height, shift; - struct tmem_objnode **slot = NULL; - - BUG_ON(obj == NULL); - ASSERT_SENTINEL(obj, OBJ); - BUG_ON(obj->pool == NULL); - ASSERT_SENTINEL(obj->pool, POOL); - - height = obj->objnode_tree_height; - if (index > tmem_objnode_tree_h2max[obj->objnode_tree_height]) - goto out; - if (height == 0 && obj->objnode_tree_root) { - slot = &obj->objnode_tree_root; - goto out; - } - shift = (height-1) * OBJNODE_TREE_MAP_SHIFT; - slot = &obj->objnode_tree_root; - while (height > 0) { - if (*slot == NULL) - goto out; - slot = (struct tmem_objnode **) - ((*slot)->slots + - ((index >> shift) & OBJNODE_TREE_MAP_MASK)); - shift -= OBJNODE_TREE_MAP_SHIFT; - height--; - } -out: - return slot != NULL ? (void **)slot : NULL; -} - -static void *tmem_pampd_lookup_in_obj(struct tmem_obj *obj, uint32_t index) -{ - struct tmem_objnode **slot; - - slot = (struct tmem_objnode **)__tmem_pampd_lookup_in_obj(obj, index); - return slot != NULL ? *slot : NULL; -} - -static void *tmem_pampd_replace_in_obj(struct tmem_obj *obj, uint32_t index, - void *new_pampd) -{ - struct tmem_objnode **slot; - void *ret = NULL; - - slot = (struct tmem_objnode **)__tmem_pampd_lookup_in_obj(obj, index); - if ((slot != NULL) && (*slot != NULL)) { - void *old_pampd = *(void **)slot; - *(void **)slot = new_pampd; - (*tmem_pamops.free)(old_pampd, obj->pool, NULL, 0); - ret = new_pampd; - } - return ret; -} - -static int tmem_pampd_add_to_obj(struct tmem_obj *obj, uint32_t index, - void *pampd) -{ - int ret = 0; - struct tmem_objnode *objnode = NULL, *newnode, *slot; - unsigned int height, shift; - int offset = 0; - - /* if necessary, extend the tree to be higher */ - if (index > tmem_objnode_tree_h2max[obj->objnode_tree_height]) { - height = obj->objnode_tree_height + 1; - if (index > tmem_objnode_tree_h2max[height]) - while (index > tmem_objnode_tree_h2max[height]) - height++; - if (obj->objnode_tree_root == NULL) { - obj->objnode_tree_height = height; - goto insert; - } - do { - newnode = tmem_objnode_alloc(obj); - if (!newnode) { - ret = -ENOMEM; - goto out; - } - newnode->slots[0] = obj->objnode_tree_root; - newnode->slots_in_use = 1; - obj->objnode_tree_root = newnode; - obj->objnode_tree_height++; - } while (height > obj->objnode_tree_height); - } -insert: - slot = obj->objnode_tree_root; - height = obj->objnode_tree_height; - shift = (height-1) * OBJNODE_TREE_MAP_SHIFT; - while (height > 0) { - if (slot == NULL) { - /* add a child objnode. */ - slot = tmem_objnode_alloc(obj); - if (!slot) { - ret = -ENOMEM; - goto out; - } - if (objnode) { - - objnode->slots[offset] = slot; - objnode->slots_in_use++; - } else - obj->objnode_tree_root = slot; - } - /* go down a level */ - offset = (index >> shift) & OBJNODE_TREE_MAP_MASK; - objnode = slot; - slot = objnode->slots[offset]; - shift -= OBJNODE_TREE_MAP_SHIFT; - height--; - } - BUG_ON(slot != NULL); - if (objnode) { - objnode->slots_in_use++; - objnode->slots[offset] = pampd; - } else - obj->objnode_tree_root = pampd; - obj->pampd_count++; -out: - return ret; -} - -static void *tmem_pampd_delete_from_obj(struct tmem_obj *obj, uint32_t index) -{ - struct tmem_objnode_tree_path path[OBJNODE_TREE_MAX_PATH + 1]; - struct tmem_objnode_tree_path *pathp = path; - struct tmem_objnode *slot = NULL; - unsigned int height, shift; - int offset; - - BUG_ON(obj == NULL); - ASSERT_SENTINEL(obj, OBJ); - BUG_ON(obj->pool == NULL); - ASSERT_SENTINEL(obj->pool, POOL); - height = obj->objnode_tree_height; - if (index > tmem_objnode_tree_h2max[height]) - goto out; - slot = obj->objnode_tree_root; - if (height == 0 && obj->objnode_tree_root) { - obj->objnode_tree_root = NULL; - goto out; - } - shift = (height - 1) * OBJNODE_TREE_MAP_SHIFT; - pathp->objnode = NULL; - do { - if (slot == NULL) - goto out; - pathp++; - offset = (index >> shift) & OBJNODE_TREE_MAP_MASK; - pathp->offset = offset; - pathp->objnode = slot; - slot = slot->slots[offset]; - shift -= OBJNODE_TREE_MAP_SHIFT; - height--; - } while (height > 0); - if (slot == NULL) - goto out; - while (pathp->objnode) { - pathp->objnode->slots[pathp->offset] = NULL; - pathp->objnode->slots_in_use--; - if (pathp->objnode->slots_in_use) { - if (pathp->objnode == obj->objnode_tree_root) { - while (obj->objnode_tree_height > 0 && - obj->objnode_tree_root->slots_in_use == 1 && - obj->objnode_tree_root->slots[0]) { - struct tmem_objnode *to_free = - obj->objnode_tree_root; - - obj->objnode_tree_root = - to_free->slots[0]; - obj->objnode_tree_height--; - to_free->slots[0] = NULL; - to_free->slots_in_use = 0; - tmem_objnode_free(to_free); - } - } - goto out; - } - tmem_objnode_free(pathp->objnode); /* 0 slots used, free it */ - pathp--; - } - obj->objnode_tree_height = 0; - obj->objnode_tree_root = NULL; - -out: - if (slot != NULL) - obj->pampd_count--; - BUG_ON(obj->pampd_count < 0); - return slot; -} - -/* recursively walk the objnode_tree destroying pampds and objnodes */ -static void tmem_objnode_node_destroy(struct tmem_obj *obj, - struct tmem_objnode *objnode, - unsigned int ht) -{ - int i; - - if (ht == 0) - return; - for (i = 0; i < OBJNODE_TREE_MAP_SIZE; i++) { - if (objnode->slots[i]) { - if (ht == 1) { - obj->pampd_count--; - (*tmem_pamops.free)(objnode->slots[i], - obj->pool, NULL, 0); - objnode->slots[i] = NULL; - continue; - } - tmem_objnode_node_destroy(obj, objnode->slots[i], ht-1); - tmem_objnode_free(objnode->slots[i]); - objnode->slots[i] = NULL; - } - } -} - -static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *obj) -{ - if (obj->objnode_tree_root == NULL) - return; - if (obj->objnode_tree_height == 0) { - obj->pampd_count--; - (*tmem_pamops.free)(obj->objnode_tree_root, obj->pool, NULL, 0); - } else { - tmem_objnode_node_destroy(obj, obj->objnode_tree_root, - obj->objnode_tree_height); - tmem_objnode_free(obj->objnode_tree_root); - obj->objnode_tree_height = 0; - } - obj->objnode_tree_root = NULL; - (*tmem_pamops.free_obj)(obj->pool, obj); -} - -/* - * Tmem is operated on by a set of well-defined actions: - * "put", "get", "flush", "flush_object", "new pool" and "destroy pool". - * (The tmem ABI allows for subpages and exchanges but these operations - * are not included in this implementation.) - * - * These "tmem core" operations are implemented in the following functions. - */ - -/* - * "Put" a page, e.g. copy a page from the kernel into newly allocated - * PAM space (if such space is available). Tmem_put is complicated by - * a corner case: What if a page with matching handle already exists in - * tmem? To guarantee coherency, one of two actions is necessary: Either - * the data for the page must be overwritten, or the page must be - * "flushed" so that the data is not accessible to a subsequent "get". - * Since these "duplicate puts" are relatively rare, this implementation - * always flushes for simplicity. - */ -int tmem_put(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index, - char *data, size_t size, bool raw, bool ephemeral) -{ - struct tmem_obj *obj = NULL, *objfound = NULL, *objnew = NULL; - void *pampd = NULL, *pampd_del = NULL; - int ret = -ENOMEM; - struct tmem_hashbucket *hb; - - lock_tmem_state(); - if (!tmem_enabled) - goto disabled; - hb = &pool->hashbucket[tmem_oid_hash(oidp)]; - spin_lock(&hb->lock); - obj = objfound = tmem_obj_find(hb, oidp); - if (obj != NULL) { - pampd = tmem_pampd_lookup_in_obj(objfound, index); - if (pampd != NULL) { - /* if found, is a dup put, flush the old one */ - pampd_del = tmem_pampd_delete_from_obj(obj, index); - BUG_ON(pampd_del != pampd); - (*tmem_pamops.free)(pampd, pool, oidp, index); - if (obj->pampd_count == 0) { - objnew = obj; - objfound = NULL; - } - pampd = NULL; - } - } else { - obj = objnew = (*tmem_hostops.obj_alloc)(pool); - if (unlikely(obj == NULL)) { - ret = -ENOMEM; - goto out; - } - tmem_obj_init(obj, hb, pool, oidp); - } - BUG_ON(obj == NULL); - BUG_ON(((objnew != obj) && (objfound != obj)) || (objnew == objfound)); - pampd = (*tmem_pamops.create)(data, size, raw, ephemeral, - obj->pool, &obj->oid, index); - if (unlikely(pampd == NULL)) - goto free; - ret = tmem_pampd_add_to_obj(obj, index, pampd); - if (unlikely(ret == -ENOMEM)) - /* may have partially built objnode tree ("stump") */ - goto delete_and_free; - goto out; - -delete_and_free: - (void)tmem_pampd_delete_from_obj(obj, index); -free: - if (pampd) - (*tmem_pamops.free)(pampd, pool, NULL, 0); - if (objnew) { - tmem_obj_free(objnew, hb); - (*tmem_hostops.obj_free)(objnew, pool); - } -out: - spin_unlock(&hb->lock); -disabled: - unlock_tmem_state(); - return ret; -} - -/* - * "Get" a page, e.g. if one can be found, copy the tmem page with the - * matching handle from PAM space to the kernel. By tmem definition, - * when a "get" is successful on an ephemeral page, the page is "flushed", - * and when a "get" is successful on a persistent page, the page is retained - * in tmem. Note that to preserve - * coherency, "get" can never be skipped if tmem contains the data. - * That is, if a get is done with a certain handle and fails, any - * subsequent "get" must also fail (unless of course there is a - * "put" done with the same handle). - - */ -int tmem_get(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index, - char *data, size_t *size, bool raw, int get_and_free) -{ - struct tmem_obj *obj; - void *pampd; - bool ephemeral = is_ephemeral(pool); - int ret = -1; - struct tmem_hashbucket *hb; - bool free = (get_and_free == 1) || ((get_and_free == 0) && ephemeral); - bool lock_held = false; - - lock_tmem_state(); - if (!tmem_enabled) - goto disabled; - hb = &pool->hashbucket[tmem_oid_hash(oidp)]; - spin_lock(&hb->lock); - lock_held = true; - obj = tmem_obj_find(hb, oidp); - if (obj == NULL) - goto out; - if (free) - pampd = tmem_pampd_delete_from_obj(obj, index); - else - pampd = tmem_pampd_lookup_in_obj(obj, index); - if (pampd == NULL) - goto out; - if (free) { - if (obj->pampd_count == 0) { - tmem_obj_free(obj, hb); - (*tmem_hostops.obj_free)(obj, pool); - obj = NULL; - } - } - if (tmem_pamops.is_remote(pampd)) { - lock_held = false; - spin_unlock(&hb->lock); - } - if (free) - ret = (*tmem_pamops.get_data_and_free)( - data, size, raw, pampd, pool, oidp, index); - else - ret = (*tmem_pamops.get_data)( - data, size, raw, pampd, pool, oidp, index); - if (ret < 0) - goto out; - ret = 0; -out: - if (lock_held) - spin_unlock(&hb->lock); -disabled: - unlock_tmem_state(); - return ret; -} - -/* - * If a page in tmem matches the handle, "flush" this page from tmem such - * that any subsequent "get" does not succeed (unless, of course, there - * was another "put" with the same handle). - */ -int tmem_flush_page(struct tmem_pool *pool, - struct tmem_oid *oidp, uint32_t index) -{ - struct tmem_obj *obj; - void *pampd; - int ret = -1; - struct tmem_hashbucket *hb; - - hb = &pool->hashbucket[tmem_oid_hash(oidp)]; - spin_lock(&hb->lock); - obj = tmem_obj_find(hb, oidp); - if (obj == NULL) - goto out; - pampd = tmem_pampd_delete_from_obj(obj, index); - if (pampd == NULL) - goto out; - (*tmem_pamops.free)(pampd, pool, oidp, index); - if (obj->pampd_count == 0) { - tmem_obj_free(obj, hb); - (*tmem_hostops.obj_free)(obj, pool); - } - ret = 0; - -out: - spin_unlock(&hb->lock); - return ret; -} - -/* - * If a page in tmem matches the handle, replace the page so that any - * subsequent "get" gets the new page. Returns 0 if - * there was a page to replace, else returns -1. - */ -int tmem_replace(struct tmem_pool *pool, struct tmem_oid *oidp, - uint32_t index, void *new_pampd) -{ - struct tmem_obj *obj; - int ret = -1; - struct tmem_hashbucket *hb; - - lock_tmem_state(); - if (!tmem_enabled) - goto disabled; - hb = &pool->hashbucket[tmem_oid_hash(oidp)]; - spin_lock(&hb->lock); - obj = tmem_obj_find(hb, oidp); - if (obj == NULL) - goto out; - new_pampd = tmem_pampd_replace_in_obj(obj, index, new_pampd); - ret = (*tmem_pamops.replace_in_obj)(new_pampd, obj); -out: - spin_unlock(&hb->lock); -disabled: - unlock_tmem_state(); - return ret; -} - -/* - * "Flush" all pages in tmem matching this oid. - */ -int tmem_flush_object(struct tmem_pool *pool, struct tmem_oid *oidp) -{ - struct tmem_obj *obj; - struct tmem_hashbucket *hb; - int ret = -1; - - hb = &pool->hashbucket[tmem_oid_hash(oidp)]; - spin_lock(&hb->lock); - obj = tmem_obj_find(hb, oidp); - if (obj == NULL) - goto out; - tmem_pampd_destroy_all_in_obj(obj); - tmem_obj_free(obj, hb); - (*tmem_hostops.obj_free)(obj, pool); - ret = 0; - -out: - spin_unlock(&hb->lock); - return ret; -} - -/* - * "Flush" all pages (and tmem_objs) from this tmem_pool and disable - * all subsequent access to this tmem_pool. - */ -int tmem_destroy_pool(struct tmem_pool *pool) -{ - int ret = -1; - - if (pool == NULL) - goto out; - tmem_pool_flush(pool, 1); - ret = 0; -out: - return ret; -} - -int tmem_flush_pool(struct tmem_pool *pool) -{ - int ret = -1; - - if (pool == NULL) - goto out; - tmem_pool_flush(pool, 0); - ret = 0; -out: - return ret; -} - -static LIST_HEAD(tmem_global_pool_list); - -/* - * Create a new tmem_pool with the provided flag and return - * a pool id provided by the tmem host implementation. - */ -void tmem_new_pool(struct tmem_pool *pool, uint32_t flags) -{ - int persistent = flags & TMEM_POOL_PERSIST; - int shared = flags & TMEM_POOL_SHARED; - struct tmem_hashbucket *hb = &pool->hashbucket[0]; - int i; - - for (i = 0; i < TMEM_HASH_BUCKETS; i++, hb++) { - hb->obj_rb_root = RB_ROOT; - spin_lock_init(&hb->lock); - } - INIT_LIST_HEAD(&pool->pool_list); - atomic_set(&pool->obj_count, 0); - SET_SENTINEL(pool, POOL); - list_add_tail(&pool->pool_list, &tmem_global_pool_list); - pool->persistent = persistent; - pool->shared = shared; -} - -/* The following must be called with tmem state locked */ -static void tmem_cleanup(void) -{ - (*tmem_hostops.flush_all_obj)(); -} - -void tmem_enable(void) -{ - pr_info("turning tmem on\n"); - tmem_enabled = true; - - (*tmem_hostops.control)(false); -} - -void tmem_disable(void) -{ - pr_info("turning tmem off\n"); - tmem_enabled = false; - - tmem_cleanup(); - (*tmem_hostops.control)(true); -} diff --git a/drivers/staging/qcache/tmem.h b/drivers/staging/qcache/tmem.h deleted file mode 100644 index 359c2010..00000000 --- a/drivers/staging/qcache/tmem.h +++ /dev/null @@ -1,212 +0,0 @@ -/* - * tmem.h - * - * Transcendent memory - * - * Copyright (c) 2009-2011, Dan Magenheimer, Oracle Corp. - * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. - */ - -#ifndef _TMEM_H_ -#define _TMEM_H_ - -#include -#include -#include -#include -#include - -/* - * These are pre-defined by the Xen<->Linux ABI - */ -#define TMEM_PUT_PAGE 4 -#define TMEM_GET_PAGE 5 -#define TMEM_FLUSH_PAGE 6 -#define TMEM_FLUSH_OBJECT 7 -#define TMEM_POOL_PERSIST 1 -#define TMEM_POOL_SHARED 2 -#define TMEM_POOL_PRECOMPRESSED 4 -#define TMEM_POOL_PAGESIZE_SHIFT 4 -#define TMEM_POOL_PAGESIZE_MASK 0xf -#define TMEM_POOL_RESERVED_BITS 0x00ffff00 - -/* - * sentinels have proven very useful for debugging but can be removed - * or disabled before final merge. - */ -#define SENTINELS -#ifdef SENTINELS -#define DECL_SENTINEL uint32_t sentinel; -#define SET_SENTINEL(_x, _y) (_x->sentinel = _y##_SENTINEL) -#define INVERT_SENTINEL(_x, _y) (_x->sentinel = ~_y##_SENTINEL) -#define ASSERT_SENTINEL(_x, _y) WARN_ON(_x->sentinel != _y##_SENTINEL) -#define ASSERT_INVERTED_SENTINEL(_x, _y) WARN_ON(_x->sentinel != ~_y##_SENTINEL) -#else -#define DECL_SENTINEL -#define SET_SENTINEL(_x, _y) do { } while (0) -#define INVERT_SENTINEL(_x, _y) do { } while (0) -#define ASSERT_SENTINEL(_x, _y) do { } while (0) -#define ASSERT_INVERTED_SENTINEL(_x, _y) do { } while (0) -#endif - -/* - * A pool is the highest-level data structure managed by tmem and - * usually corresponds to a large independent set of pages such as - * a filesystem. Each pool has an id, and certain attributes and counters. - * It also contains a set of hash buckets, each of which contains an rbtree - * of objects and a lock to manage concurrency within the pool. - */ - -#define TMEM_HASH_BUCKET_BITS 8 -#define TMEM_HASH_BUCKETS (1<persistent) -#define is_ephemeral(_p) (!(_p->persistent)) - -/* - * An object id ("oid") is large: 192-bits (to ensure, for example, files - * in a modern filesystem can be uniquely identified). - */ - -struct tmem_oid { - uint64_t oid[3]; -}; - -static inline void tmem_oid_set_invalid(struct tmem_oid *oidp) -{ - oidp->oid[0] = oidp->oid[1] = oidp->oid[2] = -1UL; -} - -static inline bool tmem_oid_valid(struct tmem_oid *oidp) -{ - return oidp->oid[0] != -1UL || oidp->oid[1] != -1UL || - oidp->oid[2] != -1UL; -} - -static inline int tmem_oid_compare(struct tmem_oid *left, - struct tmem_oid *right) -{ - int ret; - - if (left->oid[2] == right->oid[2]) { - if (left->oid[1] == right->oid[1]) { - if (left->oid[0] == right->oid[0]) - ret = 0; - else if (left->oid[0] < right->oid[0]) - ret = -1; - else - return 1; - } else if (left->oid[1] < right->oid[1]) - ret = -1; - else - ret = 1; - } else if (left->oid[2] < right->oid[2]) - ret = -1; - else - ret = 1; - return ret; -} - -static inline unsigned tmem_oid_hash(struct tmem_oid *oidp) -{ - return hash_long(oidp->oid[0] ^ oidp->oid[1] ^ oidp->oid[2], - TMEM_HASH_BUCKET_BITS); -} - -/* - * A tmem_obj contains an identifier (oid), pointers to the parent - * pool and the rb_tree to which it belongs, counters, and an ordered - * set of pampds, structured in a radix-tree-like tree. The intermediate - * nodes of the tree are called tmem_objnodes. - */ - -struct tmem_objnode; - -struct tmem_obj { - struct tmem_oid oid; - struct tmem_pool *pool; - struct rb_node rb_tree_node; - struct tmem_objnode *objnode_tree_root; - unsigned int objnode_tree_height; - unsigned long objnode_count; - long pampd_count; - void *extra; /* for private use by pampd implementation */ - DECL_SENTINEL -}; - -#define OBJNODE_TREE_MAP_SHIFT 6 -#define OBJNODE_TREE_MAP_SIZE (1UL << OBJNODE_TREE_MAP_SHIFT) -#define OBJNODE_TREE_MAP_MASK (OBJNODE_TREE_MAP_SIZE-1) -#define OBJNODE_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long)) -#define OBJNODE_TREE_MAX_PATH \ - (OBJNODE_TREE_INDEX_BITS/OBJNODE_TREE_MAP_SHIFT + 2) - -struct tmem_objnode { - struct tmem_obj *obj; - DECL_SENTINEL - void *slots[OBJNODE_TREE_MAP_SIZE]; - unsigned int slots_in_use; -}; - -/* pampd abstract datatype methods provided by the PAM implementation */ -struct tmem_pamops { - void *(*create)(char *, size_t, bool, int, - struct tmem_pool *, struct tmem_oid *, uint32_t); - int (*get_data)(char *, size_t *, bool, void *, struct tmem_pool *, - struct tmem_oid *, uint32_t); - int (*get_data_and_free)(char *, size_t *, bool, void *, - struct tmem_pool *, struct tmem_oid *, - uint32_t); - void (*free)(void *, struct tmem_pool *, struct tmem_oid *, uint32_t); - void (*free_obj)(struct tmem_pool *, struct tmem_obj *); - bool (*is_remote)(void *); - void (*new_obj)(struct tmem_obj *); - int (*replace_in_obj)(void *, struct tmem_obj *); -}; -extern void tmem_register_pamops(struct tmem_pamops *m); - -/* memory allocation methods provided by the host implementation */ -struct tmem_hostops { - struct tmem_obj *(*obj_alloc)(struct tmem_pool *); - void (*obj_free)(struct tmem_obj *, struct tmem_pool *); - struct tmem_objnode *(*objnode_alloc)(struct tmem_pool *); - void (*objnode_free)(struct tmem_objnode *, struct tmem_pool *); - void (*flush_all_obj)(void); - void (*control)(bool); -}; -extern void tmem_register_hostops(struct tmem_hostops *m); - -/* core tmem accessor functions */ -extern int tmem_put(struct tmem_pool *, struct tmem_oid *, uint32_t index, - char *, size_t, bool, bool); -extern int tmem_get(struct tmem_pool *, struct tmem_oid *, uint32_t index, - char *, size_t *, bool, int); -extern int tmem_replace(struct tmem_pool *, struct tmem_oid *, uint32_t index, - void *); -extern int tmem_flush_page(struct tmem_pool *, struct tmem_oid *, - uint32_t index); -extern int tmem_flush_object(struct tmem_pool *, struct tmem_oid *); -extern int tmem_destroy_pool(struct tmem_pool *); -extern int tmem_flush_pool(struct tmem_pool *); -extern void tmem_new_pool(struct tmem_pool *, uint32_t); - -extern void tmem_enable(void); -extern void tmem_disable(void); -#endif /* _TMEM_H */ diff --git a/drivers/staging/zcache/Kconfig b/drivers/staging/zcache/Kconfig index 342676ec..4c9141bd 100644 --- a/drivers/staging/zcache/Kconfig +++ b/drivers/staging/zcache/Kconfig @@ -1,6 +1,7 @@ + config ZCACHE bool "Dynamic compression of swap pages and clean pagecache pages" - depends on (CLEANCACHE) && CRYPTO=y && ZSMALLOC=y + depends on (CLEANCACHE || FRONTSWAP) && CRYPTO=y && ZSMALLOC=y select CRYPTO_LZO default n help diff --git a/drivers/staging/zcache/tmem.c b/drivers/staging/zcache/tmem.c index 1ca66ea9..56c8e606 100644 --- a/drivers/staging/zcache/tmem.c +++ b/drivers/staging/zcache/tmem.c @@ -3,7 +3,7 @@ * * Copyright (c) 2009-2011, Dan Magenheimer, Oracle Corp. * - * The primary purpose of Transcedent Memory ("tmem") is to map object-oriented + * The primary purpose of Transcendent Memory ("tmem") is to map object-oriented * "handles" (triples containing a pool id, and object id, and an index), to * pages in a page-accessible memory (PAM). Tmem references the PAM pages via * an abstract "pampd" (PAM page-descriptor), which can be operated on by a @@ -72,33 +72,49 @@ void tmem_register_pamops(struct tmem_pamops *m) * the hashbucket lock must be held. */ -/* searches for object==oid in pool, returns locked object if found */ -static struct tmem_obj *tmem_obj_find(struct tmem_hashbucket *hb, - struct tmem_oid *oidp) +static struct tmem_obj +*__tmem_obj_find(struct tmem_hashbucket*hb, struct tmem_oid *oidp, + struct rb_node **parent, struct rb_node ***link) { - struct rb_node *rbnode; - struct tmem_obj *obj; - - rbnode = hb->obj_rb_root.rb_node; - while (rbnode) { - BUG_ON(RB_EMPTY_NODE(rbnode)); - obj = rb_entry(rbnode, struct tmem_obj, rb_tree_node); + struct rb_node *_parent = NULL, **rbnode; + struct tmem_obj *obj = NULL; + + rbnode = &hb->obj_rb_root.rb_node; + while (*rbnode) { + BUG_ON(RB_EMPTY_NODE(*rbnode)); + _parent = *rbnode; + obj = rb_entry(*rbnode, struct tmem_obj, + rb_tree_node); switch (tmem_oid_compare(oidp, &obj->oid)) { case 0: /* equal */ goto out; case -1: - rbnode = rbnode->rb_left; + rbnode = &(*rbnode)->rb_left; break; case 1: - rbnode = rbnode->rb_right; + rbnode = &(*rbnode)->rb_right; break; } } + + if (parent) + *parent = _parent; + if (link) + *link = rbnode; + obj = NULL; out: return obj; } + +/* searches for object==oid in pool, returns locked object if found */ +static struct tmem_obj *tmem_obj_find(struct tmem_hashbucket *hb, + struct tmem_oid *oidp) +{ + return __tmem_obj_find(hb, oidp, NULL, NULL); +} + static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *); /* free an object that has no more pampds in it */ @@ -131,8 +147,7 @@ static void tmem_obj_init(struct tmem_obj *obj, struct tmem_hashbucket *hb, struct tmem_oid *oidp) { struct rb_root *root = &hb->obj_rb_root; - struct rb_node **new = &(root->rb_node), *parent = NULL; - struct tmem_obj *this; + struct rb_node **new = NULL, *parent = NULL; BUG_ON(pool == NULL); atomic_inc(&pool->obj_count); @@ -144,22 +159,10 @@ static void tmem_obj_init(struct tmem_obj *obj, struct tmem_hashbucket *hb, obj->pampd_count = 0; (*tmem_pamops.new_obj)(obj); SET_SENTINEL(obj, OBJ); - while (*new) { - BUG_ON(RB_EMPTY_NODE(*new)); - this = rb_entry(*new, struct tmem_obj, rb_tree_node); - parent = *new; - switch (tmem_oid_compare(oidp, &this->oid)) { - case 0: - BUG(); /* already present; should never happen! */ - break; - case -1: - new = &(*new)->rb_left; - break; - case 1: - new = &(*new)->rb_right; - break; - } - } + + if (__tmem_obj_find(hb, oidp, &parent, &new)) + BUG(); + rb_link_node(&obj->rb_tree_node, parent, new); rb_insert_color(&obj->rb_tree_node, root); } diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c index bb927535..f81a1041 100644 --- a/drivers/staging/zcache/zcache-main.c +++ b/drivers/staging/zcache/zcache-main.c @@ -31,13 +31,11 @@ #include #include #include +#include #include "tmem.h" -#include "../zsmalloc/zsmalloc.h" +#include -#if (!defined(CONFIG_CLEANCACHE) && !defined(CONFIG_FRONTSWAP)) -#error "zcache is useless without CONFIG_CLEANCACHE or CONFIG_FRONTSWAP" -#endif #ifdef CONFIG_CLEANCACHE #include #endif @@ -53,15 +51,13 @@ (__GFP_FS | __GFP_NORETRY | __GFP_NOWARN | __GFP_NOMEMALLOC) #endif -#define MAX_POOLS_PER_CLIENT 16 - #define MAX_CLIENTS 16 #define LOCAL_CLIENT ((uint16_t)-1) MODULE_LICENSE("GPL"); struct zcache_client { - struct tmem_pool *tmem_pools[MAX_POOLS_PER_CLIENT]; + struct idr tmem_pools; struct zs_pool *zspool; bool allocated; atomic_t refcount; @@ -78,6 +74,17 @@ static inline uint16_t get_client_id_from_client(struct zcache_client *cli) return cli - &zcache_clients[0]; } +static struct zcache_client *get_zcache_client(uint16_t cli_id) +{ + if (cli_id == LOCAL_CLIENT) + return &zcache_host; + + if ((unsigned int)cli_id < MAX_CLIENTS) + return &zcache_clients[cli_id]; + + return NULL; +} + static inline bool is_local_client(struct zcache_client *cli) { return cli == &zcache_host; @@ -98,7 +105,7 @@ static inline int zcache_comp_op(enum comp_op op, u8 *dst, unsigned int *dlen) { struct crypto_comp *tfm; - int ret = 0; + int ret; BUG_ON(!zcache_comp_pcpu_tfms); tfm = *per_cpu_ptr(zcache_comp_pcpu_tfms, get_cpu()); @@ -110,6 +117,8 @@ static inline int zcache_comp_op(enum comp_op op, case ZCACHE_COMPOP_DECOMPRESS: ret = crypto_comp_decompress(tfm, src, slen, dst, dlen); break; + default: + ret = -EINVAL; } put_cpu(); return ret; @@ -602,16 +611,14 @@ static void zbud_evict_pages(int nr) return; } -static void zbud_init(void) +static void __init zbud_init(void) { int i; INIT_LIST_HEAD(&zbud_buddied_list); - zcache_zbud_buddied_count = 0; - for (i = 0; i < NCHUNKS; i++) { + + for (i = 0; i < NCHUNKS; i++) INIT_LIST_HEAD(&zbud_unbuddied[i].list); - zbud_unbuddied[i].count = 0; - } } #ifdef CONFIG_SYSFS @@ -693,71 +700,71 @@ static unsigned int zv_max_mean_zsize = (PAGE_SIZE / 8) * 5; static atomic_t zv_curr_dist_counts[NCHUNKS]; static atomic_t zv_cumul_dist_counts[NCHUNKS]; -static struct zv_hdr *zv_create(struct zs_pool *pool, uint32_t pool_id, +static unsigned long zv_create(struct zs_pool *pool, uint32_t pool_id, struct tmem_oid *oid, uint32_t index, void *cdata, unsigned clen) { struct zv_hdr *zv; u32 size = clen + sizeof(struct zv_hdr); int chunks = (size + (CHUNK_SIZE - 1)) >> CHUNK_SHIFT; - void *handle = NULL; + unsigned long handle = 0; BUG_ON(!irqs_disabled()); BUG_ON(chunks >= NCHUNKS); - handle = (void *)zs_malloc(pool, size); + handle = zs_malloc(pool, size); if (!handle) goto out; atomic_inc(&zv_curr_dist_counts[chunks]); atomic_inc(&zv_cumul_dist_counts[chunks]); - zv = zs_map_object(pool, (unsigned long)handle, ZS_MM_RW); + zv = zs_map_object(pool, handle, ZS_MM_WO); zv->index = index; zv->oid = *oid; zv->pool_id = pool_id; zv->size = clen; SET_SENTINEL(zv, ZVH); memcpy((char *)zv + sizeof(struct zv_hdr), cdata, clen); - zs_unmap_object(pool, (unsigned long)handle); + zs_unmap_object(pool, handle); out: return handle; } -static void zv_free(struct zs_pool *pool, void *handle) +static void zv_free(struct zs_pool *pool, unsigned long handle) { unsigned long flags; struct zv_hdr *zv; uint16_t size; int chunks; - zv = zs_map_object(pool, (unsigned long)handle, ZS_MM_RW); + zv = zs_map_object(pool, handle, ZS_MM_RW); ASSERT_SENTINEL(zv, ZVH); size = zv->size + sizeof(struct zv_hdr); INVERT_SENTINEL(zv, ZVH); - zs_unmap_object(pool, (unsigned long)handle); + zs_unmap_object(pool, handle); chunks = (size + (CHUNK_SIZE - 1)) >> CHUNK_SHIFT; BUG_ON(chunks >= NCHUNKS); atomic_dec(&zv_curr_dist_counts[chunks]); local_irq_save(flags); - zs_free(pool, (unsigned long)handle); + zs_free(pool, handle); local_irq_restore(flags); } -static void zv_decompress(struct page *page, void *handle) +static void zv_decompress(struct page *page, unsigned long handle) { unsigned int clen = PAGE_SIZE; char *to_va; int ret; struct zv_hdr *zv; - zv = zs_map_object(zcache_host.zspool, (unsigned long)handle, ZS_MM_RW); + zv = zs_map_object(zcache_host.zspool, handle, ZS_MM_RO); BUG_ON(zv->size == 0); ASSERT_SENTINEL(zv, ZVH); to_va = kmap_atomic(page); ret = zcache_comp_op(ZCACHE_COMPOP_DECOMPRESS, (char *)zv + sizeof(*zv), zv->size, to_va, &clen); kunmap_atomic(to_va); - zs_unmap_object(zcache_host.zspool, (unsigned long)handle); + zs_unmap_object(zcache_host.zspool, handle); BUG_ON(ret); BUG_ON(clen != PAGE_SIZE); } @@ -939,21 +946,14 @@ static struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id, uint16_t poolid) struct tmem_pool *pool = NULL; struct zcache_client *cli = NULL; - if (cli_id == LOCAL_CLIENT) - cli = &zcache_host; - else { - if (cli_id >= MAX_CLIENTS) - goto out; - cli = &zcache_clients[cli_id]; - if (cli == NULL) - goto out; - atomic_inc(&cli->refcount); - } - if (poolid < MAX_POOLS_PER_CLIENT) { - pool = cli->tmem_pools[poolid]; - if (pool != NULL) - atomic_inc(&pool->refcount); - } + cli = get_zcache_client(cli_id); + if (!cli) + goto out; + + atomic_inc(&cli->refcount); + pool = idr_find(&cli->tmem_pools, poolid); + if (pool != NULL) + atomic_inc(&pool->refcount); out: return pool; } @@ -971,22 +971,21 @@ static void zcache_put_pool(struct tmem_pool *pool) int zcache_new_client(uint16_t cli_id) { - struct zcache_client *cli = NULL; + struct zcache_client *cli; int ret = -1; - if (cli_id == LOCAL_CLIENT) - cli = &zcache_host; - else if ((unsigned int)cli_id < MAX_CLIENTS) - cli = &zcache_clients[cli_id]; + cli = get_zcache_client(cli_id); + if (cli == NULL) goto out; if (cli->allocated) goto out; cli->allocated = 1; #ifdef CONFIG_FRONTSWAP - cli->zspool = zs_create_pool("zcache", ZCACHE_GFP_MASK); + cli->zspool = zs_create_pool(ZCACHE_GFP_MASK); if (cli->zspool == NULL) goto out; + idr_init(&cli->tmem_pools); #endif ret = 0; out: @@ -1035,45 +1034,38 @@ static int zcache_do_preload(struct tmem_pool *pool) goto out; if (unlikely(zcache_obj_cache == NULL)) goto out; - preempt_disable(); + + /* IRQ has already been disabled. */ kp = &__get_cpu_var(zcache_preloads); while (kp->nr < ARRAY_SIZE(kp->objnodes)) { - preempt_enable_no_resched(); objnode = kmem_cache_alloc(zcache_objnode_cache, ZCACHE_GFP_MASK); if (unlikely(objnode == NULL)) { zcache_failed_alloc++; goto out; } - preempt_disable(); - kp = &__get_cpu_var(zcache_preloads); - if (kp->nr < ARRAY_SIZE(kp->objnodes)) - kp->objnodes[kp->nr++] = objnode; - else - kmem_cache_free(zcache_objnode_cache, objnode); + + kp->objnodes[kp->nr++] = objnode; } - preempt_enable_no_resched(); - obj = kmem_cache_alloc(zcache_obj_cache, ZCACHE_GFP_MASK); - if (unlikely(obj == NULL)) { - zcache_failed_alloc++; - goto out; + + if (!kp->obj) { + obj = kmem_cache_alloc(zcache_obj_cache, ZCACHE_GFP_MASK); + if (unlikely(obj == NULL)) { + zcache_failed_alloc++; + goto out; + } + kp->obj = obj; } - page = (void *)__get_free_page(ZCACHE_GFP_MASK); - if (unlikely(page == NULL)) { - zcache_failed_get_free_pages++; - kmem_cache_free(zcache_obj_cache, obj); - goto out; + + if (!kp->page) { + page = (void *)__get_free_page(ZCACHE_GFP_MASK); + if (unlikely(page == NULL)) { + zcache_failed_get_free_pages++; + goto out; + } + kp->page = page; } - preempt_disable(); - kp = &__get_cpu_var(zcache_preloads); - if (kp->obj == NULL) - kp->obj = obj; - else - kmem_cache_free(zcache_obj_cache, obj); - if (kp->page == NULL) - kp->page = page; - else - free_page((unsigned long)page); + ret = 0; out: return ret; @@ -1247,7 +1239,7 @@ static int zcache_pampd_get_data(char *data, size_t *bufsize, bool raw, int ret = 0; BUG_ON(is_ephemeral(pool)); - zv_decompress((struct page *)(data), pampd); + zv_decompress((struct page *)(data), (unsigned long)pampd); return ret; } @@ -1281,7 +1273,7 @@ static void zcache_pampd_free(void *pampd, struct tmem_pool *pool, atomic_dec(&zcache_curr_eph_pampd_count); BUG_ON(atomic_read(&zcache_curr_eph_pampd_count) < 0); } else { - zv_free(cli->zspool, pampd); + zv_free(cli->zspool, (unsigned long)pampd); atomic_dec(&zcache_curr_pers_pampd_count); BUG_ON(atomic_read(&zcache_curr_pers_pampd_count) < 0); } @@ -1582,15 +1574,14 @@ static int zcache_put_page(int cli_id, int pool_id, struct tmem_oid *oidp, else zcache_failed_pers_puts++; } - zcache_put_pool(pool); - preempt_enable_no_resched(); } else { zcache_put_to_flush++; if (atomic_read(&pool->obj_count) > 0) /* the put fails whether the flush succeeds or not */ (void)tmem_flush_page(pool, oidp, index); - zcache_put_pool(pool); } + + zcache_put_pool(pool); out: return ret; } @@ -1660,22 +1651,21 @@ static int zcache_flush_object(int cli_id, int pool_id, static int zcache_destroy_pool(int cli_id, int pool_id) { struct tmem_pool *pool = NULL; - struct zcache_client *cli = NULL; + struct zcache_client *cli; int ret = -1; if (pool_id < 0) goto out; - if (cli_id == LOCAL_CLIENT) - cli = &zcache_host; - else if ((unsigned int)cli_id < MAX_CLIENTS) - cli = &zcache_clients[cli_id]; + + cli = get_zcache_client(cli_id); if (cli == NULL) goto out; + atomic_inc(&cli->refcount); - pool = cli->tmem_pools[pool_id]; + pool = idr_find(&cli->tmem_pools, pool_id); if (pool == NULL) goto out; - cli->tmem_pools[pool_id] = NULL; + idr_remove(&cli->tmem_pools, pool_id); /* wait for pool activity on other cpus to quiesce */ while (atomic_read(&pool->refcount) != 0) ; @@ -1695,13 +1685,12 @@ static int zcache_new_pool(uint16_t cli_id, uint32_t flags) int poolid = -1; struct tmem_pool *pool; struct zcache_client *cli = NULL; + int r; - if (cli_id == LOCAL_CLIENT) - cli = &zcache_host; - else if ((unsigned int)cli_id < MAX_CLIENTS) - cli = &zcache_clients[cli_id]; + cli = get_zcache_client(cli_id); if (cli == NULL) goto out; + atomic_inc(&cli->refcount); pool = kmalloc(sizeof(struct tmem_pool), GFP_ATOMIC); if (pool == NULL) { @@ -1709,20 +1698,25 @@ static int zcache_new_pool(uint16_t cli_id, uint32_t flags) goto out; } - for (poolid = 0; poolid < MAX_POOLS_PER_CLIENT; poolid++) - if (cli->tmem_pools[poolid] == NULL) - break; - if (poolid >= MAX_POOLS_PER_CLIENT) { - pr_info("zcache: pool creation failed: max exceeded\n"); + do { + r = idr_pre_get(&cli->tmem_pools, GFP_ATOMIC); + if (r != 1) { + kfree(pool); + pr_info("zcache: pool creation failed: out of memory\n"); + goto out; + } + r = idr_get_new(&cli->tmem_pools, pool, &poolid); + } while (r == -EAGAIN); + if (r) { + pr_info("zcache: pool creation failed: error %d\n", r); kfree(pool); - poolid = -1; goto out; } + atomic_set(&pool->refcount, 0); pool->client = cli; pool->pool_id = poolid; tmem_new_pool(pool, flags); - cli->tmem_pools[poolid] = pool; pr_info("zcache: created %s tmem pool, id=%d, client=%d\n", flags & TMEM_POOL_PERSIST ? "persistent" : "ephemeral", poolid, cli_id); @@ -1817,9 +1811,9 @@ static struct cleancache_ops zcache_cleancache_ops = { .init_fs = zcache_cleancache_init_fs }; -struct cleancache_ops *zcache_cleancache_register_ops(void) +struct cleancache_ops zcache_cleancache_register_ops(void) { - struct cleancache_ops *old_ops = + struct cleancache_ops old_ops = cleancache_register_ops(&zcache_cleancache_ops); return old_ops; @@ -1834,7 +1828,7 @@ static int zcache_frontswap_poolid = -1; * Swizzling increases objects per swaptype, increasing tmem concurrency * for heavy swaploads. Later, larger nr_cpus -> larger SWIZ_BITS * Setting SWIZ_BITS to 27 basically reconstructs the swap entry from - * frontswap_get_page(), but has side-effects. Hence using 8. + * frontswap_load(), but has side-effects. Hence using 8. */ #define SWIZ_BITS 8 #define SWIZ_MASK ((1 << SWIZ_BITS) - 1) @@ -1848,7 +1842,7 @@ static inline struct tmem_oid oswiz(unsigned type, u32 ind) return oid; } -static int zcache_frontswap_put_page(unsigned type, pgoff_t offset, +static int zcache_frontswap_store(unsigned type, pgoff_t offset, struct page *page) { u64 ind64 = (u64)offset; @@ -1869,7 +1863,7 @@ static int zcache_frontswap_put_page(unsigned type, pgoff_t offset, /* returns 0 if the page was successfully gotten from frontswap, -1 if * was not present (should never happen!) */ -static int zcache_frontswap_get_page(unsigned type, pgoff_t offset, +static int zcache_frontswap_load(unsigned type, pgoff_t offset, struct page *page) { u64 ind64 = (u64)offset; @@ -1918,8 +1912,8 @@ static void zcache_frontswap_init(unsigned ignored) } static struct frontswap_ops zcache_frontswap_ops = { - .put_page = zcache_frontswap_put_page, - .get_page = zcache_frontswap_get_page, + .store = zcache_frontswap_store, + .load = zcache_frontswap_load, .invalidate_page = zcache_frontswap_flush_page, .invalidate_area = zcache_frontswap_flush_area, .init = zcache_frontswap_init @@ -1940,7 +1934,7 @@ struct frontswap_ops zcache_frontswap_register_ops(void) * NOTHING HAPPENS! */ -static int zcache_enabled; +static int zcache_enabled = 1; static int __init enable_zcache(char *s) { @@ -1980,7 +1974,7 @@ static int __init enable_zcache_compressor(char *s) __setup("zcache=", enable_zcache_compressor); -static int zcache_comp_init(void) +static int __init zcache_comp_init(void) { int ret = 0; @@ -2020,7 +2014,7 @@ static int __init zcache_init(void) goto out; } #endif /* CONFIG_SYSFS */ -#if defined(CONFIG_CLEANCACHE) || defined(CONFIG_FRONTSWAP) + if (zcache_enabled) { unsigned int cpu; @@ -2051,17 +2045,17 @@ static int __init zcache_init(void) pr_err("zcache: can't create client\n"); goto out; } -#endif + #ifdef CONFIG_CLEANCACHE if (zcache_enabled && use_cleancache) { - struct cleancache_ops *old_ops; + struct cleancache_ops old_ops; zbud_init(); register_shrinker(&zcache_shrinker); old_ops = zcache_cleancache_register_ops(); pr_info("zcache: cleancache enabled using kernel " "transcendent memory and compression buddies\n"); - if (old_ops != NULL) + if (old_ops.init_fs != NULL) pr_warning("zcache: cleancache_ops overridden"); } #endif diff --git a/drivers/staging/zram/Makefile b/drivers/staging/zram/Makefile deleted file mode 100644 index cb0f9ced..00000000 --- a/drivers/staging/zram/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -zram-y := zram_drv.o - -obj-$(CONFIG_ZRAM) += zram.o diff --git a/drivers/staging/zram/zram.txt b/drivers/staging/zram/zram.txt deleted file mode 100644 index 765d790a..00000000 --- a/drivers/staging/zram/zram.txt +++ /dev/null @@ -1,77 +0,0 @@ -zram: Compressed RAM based block devices ----------------------------------------- - -Project home: http://compcache.googlecode.com/ - -* Introduction - -The zram module creates RAM based block devices named /dev/zram -( = 0, 1, ...). Pages written to these disks are compressed and stored -in memory itself. These disks allow very fast I/O and compression provides -good amounts of memory savings. Some of the usecases include /tmp storage, -use as swap disks, various caches under /var and maybe many more :) - -Statistics for individual zram devices are exported through sysfs nodes at -/sys/block/zram/ - -* Usage - -Following shows a typical sequence of steps for using zram. - -1) Load Module: - modprobe zram num_devices=4 - This creates 4 devices: /dev/zram{0,1,2,3} - (num_devices parameter is optional. Default: 1) - -2) Set Disksize - Set disk size by writing the value to sysfs node 'disksize'. - The value can be either in bytes or you can use mem suffixes. - Examples: - # Initialize /dev/zram0 with 50MB disksize - echo $((50*1024*1024)) > /sys/block/zram0/disksize - - # Using mem suffixes - echo 256K > /sys/block/zram0/disksize - echo 512M > /sys/block/zram0/disksize - echo 1G > /sys/block/zram0/disksize - -3) Activate: - mkswap /dev/zram0 - swapon /dev/zram0 - - mkfs.ext4 /dev/zram1 - mount /dev/zram1 /tmp - -4) Stats: - Per-device statistics are exported as various nodes under - /sys/block/zram/ - disksize - num_reads - num_writes - invalid_io - notify_free - discard - zero_pages - orig_data_size - compr_data_size - mem_used_total - -5) Deactivate: - swapoff /dev/zram0 - umount /dev/zram1 - -6) Reset: - Write any positive value to 'reset' sysfs node - echo 1 > /sys/block/zram0/reset - echo 1 > /sys/block/zram1/reset - - This frees all the memory allocated for the given device and - resets the disksize to zero. You must set the disksize again - before reusing the device. - -Please report any problems at: - - Mailing list: linux-mm-cc at laptop dot org - - Issue tracker: http://code.google.com/p/compcache/issues/list - -Nitin Gupta -ngupta@vflare.org diff --git a/drivers/staging/zsmalloc/Kconfig b/drivers/staging/zsmalloc/Kconfig deleted file mode 100644 index 08adf5dc..00000000 --- a/drivers/staging/zsmalloc/Kconfig +++ /dev/null @@ -1,24 +0,0 @@ -config ZSMALLOC - tristate "Memory allocator for compressed pages" - depends on MMU - default n - help - zsmalloc is a slab-based memory allocator designed to store - compressed RAM pages. zsmalloc uses virtual memory mapping - in order to reduce fragmentation. However, this results in a - non-standard allocator interface where a handle, not a pointer, is - returned by an alloc(). This handle must be mapped in order to - access the allocated space. - -config PGTABLE_MAPPING - bool "Use page table mapping to access object in zsmalloc" - depends on ZSMALLOC - help - By default, zsmalloc uses a copy-based object mapping method to - access allocations that span two pages. However, if a particular - architecture (ex, ARM) performs VM mapping faster than copying, - then you should select this. This causes zsmalloc to use page table - mapping rather than copying for object mapping. - - You can check speed with zsmalloc benchmark[1]. - [1] https://github.com/spartacus06/zsmalloc diff --git a/drivers/staging/zsmalloc/Makefile b/drivers/staging/zsmalloc/Makefile deleted file mode 100644 index b134848a..00000000 --- a/drivers/staging/zsmalloc/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -zsmalloc-y := zsmalloc-main.o - -obj-$(CONFIG_ZSMALLOC) += zsmalloc.o diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 92c5eafd..b83cded5 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -2445,19 +2445,19 @@ static int msm_hs_runtime_idle(struct device *dev) static int msm_hs_runtime_resume(struct device *dev) { - struct platform_device *pdev = container_of(dev, struct +/* struct platform_device *pdev = container_of(dev, struct platform_device, dev); struct msm_hs_port *msm_uport = &q_uart_port[pdev->id]; - msm_hs_request_clock_on(&msm_uport->uport); + msm_hs_request_clock_on(&msm_uport->uport);*/ return 0; } static int msm_hs_runtime_suspend(struct device *dev) { - struct platform_device *pdev = container_of(dev, struct +/* struct platform_device *pdev = container_of(dev, struct platform_device, dev); struct msm_hs_port *msm_uport = &q_uart_port[pdev->id]; - msm_hs_request_clock_off(&msm_uport->uport); + msm_hs_request_clock_off(&msm_uport->uport);*/ return 0; } diff --git a/drivers/video/msm.zip b/drivers/video/msm.zip new file mode 100644 index 00000000..68bb3918 Binary files /dev/null and b/drivers/video/msm.zip differ diff --git a/drivers/video/msm/external_common.c b/drivers/video/msm/external_common.c index d9bb4b9e..ca70bc0e 100644 --- a/drivers/video/msm/external_common.c +++ b/drivers/video/msm/external_common.c @@ -244,6 +244,7 @@ struct hdmi_disp_mode_timing_type VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p240_16_9), VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i240_4_3), VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i240_16_9), + VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1280x1024p60_5_4) }; EXPORT_SYMBOL(hdmi_common_supported_video_mode_lut); @@ -308,6 +309,7 @@ struct hdmi_disp_mode_timing_type VFRMT_NOT_SUPPORTED(HDMI_VFRMT_720x480p240_16_9), VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i240_4_3), VFRMT_NOT_SUPPORTED(HDMI_VFRMT_1440x480i240_16_9), + HDMI_SETTINGS_1280x1024p60_5_4 }; EXPORT_SYMBOL(hdmi_mhl_supported_video_mode_lut); @@ -829,6 +831,74 @@ static ssize_t hdmi_common_rda_hdmi_primary(struct device *dev, return ret; } +static ssize_t hdmi_common_rda_audio_data_block(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int adb_size = 0; + int adb_count = 0; + ssize_t ret = 0; + char *data = buf; + + if (!external_common_state) + return 0; + + adb_count = 1; + adb_size = external_common_state->adb_size; + ret = sizeof(adb_count) + sizeof(adb_size) + adb_size; + + if (ret > PAGE_SIZE) { + DEV_DBG("%s: Insufficient buffer size\n", __func__); + return 0; + } + + /* Currently only extracting one audio data block */ + memcpy(data, &adb_count, sizeof(adb_count)); + data += sizeof(adb_count); + memcpy(data, &adb_size, sizeof(adb_size)); + data += sizeof(adb_size); + memcpy(data, external_common_state->audio_data_block, + external_common_state->adb_size); + + print_hex_dump(KERN_DEBUG, "AUDIO DATA BLOCK: ", DUMP_PREFIX_NONE, + 32, 8, buf, ret, false); + + return ret; +} + +static ssize_t hdmi_common_rda_spkr_alloc_data_block(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int sadb_size = 0; + int sadb_count = 0; + ssize_t ret = 0; + char *data = buf; + + if (!external_common_state) + return 0; + + sadb_count = 1; + sadb_size = external_common_state->sadb_size; + ret = sizeof(sadb_count) + sizeof(sadb_size) + sadb_size; + + if (ret > PAGE_SIZE) { + DEV_DBG("%s: Insufficient buffer size\n", __func__); + return 0; + } + + /* Currently only extracting one speaker allocation data block */ + memcpy(data, &sadb_count, sizeof(sadb_count)); + data += sizeof(sadb_count); + memcpy(data, &sadb_size, sizeof(sadb_size)); + data += sizeof(sadb_size); + memcpy(data, external_common_state->spkr_alloc_data_block, + external_common_state->sadb_size); + + print_hex_dump(KERN_DEBUG, "SPKR ALLOC DATA BLOCK: ", DUMP_PREFIX_NONE, + 32, 8, buf, ret, false); + + return ret; +} + static DEVICE_ATTR(video_mode, S_IRUGO | S_IWUGO, external_common_rda_video_mode, external_common_wta_video_mode); static DEVICE_ATTR(video_mode_str, S_IRUGO, external_common_rda_video_mode_str, @@ -859,6 +929,10 @@ static DEVICE_ATTR(format_3d, S_IRUGO | S_IWUGO, hdmi_3d_rda_format_3d, hdmi_3d_wta_format_3d); #endif static DEVICE_ATTR(hdmi_primary, S_IRUGO, hdmi_common_rda_hdmi_primary, NULL); +static DEVICE_ATTR(audio_data_block, S_IRUGO, hdmi_common_rda_audio_data_block, + NULL); +static DEVICE_ATTR(spkr_alloc_data_block, S_IRUGO, + hdmi_common_rda_spkr_alloc_data_block, NULL); static struct attribute *external_common_fs_attrs[] = { &dev_attr_video_mode.attr, @@ -887,6 +961,8 @@ static struct attribute *external_common_fs_attrs[] = { &dev_attr_cec_wr_frame.attr, #endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */ &dev_attr_hdmi_primary.attr, + &dev_attr_audio_data_block.attr, + &dev_attr_spkr_alloc_data_block.attr, NULL, }; static struct attribute_group external_common_fs_attr_group = { @@ -1001,6 +1077,8 @@ static struct hdmi_edid_video_mode_property_type 89909, 119880, 148352, 119880, FALSE}, {HDMI_VFRMT_1280x720p120_16_9, 1280, 720, FALSE, 1650, 370, 750, 30, 90000, 120000, 148500, 120000, FALSE}, + {HDMI_VFRMT_1280x1024p60_5_4, 1280, 1024, FALSE, 1688, 408, 1066, 42, + 63981, 60020, 108000, 60000, FALSE}, /* All 1440 H Active */ {HDMI_VFRMT_1440x576i50_4_3, 1440, 576, TRUE, 1728, 288, 625, 24, @@ -1205,45 +1283,33 @@ static void hdmi_edid_extract_latency_fields(const uint8 *in_buf) static void hdmi_edid_extract_speaker_allocation_data(const uint8 *in_buf) { uint8 len; - const uint8 *sad = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 4, + const uint8 *sadb = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 4, &len); - if (sad == NULL) + if (sadb == NULL) + return; + + if (len != MAX_SPKR_ALLOC_DATA_BLOCK_SIZE) return; - external_common_state->speaker_allocation_block = sad[1]; - DEV_DBG("EDID: speaker allocation data SP byte = %08x %s%s%s%s%s%s%s\n", - sad[1], - (sad[1] & BIT(0)) ? "FL/FR," : "", - (sad[1] & BIT(1)) ? "LFE," : "", - (sad[1] & BIT(2)) ? "FC," : "", - (sad[1] & BIT(3)) ? "RL/RR," : "", - (sad[1] & BIT(4)) ? "RC," : "", - (sad[1] & BIT(5)) ? "FLC/FRC," : "", - (sad[1] & BIT(6)) ? "RLC/RRC," : ""); + memcpy(external_common_state->spkr_alloc_data_block, sadb + 1, len); + external_common_state->sadb_size = len; } static void hdmi_edid_extract_audio_data_blocks(const uint8 *in_buf) { uint8 len; - const uint8 *sad = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 1, + const uint8 *adb = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 1, &len); - uint32 *adb = external_common_state->audio_data_blocks; - if (sad == NULL) + if (external_common_state->audio_data_block == NULL) return; - external_common_state->audio_data_block_cnt = 0; - while (len >= 3 && external_common_state->audio_data_block_cnt < 16) { - DEV_DBG("EDID: Audio Data Block=\n", - (sad[1] & 0x7)+1, sad[1] >> 3, sad[2], sad[3]); - *adb++ = (uint32)sad[1] + ((uint32)sad[2] << 8) - + ((uint32)sad[2] << 16); - ++external_common_state->audio_data_block_cnt; - len -= 3; - sad += 3; - } + if (len > MAX_AUDIO_DATA_BLOCK_SIZE) + return; + + memcpy(external_common_state->audio_data_block, adb + 1, len); + external_common_state->adb_size = len; } static void hdmi_edid_extract_extended_data_blocks(const uint8 *in_buf) @@ -1407,30 +1473,37 @@ static void add_supported_video_format( struct hdmi_disp_mode_list_type *disp_mode_list, uint32 video_format) { - const struct hdmi_disp_mode_timing_type *timing = - hdmi_common_get_supported_mode(video_format); - boolean supported = timing != NULL; + const struct hdmi_disp_mode_timing_type *timing; + boolean supported = false; + boolean mhl_supported = true; if (video_format >= HDMI_VFRMT_MAX) return; + timing = hdmi_common_get_supported_mode(video_format); + supported = timing != NULL; DEV_DBG("EDID: format: %d [%s], %s\n", video_format, video_format_2string(video_format), supported ? "Supported" : "Not-Supported"); - if (supported) { - if (mhl_is_connected()) { - const struct hdmi_disp_mode_timing_type *mhl_timing = - hdmi_mhl_get_supported_mode(video_format); - boolean mhl_supported = mhl_timing != NULL; - DEV_DBG("EDID: format: %d [%s], %s by MHL\n", + + if (mhl_is_enabled()) { + const struct hdmi_disp_mode_timing_type *mhl_timing = + hdmi_mhl_get_supported_mode(video_format); + mhl_supported = mhl_timing != NULL; + DEV_DBG("EDID: format: %d [%s], %s by MHL\n", video_format, video_format_2string(video_format), - mhl_supported ? "Supported" : "Not-Supported"); - if (mhl_supported) - disp_mode_list->disp_mode_list[ - disp_mode_list->num_of_elements++] = video_format; - } else - disp_mode_list->disp_mode_list[ + mhl_supported ? "Supported" : "Not-Supported"); + } + + if (supported && mhl_supported) { + disp_mode_list->disp_mode_list[ disp_mode_list->num_of_elements++] = video_format; + if (video_format == external_common_state->video_resolution) { + DEV_DBG("%s: Default resolution %d [%s] supported\n", + __func__, video_format, + video_format_2string(video_format)); + external_common_state->default_res_supported = true; + } } } @@ -1500,7 +1573,8 @@ static void hdmi_edid_get_display_vsd_3d_mode(const uint8 *data_buf, struct hdmi_disp_mode_list_type *disp_mode_list, uint32 num_og_cea_blocks) { - uint8 len, offset, present_multi_3d, hdmi_vic_len, hdmi_3d_len; + uint8 len, offset, present_multi_3d, hdmi_vic_len; + int hdmi_3d_len; uint16 structure_all, structure_mask; const uint8 *vsd = num_og_cea_blocks ? hdmi_edid_find_block(data_buf+0x80, DBC_START_OFFSET, @@ -1624,7 +1698,7 @@ static void hdmi_edid_get_display_mode(const uint8 *data_buf, struct hdmi_disp_mode_list_type *disp_mode_list, uint32 num_og_cea_blocks) { - uint8 i = 0; + uint8 i = 0, offset = 0, std_blk = 0; uint32 video_format = HDMI_VFRMT_640x480p60_4_3; boolean has480p = FALSE; uint8 len; @@ -1755,6 +1829,66 @@ static void hdmi_edid_get_display_mode(const uint8 *data_buf, } } + + /* + * Check SD Timings if it contains 1280x1024@60Hz. + * SD Timing can be max 8 with 2 byte in size. + */ + std_blk = 0; + offset = 0; + while (std_blk < 8) { + if ((edid_blk0[0x26 + offset] == 0x81) && + (edid_blk0[0x26 + offset + 1] == 0x80)) { + add_supported_video_format(disp_mode_list, + HDMI_VFRMT_1280x1024p60_5_4); + break; + } else { + offset += 2; + } + std_blk++; + } + + /* check if the EDID revision is 4 (version 1.4) */ + if (edid_blk0[0x13] == 4) { + uint8 start = 0x36; + + i = 0; + + /* Check each of 4 - 18 bytes descriptors */ + while (i < 4) { + uint8 itrate = start; + uint32 header_1 = 0; + uint8 header_2 = 0; + + /* + * First 5 bytes are header. + * If they match 0x000000F700, it means its an + * established Timing III descriptor. + */ + header_1 = edid_blk0[itrate++]; + header_1 = header_1 << 8 | edid_blk0[itrate++]; + header_1 = header_1 << 8 | edid_blk0[itrate++]; + header_1 = header_1 << 8 | edid_blk0[itrate++]; + header_2 = edid_blk0[itrate]; + + if (header_1 == 0x000000F7 && + header_2 == 0x00) { + itrate++; /* VESA DMT Standard Version (0x0A)*/ + itrate++; /* First set of supported formats */ + itrate++; /* Second set of supported formats */ + /* BIT(1) indicates 1280x1024@60Hz */ + if (edid_blk0[itrate] & 0x02) { + add_supported_video_format( + disp_mode_list, + HDMI_VFRMT_1280x1024p60_5_4); + break; + } + } + i++; + start += 0x12; + } + } + /* mandaroty 3d format */ if (external_common_state->present_3d) { if (has60hz_mode) { @@ -1866,6 +2000,13 @@ int hdmi_common_read_edid(void) memset(&external_common_state->disp_mode_list, 0, sizeof(external_common_state->disp_mode_list)); memset(edid_buf, 0, sizeof(edid_buf)); + external_common_state->default_res_supported = false; + memset(external_common_state->audio_data_block, 0, + sizeof(external_common_state->audio_data_block)); + memset(external_common_state->spkr_alloc_data_block, 0, + sizeof(external_common_state->spkr_alloc_data_block)); + external_common_state->adb_size = 0; + external_common_state->sadb_size = 0; status = hdmi_common_read_edid_block(0, edid_buf); if (status || !check_edid_header(edid_buf)) { @@ -1977,7 +2118,7 @@ EXPORT_SYMBOL(hdmi_common_read_edid); bool hdmi_common_get_video_format_from_drv_data(struct msm_fb_data_type *mfd) { - uint32 format = HDMI_VFRMT_1920x1080p60_16_9; + uint32 format = external_common_state->video_resolution; struct fb_var_screeninfo *var = &mfd->fbi->var; bool changed = TRUE; @@ -2000,7 +2141,9 @@ bool hdmi_common_get_video_format_from_drv_data(struct msm_fb_data_type *mfd) : HDMI_VFRMT_720x576p50_16_9; break; case 1280: - if (mfd->var_frame_rate == 50000) + if (mfd->var_yres == 1024) + format = HDMI_VFRMT_1280x1024p60_5_4; + else if (mfd->var_frame_rate == 50000) format = HDMI_VFRMT_1280x720p50_16_9; else format = HDMI_VFRMT_1280x720p60_16_9; diff --git a/drivers/video/msm/external_common.h b/drivers/video/msm/external_common.h index 43741541..feead43c 100644 --- a/drivers/video/msm/external_common.h +++ b/drivers/video/msm/external_common.h @@ -109,9 +109,19 @@ #define HDMI_VFRMT_1440x480i240_4_3 HDMI_VFRMT_720x480i240_4_3 #define HDMI_VFRMT_720x480i240_16_9 58 #define HDMI_VFRMT_1440x480i240_16_9 HDMI_VFRMT_720x480i240_16_9 -#define HDMI_VFRMT_MAX 59 #define HDMI_VFRMT_FORCE_32BIT 0x7FFFFFFF +/* Video Identification Codes from 65-127 are reserved for the future */ +#define HDMI_VFRMT_END 127 + +/* VESA DMT TIMINGS */ +/* DMT ID: 23h, STD code: (81h, 80h), also a part of Established Timing III */ +#define HDMI_VFRMT_1280x1024p60_5_4 (HDMI_VFRMT_END + 1) +#define DMT_VFRMT_END HDMI_VFRMT_1280x1024p60_5_4 + +#define HDMI_VFRMT_MAX (DMT_VFRMT_END + 1) + + extern int ext_resolution; struct hdmi_disp_mode_timing_type { @@ -185,6 +195,9 @@ struct hdmi_disp_mode_timing_type { #define HDMI_SETTINGS_1920x1080p30_16_9 \ {HDMI_VFRMT_1920x1080p30_16_9, 1920, 88, 44, 148, FALSE, \ 1080, 4, 5, 36, FALSE, 74250, 30000, FALSE, TRUE} +#define HDMI_SETTINGS_1280x1024p60_5_4 \ + {HDMI_VFRMT_1280x1024p60_5_4, 1280, 48, 112, 248, FALSE, \ + 1024, 1, 3, 38, FALSE, 108000, 60000, FALSE, TRUE} /* A lookup table for all the supported display modes by the HDMI * hardware and driver. Use HDMI_SETUP_LUT in the module init to @@ -206,12 +219,22 @@ struct hdmi_disp_mode_list_type { }; #endif +/* + * As per the CEA-861E spec, there can be a total of 10 short audio + * descriptors with each SAD being 3 bytes long. + * Thus, the maximum length of the audio data block would be 30 bytes + */ +#define MAX_AUDIO_DATA_BLOCK_SIZE 30 +#define MAX_SPKR_ALLOC_DATA_BLOCK_SIZE 3 + struct external_common_state_type { boolean hpd_state; struct kobject *uevent_kobj; uint32 video_resolution; + boolean default_res_supported; struct device *dev; struct switch_dev sdev; + struct switch_dev audio_sdev; #ifdef CONFIG_FB_MSM_HDMI_3D boolean format_3d; void (*switch_3d)(boolean on); @@ -221,9 +244,7 @@ struct external_common_state_type { boolean hpd_feature_on; boolean hdmi_sink; struct hdmi_disp_mode_list_type disp_mode_list; - uint8 speaker_allocation_block; uint16 video_latency, audio_latency; - uint8 audio_data_block_cnt; uint16 physical_address; uint32 preferred_video_format; uint8 pt_scan_info; @@ -233,7 +254,10 @@ struct external_common_state_type { uint8 spd_product_description[16]; boolean present_3d; boolean present_hdcp; - uint32 audio_data_blocks[16]; + uint8 audio_data_block[MAX_AUDIO_DATA_BLOCK_SIZE]; + int adb_size; + uint8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE]; + int sadb_size; int (*read_edid_block)(int block, uint8 *edid_buf); int (*hpd_feature)(int on); #endif diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c index c8d3ca13..476d1435 100644 --- a/drivers/video/msm/hdmi_msm.c +++ b/drivers/video/msm/hdmi_msm.c @@ -57,25 +57,40 @@ static int msm_hdmi_sample_rate = MSM_HDMI_SAMPLE_RATE_48KHZ; #define HDCP_DDC_CTRL_1 0x0124 #define HDMI_DDC_CTRL 0x020C +#define HPD_EVENT_OFFLINE 0 +#define HPD_EVENT_ONLINE 1 + +#define SWITCH_SET_HDMI_AUDIO(d, force) \ + do {\ + if (!hdmi_msm_is_dvi_mode() &&\ + ((force) ||\ + (external_common_state->audio_sdev.state != (d)))) {\ + switch_set_state(&external_common_state->audio_sdev,\ + (d));\ + DEV_INFO("%s: hdmi_audio state switched to %d\n",\ + __func__,\ + external_common_state->audio_sdev.state);\ + } \ + } while (0) + struct workqueue_struct *hdmi_work_queue; struct hdmi_msm_state_type *hdmi_msm_state; +/* Enable HDCP by default */ +static bool hdcp_feature_on = true; + DEFINE_MUTEX(hdmi_msm_state_mutex); EXPORT_SYMBOL(hdmi_msm_state_mutex); static DEFINE_MUTEX(hdcp_auth_state_mutex); static void hdmi_msm_dump_regs(const char *prefix); -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT static void hdmi_msm_hdcp_enable(void); -#else -static inline void hdmi_msm_hdcp_enable(void) {} -#endif - static void hdmi_msm_turn_on(void); static int hdmi_msm_audio_off(void); static int hdmi_msm_read_edid(void); static void hdmi_msm_hpd_off(void); +static boolean hdmi_msm_is_dvi_mode(void); #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT @@ -632,6 +647,7 @@ static void hdmi_msm_setup_video_mode_lut(void) HDMI_SETUP_LUT(1920x1080p24_16_9); HDMI_SETUP_LUT(1920x1080p25_16_9); HDMI_SETUP_LUT(1920x1080p30_16_9); + HDMI_SETUP_LUT(1280x1024p60_5_4); } #ifdef PORT_DEBUG @@ -675,7 +691,6 @@ const char *hdmi_msm_name(uint32 offset) case 0x00D8: return "ACR_48_1"; case 0x00E4: return "AUDIO_INFO0"; case 0x00E8: return "AUDIO_INFO1"; -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT case 0x0110: return "HDCP_CTRL"; case 0x0114: return "HDCP_DEBUG_CTRL"; case 0x0118: return "HDCP_INT_CTRL"; @@ -690,7 +705,6 @@ const char *hdmi_msm_name(uint32 offset) case 0x014C: return "HDCP_RCVPORT_DATA5"; case 0x0150: return "HDCP_RCVPORT_DATA6"; case 0x0168: return "HDCP_RCVPORT_DATA12"; -#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ case 0x01D0: return "AUDIO_CFG"; case 0x0208: return "USEC_REFTIMER"; case 0x020C: return "DDC_CTRL"; @@ -705,14 +719,10 @@ const char *hdmi_msm_name(uint32 offset) case 0x0250: return "HPD_INT_STATUS"; case 0x0254: return "HPD_INT_CTRL"; case 0x0258: return "HPD_CTRL"; -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT case 0x025C: return "HDCP_ENTROPY_CTRL1"; -#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ case 0x027C: return "DDC_REF"; -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT case 0x0284: return "HDCP_SW_UPPER_AKSV"; case 0x0288: return "HDCP_SW_LOWER_AKSV"; -#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ case 0x02B4: return "ACTIVE_H"; case 0x02B8: return "ACTIVE_V"; case 0x02BC: return "ACTIVE_V_F2"; @@ -761,130 +771,63 @@ static int hdmi_msm_audio_off(void); static int hdmi_msm_read_edid(void); static void hdmi_msm_hpd_off(void); -static void hdmi_msm_hpd_state_work(struct work_struct *work) +static bool hdmi_ready(void) { - boolean hpd_state = false; - char *envp[2]; - - if (hdmi_msm_state->is_mhl_enabled) { - /* - * HPD will be controlled from MHL - */ - envp[0] = ""; - DEV_DBG("%s %u\n", envp[0], hpd_state); - return; - } - - if (!hdmi_msm_state || !hdmi_msm_state->hpd_initialized || - !MSM_HDMI_BASE) { - DEV_DBG("%s: ignored, probe failed\n", __func__); - return; - } - - DEV_DBG("%s:Got interrupt\n", __func__); - /* HPD_INT_STATUS[0x0250] */ - hpd_state = (HDMI_INP(0x0250) & 0x2) >> 1; - mutex_lock(&external_common_state_hpd_mutex); - mutex_lock(&hdmi_msm_state_mutex); - if ((external_common_state->hpd_state != hpd_state) || (hdmi_msm_state-> - hpd_prev_state != external_common_state->hpd_state)) { - external_common_state->hpd_state = hpd_state; - hdmi_msm_state->hpd_prev_state = - external_common_state->hpd_state; - DEV_DBG("%s: state not stable yet, wait again (%d|%d|%d)\n", - __func__, hdmi_msm_state->hpd_prev_state, - external_common_state->hpd_state, hpd_state); - mutex_unlock(&external_common_state_hpd_mutex); - hdmi_msm_state->hpd_stable = 0; - mutex_unlock(&hdmi_msm_state_mutex); - mod_timer(&hdmi_msm_state->hpd_state_timer, jiffies + HZ/2); - return; - } - mutex_unlock(&external_common_state_hpd_mutex); + return MSM_HDMI_BASE && + hdmi_msm_state && + hdmi_msm_state->hdmi_app_clk && + hdmi_msm_state->hpd_initialized; +} - if (hdmi_msm_state->hpd_stable++) { - mutex_unlock(&hdmi_msm_state_mutex); - DEV_DBG("%s: no more timer, depending for IRQ now\n", - __func__); - return; - } +static void hdmi_msm_send_event(boolean on) +{ + char *envp[2]; - hdmi_msm_state->hpd_stable = 1; - DEV_INFO("HDMI HPD: event detected\n"); + /* QDSP OFF preceding the HPD event notification */ + envp[0] = "HDCP_STATE=FAIL"; + envp[1] = NULL; + DEV_ERR("hdmi: HDMI HPD: QDSP OFF\n"); + kobject_uevent_env(external_common_state->uevent_kobj, + KOBJ_CHANGE, envp); - if (!hdmi_msm_state->hpd_cable_chg_detected) { - mutex_unlock(&hdmi_msm_state_mutex); - if (hpd_state) { - if (!external_common_state-> - disp_mode_list.num_of_elements) - hdmi_msm_read_edid(); - } - } else { - hdmi_msm_state->hpd_cable_chg_detected = FALSE; - mutex_unlock(&hdmi_msm_state_mutex); - /* QDSP OFF preceding the HPD event notification */ - envp[0] = "HDCP_STATE=FAIL"; - envp[1] = NULL; - DEV_INFO("HDMI HPD: QDSP OFF\n"); - kobject_uevent_env(external_common_state->uevent_kobj, - KOBJ_CHANGE, envp); - if (hpd_state) { - /* Build EDID table */ - hdmi_msm_read_edid(); -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT - hdmi_msm_state->reauth = FALSE ; -#endif - switch_set_state(&external_common_state->sdev, 1); - DEV_INFO("Hdmi state switched to %d: %s\n", - external_common_state->sdev.state, __func__); + if (on) { + /* Build EDID table */ + hdmi_msm_read_edid(); + switch_set_state(&external_common_state->sdev, 1); + DEV_INFO("%s: hdmi state switched to %d\n", __func__, + external_common_state->sdev.state); - DEV_INFO("HDMI HPD: CONNECTED: send ONLINE\n"); - kobject_uevent(external_common_state->uevent_kobj, - KOBJ_ONLINE); - switch_set_state(&external_common_state->sdev, 1); - DEV_INFO("Hdmi state switch to %d: %s\n", - external_common_state->sdev.state, __func__); -#ifndef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + DEV_INFO("HDMI HPD: CONNECTED: send ONLINE\n"); + kobject_uevent(external_common_state->uevent_kobj, KOBJ_ONLINE); + if (!hdmi_msm_state->hdcp_enable) { /* Send Audio for HDMI Compliance Cases*/ envp[0] = "HDCP_STATE=PASS"; envp[1] = NULL; DEV_INFO("HDMI HPD: sense : send HDCP_PASS\n"); kobject_uevent_env(external_common_state->uevent_kobj, KOBJ_CHANGE, envp); -#endif - } else { - switch_set_state(&external_common_state->sdev, 0); - DEV_INFO("Hdmi state switched to %d: %s\n", - external_common_state->sdev.state, __func__); - - DEV_INFO("HDMI HPD: DISCONNECTED: send OFFLINE\n"); - kobject_uevent(external_common_state->uevent_kobj, - KOBJ_OFFLINE); } + } else { + switch_set_state(&external_common_state->sdev, 0); + DEV_INFO("%s: hdmi state switch to %d\n", __func__, + external_common_state->sdev.state); + DEV_INFO("hdmi: HDMI HPD: sense DISCONNECTED: send OFFLINE\n"); + kobject_uevent(external_common_state->uevent_kobj, + KOBJ_OFFLINE); } - /* HPD_INT_CTRL[0x0254] - * 31:10 Reserved - * 9 RCV_PLUGIN_DET_MASK receiver plug in interrupt mask. - * When programmed to 1, - * RCV_PLUGIN_DET_INT will toggle - * the interrupt line - * 8:6 Reserved - * 5 RX_INT_EN Panel RX interrupt enable - * 0: Disable - * 1: Enable - * 4 RX_INT_ACK WRITE ONLY. Panel RX interrupt - * ack - * 3 Reserved - * 2 INT_EN Panel interrupt control - * 0: Disable - * 1: Enable - * 1 INT_POLARITY Panel interrupt polarity - * 0: generate interrupt on disconnect - * 1: generate interrupt on connect - * 0 INT_ACK WRITE ONLY. Panel interrupt ack */ - /* Set IRQ for HPD */ - HDMI_OUTP(0x0254, 4 | (hpd_state ? 0 : 2)); + if (!completion_done(&hdmi_msm_state->hpd_event_processed)) + complete(&hdmi_msm_state->hpd_event_processed); +} + +static void hdmi_msm_hpd_state_work(struct work_struct *work) +{ + if (!hdmi_ready()) { + DEV_ERR("hdmi: %s: ignored, probe failed\n", __func__); + return; + } + + hdmi_msm_send_event(external_common_state->hpd_state); } #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT @@ -894,10 +837,13 @@ static void hdmi_msm_cec_latch_work(struct work_struct *work) } #endif -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT static void hdcp_deauthenticate(void); static void hdmi_msm_hdcp_reauth_work(struct work_struct *work) { + if (!hdmi_msm_state->hdcp_enable) { + DEV_DBG("%s: HDCP not enabled\n", __func__); + return; + } /* Don't process recursive actions */ mutex_lock(&hdmi_msm_state_mutex); @@ -915,17 +861,20 @@ static void hdmi_msm_hdcp_reauth_work(struct work_struct *work) * Therefore, as surprising as it may sound do reauth * only if the device is HDCP-capable */ - if (external_common_state->present_hdcp) { - hdcp_deauthenticate(); - mutex_lock(&hdcp_auth_state_mutex); - hdmi_msm_state->reauth = TRUE; - mutex_unlock(&hdcp_auth_state_mutex); - mod_timer(&hdmi_msm_state->hdcp_timer, jiffies + HZ/2); - } + hdcp_deauthenticate(); + mutex_lock(&hdcp_auth_state_mutex); + hdmi_msm_state->reauth = TRUE; + mutex_unlock(&hdcp_auth_state_mutex); + mod_timer(&hdmi_msm_state->hdcp_timer, jiffies + HZ/2); } static void hdmi_msm_hdcp_work(struct work_struct *work) { + if (!hdmi_msm_state->hdcp_enable) { + DEV_DBG("%s: HDCP not enabled\n", __func__); + return; + } + /* Only re-enable if cable still connected */ mutex_lock(&external_common_state_hpd_mutex); if (external_common_state->hpd_state && @@ -946,7 +895,109 @@ static void hdmi_msm_hdcp_work(struct work_struct *work) hdmi_msm_state->reauth = FALSE; } } -#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ + +int hdmi_msm_process_hdcp_interrupts(void) +{ + int rc = -1; + uint32 hdcp_int_val; + char *envp[2]; + + if (!hdmi_msm_state->hdcp_enable) { + DEV_DBG("%s: HDCP not enabled\n", __func__); + return -EINVAL; + } + + /* HDCP_INT_CTRL[0x0118] + * [0] AUTH_SUCCESS_INT [R] HDCP Authentication Success + * interrupt status + * [1] AUTH_SUCCESS_ACK [W] Acknowledge bit for HDCP + * Authentication Success bit - write 1 to clear + * [2] AUTH_SUCCESS_MASK [R/W] Mask bit for HDCP Authentication + * Success interrupt - set to 1 to enable interrupt */ + hdcp_int_val = HDMI_INP_ND(0x0118); + if ((hdcp_int_val & (1 << 2)) && (hdcp_int_val & (1 << 0))) { + /* AUTH_SUCCESS_INT */ + HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 1)) & ~(1 << 0)); + DEV_INFO("HDCP: AUTH_SUCCESS_INT received\n"); + complete_all(&hdmi_msm_state->hdcp_success_done); + return 0; + } + + /* [4] AUTH_FAIL_INT [R] HDCP Authentication Lost + * interrupt Status + * [5] AUTH_FAIL_ACK [W] Acknowledge bit for HDCP + * Authentication Lost bit - write 1 to clear + * [6] AUTH_FAIL_MASK [R/W] Mask bit fo HDCP Authentication + * Lost interrupt set to 1 to enable interrupt + * [7] AUTH_FAIL_INFO_ACK [W] Acknowledge bit for HDCP + * Authentication Failure Info field - write 1 to clear */ + if ((hdcp_int_val & (1 << 6)) && (hdcp_int_val & (1 << 4))) { + /* AUTH_FAIL_INT */ + /* Clear and Disable */ + uint32 link_status = HDMI_INP_ND(0x011C); + HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 5)) + & ~((1 << 6) | (1 << 4))); + DEV_INFO("HDCP: AUTH_FAIL_INT received, LINK0_STATUS=0x%08x\n", + link_status); + if (hdmi_msm_state->full_auth_done) { + SWITCH_SET_HDMI_AUDIO(0, 0); + + envp[0] = "HDCP_STATE=FAIL"; + envp[1] = NULL; + DEV_INFO("HDMI HPD:QDSP OFF\n"); + kobject_uevent_env(external_common_state->uevent_kobj, + KOBJ_CHANGE, envp); + + mutex_lock(&hdcp_auth_state_mutex); + hdmi_msm_state->full_auth_done = FALSE; + mutex_unlock(&hdcp_auth_state_mutex); + /* Calling reauth only when authentication + * is sucessful or else we always go into + * the reauth loop. Also, No need to reauthenticate + * if authentication failed because of cable disconnect + */ + if (((link_status & 0xF0) >> 4) != 0x7) { + DEV_DBG("Reauthenticate From %s HDCP FAIL INT ", + __func__); + queue_work(hdmi_work_queue, + &hdmi_msm_state->hdcp_reauth_work); + } else { + DEV_INFO("HDCP: HDMI cable disconnected\n"); + } + } + + /* Clear AUTH_FAIL_INFO as well */ + HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 7))); + return 0; + } + + /* [8] DDC_XFER_REQ_INT [R] HDCP DDC Transfer Request + * interrupt status + * [9] DDC_XFER_REQ_ACK [W] Acknowledge bit for HDCP DDC + * Transfer Request bit - write 1 to clear + * [10] DDC_XFER_REQ_MASK [R/W] Mask bit for HDCP DDC Transfer + * Request interrupt - set to 1 to enable interrupt */ + if ((hdcp_int_val & (1 << 10)) && (hdcp_int_val & (1 << 8))) { + /* DDC_XFER_REQ_INT */ + HDMI_OUTP_ND(0x0118, (hdcp_int_val | (1 << 9)) & ~(1 << 8)); + if (!(hdcp_int_val & (1 << 12))) + return 0; + } + /* [12] DDC_XFER_DONE_INT [R] HDCP DDC Transfer done interrupt + * status + * [13] DDC_XFER_DONE_ACK [W] Acknowledge bit for HDCP DDC + * Transfer done bit - write 1 to clear + * [14] DDC_XFER_DONE_MASK [R/W] Mask bit for HDCP DDC Transfer + * done interrupt - set to 1 to enable interrupt */ + if ((hdcp_int_val & (1 << 14)) && (hdcp_int_val & (1 << 12))) { + /* DDC_XFER_DONE_INT */ + HDMI_OUTP_ND(0x0118, (hdcp_int_val | (1 << 13)) & ~(1 << 12)); + DEV_INFO("HDCP: DDC_XFER_DONE received\n"); + return 0; + } + + return rc; +} static irqreturn_t hdmi_msm_isr(int irq, void *dev_id) { @@ -957,16 +1008,11 @@ static irqreturn_t hdmi_msm_isr(int irq, void *dev_id) #endif uint32 ddc_int_ctrl; uint32 audio_int_val; -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT - uint32 hdcp_int_val; - char *envp[2]; -#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ static uint32 fifo_urun_int_occurred; static uint32 sample_drop_int_occurred; const uint32 occurrence_limit = 5; - if (!hdmi_msm_state || !hdmi_msm_state->hpd_initialized || - !MSM_HDMI_BASE) { + if (!hdmi_ready()) { DEV_DBG("ISR ignored, probe failed\n"); return IRQ_HANDLED; } @@ -977,42 +1023,18 @@ static irqreturn_t hdmi_msm_isr(int irq, void *dev_id) /* HDMI_HPD_INT_CTRL[0x0254] */ hpd_int_ctrl = HDMI_INP_ND(0x0254); if ((hpd_int_ctrl & (1 << 2)) && (hpd_int_status & (1 << 0))) { - boolean cable_detected = (hpd_int_status & 2) >> 1; - - /* HDMI_HPD_INT_CTRL[0x0254] */ - /* Clear all interrupts, timer will turn IRQ back on - * Leaving the bit[2] on, else core goes off - * on getting HPD during power off - */ - HDMI_OUTP(0x0254, (1 << 2) | (1 << 0)); - - DEV_DBG("%s: HPD IRQ, Ctrl=%04x, State=%04x\n", __func__, - hpd_int_ctrl, hpd_int_status); - mutex_lock(&hdmi_msm_state_mutex); - hdmi_msm_state->hpd_cable_chg_detected = TRUE; - - /* ensure 2 readouts */ - hdmi_msm_state->hpd_prev_state = cable_detected ? 0 : 1; - external_common_state->hpd_state = cable_detected ? 1 : 0; - hdmi_msm_state->hpd_stable = 0; - mod_timer(&hdmi_msm_state->hpd_state_timer, jiffies + HZ/2); - mutex_unlock(&hdmi_msm_state_mutex); /* - * HDCP Compliance 1A-01: - * The Quantum Data Box 882 triggers two consecutive - * HPD events very close to each other as a part of this - * test which can trigger two parallel HDCP auth threads - * if HDCP authentication is going on and we get ISR - * then stop the authentication , rather than - * reauthenticating it again + * Got HPD interrupt. Ack the interrupt and disable any + * further HPD interrupts until we process this interrupt. */ - if (!(hdmi_msm_state->full_auth_done)) { - DEV_DBG("%s getting hpd while authenticating\n",\ - __func__); - mutex_lock(&hdcp_auth_state_mutex); - hdmi_msm_state->hpd_during_auth = TRUE; - mutex_unlock(&hdcp_auth_state_mutex); - } + HDMI_OUTP(0x0254, ((hpd_int_ctrl | (BIT(0))) & ~BIT(2))); + + external_common_state->hpd_state = + (HDMI_INP(0x0250) & BIT(1)) >> 1; + DEV_DBG("%s: Queuing work to handle HPD %s event\n", __func__, + external_common_state->hpd_state ? "connect" : + "disconnect"); + queue_work(hdmi_work_queue, &hdmi_msm_state->hpd_state_work); return IRQ_HANDLED; } @@ -1065,97 +1087,8 @@ static irqreturn_t hdmi_msm_isr(int irq, void *dev_id) return IRQ_HANDLED; } -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT - /* HDCP_INT_CTRL[0x0118] - * [0] AUTH_SUCCESS_INT [R] HDCP Authentication Success - * interrupt status - * [1] AUTH_SUCCESS_ACK [W] Acknowledge bit for HDCP - * Authentication Success bit - write 1 to clear - * [2] AUTH_SUCCESS_MASK [R/W] Mask bit for HDCP Authentication - * Success interrupt - set to 1 to enable interrupt */ - hdcp_int_val = HDMI_INP_ND(0x0118); - if ((hdcp_int_val & (1 << 2)) && (hdcp_int_val & (1 << 0))) { - /* AUTH_SUCCESS_INT */ - HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 1)) & ~(1 << 0)); - DEV_INFO("HDCP: AUTH_SUCCESS_INT received\n"); - complete_all(&hdmi_msm_state->hdcp_success_done); + if (!hdmi_msm_process_hdcp_interrupts()) return IRQ_HANDLED; - } - /* [4] AUTH_FAIL_INT [R] HDCP Authentication Lost - * interrupt Status - * [5] AUTH_FAIL_ACK [W] Acknowledge bit for HDCP - * Authentication Lost bit - write 1 to clear - * [6] AUTH_FAIL_MASK [R/W] Mask bit fo HDCP Authentication - * Lost interrupt set to 1 to enable interrupt - * [7] AUTH_FAIL_INFO_ACK [W] Acknowledge bit for HDCP - * Authentication Failure Info field - write 1 to clear */ - if ((hdcp_int_val & (1 << 6)) && (hdcp_int_val & (1 << 4))) { - /* AUTH_FAIL_INT */ - /* Clear and Disable */ - HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 5)) - & ~((1 << 6) | (1 << 4))); - DEV_INFO("HDCP: AUTH_FAIL_INT received, LINK0_STATUS=0x%08x\n", - HDMI_INP_ND(0x011C)); - if (hdmi_msm_state->full_auth_done) { - switch_set_state(&external_common_state->sdev, 0); - DEV_INFO("Hdmi state switched to %d: %s\n", - external_common_state->sdev.state, __func__); - - envp[0] = "HDCP_STATE=FAIL"; - envp[1] = NULL; - DEV_INFO("HDMI HPD:QDSP OFF\n"); - kobject_uevent_env(external_common_state->uevent_kobj, - KOBJ_CHANGE, envp); - - mutex_lock(&hdcp_auth_state_mutex); - hdmi_msm_state->full_auth_done = FALSE; - mutex_unlock(&hdcp_auth_state_mutex); - /* Calling reauth only when authentication - * is sucessful or else we always go into - * the reauth loop - */ - queue_work(hdmi_work_queue, - &hdmi_msm_state->hdcp_reauth_work); - } - mutex_lock(&hdcp_auth_state_mutex); - /* This flag prevents other threads from re-authenticating - * after we've just authenticated (i.e., finished part3) - */ - hdmi_msm_state->full_auth_done = FALSE; - - mutex_unlock(&hdcp_auth_state_mutex); - DEV_DBG("calling reauthenticate from %s HDCP FAIL INT ", - __func__); - - /* Clear AUTH_FAIL_INFO as well */ - HDMI_OUTP(0x0118, (hdcp_int_val | (1 << 7))); - return IRQ_HANDLED; - } - /* [8] DDC_XFER_REQ_INT [R] HDCP DDC Transfer Request - * interrupt status - * [9] DDC_XFER_REQ_ACK [W] Acknowledge bit for HDCP DDC - * Transfer Request bit - write 1 to clear - * [10] DDC_XFER_REQ_MASK [R/W] Mask bit for HDCP DDC Transfer - * Request interrupt - set to 1 to enable interrupt */ - if ((hdcp_int_val & (1 << 10)) && (hdcp_int_val & (1 << 8))) { - /* DDC_XFER_REQ_INT */ - HDMI_OUTP_ND(0x0118, (hdcp_int_val | (1 << 9)) & ~(1 << 8)); - if (!(hdcp_int_val & (1 << 12))) - return IRQ_HANDLED; - } - /* [12] DDC_XFER_DONE_INT [R] HDCP DDC Transfer done interrupt - * status - * [13] DDC_XFER_DONE_ACK [W] Acknowledge bit for HDCP DDC - * Transfer done bit - write 1 to clear - * [14] DDC_XFER_DONE_MASK [R/W] Mask bit for HDCP DDC Transfer - * done interrupt - set to 1 to enable interrupt */ - if ((hdcp_int_val & (1 << 14)) && (hdcp_int_val & (1 << 12))) { - /* DDC_XFER_DONE_INT */ - HDMI_OUTP_ND(0x0118, (hdcp_int_val | (1 << 13)) & ~(1 << 12)); - DEV_INFO("HDCP: DDC_XFER_DONE received\n"); - return IRQ_HANDLED; - } -#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT /* Process CEC Interrupt */ @@ -1314,7 +1247,7 @@ void hdmi_msm_set_mode(boolean power_on) if (external_common_state->hdmi_sink == 0) { /* HDMI_DVI_SEL */ reg_val |= 0x00000002; - if (external_common_state->present_hdcp) + if (hdmi_msm_state->hdcp_enable) /* HDMI Encryption */ reg_val |= 0x00000004; /* HDMI_CTRL */ @@ -1322,7 +1255,7 @@ void hdmi_msm_set_mode(boolean power_on) /* HDMI_DVI_SEL */ reg_val &= ~0x00000002; } else { - if (external_common_state->present_hdcp) + if (hdmi_msm_state->hdcp_enable) /* HDMI_Encryption_ON */ reg_val |= 0x00000006; else @@ -1402,7 +1335,6 @@ static int hdmi_msm_ddc_clear_irq(const char *what) return 0; } -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT static int hdmi_msm_ddc_write(uint32 dev_addr, uint32 offset, const uint8 *data_buf, uint32 data_len, const char *what) { @@ -1598,7 +1530,6 @@ static int hdmi_msm_ddc_write(uint32 dev_addr, uint32 offset, error: return status; } -#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ static int hdmi_msm_ddc_read_retry(uint32 dev_addr, uint32 offset, uint8 *data_buf, uint32 data_len, uint32 request_len, int retry, @@ -2159,9 +2090,13 @@ static int hdmi_msm_read_edid(void) return status; } -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT static void hdcp_auth_info(uint32 auth_info) { + if (!hdmi_msm_state->hdcp_enable) { + DEV_DBG("%s: HDCP not enabled\n", __func__); + return; + } + switch (auth_info) { case 0: DEV_INFO("%s: None", __func__); @@ -2196,6 +2131,11 @@ static void hdcp_auth_info(uint32 auth_info) static void hdcp_key_state(uint32 key_state) { + if (!hdmi_msm_state->hdcp_enable) { + DEV_DBG("%s: HDCP not enabled\n", __func__); + return; + } + switch (key_state) { case 0: DEV_WARN("%s: No HDCP Keys", __func__); @@ -2239,10 +2179,17 @@ static void hdcp_deauthenticate(void) { int hdcp_link_status = HDMI_INP(0x011C); + if (!hdmi_msm_state->hdcp_enable) { + DEV_DBG("%s: HDCP not enabled\n", __func__); + return; + } + /* Disable HDCP interrupts */ HDMI_OUTP(0x0118, 0x0); + mutex_lock(&hdcp_auth_state_mutex); external_common_state->hdcp_active = FALSE; + mutex_unlock(&hdcp_auth_state_mutex); /* 0x0130 HDCP_RESET [0] LINK0_DEAUTHENTICATE */ HDMI_OUTP(0x0130, 0x1); @@ -2264,6 +2211,11 @@ static void check_and_clear_HDCP_DDC_Failure(void) int failure; int nack0; + if (!hdmi_msm_state->hdcp_enable) { + DEV_DBG("%s: HDCP not enabled\n", __func__); + return; + } + /* * Check for any DDC transfer failures * 0x0128 HDCP_DDC_STATUS @@ -2374,6 +2326,11 @@ static int hdcp_authentication_part1(void) static uint8 buf[0xFF]; memset(buf, 0, sizeof(buf)); + if (!hdmi_msm_state->hdcp_enable) { + DEV_DBG("%s: HDCP not enabled\n", __func__); + return 0; + } + if (!is_part1_done) { is_part1_done = TRUE; @@ -2446,8 +2403,8 @@ static int hdcp_authentication_part1(void) /* 0x0110 HDCP_CTRL [8] ENCRYPTION_ENABLE [0] ENABLE */ - /* encryption_enable | enable */ - HDMI_OUTP(0x0110, (1 << 8) | (1 << 0)); + /* Enable HDCP. Encryption should be enabled after reading R0 */ + HDMI_OUTP(0x0110, BIT(0)); /* * Check to see if a HDCP DDC Failure is indicated in @@ -2642,6 +2599,9 @@ static int hdcp_authentication_part1(void) goto error; } + /* Enable HDCP Encryption */ + HDMI_OUTP(0x0110, BIT(0) | BIT(8)); + DEV_INFO("HDCP: authentication part I, successful\n"); is_part1_done = FALSE; return 0; @@ -2661,6 +2621,11 @@ static int hdmi_msm_transfer_v_h(void) int ret; uint8 buf[4]; + if (!hdmi_msm_state->hdcp_enable) { + DEV_DBG("%s: HDCP not enabled\n", __func__); + return 0; + } + snprintf(what, sizeof(what), "V' H0"); ret = hdmi_msm_ddc_read(0x74, 0x20, buf, 4, 5, what, TRUE); if (ret) { @@ -2753,6 +2718,11 @@ static int hdcp_authentication_part2(void) boolean ksv_done = FALSE; + if (!hdmi_msm_state->hdcp_enable) { + DEV_DBG("%s: HDCP not enabled\n", __func__); + return 0; + } + memset(buf, 0, sizeof(buf)); memset(kvs_fifo, 0, sizeof(kvs_fifo)); @@ -2945,6 +2915,12 @@ static int hdcp_authentication_part3(uint32 found_repeater) { int ret = 0; int poll = 3000; + + if (!hdmi_msm_state->hdcp_enable) { + DEV_DBG("%s: HDCP not enabled\n", __func__); + return 0; + } + while (poll) { /* 0x011C HDCP_LINK0_STATUS [30:28] KEYS_STATE = 3 = "Valid" @@ -2975,7 +2951,7 @@ static void hdmi_msm_hdcp_enable(void) uint32 found_repeater = 0x0; char *envp[2]; - if (!hdmi_msm_has_hdcp()) { + if (!hdmi_msm_state->hdcp_enable) { DEV_INFO("%s: HDCP NOT ENABLED\n", __func__); return; } @@ -2984,20 +2960,16 @@ static void hdmi_msm_hdcp_enable(void) hdmi_msm_state->hdcp_activating = TRUE; mutex_unlock(&hdmi_msm_state_mutex); - fill_black_screen(); - mutex_lock(&hdcp_auth_state_mutex); - /* - * Initialize this to zero here to make - * sure HPD has not happened yet - */ - hdmi_msm_state->hpd_during_auth = FALSE; /* This flag prevents other threads from re-authenticating * after we've just authenticated (i.e., finished part3) * We probably need to protect this in a mutex lock */ hdmi_msm_state->full_auth_done = FALSE; mutex_unlock(&hdcp_auth_state_mutex); + /* Disable HDCP before we start part1 */ + HDMI_OUTP(0x0110, 0x0); + /* PART I Authentication*/ ret = hdcp_authentication_part1(); if (ret) @@ -3027,9 +2999,6 @@ static void hdmi_msm_hdcp_enable(void) if (ret) goto error; - unfill_black_screen(); - - external_common_state->hdcp_active = TRUE; mutex_lock(&hdmi_msm_state_mutex); hdmi_msm_state->hdcp_activating = FALSE; mutex_unlock(&hdmi_msm_state_mutex); @@ -3040,6 +3009,7 @@ static void hdmi_msm_hdcp_enable(void) * after we've just authenticated (i.e., finished part3) */ hdmi_msm_state->full_auth_done = TRUE; + external_common_state->hdcp_active = TRUE; mutex_unlock(&hdcp_auth_state_mutex); if (!hdmi_msm_is_dvi_mode()) { @@ -3048,17 +3018,13 @@ static void hdmi_msm_hdcp_enable(void) envp[1] = NULL; kobject_uevent_env(external_common_state->uevent_kobj, KOBJ_CHANGE, envp); + + SWITCH_SET_HDMI_AUDIO(1, 0); } - switch_set_state(&external_common_state->sdev, 1); - DEV_INFO("Hdmi state switched to %d: %s\n", - external_common_state->sdev.state, __func__); return; error: - mutex_lock(&hdmi_msm_state_mutex); - hdmi_msm_state->hdcp_activating = FALSE; - mutex_unlock(&hdmi_msm_state_mutex); if (hdmi_msm_state->hpd_during_auth) { DEV_WARN("Calling Deauthentication: HPD occured during " "authentication from [%s]\n", __func__); @@ -3072,11 +3038,10 @@ static void hdmi_msm_hdcp_enable(void) queue_work(hdmi_work_queue, &hdmi_msm_state->hdcp_reauth_work); } - switch_set_state(&external_common_state->sdev, 0); - DEV_INFO("Hdmi state switched to %d: %s\n", - external_common_state->sdev.state, __func__); + mutex_lock(&hdmi_msm_state_mutex); + hdmi_msm_state->hdcp_activating = FALSE; + mutex_unlock(&hdmi_msm_state_mutex); } -#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ static void hdmi_msm_video_setup(int video_format) { @@ -3188,6 +3153,9 @@ static void hdmi_msm_audio_acr_setup(boolean enabled, int video_format, /* HDMI_ACR_PKT_CTRL[0x0024] */ uint32 acr_pck_ctrl_reg = HDMI_INP(0x0024); + /* Clear N/CTS selection bits */ + acr_pck_ctrl_reg &= ~(3 << 4); + if (enabled) { const struct hdmi_disp_mode_timing_type *timing = hdmi_common_get_supported_mode(video_format); @@ -3592,13 +3560,14 @@ EXPORT_SYMBOL(hdmi_msm_audio_get_sample_rate); void hdmi_msm_audio_sample_rate_reset(int rate) { + if (msm_hdmi_sample_rate == rate) + return; + msm_hdmi_sample_rate = rate; -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT - if (hdmi_msm_has_hdcp()) + if (hdmi_msm_state->hdcp_enable) hdcp_deauthenticate(); else -#endif hdmi_msm_turn_on(); } EXPORT_SYMBOL(hdmi_msm_audio_sample_rate_reset); @@ -3626,64 +3595,59 @@ static void hdmi_msm_audio_setup(void) static int hdmi_msm_audio_off(void) { - uint32 audio_pkt_ctrl, audio_cfg; - /* Number of wait iterations */ - int i = 10; - audio_pkt_ctrl = HDMI_INP_ND(0x0020); - audio_cfg = HDMI_INP_ND(0x01D0); - - /* Checking BIT[0] of AUDIO PACKET CONTROL and */ - /* AUDIO CONFIGURATION register */ - while (((audio_pkt_ctrl & 0x00000001) || (audio_cfg & 0x00000001)) - && (i--)) { - audio_pkt_ctrl = HDMI_INP_ND(0x0020); - audio_cfg = HDMI_INP_ND(0x01D0); - DEV_DBG("%d times :: HDMI AUDIO PACKET is %08x and " - "AUDIO CFG is %08x", i, audio_pkt_ctrl, audio_cfg); - msleep(100); - if (!i) { - DEV_ERR("%s:failed to set BIT[0] AUDIO PACKET" - "CONTROL or AUDIO CONFIGURATION REGISTER\n", - __func__); - return -ETIMEDOUT; + uint32 audio_cfg; + int i, timeout_val = 50; + + for (i = 0; (i < timeout_val) && + ((audio_cfg = HDMI_INP_ND(0x01D0)) & BIT(0)); i++) { + DEV_DBG("%s: %d times: AUDIO CFG is %08xi\n", __func__, + i+1, audio_cfg); + if (!((i+1) % 10)) { + DEV_ERR("%s: audio still on after %d sec. try again\n", + __func__, (i+1)/10); + SWITCH_SET_HDMI_AUDIO(0, 1); } + msleep(100); } + + if (i == timeout_val) + DEV_ERR("%s: Error: cannot turn off audio engine\n", __func__); + hdmi_msm_audio_info_setup(FALSE, 0, 0, 0, FALSE); hdmi_msm_audio_acr_setup(FALSE, 0, 0, 0); DEV_INFO("HDMI Audio: Disabled\n"); return 0; } - -static uint8 hdmi_msm_avi_iframe_lut[][16] = { +static uint8 hdmi_msm_avi_iframe_lut[][17] = { /* 480p60 480i60 576p50 576i50 720p60 720p50 1080p60 1080i60 1080p50 1080i50 1080p24 1080p30 1080p25 640x480p 480p60_16_9 576p50_4_3 */ {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, /*00*/ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, /*00*/ {0x18, 0x18, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x18, 0x28, 0x18}, /*01*/ + 0x28, 0x28, 0x28, 0x28, 0x18, 0x28, 0x18, 0x08}, /*01*/ {0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x88, 0x00, 0x04}, /*02*/ + 0x04, 0x04, 0x04, 0x04, 0x88, 0x00, 0x04, 0x04}, /*02*/ {0x02, 0x06, 0x11, 0x15, 0x04, 0x13, 0x10, 0x05, 0x1F, - 0x14, 0x20, 0x22, 0x21, 0x01, 0x03, 0x11}, /*03*/ + 0x14, 0x20, 0x22, 0x21, 0x01, 0x03, 0x11, 0x00}, /*03*/ {0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*04*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*04*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*05*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*05*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*06*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*06*/ {0xE1, 0xE1, 0x41, 0x41, 0xD1, 0xd1, 0x39, 0x39, 0x39, - 0x39, 0x39, 0x39, 0x39, 0xe1, 0xE1, 0x41}, /*07*/ + 0x39, 0x39, 0x39, 0x39, 0xe1, 0xE1, 0x41, 0x01}, /*07*/ {0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x01, 0x01, 0x02}, /*08*/ + 0x04, 0x04, 0x04, 0x04, 0x01, 0x01, 0x02, 0x04}, /*08*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*09*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*09*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*10*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*10*/ {0xD1, 0xD1, 0xD1, 0xD1, 0x01, 0x01, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0xD1, 0xD1}, /*11*/ + 0x81, 0x81, 0x81, 0x81, 0x81, 0xD1, 0xD1, 0x01}, /*11*/ {0x02, 0x02, 0x02, 0x02, 0x05, 0x05, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x02, 0x02, 0x02} /*12*/ + 0x07, 0x07, 0x07, 0x07, 0x02, 0x02, 0x02, 0x05} /*12*/ }; static void hdmi_msm_avi_info_frame(void) @@ -3746,6 +3710,9 @@ static void hdmi_msm_avi_info_frame(void) case HDMI_VFRMT_720x576p50_4_3: mode = 15; break; + case HDMI_VFRMT_1280x1024p60_5_4: + mode = 16; + break; default: DEV_INFO("%s: mode %d not supported\n", __func__, external_common_state->video_resolution); @@ -4160,23 +4127,27 @@ static void hdmi_msm_turn_on(void) hdmi_msm_set_mode(TRUE); hdmi_msm_video_setup(external_common_state->video_resolution); - if (!hdmi_msm_is_dvi_mode()) + if (!hdmi_msm_is_dvi_mode()) { hdmi_msm_audio_setup(); + + /* + * Send the audio switch device notification if HDCP is + * not enabled. Otherwise, the notification would be + * sent after HDCP authentication is successful. + */ + if (!hdmi_msm_state->hdcp_enable) + SWITCH_SET_HDMI_AUDIO(1, 0); + } hdmi_msm_avi_info_frame(); #ifdef CONFIG_FB_MSM_HDMI_3D hdmi_msm_vendor_infoframe_packetsetup(); #endif hdmi_msm_spd_infoframe_packetsetup(); - /* Set IRQ for HPD */ - HDMI_OUTP(0x0254, 4 | (external_common_state->hpd_state ? 0 : 2)); - -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT - if (hdmi_msm_state->reauth) { + if (hdmi_msm_state->hdcp_enable && hdmi_msm_state->reauth) { hdmi_msm_hdcp_enable(); hdmi_msm_state->reauth = FALSE ; } -#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT /* re-initialize CEC if enabled */ @@ -4191,17 +4162,15 @@ static void hdmi_msm_turn_on(void) DEV_INFO("HDMI Core: Initialized\n"); } -static void hdmi_msm_hpd_state_timer(unsigned long data) -{ - queue_work(hdmi_work_queue, &hdmi_msm_state->hpd_state_work); -} - -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT static void hdmi_msm_hdcp_timer(unsigned long data) { + if (!hdmi_msm_state->hdcp_enable) { + DEV_DBG("%s: HDCP not enabled\n", __func__); + return; + } + queue_work(hdmi_work_queue, &hdmi_msm_state->hdcp_work); } -#endif #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT static void hdmi_msm_cec_read_timer_func(unsigned long data) @@ -4210,6 +4179,36 @@ static void hdmi_msm_cec_read_timer_func(unsigned long data) } #endif +static void hdmi_msm_hpd_polarity_setup(void) +{ + u32 cable_sense; + bool polarity = !external_common_state->hpd_state; + bool trigger = false; + + if (polarity) + HDMI_OUTP(0x0254, BIT(2) | BIT(1)); + else + HDMI_OUTP(0x0254, BIT(2)); + + cable_sense = (HDMI_INP(0x0250) & BIT(1)) >> 1; + + if (cable_sense == polarity) + trigger = true; + + DEV_DBG("%s: listen=%s, sense=%s, trigger=%s\n", __func__, + polarity ? "connect" : "disconnect", + cable_sense ? "connect" : "disconnect", + trigger ? "Yes" : "No"); + + if (trigger) { + u32 reg_val = HDMI_INP(0x0258); + + /* Toggle HPD circuit to trigger HPD sense */ + HDMI_OUTP(0x0258, reg_val & ~BIT(28)); + HDMI_OUTP(0x0258, reg_val | BIT(28)); + } +} + static void hdmi_msm_hpd_off(void) { int rc = 0; @@ -4220,9 +4219,12 @@ static void hdmi_msm_hpd_off(void) } DEV_DBG("%s: (timer, 5V, IRQ off)\n", __func__); - del_timer(&hdmi_msm_state->hpd_state_timer); disable_irq(hdmi_msm_state->irq); + /* Disable HPD interrupt */ + HDMI_OUTP(0x0254, 0); + DEV_DBG("%s: Disabling HPD_CTRLd\n", __func__); + hdmi_msm_set_mode(FALSE); hdmi_msm_state->pd->enable_5v(0); hdmi_msm_clk(0); @@ -4241,7 +4243,7 @@ static void hdmi_msm_dump_regs(const char *prefix) #endif } -static int hdmi_msm_hpd_on(bool trigger_handler) +static int hdmi_msm_hpd_on(void) { static int phy_reset_done; uint32 hpd_ctrl; @@ -4282,35 +4284,28 @@ static int hdmi_msm_hpd_on(bool trigger_handler) /* HDMI_USEC_REFTIMER[0x0208] */ HDMI_OUTP(0x0208, 0x0001001B); - /* set timeout to 4.1ms (max) for hardware debounce */ - hpd_ctrl = HDMI_INP(0x0258) | 0x1FFF; + /* Set up HPD state variables */ + mutex_lock(&external_common_state_hpd_mutex); + external_common_state->hpd_state = 0; + mutex_unlock(&external_common_state_hpd_mutex); + mutex_lock(&hdmi_msm_state_mutex); + mutex_unlock(&hdmi_msm_state_mutex); - /* Toggle HPD circuit to trigger HPD sense */ - HDMI_OUTP(0x0258, ~(1 << 28) & hpd_ctrl); - HDMI_OUTP(0x0258, (1 << 28) | hpd_ctrl); + enable_irq(hdmi_msm_state->irq); hdmi_msm_state->hpd_initialized = TRUE; - /* Check HPD State */ - enable_irq(hdmi_msm_state->irq); - } + /* set timeout to 4.1ms (max) for hardware debounce */ + hpd_ctrl = HDMI_INP(0x0258) | 0x1FFF; - if (trigger_handler) { - /* Set HPD state machine: ensure at least 2 readouts */ - mutex_lock(&external_common_state_hpd_mutex); - mutex_lock(&hdmi_msm_state_mutex); - hdmi_msm_state->hpd_stable = 0; - hdmi_msm_state->hpd_prev_state = TRUE; - external_common_state->hpd_state = FALSE; - hdmi_msm_state->hpd_cable_chg_detected = TRUE; - mutex_unlock(&hdmi_msm_state_mutex); - mutex_unlock(&external_common_state_hpd_mutex); - mod_timer(&hdmi_msm_state->hpd_state_timer, - jiffies + HZ/2); + /* Turn on HPD HW circuit */ + HDMI_OUTP(0x0258, hpd_ctrl | BIT(28)); + + /* Set HPD cable sense polarity */ + hdmi_msm_hpd_polarity_setup(); } - DEV_DBG("%s: (IRQ, 5V on) \n", __func__, - trigger_handler ? "true" : "false"); + DEV_DBG("%s: (IRQ, 5V on)\n", __func__); return 0; error3: @@ -4323,7 +4318,8 @@ static int hdmi_msm_hpd_on(bool trigger_handler) static int hdmi_msm_power_ctrl(boolean enable) { - int rc = 0; + int rc = 0; + int time = 0; if (enable) { /* @@ -4333,7 +4329,22 @@ static int hdmi_msm_power_ctrl(boolean enable) if (hdmi_prim_display || external_common_state->hpd_feature_on) { DEV_DBG("%s: Turning HPD ciruitry on\n", __func__); - rc = hdmi_msm_hpd_on(true); + + rc = hdmi_msm_hpd_on(); + if (rc) { + DEV_ERR("%s: HPD ON FAILED\n", __func__); + return rc; + } + + /* Wait for HPD initialization to complete */ + INIT_COMPLETION(hdmi_msm_state->hpd_event_processed); + time = wait_for_completion_interruptible_timeout( + &hdmi_msm_state->hpd_event_processed, HZ); + if (!time && !external_common_state->hpd_state) { + DEV_DBG("%s: cable not detected\n", __func__); + queue_work(hdmi_work_queue, + &hdmi_msm_state->hpd_state_work); + } } } else { DEV_DBG("%s: Turning HPD ciruitry off\n", __func__); @@ -4346,39 +4357,62 @@ static int hdmi_msm_power_ctrl(boolean enable) static int hdmi_msm_power_on(struct platform_device *pdev) { struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + int ret = 0; bool changed; - if (!hdmi_msm_state || !hdmi_msm_state->hdmi_app_clk || !MSM_HDMI_BASE) - return -ENODEV; + if (!hdmi_ready()) { + DEV_ERR("%s: HDMI/HPD not initialized\n", __func__); + return ret; + } - DEV_INFO("power: ON (%dx%d %d)\n", mfd->var_xres, mfd->var_yres, - mfd->var_pixclock); + if (!external_common_state->hpd_state) { + DEV_DBG("%s:HDMI cable not connected\n", __func__); + goto error; + } + /* Only start transmission with supported resolution */ changed = hdmi_common_get_video_format_from_drv_data(mfd); - hdmi_msm_audio_info_setup(TRUE, 0, 0, 0, FALSE); + if (changed || external_common_state->default_res_supported) { + mutex_lock(&external_common_state_hpd_mutex); + if (external_common_state->hpd_state && + hdmi_msm_is_power_on()) { + mutex_unlock(&external_common_state_hpd_mutex); - mutex_lock(&external_common_state_hpd_mutex); - hdmi_msm_state->panel_power_on = TRUE; - if (external_common_state->hpd_state && hdmi_msm_is_power_on()) { - DEV_DBG("%s: Turning HDMI on\n", __func__); - mutex_unlock(&external_common_state_hpd_mutex); - hdmi_msm_turn_on(); + DEV_INFO("HDMI cable connected %s(%dx%d, %d)\n", + __func__, mfd->var_xres, mfd->var_yres, + mfd->var_pixclock); - /* Kick off HDCP Authentication */ - mutex_lock(&hdcp_auth_state_mutex); - hdmi_msm_state->reauth = FALSE; - hdmi_msm_state->full_auth_done = FALSE; - mutex_unlock(&hdcp_auth_state_mutex); - mod_timer(&hdmi_msm_state->hdcp_timer, jiffies + HZ/2); - } else - mutex_unlock(&external_common_state_hpd_mutex); + hdmi_msm_turn_on(); + hdmi_msm_state->panel_power_on = TRUE; + + if (hdmi_msm_state->hdcp_enable) { + /* Kick off HDCP Authentication */ + mutex_lock(&hdcp_auth_state_mutex); + hdmi_msm_state->reauth = FALSE; + hdmi_msm_state->full_auth_done = FALSE; + mutex_unlock(&hdcp_auth_state_mutex); + mod_timer(&hdmi_msm_state->hdcp_timer, + jiffies + HZ/2); + } + } else { + mutex_unlock(&external_common_state_hpd_mutex); + } - hdmi_msm_dump_regs("HDMI-ON: "); + hdmi_msm_dump_regs("HDMI-ON: "); + DEV_INFO("power=%s DVI= %s\n", + hdmi_msm_is_power_on() ? "ON" : "OFF" , + hdmi_msm_is_dvi_mode() ? "ON" : "OFF"); + } else { + DEV_ERR("%s: Video fmt %d not supp. Returning\n", + __func__, + external_common_state->video_resolution); + } - DEV_INFO("power=%s DVI= %s\n", - hdmi_msm_is_power_on() ? "ON" : "OFF" , - hdmi_msm_is_dvi_mode() ? "ON" : "OFF"); - return 0; +error: + /* Set HPD cable sense polarity */ + hdmi_msm_hpd_polarity_setup(); + + return ret; } void mhl_connect_api(boolean on) @@ -4386,23 +4420,16 @@ void mhl_connect_api(boolean on) char *envp[2]; /* Simulating a HPD event based on MHL event */ - hdmi_msm_state->hpd_cable_chg_detected = FALSE; - /* QDSP OFF preceding the HPD event notification */ - switch_set_state(&external_common_state->sdev, 0); - DEV_INFO("Hdmi state switched to %d: %s\n", - external_common_state->sdev.state, __func__); if (on) { hdmi_msm_read_edid(); - if (hdmi_msm_has_hdcp()) - hdmi_msm_state->reauth = FALSE ; + hdmi_msm_state->reauth = FALSE ; /* Build EDID table */ hdmi_msm_turn_on(); DEV_INFO("HDMI HPD: CONNECTED: send ONLINE\n"); kobject_uevent(external_common_state->uevent_kobj, KOBJ_ONLINE); - hdmi_msm_hdcp_enable(); envp[0] = 0; - if (!hdmi_msm_has_hdcp()) { + if (!hdmi_msm_state->hdcp_enable) { /* Send Audio for HDMI Compliance Cases*/ envp[0] = "HDCP_STATE=PASS"; envp[1] = NULL; @@ -4410,16 +4437,18 @@ void mhl_connect_api(boolean on) kobject_uevent_env(external_common_state->uevent_kobj, KOBJ_CHANGE, envp); switch_set_state(&external_common_state->sdev, 1); - DEV_INFO("Hdmi state switched to %d: %s\n", - external_common_state->sdev.state, __func__); + DEV_INFO("%s: hdmi state switched to %d\n", + __func__, external_common_state->sdev.state); + } else { + hdmi_msm_hdcp_enable(); } } else { DEV_INFO("HDMI HPD: DISCONNECTED: send OFFLINE\n"); kobject_uevent(external_common_state->uevent_kobj, KOBJ_OFFLINE); switch_set_state(&external_common_state->sdev, 0); - DEV_INFO("Hdmi state switched to %d: %s\n", - external_common_state->sdev.state, __func__); + DEV_INFO("%s: hdmi state switched to %d\n", __func__, + external_common_state->sdev.state); } } EXPORT_SYMBOL(mhl_connect_api); @@ -4432,35 +4461,108 @@ EXPORT_SYMBOL(mhl_connect_api); */ static int hdmi_msm_power_off(struct platform_device *pdev) { - if (!hdmi_msm_state->hdmi_app_clk) - return -ENODEV; + int ret = 0; -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT - mutex_lock(&hdmi_msm_state_mutex); - if (hdmi_msm_state->hdcp_activating) { - hdmi_msm_state->panel_power_on = FALSE; - mutex_unlock(&hdmi_msm_state_mutex); - DEV_INFO("HDCP: activating, returning\n"); - return 0; + if (!hdmi_ready()) { + DEV_ERR("%s: HDMI/HPD not initialized\n", __func__); + return ret; } - mutex_unlock(&hdmi_msm_state_mutex); -#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ - DEV_INFO("power: OFF (audio off, Reset Core)\n"); - hdmi_msm_audio_off(); -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT - hdcp_deauthenticate(); -#endif + if (!hdmi_msm_state->panel_power_on) { + DEV_DBG("%s: panel not ON\n", __func__); + goto error; + } + + if (hdmi_msm_state->hdcp_enable) { + if (hdmi_msm_state->hdcp_activating) { + /* + * Let the HDCP work know that we got an HPD + * disconnect so that it can stop the + * reauthentication loop. + */ + mutex_lock(&hdcp_auth_state_mutex); + hdmi_msm_state->hpd_during_auth = TRUE; + mutex_unlock(&hdcp_auth_state_mutex); + } + + /* + * Cancel any pending reauth attempts. + * If one is ongoing, wait for it to finish + */ + cancel_work_sync(&hdmi_msm_state->hdcp_reauth_work); + cancel_work_sync(&hdmi_msm_state->hdcp_work); + del_timer_sync(&hdmi_msm_state->hdcp_timer); + hdmi_msm_state->reauth = FALSE; + + hdcp_deauthenticate(); + } + + SWITCH_SET_HDMI_AUDIO(0, 0); + + if (!hdmi_msm_is_dvi_mode()) + hdmi_msm_audio_off(); + hdmi_msm_powerdown_phy(); hdmi_msm_state->panel_power_on = FALSE; - return 0; + DEV_INFO("power: OFF (audio off)\n"); + + if (!completion_done(&hdmi_msm_state->hpd_event_processed)) + complete(&hdmi_msm_state->hpd_event_processed); +error: + /* Set HPD cable sense polarity */ + hdmi_msm_hpd_polarity_setup(); + + return ret; +} + +bool mhl_is_enabled(void) +{ + return hdmi_msm_state->is_mhl_enabled; +} + +void hdmi_msm_config_hdcp_feature(void) +{ + if (hdcp_feature_on && hdmi_msm_has_hdcp()) { + init_timer(&hdmi_msm_state->hdcp_timer); + hdmi_msm_state->hdcp_timer.function = hdmi_msm_hdcp_timer; + hdmi_msm_state->hdcp_timer.data = (uint32)NULL; + hdmi_msm_state->hdcp_timer.expires = 0xffffffffL; + + init_completion(&hdmi_msm_state->hdcp_success_done); + INIT_WORK(&hdmi_msm_state->hdcp_reauth_work, + hdmi_msm_hdcp_reauth_work); + INIT_WORK(&hdmi_msm_state->hdcp_work, hdmi_msm_hdcp_work); + hdmi_msm_state->hdcp_enable = TRUE; + } else { + del_timer(&hdmi_msm_state->hdcp_timer); + hdmi_msm_state->hdcp_enable = FALSE; + } + external_common_state->present_hdcp = hdmi_msm_state->hdcp_enable; + DEV_INFO("%s: HDCP Feature: %s\n", __func__, + hdmi_msm_state->hdcp_enable ? "Enabled" : "Disabled"); +} + +static void hdmi_msm_update_panel_info(struct msm_fb_data_type *mfd) +{ + if (!mfd) + return; + + if (hdmi_common_get_video_format_from_drv_data(mfd)) + hdmi_common_init_panel_info(&mfd->panel_info); +} + +static bool hdmi_msm_cable_connected(void) +{ + return hdmi_msm_state->hpd_initialized && + external_common_state->hpd_state; } static int __devinit hdmi_msm_probe(struct platform_device *pdev) { int rc; struct platform_device *fb_dev; + struct msm_fb_data_type *mfd = NULL; if (!hdmi_msm_state) { pr_err("%s: hdmi_msm_state is NULL\n", __func__); @@ -4562,22 +4664,6 @@ static int __devinit hdmi_msm_probe(struct platform_device *pdev) } disable_irq(hdmi_msm_state->irq); - init_timer(&hdmi_msm_state->hpd_state_timer); - hdmi_msm_state->hpd_state_timer.function = - hdmi_msm_hpd_state_timer; - hdmi_msm_state->hpd_state_timer.data = (uint32)NULL; - - hdmi_msm_state->hpd_state_timer.expires = 0xffffffffL; - -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT - init_timer(&hdmi_msm_state->hdcp_timer); - hdmi_msm_state->hdcp_timer.function = - hdmi_msm_hdcp_timer; - hdmi_msm_state->hdcp_timer.data = (uint32)NULL; - - hdmi_msm_state->hdcp_timer.expires = 0xffffffffL; -#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ - #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT init_timer(&hdmi_msm_state->cec_read_timer); hdmi_msm_state->cec_read_timer.function = @@ -4598,37 +4684,43 @@ static int __devinit hdmi_msm_probe(struct platform_device *pdev) } else DEV_ERR("Init FAILED: failed to add fb device\n"); + mfd = platform_get_drvdata(fb_dev); + mfd->update_panel_info = hdmi_msm_update_panel_info; + mfd->is_panel_ready = hdmi_msm_cable_connected; + if (hdmi_prim_display) { - rc = hdmi_msm_hpd_on(true); + rc = hdmi_msm_hpd_on(); if (rc) goto error; } - if (hdmi_msm_has_hdcp()) { - /* Don't Set Encryption in case of non HDCP builds */ - external_common_state->present_hdcp = FALSE; -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT - external_common_state->present_hdcp = TRUE; -#endif - } else { - external_common_state->present_hdcp = FALSE; -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT - /* - * If the device is not hdcp capable do - * not start hdcp timer. - */ - del_timer(&hdmi_msm_state->hdcp_timer); -#endif - } + hdmi_msm_config_hdcp_feature(); /* Initialize hdmi node and register with switch driver */ if (hdmi_prim_display) external_common_state->sdev.name = "hdmi_as_primary"; else external_common_state->sdev.name = "hdmi"; - if (switch_dev_register(&external_common_state->sdev) < 0) + if (switch_dev_register(&external_common_state->sdev) < 0) { DEV_ERR("Hdmi switch registration failed\n"); + rc = -ENODEV; + goto error; + } + + external_common_state->audio_sdev.name = "hdmi_audio"; + if (switch_dev_register(&external_common_state->audio_sdev) < 0) { + DEV_ERR("Hdmi audio switch registration failed\n"); + switch_dev_unregister(&external_common_state->sdev); + rc = -ENODEV; + goto error; + } + /* Set the default video resolution for MHL-enabled display */ + if (hdmi_msm_state->is_mhl_enabled) { + DEV_DBG("MHL Enabled. Restricting default video resolution\n"); + external_common_state->video_resolution = + HDMI_VFRMT_1920x1080p30_16_9; + } return 0; error: @@ -4664,6 +4756,7 @@ static int __devexit hdmi_msm_remove(struct platform_device *pdev) /* Unregister hdmi node from switch driver */ switch_dev_unregister(&external_common_state->sdev); + switch_dev_unregister(&external_common_state->audio_sdev); hdmi_msm_hpd_off(); free_irq(hdmi_msm_state->irq, NULL); @@ -4701,11 +4794,26 @@ static int hdmi_msm_hpd_feature(int on) DEV_INFO("%s: %d\n", __func__, on); if (on) { - rc = hdmi_msm_hpd_on(true); + rc = hdmi_msm_hpd_on(); } else { + if (external_common_state->hpd_state) { + external_common_state->hpd_state = 0; + + /* Send offline event to switch OFF HDMI and HAL FD */ + hdmi_msm_send_event(HPD_EVENT_OFFLINE); + + /* Wait for HDMI and FD to close */ + INIT_COMPLETION(hdmi_msm_state->hpd_event_processed); + wait_for_completion_interruptible_timeout( + &hdmi_msm_state->hpd_event_processed, HZ); + } + hdmi_msm_hpd_off(); + /* Set HDMI switch node to 0 on HPD feature disable */ switch_set_state(&external_common_state->sdev, 0); + DEV_INFO("%s: hdmi state switched to %d\n", __func__, + external_common_state->sdev.state); } return rc; @@ -4796,12 +4904,8 @@ static int __init hdmi_msm_init(void) hdmi_common_init_panel_info(&hdmi_msm_panel_data.panel_info); init_completion(&hdmi_msm_state->ddc_sw_done); + init_completion(&hdmi_msm_state->hpd_event_processed); INIT_WORK(&hdmi_msm_state->hpd_state_work, hdmi_msm_hpd_state_work); -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT - init_completion(&hdmi_msm_state->hdcp_success_done); - INIT_WORK(&hdmi_msm_state->hdcp_reauth_work, hdmi_msm_hdcp_reauth_work); - INIT_WORK(&hdmi_msm_state->hdcp_work, hdmi_msm_hdcp_work); -#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT INIT_WORK(&hdmi_msm_state->cec_latch_detect_work, @@ -4825,9 +4929,6 @@ static int __init hdmi_msm_init(void) " RELEASE" #endif " AUDIO EDID HPD HDCP" -#ifndef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT - ":0" -#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ " DVI" #ifndef CONFIG_FB_MSM_HDMI_MSM_PANEL_DVI_SUPPORT ":0" @@ -4849,6 +4950,36 @@ static void __exit hdmi_msm_exit(void) platform_driver_unregister(&this_driver); } +static int set_hdcp_feature_on(const char *val, const struct kernel_param *kp) +{ + int rv = param_set_bool(val, kp); + + if (rv) + return rv; + + pr_debug("%s: HDCP feature = %d\n", __func__, hdcp_feature_on); + if (hdmi_msm_state) { + if ((HDMI_INP(0x0250) & 0x2)) { + pr_err("%s: Unable to set HDCP feature", __func__); + pr_err("%s: HDMI panel is currently turned on", + __func__); + } else if (hdcp_feature_on != hdmi_msm_state->hdcp_enable) { + hdmi_msm_config_hdcp_feature(); + } + } + + return 0; +} + +static struct kernel_param_ops hdcp_feature_on_param_ops = { + .set = set_hdcp_feature_on, + .get = param_get_bool, +}; + +module_param_cb(hdcp, &hdcp_feature_on_param_ops, &hdcp_feature_on, + S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(hdcp, "Enable or Disable HDCP"); + module_init(hdmi_msm_init); module_exit(hdmi_msm_exit); diff --git a/drivers/video/msm/hdmi_msm.h b/drivers/video/msm/hdmi_msm.h index 76ffce50..f8fcdc06 100644 --- a/drivers/video/msm/hdmi_msm.h +++ b/drivers/video/msm/hdmi_msm.h @@ -56,22 +56,17 @@ struct hdmi_msm_state_type { #ifdef CONFIG_SUSPEND boolean pm_suspended; #endif - int hpd_stable; - boolean hpd_prev_state; - boolean hpd_cable_chg_detected; boolean full_auth_done; boolean hpd_during_auth; struct work_struct hpd_state_work; - struct timer_list hpd_state_timer; struct completion ddc_sw_done; -#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT + bool hdcp_enable; boolean hdcp_activating; boolean reauth ; struct work_struct hdcp_reauth_work, hdcp_work; struct completion hdcp_success_done; struct timer_list hdcp_timer; -#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */ #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT boolean cec_enabled; @@ -111,6 +106,7 @@ struct hdmi_msm_state_type { struct external_common_state_type common; boolean is_mhl_enabled; + struct completion hpd_event_processed; }; extern struct hdmi_msm_state_type *hdmi_msm_state; diff --git a/drivers/video/msm/lcdc.c b/drivers/video/msm/lcdc.c index ef13c0e6..24c4ec94 100644 --- a/drivers/video/msm/lcdc.c +++ b/drivers/video/msm/lcdc.c @@ -84,8 +84,6 @@ static int lcdc_off(struct platform_device *pdev) } clk_disable_unprepare(mfd->ebi1_clk); } -#else - mdp_bus_scale_update_request(0); #endif return ret; @@ -108,9 +106,7 @@ static int lcdc_on(struct platform_device *pdev) if (!panel_pixclock_freq) panel_pixclock_freq = mfd->fbi->var.pixclock; -#ifdef CONFIG_MSM_BUS_SCALING - mdp_bus_scale_update_request(2); -#else +#ifndef CONFIG_MSM_BUS_SCALING if (panel_pixclock_freq > 65000000) /* pm_qos_rate should be in Khz */ pm_qos_rate = panel_pixclock_freq / 1000 ; diff --git a/drivers/video/msm/lvds.c b/drivers/video/msm/lvds.c index da9dbd1e..4e31d6a2 100644 --- a/drivers/video/msm/lvds.c +++ b/drivers/video/msm/lvds.c @@ -92,11 +92,11 @@ static void lvds_init(struct msm_fb_data_type *mfd) MDP_OUTP(MDP_BASE + 0xc3064, 0x05); MDP_OUTP(MDP_BASE + 0xc3050, 0x20); } else { - MDP_OUTP(MDP_BASE + 0xc3004, 0x62); + MDP_OUTP(MDP_BASE + 0xc3004, 0x8f); MDP_OUTP(MDP_BASE + 0xc3008, 0x30); - MDP_OUTP(MDP_BASE + 0xc300c, 0xc4); + MDP_OUTP(MDP_BASE + 0xc300c, 0xc6); MDP_OUTP(MDP_BASE + 0xc3014, 0x10); - MDP_OUTP(MDP_BASE + 0xc3018, 0x05); + MDP_OUTP(MDP_BASE + 0xc3018, 0x07); MDP_OUTP(MDP_BASE + 0xc301c, 0x62); MDP_OUTP(MDP_BASE + 0xc3020, 0x41); MDP_OUTP(MDP_BASE + 0xc3024, 0x0d); @@ -173,7 +173,7 @@ static void lvds_init(struct msm_fb_data_type *mfd) /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_6_TO_4 */ MDP_OUTP(MDP_BASE + 0xc2020, 0x00090a0b); /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_3_TO_0 */ - MDP_OUTP(MDP_BASE + 0xc2024, 0x151a191a); + MDP_OUTP(MDP_BASE + 0xc2024, 0x1518191a); /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */ MDP_OUTP(MDP_BASE + 0xc2028, 0x00121314); /* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_3_TO_0 */ @@ -254,10 +254,6 @@ static int lvds_off(struct platform_device *pdev) if (lvds_pdata && lvds_pdata->lcdc_gpio_config) ret = lvds_pdata->lcdc_gpio_config(0); -#ifdef CONFIG_MSM_BUS_SCALING - mdp_bus_scale_update_request(0); -#endif - return ret; } @@ -273,9 +269,6 @@ static int lvds_on(struct platform_device *pdev) if (!panel_pixclock_freq) panel_pixclock_freq = mfd->fbi->var.pixclock; -#ifdef CONFIG_MSM_BUS_SCALING - mdp_bus_scale_update_request(2); -#endif mfd = platform_get_drvdata(pdev); if (lvds_clk) { diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 66c6ae86..88a476ca 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "mdp.h" #include "msm_fb.h" @@ -53,9 +54,10 @@ static struct clk *mdp_lut_clk; int mdp_rev; int mdp_iommu_split_domain; u32 mdp_max_clk = 200000000; +u64 mdp_max_bw = 2000000000; static struct platform_device *mdp_init_pdev; -static struct regulator *footswitch; +static struct regulator *footswitch, *dsi_pll_vdda, *dsi_pll_vddio; static unsigned int mdp_footswitch_on; struct completion mdp_ppp_comp; @@ -185,7 +187,7 @@ uint32_t mdp_block2base(uint32_t block) base = 0x18000; break; case MDP_BLOCK_OVERLAY_2: - base = (mdp_rev >= MDP_REV_44) ? 0x88000 : 0; + base = (mdp_rev >= MDP_REV_43) ? 0x88000 : 0; break; default: break; @@ -483,6 +485,7 @@ int mdp_hist_lut_config(struct mdp_hist_lut_data *data) DEFINE_MUTEX(mdp_lut_push_sem); static int mdp_lut_i; + static int mdp_lut_hw_update(struct fb_cmap *cmap) { int i; @@ -520,7 +523,9 @@ static int mdp_lut_update_nonlcdc(struct fb_info *info, struct fb_cmap *cmap) int ret; mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_clk_ctrl(1); ret = mdp_lut_hw_update(cmap); + mdp_clk_ctrl(0); mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); if (ret) @@ -542,16 +547,19 @@ static int mdp_lut_update_lcdc(struct fb_info *info, struct fb_cmap *cmap) uint32_t out; mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_clk_ctrl(1); ret = mdp_lut_hw_update(cmap); if (ret) { + mdp_clk_ctrl(0); mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); return ret; } /*mask off non LUT select bits*/ - out = inpdw(MDP_BASE + 0x90070) & ~((0x1 << 10) | 0x7); + out = inpdw(MDP_BASE + 0x90070); MDP_OUTP(MDP_BASE + 0x90070, (mdp_lut_i << 10) | 0x7 | out); + mdp_clk_ctrl(0); mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); mdp_lut_i = (mdp_lut_i + 1)%2; @@ -819,14 +827,32 @@ static int mdp_histogram_enable(struct mdp_hist_mgmt *mgmt) } mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + base = (uint32_t) (MDP_BASE + mgmt->base); + /*First make sure that device is not collecting histogram*/ + mgmt->mdp_is_hist_data = FALSE; + mgmt->mdp_is_hist_valid = FALSE; + mgmt->mdp_is_hist_init = FALSE; + spin_lock_irqsave(&mdp_spin_lock, flag); + outp32(MDP_INTR_CLEAR, mgmt->intr); + mdp_intr_mask &= ~mgmt->intr; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + MDP_OUTP(base + 0x001C, 0); + MDP_OUTP(base + 0x0018, INTR_HIST_DONE | INTR_HIST_RESET_SEQ_DONE); + MDP_OUTP(base + 0x0024, 0); + spin_unlock_irqrestore(&mdp_spin_lock, flag); + + mutex_unlock(&mgmt->mdp_hist_mutex); + cancel_work_sync(&mgmt->mdp_histogram_worker); + mutex_lock(&mgmt->mdp_hist_mutex); + + /*Then initialize histogram*/ INIT_COMPLETION(mgmt->mdp_hist_comp); - base = (uint32_t) (MDP_BASE + mgmt->base); + spin_lock_irqsave(&mdp_spin_lock, flag); MDP_OUTP(base + 0x0018, INTR_HIST_DONE | INTR_HIST_RESET_SEQ_DONE); MDP_OUTP(base + 0x0010, 1); MDP_OUTP(base + 0x001C, INTR_HIST_DONE | INTR_HIST_RESET_SEQ_DONE); - spin_lock_irqsave(&mdp_spin_lock, flag); outp32(MDP_INTR_CLEAR, mgmt->intr); mdp_intr_mask |= mgmt->intr; outp32(MDP_INTR_ENABLE, mdp_intr_mask); @@ -890,10 +916,12 @@ int _mdp_histogram_ctrl(boolean en, struct mdp_hist_mgmt *mgmt) int ret = 0; mutex_lock(&mgmt->mdp_hist_mutex); - if (mgmt->mdp_is_hist_start && !mgmt->mdp_is_hist_data && en) - ret = mdp_histogram_enable(mgmt); - else if (mgmt->mdp_is_hist_data && !en) - ret = mdp_histogram_disable(mgmt); + if (mgmt->mdp_is_hist_start == TRUE) { + if (en) + ret = mdp_histogram_enable(mgmt); + else + ret = mdp_histogram_disable(mgmt); + } mutex_unlock(&mgmt->mdp_hist_mutex); if (en == false) @@ -991,6 +1019,7 @@ int mdp_histogram_stop(struct fb_info *info, uint32_t block) mgmt->mdp_is_hist_start = FALSE; if (!mfd->panel_power_on) { + mgmt->mdp_is_hist_data = FALSE; if (mgmt->hist != NULL) { mgmt->hist = NULL; complete(&mgmt->mdp_hist_comp); @@ -1047,6 +1076,7 @@ static int _mdp_histogram_read_dma_data(struct mdp_hist_mgmt *mgmt) } mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_clk_ctrl(1); for (i = 0; i < mgmt->num_bins; i++) { mgmt->c0[i] = inpdw(mdp_hist_base + r_data_offset + (4*i)); mgmt->c1[i] = inpdw(mdp_hist_base + g_data_offset + (4*i)); @@ -1062,6 +1092,7 @@ static int _mdp_histogram_read_dma_data(struct mdp_hist_mgmt *mgmt) } else ret = -ENOMEM; } + mdp_clk_ctrl(0); mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); if (!ret) @@ -1097,6 +1128,7 @@ static int _mdp_histogram_read_vg_data(struct mdp_hist_mgmt *mgmt) } mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_clk_ctrl(1); for (i = 0; i < mgmt->num_bins; i++) mgmt->c0[i] = inpdw(mdp_hist_base + MDP_HIST_DATA_LUMA_OFF + (4*i)); @@ -1108,6 +1140,7 @@ static int _mdp_histogram_read_vg_data(struct mdp_hist_mgmt *mgmt) } else ret = -ENOMEM; } + mdp_clk_ctrl(0); mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); if (!ret) @@ -1304,7 +1337,7 @@ static void vsync_isr_handler(void) } #endif -static ssize_t vsync_show_event(struct device *dev, +ssize_t mdp_dma_show_event(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret = 0; @@ -1317,7 +1350,7 @@ static ssize_t vsync_show_event(struct device *dev, wait_for_completion(&vsync_cntrl.vsync_wait); ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", - ktime_to_ns(vsync_cntrl.vsync_time)); + ktime_to_ns(vsync_cntrl.vsync_time)); buf[strlen(buf) + 1] = '\0'; return ret; } @@ -1338,15 +1371,125 @@ int mdp_ppp_pipe_wait(void) if (wait == TRUE) { ret = wait_for_completion_interruptible_timeout(&mdp_ppp_comp, 5 * HZ); - if (!ret) printk(KERN_ERR "%s: Timed out waiting for the MDP.\n", - __func__); + __func__); } return ret; } +#define MAX_VSYNC_GAP 4 +#define DEFAULT_FRAME_RATE 60 + +u32 mdp_get_panel_framerate(struct msm_fb_data_type *mfd) +{ + u32 frame_rate = 0, pixel_rate = 0, total_pixel; + struct msm_panel_info *panel_info = &mfd->panel_info; + + if ((panel_info->type == MIPI_VIDEO_PANEL || + panel_info->type == MIPI_CMD_PANEL) && + panel_info->mipi.frame_rate) + frame_rate = panel_info->mipi.frame_rate; + + if (mfd->dest == DISPLAY_LCD) { + if (panel_info->type == MDDI_PANEL && panel_info->mddi.is_type1) + frame_rate = panel_info->lcd.refx100 / (100 * 2); + else if (panel_info->type != MIPI_CMD_PANEL) + frame_rate = panel_info->lcd.refx100 / 100; + } + pr_debug("%s type=%d frame_rate=%d\n", __func__, + panel_info->type, frame_rate); + + if (frame_rate) + return frame_rate; + + pixel_rate = + (panel_info->type == MIPI_CMD_PANEL || + panel_info->type == MIPI_VIDEO_PANEL) ? + panel_info->mipi.dsi_pclk_rate : + panel_info->clk_rate; + + if (!pixel_rate) + pr_warn("%s pixel rate is zero\n", __func__); + + total_pixel = + (panel_info->lcdc.h_back_porch + + panel_info->lcdc.h_front_porch + + panel_info->lcdc.h_pulse_width + + panel_info->xres) * + (panel_info->lcdc.v_back_porch + + panel_info->lcdc.v_front_porch + + panel_info->lcdc.v_pulse_width + + panel_info->yres); + + if (total_pixel) + frame_rate = pixel_rate / total_pixel; + else + pr_warn("%s total pixels are zero\n", __func__); + + if (frame_rate == 0) { + frame_rate = DEFAULT_FRAME_RATE; + pr_warn("%s frame rate=%d is default\n", __func__, frame_rate); + } + pr_debug("%s frame rate=%d total_pixel=%d, pixel_rate=%d\n", __func__, + frame_rate, total_pixel, pixel_rate); + + return frame_rate; +} + +static int mdp_diff_to_next_vsync(ktime_t cur_time, + ktime_t last_vsync, u32 vsync_period) +{ + int diff_from_last, diff_to_next; + /* + * Get interval beween last vsync and current time + * Current time = CPU programming MDP for next Vsync + */ + diff_from_last = + (ktime_to_us(ktime_sub(cur_time, last_vsync))); + diff_from_last /= USEC_PER_MSEC; + /* + * If the last Vsync occurred too long ago, skip programming + * the timer + */ + if (diff_from_last < (vsync_period * MAX_VSYNC_GAP)) { + if (diff_from_last > vsync_period) + diff_to_next = + (diff_from_last - vsync_period) % vsync_period; + else + diff_to_next = vsync_period - diff_from_last; + } else { + /* mark it out of range */ + diff_to_next = vsync_period + 1; + } + return diff_to_next; +} + +void mdp_update_pm(struct msm_fb_data_type *mfd, ktime_t pre_vsync) +{ + u32 vsync_period; + int diff_to_next; + ktime_t cur_time, wakeup_time; + + if (!mfd->cpu_pm_hdl) + return; + vsync_period = mfd->panel_info.frame_interval; + cur_time = ktime_get(); + diff_to_next = mdp_diff_to_next_vsync(cur_time, + pre_vsync, + vsync_period); + if (diff_to_next > vsync_period) + return; + pr_debug("%s cur_time %d, pre_vsync %d, to_next %d\n", + __func__, + (int)ktime_to_ms(cur_time), + (int)ktime_to_ms(pre_vsync), + diff_to_next); + wakeup_time = ktime_add_ns(cur_time, diff_to_next * NSEC_PER_MSEC); + activate_event_timer(mfd->cpu_pm_hdl, wakeup_time); +} + static DEFINE_SPINLOCK(mdp_lock); static int mdp_irq_mask; static int mdp_irq_enabled; @@ -1479,15 +1622,12 @@ void mdp_pipe_kickoff(uint32 term, struct msm_fb_data_type *mfd) outpdw(MDP_BASE + 0x0014, 0x0); /* start DMA */ } else if (term == MDP_OVERLAY0_TERM) { mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - mdp_lut_enable(); outpdw(MDP_BASE + 0x0004, 0); } else if (term == MDP_OVERLAY1_TERM) { mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - mdp_lut_enable(); outpdw(MDP_BASE + 0x0008, 0); } else if (term == MDP_OVERLAY2_TERM) { mdp_pipe_ctrl(MDP_OVERLAY2_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - mdp_lut_enable(); outpdw(MDP_BASE + 0x00D0, 0); } #else @@ -1565,7 +1705,9 @@ void mdp_clk_ctrl(int on) mdp_clk_cnt--; if (mdp_clk_cnt == 0) mdp_clk_disable_unprepare(); - } + } else + pr_err("%s: %d: mdp clk off is invalid\n", + __func__, __LINE__); } pr_debug("%s: on=%d cnt=%d\n", __func__, on, mdp_clk_cnt); mutex_unlock(&mdp_suspend_mutex); @@ -1758,10 +1900,8 @@ void mdp_histogram_handle_isr(struct mdp_hist_mgmt *mgmt) isr &= mask; if (isr & INTR_HIST_RESET_SEQ_DONE) __mdp_histogram_kickoff(mgmt); - - if (isr & INTR_HIST_DONE) { + else if (isr & INTR_HIST_DONE) queue_work(mdp_hist_wq, &mgmt->mdp_histogram_worker); - } } #ifndef CONFIG_FB_MSM_MDP40 @@ -1857,14 +1997,13 @@ irqreturn_t mdp_isr(int irq, void *ptr) dma = &dma2_data; spin_lock_irqsave(&mdp_spin_lock, flag); vsync_isr = vsync_cntrl.vsync_irq_enabled; - disabled_clocks = vsync_cntrl.disabled_clocks; /* let's disable LCDC interrupt */ if (dma->waiting) { dma->waiting = FALSE; complete(&dma->comp); } - if (!vsync_isr && !vsync_cntrl.disabled_clocks) { + if (!vsync_isr) { mdp_intr_mask &= ~LCDC_FRAME_START; outp32(MDP_INTR_ENABLE, mdp_intr_mask); mdp_disable_irq_nosync(MDP_VSYNC_TERM); @@ -1874,7 +2013,7 @@ irqreturn_t mdp_isr(int irq, void *ptr) } spin_unlock_irqrestore(&mdp_spin_lock, flag); - if (!vsync_isr && !disabled_clocks) + if (!vsync_isr) mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, TRUE); @@ -2110,12 +2249,16 @@ static int mdp_off(struct platform_device *pdev) mfd->panel.type == LCDC_PANEL || mfd->panel.type == LVDS_PANEL) mdp4_lcdc_off(pdev); + else if (mfd->panel.type == MDDI_PANEL) + mdp4_mddi_off(pdev); mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); ret = panel_next_off(pdev); mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); mdp_clk_ctrl(0); - +#ifdef CONFIG_MSM_BUS_SCALING + mdp_bus_scale_update_request(0, 0); +#endif if (mdp_rev >= MDP_REV_41 && mfd->panel.type == MIPI_CMD_PANEL) mdp_dsi_cmd_overlay_suspend(mfd); pr_debug("%s:-\n", __func__); @@ -2133,15 +2276,6 @@ void mdp4_hw_init(void) } #endif -static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL); -static struct attribute *vsync_fs_attrs[] = { - &dev_attr_vsync_event.attr, - NULL, -}; -static struct attribute_group vsync_fs_attr_group = { - .attrs = vsync_fs_attrs, -}; - static int mdp_on(struct platform_device *pdev) { int ret = 0; @@ -2164,6 +2298,9 @@ static int mdp_on(struct platform_device *pdev) mfd->panel.type == LCDC_PANEL || mfd->panel.type == LVDS_PANEL) { mdp4_lcdc_on(pdev); + } else if (mfd->panel.type == MDDI_PANEL) { + mdp_vsync_cfg_regs(mfd, FALSE); + mdp4_mddi_on(pdev); } mdp_clk_ctrl(0); @@ -2173,21 +2310,8 @@ static int mdp_on(struct platform_device *pdev) if (mdp_rev == MDP_REV_303 && mfd->panel.type == MIPI_CMD_PANEL) { vsync_cntrl.dev = mfd->fbi->dev; - - if (!vsync_cntrl.sysfs_created) { - ret = sysfs_create_group(&vsync_cntrl.dev->kobj, - &vsync_fs_attr_group); - if (ret) { - pr_err("%s: sysfs creation failed, ret=%d\n", - __func__, ret); - return ret; - } - - kobject_uevent(&vsync_cntrl.dev->kobj, KOBJ_ADD); - pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__); - vsync_cntrl.sysfs_created = 1; - } atomic_set(&vsync_cntrl.suspend, 0); + atomic_set(&vsync_cntrl.vsync_resume, 1); } mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); @@ -2196,6 +2320,10 @@ static int mdp_on(struct platform_device *pdev) mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); mdp_histogram_ctrl_all(TRUE); + + if (ret == 0) + ret = panel_next_late_init(pdev); + pr_debug("%s:-\n", __func__); return ret; @@ -2243,20 +2371,77 @@ void mdp_hw_version(void) } #ifdef CONFIG_MSM_BUS_SCALING + +#ifndef MDP_BUS_VECTOR_ENTRY +#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \ + { \ + .src = MSM_BUS_MASTER_MDP_PORT0, \ + .dst = MSM_BUS_SLAVE_EBI_CH0, \ + .ab = (ab_val), \ + .ib = (ib_val), \ + } +#endif +/* + * Entry 0 hold 0 request + * Entry 1 and 2 do ping pong request + */ +static struct msm_bus_vectors mdp_bus_vectors[] = { + MDP_BUS_VECTOR_ENTRY(0, 0), + MDP_BUS_VECTOR_ENTRY( 128000000, 160000000), + MDP_BUS_VECTOR_ENTRY( 128000000, 160000000), +}; + +static struct msm_bus_paths mdp_bus_usecases[ARRAY_SIZE(mdp_bus_vectors)]; +static struct msm_bus_scale_pdata mdp_bus_scale_table = { + .usecase = mdp_bus_usecases, + .num_usecases = ARRAY_SIZE(mdp_bus_usecases), + .name = "mdp", +}; static uint32_t mdp_bus_scale_handle; -int mdp_bus_scale_update_request(uint32_t index) +static int mdp_bus_scale_register(void) { - if (!mdp_pdata && (!mdp_pdata->mdp_bus_scale_table - || index > (mdp_pdata->mdp_bus_scale_table->num_usecases - 1))) { - printk(KERN_ERR "%s invalid table or index\n", __func__); - return -EINVAL; + struct msm_bus_scale_pdata *bus_pdata = &mdp_bus_scale_table; + int i; + for (i = 0; i < bus_pdata->num_usecases; i++) { + mdp_bus_usecases[i].num_paths = 1; + mdp_bus_usecases[i].vectors = &mdp_bus_vectors[i]; + } + mdp_bus_scale_handle = msm_bus_scale_register_client(bus_pdata); + if (!mdp_bus_scale_handle) { + pr_err("%s: not able to get bus scale!\n", __func__); + return -ENOMEM; } + return 0; +} + +int mdp_bus_scale_update_request(u64 ab, u64 ib) +{ + static int bus_index = 1; + if (mdp_bus_scale_handle < 1) { - pr_debug("%s invalid bus handle\n", __func__); + pr_err("%s invalid bus handle\n", __func__); return -EINVAL; } - return msm_bus_scale_client_update_request(mdp_bus_scale_handle, - index); + + if (!ab) + return msm_bus_scale_client_update_request + (mdp_bus_scale_handle, 0); + + /* ping pong bus_index between table entry 1 and 2 */ + bus_index++; + bus_index = (bus_index > 2) ? 1 : bus_index; + + mdp_bus_usecases[bus_index].vectors->ab = min(ab, mdp_max_bw); + ib = max(ib, ab); + mdp_bus_usecases[bus_index].vectors->ib = min(ib, mdp_max_bw); + + pr_debug("%s: handle=%d index=%d ab=%llu ib=%llu\n", __func__, + (u32)mdp_bus_scale_handle, bus_index, + mdp_bus_usecases[bus_index].vectors->ab, + mdp_bus_usecases[bus_index].vectors->ib); + + return msm_bus_scale_client_update_request + (mdp_bus_scale_handle, bus_index); } #endif DEFINE_MUTEX(mdp_clk_lock); @@ -2306,18 +2491,40 @@ static int mdp_irq_clk_setup(struct platform_device *pdev, } disable_irq(mdp_irq); + dsi_pll_vdda = regulator_get(&pdev->dev, "dsi_pll_vdda"); + if (IS_ERR(dsi_pll_vdda)) { + dsi_pll_vdda = NULL; + } else { + if (mdp_rev == MDP_REV_42 || mdp_rev == MDP_REV_44) { + ret = regulator_set_voltage(dsi_pll_vdda, 1200000, + 1200000); + if (ret) { + pr_err("set_voltage failed for dsi_pll_vdda, ret=%d\n", + ret); + } + } + } + + dsi_pll_vddio = regulator_get(&pdev->dev, "dsi_pll_vddio"); + if (IS_ERR(dsi_pll_vddio)) { + dsi_pll_vddio = NULL; + } else { + if (mdp_rev == MDP_REV_42) { + ret = regulator_set_voltage(dsi_pll_vddio, 1800000, + 1800000); + if (ret) { + pr_err("set_voltage failed for dsi_pll_vddio, ret=%d\n", + ret); + } + } + } + footswitch = regulator_get(&pdev->dev, "vdd"); - if (IS_ERR(footswitch)) + if (IS_ERR(footswitch)) { footswitch = NULL; - else { + } else { regulator_enable(footswitch); mdp_footswitch_on = 1; - - if (mdp_rev == MDP_REV_42 && !cont_splashScreen) { - regulator_disable(footswitch); - msleep(20); - regulator_enable(footswitch); - } } mdp_clk = clk_get(&pdev->dev, "core_clk"); @@ -2368,6 +2575,17 @@ static int mdp_irq_clk_setup(struct platform_device *pdev, MSM_FB_DEBUG("mdp_clk: mdp_clk=%d\n", (int)clk_get_rate(mdp_clk)); #endif + + if (mdp_rev == MDP_REV_42 && !cont_splashScreen) { + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + /* DSI Video Timing generator disable */ + outpdw(MDP_BASE + 0xE0000, 0x0); + /* Clear MDP Interrupt Enable register */ + outpdw(MDP_BASE + 0x50, 0x0); + /* Set Overlay Proc 0 to reset state */ + outpdw(MDP_BASE + 0x10004, 0x3); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + } return 0; } @@ -2379,17 +2597,15 @@ static int mdp_probe(struct platform_device *pdev) int rc; resource_size_t size ; unsigned long flag; + u32 frame_rate; #ifdef CONFIG_FB_MSM_MDP40 int intf, if_no; #endif #if defined(CONFIG_FB_MSM_MIPI_DSI) && defined(CONFIG_FB_MSM_MDP40) struct mipi_panel_info *mipi; #endif - static int contSplash_update_done; - void *splash_virt_addr; - int cur_page; - unsigned long cur_addr; - struct splash_pages page_data; + static int contSplash_update_done; + if ((pdev->id == 0) && (pdev->num_resources > 0)) { mdp_init_pdev = pdev; @@ -2411,6 +2627,7 @@ static int mdp_probe(struct platform_device *pdev) } mdp_rev = mdp_pdata->mdp_rev; + mdp_iommu_split_domain = mdp_pdata->mdp_iommu_split_domain; rc = mdp_irq_clk_setup(pdev, mdp_pdata->cont_splash_enabled); @@ -2427,13 +2644,17 @@ static int mdp_probe(struct platform_device *pdev) if (!(mdp_pdata->cont_splash_enabled)) mdp4_hw_init(); #else - mdp_hw_init(mdp_pdata->cont_splash_enabled); + + mdp_hw_init(); + #endif #ifdef CONFIG_FB_MSM_OVERLAY mdp_hw_cursor_init(); #endif - mdp_clk_ctrl(0); + + if (!(mdp_pdata->cont_splash_enabled)) + mdp_clk_ctrl(0); mdp_resource_initialized = 1; return 0; @@ -2462,71 +2683,8 @@ static int mdp_probe(struct platform_device *pdev) mfd->mdp_rev = mdp_rev; mfd->vsync_init = NULL; - if (mdp_pdata) { + if (mdp_pdata) { if (mdp_pdata->cont_splash_enabled) { - uint32 bpp = 3; - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - /*read panel wxh and calculate splash screen - size*/ - mdp_pdata->splash_screen_size = - inpdw(MDP_BASE + 0x90004); - mdp_pdata->splash_screen_size = - (((mdp_pdata->splash_screen_size >> 16) & - 0x00000FFF) * ( - mdp_pdata->splash_screen_size & - 0x00000FFF)) * bpp; - - mdp_pdata->splash_screen_addr = - inpdw(MDP_BASE + 0x90008); - - mfd->copy_splash_buf = dma_alloc_coherent(NULL, - mdp_pdata->splash_screen_size, - (dma_addr_t *) &(mfd->copy_splash_phys), - GFP_KERNEL); - - if (!mfd->copy_splash_buf) { - pr_err("DMA ALLOC FAILED for SPLASH\n"); - return -ENOMEM; - } - - page_data.size = PFN_ALIGN(mdp_pdata->splash_screen_size); - page_data.nrpages = (page_data.size) >> PAGE_SHIFT; - page_data.pages = kzalloc(sizeof(struct page *)*page_data.nrpages, - GFP_KERNEL); - if (!page_data.pages) { - pr_err("KZALLOC FAILED for PAGES\n"); - return -ENOMEM; - } - - /* Following code for obtaining the virtual address for splash - screen address relies on the fact that, splash screen buffer is - part of the kernel's memory map and this code need to be changed - if the memory comes from outside the kernel. */ - cur_addr = mdp_pdata->splash_screen_addr; - for(cur_page = 0; cur_page < page_data.nrpages; cur_page++) { - page_data.pages[cur_page] = phys_to_page(cur_addr); - if (!page_data.pages[cur_page]) { - pr_err("PHYS_TO_PAGE FAILED for SPLASH\n"); - kfree(page_data.pages); - return -ENOMEM; - } - cur_addr += (1 << PAGE_SHIFT); - } - splash_virt_addr = vmap(page_data.pages, page_data.nrpages, - VM_IOREMAP, pgprot_kernel); - if (!splash_virt_addr) { - pr_err("VMAP FAILED for SPLASH\n"); - kfree(page_data.pages); - return -ENOMEM; - } - memcpy(mfd->copy_splash_buf, splash_virt_addr, - mdp_pdata->splash_screen_size); - vunmap(splash_virt_addr); - kfree(page_data.pages); - - MDP_OUTP(MDP_BASE + 0x90008, - mfd->copy_splash_phys); - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); mfd->cont_splash_done = 0; if (!contSplash_update_done) { @@ -2567,10 +2725,12 @@ static int mdp_probe(struct platform_device *pdev) rc = -ENOMEM; goto mdp_probe_err; } + /* data chain */ pdata = msm_fb_dev->dev.platform_data; pdata->on = mdp_on; pdata->off = mdp_off; + pdata->late_init = NULL; pdata->next = pdev; mdp_clk_ctrl(1); @@ -2586,6 +2746,9 @@ static int mdp_probe(struct platform_device *pdev) mdp_vsync_resync_workqueue_handler); mfd->hw_refresh = FALSE; + if (mfd->panel.type == MDDI_PANEL) + mdp4_mddi_rdptr_init(0); + if (mfd->panel.type == EXT_MDDI_PANEL) { /* 15 fps -> 66 msec */ mfd->refresh_timer_duration = (66 * HZ / 1000); @@ -2672,10 +2835,13 @@ static int mdp_probe(struct platform_device *pdev) pdata->off = mdp_dsi_video_off; mfd->hw_refresh = TRUE; mfd->dma_fnc = mdp_dsi_video_update; + mfd->do_histogram = mdp_do_histogram; + mfd->start_histogram = mdp_histogram_start; mfd->stop_histogram = mdp_histogram_stop; mfd->vsync_ctrl = mdp_dma_video_vsync_ctrl; + mfd->vsync_show = mdp_dma_video_show_event; if (mfd->panel_info.pdest == DISPLAY_1) mfd->dma = &dma2_data; else { @@ -2710,7 +2876,6 @@ static int mdp_probe(struct platform_device *pdev) mfd->start_histogram = mdp_histogram_start; mfd->stop_histogram = mdp_histogram_stop; mdp4_display_intf_sel(if_no, DSI_CMD_INTF); - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); spin_lock_irqsave(&mdp_spin_lock, flag); mdp_intr_mask |= INTR_OVERLAY0_DONE; @@ -2718,12 +2883,12 @@ static int mdp_probe(struct platform_device *pdev) spin_unlock_irqrestore(&mdp_spin_lock, flag); mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); #else - mfd->dma_fnc = mdp_dma2_update; mfd->do_histogram = mdp_do_histogram; mfd->start_histogram = mdp_histogram_start; mfd->stop_histogram = mdp_histogram_stop; mfd->vsync_ctrl = mdp_dma_vsync_ctrl; + mfd->vsync_show = mdp_dma_show_event; if (mfd->panel_info.pdest == DISPLAY_1) mfd->dma = &dma2_data; else { @@ -2746,9 +2911,12 @@ static int mdp_probe(struct platform_device *pdev) pdata->on = mdp4_dtv_on; pdata->off = mdp4_dtv_off; mfd->hw_refresh = TRUE; - mfd->cursor_update = mdp_hw_cursor_update; + mfd->cursor_update = mdp_hw_cursor_sync_update; mfd->dma_fnc = mdp4_dtv_overlay; mfd->dma = &dma_e_data; + mfd->do_histogram = mdp_do_histogram; + mfd->start_histogram = mdp_histogram_start; + mfd->stop_histogram = mdp_histogram_stop; mdp4_display_intf_sel(EXTERNAL_INTF_SEL, DTV_INTF); break; #endif @@ -2790,6 +2958,7 @@ static int mdp_probe(struct platform_device *pdev) #else mfd->dma = &dma2_data; mfd->vsync_ctrl = mdp_dma_lcdc_vsync_ctrl; + mfd->vsync_show = mdp_dma_lcdc_show_event; spin_lock_irqsave(&mdp_spin_lock, flag); mdp_intr_mask &= ~MDP_DMA_P_DONE; outp32(MDP_INTR_ENABLE, mdp_intr_mask); @@ -2829,6 +2998,7 @@ static int mdp_probe(struct platform_device *pdev) mdp_clk_ctrl(0); return -ENODEV; } + mdp4_wfd_init(0); pdata->on = mdp4_overlay_writeback_on; pdata->off = mdp4_overlay_writeback_off; mfd->dma_fnc = mdp4_writeback_overlay; @@ -2850,25 +3020,21 @@ static int mdp_probe(struct platform_device *pdev) mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); } + frame_rate = mdp_get_panel_framerate(mfd); + if (frame_rate) { + mfd->panel_info.frame_interval = 1000 / frame_rate; + mfd->cpu_pm_hdl = add_event_timer(NULL, (void *)mfd); + } mdp_clk_ctrl(0); #ifdef CONFIG_MSM_BUS_SCALING - if (!mdp_bus_scale_handle && mdp_pdata && - mdp_pdata->mdp_bus_scale_table) { - mdp_bus_scale_handle = - msm_bus_scale_register_client( - mdp_pdata->mdp_bus_scale_table); - if (!mdp_bus_scale_handle) { - printk(KERN_ERR "%s not able to get bus scale\n", - __func__); - return -ENOMEM; - } - } + if (mdp_bus_scale_register()) + return -ENOMEM; /* req bus bandwidth immediately */ if (!(mfd->cont_splash_done)) - mdp_bus_scale_update_request(5); - + mdp_bus_scale_update_request + (MDP_BUS_SCALE_INIT, MDP_BUS_SCALE_INIT); #endif /* set driver data */ @@ -2884,9 +3050,13 @@ static int mdp_probe(struct platform_device *pdev) pdev_list[pdev_list_cnt++] = pdev; mdp4_extn_disp = 0; - - if (mfd->vsync_init != NULL) { - mfd->vsync_init(0); + /* + * vsync_init call not required for mdp3. + * vsync_init call required for mdp4 targets. + */ + if ((mfd->vsync_init != NULL) || (mdp_rev < MDP_REV_40)) { + if (mdp_rev >= MDP_REV_40) + mfd->vsync_init(0); if (!mfd->vsync_sysfs_created) { mfd->dev_attr.attr.name = "vsync_event"; @@ -2912,8 +3082,7 @@ static int mdp_probe(struct platform_device *pdev) mdp_probe_err: platform_device_put(msm_fb_dev); #ifdef CONFIG_MSM_BUS_SCALING - if (mdp_pdata && mdp_pdata->mdp_bus_scale_table && - mdp_bus_scale_handle > 0) + if (mdp_bus_scale_handle > 0) msm_bus_scale_unregister_client(mdp_bus_scale_handle); #endif return rc; @@ -2928,6 +3097,17 @@ void mdp_footswitch_ctrl(boolean on) return; } + if (dsi_pll_vddio) + regulator_enable(dsi_pll_vddio); + + if (dsi_pll_vdda) + regulator_enable(dsi_pll_vdda); + + mipi_dsi_prepare_clocks(); + mipi_dsi_ahb_ctrl(1); + mipi_dsi_phy_ctrl(1); + mipi_dsi_clk_enable(); + if (on && !mdp_footswitch_on) { pr_debug("Enable MDP FS\n"); regulator_enable(footswitch); @@ -2938,20 +3118,21 @@ void mdp_footswitch_ctrl(boolean on) mdp_footswitch_on = 0; } - mutex_unlock(&mdp_suspend_mutex); -} + mipi_dsi_clk_disable(); + mipi_dsi_phy_ctrl(0); + mipi_dsi_ahb_ctrl(0); + mipi_dsi_unprepare_clocks(); -void mdp_free_splash_buffer(struct msm_fb_data_type *mfd) -{ - if (mfd->copy_splash_buf) { - dma_free_coherent(NULL, mdp_pdata->splash_screen_size, - mfd->copy_splash_buf, - (dma_addr_t) mfd->copy_splash_phys); + if (dsi_pll_vdda) + regulator_disable(dsi_pll_vdda); - mfd->copy_splash_buf = NULL; - } + if (dsi_pll_vddio) + regulator_disable(dsi_pll_vddio); + + mutex_unlock(&mdp_suspend_mutex); } + #ifdef CONFIG_PM static void mdp_suspend_sub(void) { @@ -2994,6 +3175,7 @@ static void mdp_early_suspend(struct early_suspend *h) { mdp_suspend_sub(); #ifdef CONFIG_FB_MSM_DTV + mdp4_solidfill_commit(MDP4_MIXER1); mdp4_dtv_set_black_screen(); #endif mdp_footswitch_ctrl(FALSE); @@ -3021,8 +3203,7 @@ static int mdp_remove(struct platform_device *pdev) iounmap(msm_mdp_base); pm_runtime_disable(&pdev->dev); #ifdef CONFIG_MSM_BUS_SCALING - if (mdp_pdata && mdp_pdata->mdp_bus_scale_table && - mdp_bus_scale_handle > 0) + if (mdp_bus_scale_handle > 0) msm_bus_scale_unregister_client(mdp_bus_scale_handle); #endif return 0; diff --git a/drivers/video/msm/mdp.h b/drivers/video/msm/mdp.h index 09d7bc82..ba8b5024 100644 --- a/drivers/video/msm/mdp.h +++ b/drivers/video/msm/mdp.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include #ifdef CONFIG_MSM_BUS_SCALING #include @@ -44,6 +44,7 @@ extern spinlock_t mdp_spin_lock; extern int mdp_rev; extern int mdp_iommu_split_domain; extern struct mdp_csc_cfg mdp_csc_convert[4]; +extern struct mdp_csc_cfg_data csc_cfg_matrix[]; extern struct workqueue_struct *mdp_hist_wq; extern uint32 mdp_intr_mask; @@ -739,7 +740,9 @@ extern struct mdp_hist_mgmt *mdp_hist_mgmt_array[]; #define MDP_DMA_P_LUT_C2_EN BIT(2) #define MDP_DMA_P_LUT_POST BIT(4) -void mdp_hw_init(int cont_splash); + +void mdp_hw_init(void); + int mdp_ppp_pipe_wait(void); void mdp_pipe_kickoff(uint32 term, struct msm_fb_data_type *mfd); void mdp_clk_ctrl(int on); @@ -785,8 +788,6 @@ int mdp_lcdc_on(struct platform_device *pdev); int mdp_lcdc_off(struct platform_device *pdev); void mdp_lcdc_update(struct msm_fb_data_type *mfd); -void mdp_free_splash_buffer(struct msm_fb_data_type *mfd); - #ifdef CONFIG_FB_MSM_MDP303 int mdp_dsi_video_on(struct platform_device *pdev); int mdp_dsi_video_off(struct platform_device *pdev); @@ -804,6 +805,10 @@ static inline int mdp4_lcdc_off(struct platform_device *pdev) { return 0; } +static inline int mdp4_mddi_off(struct platform_device *pdev) +{ + return 0; +} static inline int mdp4_dsi_cmd_on(struct platform_device *pdev) { return 0; @@ -816,6 +821,19 @@ static inline int mdp4_lcdc_on(struct platform_device *pdev) { return 0; } +static inline int mdp4_mddi_on(struct platform_device *pdev) +{ + return 0; +} +#endif + + +#ifndef CONFIG_FB_MSM_MDDI +static inline void mdp4_mddi_rdptr_init(int cndx) +{ + /* empty */ +} + #endif void set_cont_splashScreen_status(int); @@ -840,12 +858,12 @@ int mdp_set_core_clk(u32 rate); int mdp_clk_round_rate(u32 rate); unsigned long mdp_get_core_clk(void); -unsigned long mdp_perf_level2clk_rate(uint32 perf_level); #ifdef CONFIG_MSM_BUS_SCALING -int mdp_bus_scale_update_request(uint32_t index); +int mdp_bus_scale_update_request(u64 ab, u64 ib); #else -static inline int mdp_bus_scale_update_request(uint32_t index) +static inline int mdp_bus_scale_update_request(u64 ab, + u64 ib) { return 0; } @@ -853,6 +871,12 @@ static inline int mdp_bus_scale_update_request(uint32_t index) void mdp_dma_vsync_ctrl(int enable); void mdp_dma_video_vsync_ctrl(int enable); void mdp_dma_lcdc_vsync_ctrl(int enable); +ssize_t mdp_dma_show_event(struct device *dev, + struct device_attribute *attr, char *buf); +ssize_t mdp_dma_video_show_event(struct device *dev, + struct device_attribute *attr, char *buf); +ssize_t mdp_dma_lcdc_show_event(struct device *dev, + struct device_attribute *attr, char *buf); #ifdef MDP_HW_VSYNC void vsync_clk_prepare_enable(void); @@ -912,9 +936,12 @@ static inline int msmfb_overlay_vsync_ctrl(struct fb_info *info, int mdp_ppp_v4l2_overlay_set(struct fb_info *info, struct mdp_overlay *req); int mdp_ppp_v4l2_overlay_clear(void); -int mdp_ppp_v4l2_overlay_play(struct fb_info *info, +int mdp_ppp_v4l2_overlay_play(struct fb_info *info, bool bUserPtr, unsigned long srcp0_addr, unsigned long srcp0_size, unsigned long srcp1_addr, unsigned long srcp1_size); +void mdp_update_pm(struct msm_fb_data_type *mfd, ktime_t pre_vsync); + +u32 mdp_get_panel_framerate(struct msm_fb_data_type *mfd); #ifdef CONFIG_FB_MSM_DTV void mdp_vid_quant_set(void); diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h index 7a18e62a..9d93b26a 100644 --- a/drivers/video/msm/mdp4.h +++ b/drivers/video/msm/mdp4.h @@ -28,6 +28,14 @@ extern uint32 mdp4_extn_disp; extern char *mmss_cc_base; /* mutimedia sub system clock control */ extern spinlock_t dsi_clk_lock; extern u32 mdp_max_clk; +extern u32 dbg_force_ov0_blt; +extern u32 dbg_force_ov1_blt; + +extern u64 mdp_max_bw; +#define MDP4_BW_AB_FACTOR (115) /* 1.15 */ +#define MDP4_BW_IB_FACTOR (125) /* 1.25 */ +#define MDP_BUS_SCALE_AB_STEP (0x4000000) +#define MDP_BUS_SCALE_INIT (0x10000000) #define MDP4_OVERLAYPROC0_BASE 0x10000 #define MDP4_OVERLAYPROC1_BASE 0x18000 @@ -35,6 +43,7 @@ extern u32 mdp_max_clk; #define MDP4_VIDEO_BASE 0x20000 #define MDP4_VIDEO_OFF 0x10000 +#define MDP4_VIDEO_CSC_OFF 0x4000 #define MDP4_RGB_BASE 0x40000 #define MDP4_RGB_OFF 0x10000 @@ -43,13 +52,6 @@ extern u32 mdp_max_clk; #define CS_CONTROLLER_0 0x0707ffff #define CS_CONTROLLER_1 0x03073f3f -enum { - OVERLAY_PERF_LEVEL1 = 1, - OVERLAY_PERF_LEVEL2, - OVERLAY_PERF_LEVEL3, - OVERLAY_PERF_LEVEL4 -}; - typedef int (*cmd_fxn_t)(struct platform_device *pdev); enum { /* display */ @@ -92,11 +94,6 @@ enum { #define MDP4_PANEL_DSI_CMD BIT(5) #define MDP4_PANEL_WRITEBACK BIT(6) -enum { - OVERLAY_MODE_NONE, - OVERLAY_MODE_BLT -}; - enum { OVERLAY_REFRESH_ON_DEMAND, OVERLAY_REFRESH_VSYNC, @@ -182,6 +179,8 @@ enum { MDP4_CHROMA_420 }; +#define CSC_MAX_BLOCKS 6 + #define MDP4_BLEND_BG_TRANSP_EN BIT(9) #define MDP4_BLEND_FG_TRANSP_EN BIT(8) #define MDP4_BLEND_BG_MOD_ALPHA BIT(7) @@ -210,6 +209,7 @@ enum { #define MDP4_OP_FLIP_UD BIT(14) #define MDP4_OP_FLIP_LR BIT(13) #define MDP4_OP_CSC_EN BIT(11) +#define MDP4_OP_DST_DATA_YCBCR BIT(10) #define MDP4_OP_SRC_DATA_YCBCR BIT(9) #define MDP4_OP_SCALEY_FIR (0 << 4) #define MDP4_OP_SCALEY_MN_PHASE (1 << 4) @@ -233,6 +233,12 @@ enum { #define MDP4_MAX_PLANE 4 #define VSYNC_PERIOD 16 +#ifdef BLT_RGB565 +#define BLT_BPP 2 +#else +#define BLT_BPP 3 +#endif + struct mdp4_hsic_regs { int32_t params[NUM_HSIC_PARAM]; int32_t conv_matrix[3][3]; @@ -270,6 +276,7 @@ struct blend_cfg { struct mdp4_overlay_pipe *solidfill_pipe; }; + struct mdp4_overlay_pipe { uint32 pipe_used; uint32 pipe_type; /* rgb, video/graphic */ @@ -302,10 +309,16 @@ struct mdp4_overlay_pipe { uint32 is_fg; /* control alpha & color key */ uint32 srcp0_addr; /* interleave, luma */ uint32 srcp0_ystride; + struct file *srcp0_file; + int put0_need; uint32 srcp1_addr; /* pseudoplanar, chroma plane */ uint32 srcp1_ystride; + struct file *srcp1_file; + int put1_need; uint32 srcp2_addr; /* planar color 2*/ uint32 srcp2_ystride; + struct file *srcp2_file; + int put2_need; uint32 srcp3_addr; /* alpha/color 3 */ uint32 srcp3_ystride; uint32 fetch_plane; @@ -345,15 +358,16 @@ struct mdp4_overlay_pipe { uint32 ov_cnt; uint32 dmap_cnt; uint32 dmae_cnt; - uint32 blt_end; + uint32 blt_end; /* used by mddi only */ uint32 blt_ov_koff; uint32 blt_ov_done; uint32 blt_dmap_koff; uint32 blt_dmap_done; uint32 req_clk; - uint32 req_bw; + uint64 bw_ab_quota; + uint64 bw_ib_quota; uint32 luma_align_size; - struct mdp4_hsic_regs hsic_regs; + struct mdp_overlay_pp_params pp_cfg; struct mdp_overlay req_data; struct completion comp; struct completion dmas_comp; @@ -383,6 +397,7 @@ struct mdp4_statistic { ulong intr_dsi_err; ulong kickoff_ov0; ulong kickoff_ov1; + ulong kickoff_ov2; ulong kickoff_dmap; ulong kickoff_dmae; ulong kickoff_dmas; @@ -438,15 +453,11 @@ void mdp4_lcdc_update(struct msm_fb_data_type *mfd); void mdp4_intr_clear_set(ulong clear, ulong set); void mdp4_dma_p_cfg(void); unsigned is_mdp4_hw_reset(void); -void mdp4_overlay_cfg_init(void); void mdp4_hw_init(void); void mdp4_isr_read(int); void mdp4_clear_lcdc(void); void mdp4_mixer_blend_init(int mixer_num); void mdp4_vg_qseed_init(int vg_num); -void mdp4_vg_csc_setup(int vp_num); -void mdp4_mixer_csc_setup(uint32 mixer); -void mdp4_dmap_csc_setup(void); void mdp4_vg_csc_update(struct mdp_csc *p); irqreturn_t mdp4_isr(int irq, void *ptr); void mdp4_overlay_format_to_pipe(uint32 format, struct mdp4_overlay_pipe *pipe); @@ -542,12 +553,9 @@ void mdp4_overlay0_done_dsi_video(int cndx); void mdp4_overlay0_done_dsi_cmd(int cndx); void mdp4_primary_rdptr(void); void mdp4_dsi_cmd_overlay(struct msm_fb_data_type *mfd); -int mdp4_overlay_commit(struct fb_info *info, int mixer); -int mdp4_dsi_video_pipe_commit(void); -int mdp4_dsi_cmd_pipe_commit(void); +int mdp4_lcdc_pipe_commit(int cndx, int wait); +int mdp4_dtv_pipe_commit(int cndx, int wait); int mdp4_dsi_cmd_update_cnt(int cndx); -int mdp4_lcdc_pipe_commit(void); -int mdp4_dtv_pipe_commit(void); void mdp4_dsi_rdptr_init(int cndx); void mdp4_dsi_vsync_init(int cndx); void mdp4_lcdc_vsync_init(int cndx); @@ -584,9 +592,11 @@ int mdp4_overlay_unset_mixer(int mixer); int mdp4_overlay_play_wait(struct fb_info *info, struct msmfb_overlay_data *req); int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req); +int mdp4_overlay_commit(struct fb_info *info); struct mdp4_overlay_pipe *mdp4_overlay_pipe_alloc(int ptype, int mixer); void mdp4_overlay_dma_commit(int mixer); void mdp4_overlay_vsync_commit(struct mdp4_overlay_pipe *pipe); +void mdp4_solidfill_commit(int mixer); void mdp4_mixer_stage_commit(int mixer); void mdp4_dsi_cmd_do_update(int cndx, struct mdp4_overlay_pipe *pipe); void mdp4_lcdc_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe); @@ -596,12 +606,13 @@ void mdp4_overlay_dmap_cfg(struct msm_fb_data_type *mfd, int lcdc); void mdp4_overlay_dmap_xy(struct mdp4_overlay_pipe *pipe); void mdp4_overlay_dmae_cfg(struct msm_fb_data_type *mfd, int atv); void mdp4_overlay_dmae_xy(struct mdp4_overlay_pipe *pipe); -int mdp4_overlay_pipe_staged(int mixer); +int mdp4_overlay_pipe_staged(struct mdp4_overlay_pipe *pipe); void mdp4_lcdc_primary_vsyn(void); void mdp4_overlay0_done_lcdc(int cndx); -void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma); +void mdp4_overlay0_done_mddi(int cndx); void mdp4_dma_p_done_mddi(struct mdp_dma_data *dma); void mdp4_dmap_done_dsi_cmd(int cndx); +void mdp4_dmap_done_mddi(int cndx); void mdp4_dmap_done_dsi_video(int cndx); void mdp4_dmap_done_lcdc(int cndx); void mdp4_overlay1_done_dtv(void); @@ -612,6 +623,7 @@ void mdp4_lcdc_wait4vsync(int cndx, long long *vtime); void mdp4_overlay_lcdc_vsync_push(struct msm_fb_data_type *mfd, struct mdp4_overlay_pipe *pipe); void mdp4_mddi_overlay_dmas_restore(void); +void mdp4_dtv_set_avparams(struct mdp4_overlay_pipe *pipe, int id); #ifndef CONFIG_FB_MSM_MIPI_DSI void mdp4_mddi_dma_busy_wait(struct msm_fb_data_type *mfd); @@ -675,6 +687,12 @@ void mdp4_dsi_cmd_overlay_blt(struct msm_fb_data_type *mfd, void mdp4_dsi_video_overlay_blt(struct msm_fb_data_type *mfd, struct msmfb_overlay_blt *req); void mdp4_dsi_video_base_swap(int cndx, struct mdp4_overlay_pipe *pipe); +static inline void mdp4_mddi_blt_start(struct msm_fb_data_type *mfd) +{ +} +static inline void mdp4_mddi_blt_stop(struct msm_fb_data_type *mfd) +{ +} #ifdef CONFIG_FB_MSM_MDP40 static inline void mdp3_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd) @@ -683,6 +701,8 @@ static inline void mdp3_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd) } #endif #else /* CONFIG_FB_MSM_MIPI_DSI */ +void mdp4_mddi_blt_start(struct msm_fb_data_type *mfd); +void mdp4_mddi_blt_stop(struct msm_fb_data_type *mfd); int mdp4_mddi_overlay_blt_offset(struct msm_fb_data_type *mfd, struct msmfb_overlay_blt *req); void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd, @@ -690,6 +710,7 @@ void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd, int mdp4_mddi_overlay_blt_start(struct msm_fb_data_type *mfd); int mdp4_mddi_overlay_blt_stop(struct msm_fb_data_type *mfd); void mdp4_mddi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd); +void mdp4_mddi_rdptr_init(int cndx); static inline int mdp4_dsi_overlay_blt_start(struct msm_fb_data_type *mfd) { return -ENODEV; @@ -728,11 +749,9 @@ static inline void mdp4_dsi_video_base_swap(int cndx, } static inline void mdp4_dsi_cmd_blt_start(struct msm_fb_data_type *mfd) { - /* empty */ } static inline void mdp4_dsi_cmd_blt_stop(struct msm_fb_data_type *mfd) { - /* empty */ } #endif /* CONFIG_FB_MSM_MIPI_DSI */ @@ -772,6 +791,8 @@ void mdp4_dsi_cmd_wait4vsync(int cndx, long long *vtime); void mdp4_dsi_video_wait4vsync(int cndx, long long *vtime); void mdp4_dsi_cmd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe); void mdp4_dsi_video_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe); +int mdp4_dsi_video_pipe_commit(int cndx, int wait); +int mdp4_dsi_cmd_pipe_commit(int cndx, int wait); void mdp4_dsi_cmd_vsync_ctrl(struct fb_info *info, int enable); void mdp4_dsi_video_vsync_ctrl(struct fb_info *info, int enable); #ifdef CONFIG_FB_MSM_MDP303 @@ -779,11 +800,37 @@ static inline void mdp4_dsi_cmd_del_timer(void) { /* empty */ } -#else /* CONFIG_FB_MSM_MIPI_DSI */ +#else /* CONFIG_FB_MSM_MDP303 */ void mdp4_dsi_cmd_del_timer(void); +static inline int mdp4_mddi_on(struct platform_device *pdev) +{ + return 0; +} +static inline int mdp4_mddi_off(struct platform_device *pdev) +{ + return 0; +} +static inline void mdp4_mddi_wait4vsync(int cndx, long long *vtime) +{ +} +static inline void mdp4_mddi_vsync_ctrl(struct fb_info *info, + int enable) +{ +} +static inline void mdp4_mddi_pipe_queue(int cndx, + struct mdp4_overlay_pipe *pipe) +{ +} #endif #else /* CONFIG_FB_MSM_MIPI_DSI */ +int mdp4_mddi_off(struct platform_device *pdev); +int mdp4_mddi_on(struct platform_device *pdev); +void mdp4_mddi_wait4vsync(int cndx, long long *vtime); +void mdp4_mddi_vsync_ctrl(struct fb_info *info, int enable); +void mdp4_mddi_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe); +void mdp4_overlay_update_mddi(struct msm_fb_data_type *mfd); + static inline int mdp4_dsi_cmd_on(struct platform_device *pdev) { return 0; @@ -821,8 +868,16 @@ static inline void mdp4_dsi_video_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe) { } +static inline int mdp4_dsi_video_pipe_commit(int cndx, int wait) +{ + return 0; +} +static inline int mdp4_dsi_cmd_pipe_commit(int cndx, int wait) +{ + return 0; +} static inline void mdp4_dsi_cmd_vsync_ctrl(struct fb_info *info, - int enable) + int enable) { } static inline void mdp4_dsi_video_vsync_ctrl(struct fb_info *info, @@ -879,10 +934,7 @@ int mdp4_overlay_borderfill_supported(void); int mdp4_overlay_writeback_on(struct platform_device *pdev); int mdp4_overlay_writeback_off(struct platform_device *pdev); void mdp4_writeback_overlay(struct msm_fb_data_type *mfd); -void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd, - struct mdp4_overlay_pipe *pipe); -void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd); -void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma); +void mdp4_overlay2_done_wfd(struct mdp_dma_data *dma); int mdp4_writeback_start(struct fb_info *info); int mdp4_writeback_stop(struct fb_info *info); @@ -893,11 +945,11 @@ int mdp4_writeback_queue_buffer(struct fb_info *info, void mdp4_writeback_dma_stop(struct msm_fb_data_type *mfd); int mdp4_writeback_init(struct fb_info *info); int mdp4_writeback_terminate(struct fb_info *info); +int mdp4_writeback_set_mirroring_hint(struct fb_info *info, int hint); uint32_t mdp_block2base(uint32_t block); int mdp_hist_lut_config(struct mdp_hist_lut_data *data); -void mdp4_hsic_set(struct mdp4_overlay_pipe *pipe, struct dpp_ctrl *ctrl); void mdp4_hsic_update(struct mdp4_overlay_pipe *pipe); int mdp4_csc_config(struct mdp_csc_cfg_data *config); void mdp4_csc_write(struct mdp_csc_cfg *data, uint32_t base); @@ -905,6 +957,8 @@ int mdp4_csc_enable(struct mdp_csc_cfg_data *config); int mdp4_pcc_cfg(struct mdp_pcc_cfg_data *cfg_ptr); int mdp4_argc_cfg(struct mdp_pgc_lut_data *pgc_ptr); int mdp4_qseed_cfg(struct mdp_qseed_cfg_data *cfg); +int mdp4_calib_config(struct mdp_calib_config_data *cfg); +int mdp4_qseed_access_cfg(struct mdp_qseed_cfg *cfg, uint32_t base); u32 mdp4_allocate_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num); void mdp4_init_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num); void mdp4_free_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num); @@ -922,13 +976,37 @@ void mdp4_v4l2_overlay_clear(struct mdp4_overlay_pipe *pipe); int mdp4_v4l2_overlay_play(struct fb_info *info, struct mdp4_overlay_pipe *pipe, unsigned long srcp0_addr, unsigned long srcp1_addr, unsigned long srcp2_addr); - int mdp4_overlay_mdp_pipe_req(struct mdp4_overlay_pipe *pipe, struct msm_fb_data_type *mfd); +int mdp4_calc_blt_mdp_bw(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe); int mdp4_overlay_mdp_perf_req(struct msm_fb_data_type *mfd, - struct mdp4_overlay_pipe *plist); + struct mdp4_overlay_pipe *plist); void mdp4_overlay_mdp_perf_upd(struct msm_fb_data_type *mfd, int flag); int mdp4_update_base_blend(struct msm_fb_data_type *mfd, struct mdp_blend_cfg *mdp_blend_cfg); +int mdp4_update_writeback_format(struct msm_fb_data_type *mfd, + struct mdp_mixer_cfg *mdp_mixer_cfg); u32 mdp4_get_mixer_num(u32 panel_type); + +#ifndef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL +static inline void mdp4_wfd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe) +{ + /* empty */ +} +static inline void mdp4_wfd_init(int cndx) +{ + /* empty */ +} +static inline int mdp4_wfd_pipe_commit(struct msm_fb_data_type *mfd, + int cndx, int wait) +{ + return 0; +} +#else +void mdp4_wfd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe); +void mdp4_wfd_init(int cndx); +int mdp4_wfd_pipe_commit(struct msm_fb_data_type *mfd, int cndx, int wait); +#endif + #endif /* MDP_H */ diff --git a/drivers/video/msm/mdp4_dtv.c b/drivers/video/msm/mdp4_dtv.c index 6c9d81e0..ba810a9d 100644 --- a/drivers/video/msm/mdp4_dtv.c +++ b/drivers/video/msm/mdp4_dtv.c @@ -38,6 +38,7 @@ static int dtv_remove(struct platform_device *pdev); static int dtv_off(struct platform_device *pdev); static int dtv_on(struct platform_device *pdev); +static int dtv_off_sub(void); static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; static int pdev_list_cnt; @@ -45,6 +46,9 @@ static int pdev_list_cnt; static struct clk *tv_src_clk; static struct clk *hdmi_clk; static struct clk *mdp_tv_clk; +static struct platform_device *dtv_pdev; +static struct workqueue_struct *dtv_work_queue; +static struct work_struct dtv_off_work; static int mdp4_dtv_runtime_suspend(struct device *dev) @@ -86,8 +90,48 @@ static struct clk *ebi1_clk; static int dtv_off(struct platform_device *pdev) { int ret = 0; + struct msm_fb_data_type *mfd = NULL; - ret = panel_next_off(pdev); + if (!pdev) { + pr_err("%s: FAILED: invalid arg\n", __func__); + return -EINVAL; + } + + mfd = platform_get_drvdata(pdev); + if (!mfd) { + pr_err("%s: FAILED: invalid mfd\n", __func__); + return -EINVAL; + } + + dtv_pdev = pdev; + /* + * If it's a suspend operation then handle the device + * power down synchronously. + * Otherwise, queue work item to handle power down sequence. + * This is needed since we need to wait for the audio engine + * to shutdown first before we turn off the DTV device. + */ + if (!mfd->suspend.op_suspend) { + pr_debug("%s: Queuing work to turn off HDMI core\n", __func__); + queue_work(dtv_work_queue, &dtv_off_work); + } else { + pr_debug("%s: turning off HDMI core\n", __func__); + ret = dtv_off_sub(); + } + + return ret; +} + +static int dtv_off_sub(void) +{ + int ret = 0; + + if (!dtv_pdev) { + pr_err("%s: FAILED: invalid arg\n", __func__); + return -EINVAL; + } + + ret = panel_next_off(dtv_pdev); pr_info("%s\n", __func__); @@ -112,12 +156,20 @@ static int dtv_off(struct platform_device *pdev) return ret; } +static void dtv_off_work_func(struct work_struct *work) +{ + dtv_off_sub(); +} + static int dtv_on(struct platform_device *pdev) { int ret = 0; struct msm_fb_data_type *mfd; unsigned long panel_pixclock_freq , pm_qos_rate; + /* If a power down is already underway, wait for it to finish */ + flush_work_sync(&dtv_off_work); + mfd = platform_get_drvdata(pdev); panel_pixclock_freq = mfd->fbi->var.pixclock; @@ -215,6 +267,8 @@ static int dtv_probe(struct platform_device *pdev) return 0; } + dtv_work_queue = create_singlethread_workqueue("dtv_work"); + INIT_WORK(&dtv_off_work, dtv_off_work_func); mfd = platform_get_drvdata(pdev); if (!mfd) @@ -302,6 +356,8 @@ static int dtv_probe(struct platform_device *pdev) static int dtv_remove(struct platform_device *pdev) { + if (dtv_work_queue) + destroy_workqueue(dtv_work_queue); #ifdef CONFIG_MSM_BUS_SCALING if (dtv_pdata && dtv_pdata->bus_scale_table && dtv_bus_scale_handle > 0) diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c index e1209d2c..9e174306 100644 --- a/drivers/video/msm/mdp4_overlay.c +++ b/drivers/video/msm/mdp4_overlay.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -48,11 +47,12 @@ struct mdp4_overlay_ctrl { struct mdp4_overlay_pipe *stage[MDP4_MIXER_MAX][MDP4_MIXER_STAGE_MAX]; struct mdp4_overlay_pipe *baselayer[MDP4_MIXER_MAX]; struct blend_cfg blend[MDP4_MIXER_MAX][MDP4_MIXER_STAGE_MAX]; + struct mdp4_overlay_pipe sf_plist[MDP4_MIXER_MAX][OVERLAY_PIPE_MAX]; + struct mdp_mixer_cfg mdp_mixer_cfg[MDP4_MIXER_MAX]; uint32 mixer_cfg[MDP4_MIXER_MAX]; uint32 flush[MDP4_MIXER_MAX]; struct iommu_free_list iommu_free[MDP4_MIXER_MAX]; uint32 cs_controller; - uint32 hw_version; uint32 panel_3d; uint32 panel_mode; uint32 mixer0_played; @@ -107,20 +107,19 @@ static struct mdp4_overlay_ctrl *ctrl = &mdp4_overlay_db; struct mdp4_overlay_perf { u32 mdp_clk_rate; - u32 use_ov0_blt; - u32 use_ov1_blt; - u32 mdp_bw; + u32 use_ov_blt[MDP4_MIXER_MAX]; + u64 mdp_ov_ab_bw[MDP4_MIXER_MAX]; + u64 mdp_ov_ib_bw[MDP4_MIXER_MAX]; + u32 mdp_ab_bw; + u32 mdp_ib_bw; }; -struct mdp4_overlay_perf perf_request = { - .mdp_bw = OVERLAY_PERF_LEVEL4, -}; -struct mdp4_overlay_perf perf_current = { - .mdp_bw = OVERLAY_PERF_LEVEL4, -}; +struct mdp4_overlay_perf perf_request; +struct mdp4_overlay_perf perf_current; static struct ion_client *display_iclient; +static void mdp4_overlay_bg_solidfill(struct blend_cfg *blend); /* * mdp4_overlay_iommu_unmap_freelist() @@ -191,6 +190,26 @@ void mdp4_overlay_iommu_pipe_free(int ndx, int all) if (pipe == NULL) return; + if (pipe->flags & MDP_MEMORY_ID_TYPE_FB) { + pipe->flags &= ~MDP_MEMORY_ID_TYPE_FB; + if (pipe->put0_need) { + fput_light(pipe->srcp0_file, pipe->put0_need); + pipe->put0_need = 0; + } + if (pipe->put1_need) { + fput_light(pipe->srcp1_file, pipe->put1_need); + pipe->put1_need = 0; + } + if (pipe->put2_need) { + fput_light(pipe->srcp2_file, pipe->put2_need); + pipe->put2_need = 0; + } + + pr_debug("%s: ndx=%d flags=%x put=%d\n", __func__, + pipe->pipe_ndx, pipe->flags, pipe->put0_need); + return; + } + mutex_lock(&iommu_mutex); mixer = pipe->mixer_num; iom = &pipe->iommu; @@ -226,12 +245,12 @@ int mdp4_overlay_iommu_map_buf(int mem_id, return PTR_ERR(*srcp_ihdl); } pr_debug("%s(): ion_hdl %p, ion_buf %d\n", __func__, *srcp_ihdl, - ion_share_dma_buf(display_iclient, *srcp_ihdl)); + mem_id); pr_debug("mixer %u, pipe %u, plane %u\n", pipe->mixer_num, pipe->pipe_ndx, plane); if (ion_map_iommu(display_iclient, *srcp_ihdl, DISPLAY_READ_DOMAIN, GEN_POOL, SZ_4K, 0, start, - len, 0, ION_IOMMU_UNMAP_DELAYED)) { + len, 0, 0)) { ion_free(display_iclient, *srcp_ihdl); pr_err("ion_map_iommu() failed\n"); return -EINVAL; @@ -332,23 +351,9 @@ uint32 mdp4_overlay_panel_list(void) return ctrl->panel_mode; } -void mdp4_overlay_cfg_init(void) -{ - if (ctrl->hw_version == 0) { - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - ctrl->hw_version = inpdw(MDP_BASE + 0x0); /* MDP_HW_VERSION */ - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); - } - - if (ctrl->hw_version >= 0x0402030b) { - /* MDP_LAYERMIXER_IN_CFG_UPDATE_METHOD */ - outpdw(MDP_BASE + 0x100fc, 0x01); - } -} - int mdp4_overlay_borderfill_supported(void) { - return (ctrl->hw_version >= 0x0402030b); + return (mdp_rev >= MDP_REV_42); } void mdp4_overlay_dmae_cfg(struct msm_fb_data_type *mfd, int atv) @@ -396,53 +401,34 @@ void mdp4_overlay_dmae_cfg(struct msm_fb_data_type *mfd, int atv) mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); } -#ifdef CONFIG_FB_MSM_HDMI_3D -void unfill_black_screen(void) { return; } -#else -void unfill_black_screen(void) +void fill_black_screen(bool on, uint8 pipe_num, uint8 mixer_num) { - uint32 temp_src_format; - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - /* - * VG2 Constant Color - */ - temp_src_format = inpdw(MDP_BASE + 0x30050); - MDP_OUTP(MDP_BASE + 0x30050, temp_src_format&(~BIT(22))); - /* - * MDP_OVERLAY_REG_FLUSH - */ - MDP_OUTP(MDP_BASE + 0x18000, BIT(3)); - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); - return; -} -#endif + uint32 reg_base = 0x010000; + uint32 const_color_reg = reg_base * (pipe_num + 2) + 0x1008; + uint32 src_fmt_reg = reg_base * (pipe_num + 2) + 0x50; + uint32 color = 0x00000000; + uint32 temp_src_format = 0x00000000; + uint8 bit = pipe_num + 2; -#ifdef CONFIG_FB_MSM_HDMI_3D -void fill_black_screen(void) { return; } -#else -void fill_black_screen(void) -{ - /*Black color*/ - uint32 color = 0x00000000; - uint32 temp_src_format; mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - /* - * VG2 Constant Color - */ - MDP_OUTP(MDP_BASE + 0x31008, color); - /* - * MDP_VG2_SRC_FORMAT - */ - temp_src_format = inpdw(MDP_BASE + 0x30050); - MDP_OUTP(MDP_BASE + 0x30050, temp_src_format | BIT(22)); - /* - * MDP_OVERLAY_REG_FLUSH - */ - MDP_OUTP(MDP_BASE + 0x18000, BIT(3)); + + /* Fill constant color */ + MDP_OUTP(MDP_BASE + const_color_reg, color); + + /* Update source format for pipe */ + temp_src_format = inpdw(MDP_BASE + src_fmt_reg); + + if (on) + MDP_OUTP(MDP_BASE + src_fmt_reg, temp_src_format | BIT(22)); + else + MDP_OUTP(MDP_BASE + src_fmt_reg, temp_src_format | (~BIT(22))); + + /* MDP_OVERLAY_REG_FLUSH for pipe*/ + MDP_OUTP(MDP_BASE + 0x18000, BIT(bit) | BIT(mixer_num)); mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return; } -#endif void mdp4_overlay_dmae_xy(struct mdp4_overlay_pipe *pipe) { @@ -492,14 +478,18 @@ void mdp4_overlay_dmap_cfg(struct msm_fb_data_type *mfd, int lcdc) dma2_cfg_reg |= DMA_PACK_PATTERN_RGB; - if (mfd->panel_info.bpp == 18) { + if ((mfd->panel_info.type == MIPI_CMD_PANEL) || + (mfd->panel_info.type == MIPI_VIDEO_PANEL)) { + dma2_cfg_reg |= DMA_DSTC0G_8BITS | /* 888 24BPP */ + DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; + } else if (mfd->panel_info.bpp == 18) { dma2_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */ DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; } else if (mfd->panel_info.bpp == 16) { dma2_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */ DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; } else { - dma2_cfg_reg |= DMA_DSTC0G_8BITS | /* 888 16BPP */ + dma2_cfg_reg |= DMA_DSTC0G_8BITS | /* 888 24BPP */ DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; } @@ -713,8 +703,14 @@ void mdp4_overlay_rgb_setup(struct mdp4_overlay_pipe *pipe) op_mode &= ~(MDP4_OP_FLIP_LR + MDP4_OP_SCALEX_EN); op_mode &= ~(MDP4_OP_FLIP_UD + MDP4_OP_SCALEY_EN); outpdw(rgb_base + 0x0058, op_mode);/* MDP_RGB_OP_MODE */ - } else + } else { + if (pipe->op_mode & MDP4_OP_FLIP_LR && mdp_rev >= MDP_REV_42) { + /* Enable x-scaling bit to enable LR flip */ + /* for MDP > 4.2 targets */ + pipe->op_mode |= 0x01; + } outpdw(rgb_base + 0x0058, pipe->op_mode);/* MDP_RGB_OP_MODE */ + } outpdw(rgb_base + 0x005c, pipe->phasex_step); outpdw(rgb_base + 0x0060, pipe->phasey_step); @@ -731,41 +727,62 @@ static void mdp4_overlay_vg_get_src_offset(struct mdp4_overlay_pipe *pipe, *luma_off = 0; *chroma_off = 0; - if (pipe->src_x && (pipe->frame_format == + if ((pipe->src_x || pipe->src_y) && (pipe->frame_format == MDP4_FRAME_FORMAT_LINEAR)) { - src_xy = (pipe->src_y << 16) | pipe->src_x; - src_xy &= 0xffff0000; + src_xy = 0; outpdw(vg_base + 0x0004, src_xy); /* MDP_RGB_SRC_XY */ switch (pipe->src_format) { case MDP_Y_CR_CB_H2V2: case MDP_Y_CR_CB_GH2V2: case MDP_Y_CB_CR_H2V2: - *luma_off = pipe->src_x; - *chroma_off = pipe->src_x/2; + *luma_off = pipe->src_x + + (pipe->src_y * pipe->srcp0_ystride); + *chroma_off = pipe->src_x / 2 + + ((pipe->src_y / 2) * pipe->srcp1_ystride); break; case MDP_Y_CBCR_H2V2_TILE: case MDP_Y_CRCB_H2V2_TILE: case MDP_Y_CBCR_H2V2: case MDP_Y_CRCB_H2V2: + *luma_off = pipe->src_x + + (pipe->src_y * pipe->srcp0_ystride); + *chroma_off = pipe->src_x + + ((pipe->src_y / 2) * pipe->srcp1_ystride); + break; + case MDP_Y_CRCB_H1V1: case MDP_Y_CBCR_H1V1: + *luma_off = pipe->src_x + + (pipe->src_y * pipe->srcp0_ystride); + *chroma_off = pipe->src_x + + ((pipe->src_y * 2) * pipe->srcp1_ystride); + break; + case MDP_Y_CRCB_H2V1: case MDP_Y_CBCR_H2V1: - *luma_off = pipe->src_x; - *chroma_off = pipe->src_x; + case MDP_Y_CRCB_H1V2: + case MDP_Y_CBCR_H1V2: + *luma_off = pipe->src_x + + (pipe->src_y * pipe->srcp0_ystride); + *chroma_off = pipe->src_x + + (pipe->src_y * pipe->srcp1_ystride); break; + case MDP_YCBYCR_H2V1: case MDP_YCRYCB_H2V1: + case MDP_CBYCRY_H2V1: if (pipe->src_x & 0x1) pipe->src_x += 1; - *luma_off += pipe->src_x * 2; + *luma_off += pipe->src_x * 2 + + ((pipe->src_y * 2) * pipe->srcp0_ystride); break; case MDP_ARGB_8888: case MDP_RGBA_8888: case MDP_BGRA_8888: + case MDP_BGRX_8888: case MDP_RGBX_8888: case MDP_RGB_565: case MDP_BGR_565: @@ -773,7 +790,8 @@ static void mdp4_overlay_vg_get_src_offset(struct mdp4_overlay_pipe *pipe, case MDP_RGB_888: case MDP_YCBCR_H1V1: case MDP_YCRCB_H1V1: - *luma_off = pipe->src_x * pipe->bpp; + *luma_off = (pipe->src_x * pipe->bpp) + + (pipe->src_y * pipe->srcp0_ystride); break; default: @@ -789,8 +807,9 @@ void mdp4_overlay_vg_setup(struct mdp4_overlay_pipe *pipe) char *vg_base; uint32 frame_size, src_size, src_xy, dst_size, dst_xy; uint32 format, pattern, luma_offset, chroma_offset; - uint32 mask, curr, addr; - int pnum, ptype; + uint32 mask; + int pnum, ptype, i; + uint32_t block; pnum = pipe->pipe_num - OVERLAY_PIPE_VG1; /* start from 0 */ vg_base = MDP_BASE + MDP4_VIDEO_BASE; @@ -806,10 +825,43 @@ void mdp4_overlay_vg_setup(struct mdp4_overlay_pipe *pipe) format = mdp4_overlay_format(pipe); pattern = mdp4_overlay_unpack_pattern(pipe); + /* CSC Post Processing enabled? */ + if (pipe->flags & MDP_OVERLAY_PP_CFG_EN) { + if (pipe->pp_cfg.config_ops & MDP_OVERLAY_PP_CSC_CFG) { + if (pipe->pp_cfg.csc_cfg.flags & MDP_CSC_FLAG_ENABLE) + pipe->op_mode |= MDP4_OP_CSC_EN; + if (pipe->pp_cfg.csc_cfg.flags & MDP_CSC_FLAG_YUV_IN) + pipe->op_mode |= MDP4_OP_SRC_DATA_YCBCR; + if (pipe->pp_cfg.csc_cfg.flags & MDP_CSC_FLAG_YUV_OUT) + pipe->op_mode |= MDP4_OP_DST_DATA_YCBCR; + + mdp4_csc_write(&pipe->pp_cfg.csc_cfg, + (uint32_t) (vg_base + MDP4_VIDEO_CSC_OFF)); + + if (pipe->pipe_num == OVERLAY_PIPE_VG1) + block = MDP_BLOCK_VG_1; + else + block = MDP_BLOCK_VG_2; + + for (i = 0; i < CSC_MAX_BLOCKS; i++) { + if (block == csc_cfg_matrix[i].block) { + memcpy(&csc_cfg_matrix[i].csc_data, + &(pipe->pp_cfg.csc_cfg), + sizeof(struct mdp_csc_cfg)); + break; + } + } + } + if (pipe->pp_cfg.config_ops & MDP_OVERLAY_PP_QSEED_CFG) { + mdp4_qseed_access_cfg(&pipe->pp_cfg.qseed_cfg[0], + (uint32_t) vg_base); + mdp4_qseed_access_cfg(&pipe->pp_cfg.qseed_cfg[1], + (uint32_t) vg_base); + } + } /* not RGB use VG pipe, pure VG pipe */ - pipe->op_mode |= MDP4_OP_CSC_EN; if (ptype != OVERLAY_TYPE_RGB) - pipe->op_mode |= MDP4_OP_SRC_DATA_YCBCR; + pipe->op_mode |= (MDP4_OP_CSC_EN | MDP4_OP_SRC_DATA_YCBCR); #ifdef MDP4_IGC_LUT_ENABLE pipe->op_mode |= MDP4_OP_IGC_LUT_EN; @@ -853,24 +905,6 @@ void mdp4_overlay_vg_setup(struct mdp4_overlay_pipe *pipe) &chroma_offset); } - /* Ensure proper covert matrix loaded when color space swaps */ - curr = inpdw(vg_base + 0x0058); - mask = 0x600; - - if ((curr & mask) != (pipe->op_mode & mask)) { - addr = ((uint32_t)vg_base) + 0x4000; - if (ptype != OVERLAY_TYPE_RGB) - mdp4_csc_write(&(mdp_csc_convert[1]), addr); - else - mdp4_csc_write(&(mdp_csc_convert[0]), addr); - - mask = 0xFFFCFFFF; - } else { - /* Don't touch bits you don't want to configure*/ - mask = 0xFFFCF1FF; - } - pipe->op_mode = (pipe->op_mode & mask) | (curr & ~mask); - /* luma component plane */ outpdw(vg_base + 0x0010, pipe->srcp0_addr + luma_offset); @@ -903,15 +937,6 @@ void mdp4_overlay_vg_setup(struct mdp4_overlay_pipe *pipe) pipe->r_bit << 4 | pipe->b_bit << 2 | pipe->g_bit); } - if (pipe->flags & MDP_SHARPENING) { - outpdw(vg_base + 0x8200, - mdp4_ss_table_value(pipe->req_data.dpp.sharp_strength, - 0)); - outpdw(vg_base + 0x8204, - mdp4_ss_table_value(pipe->req_data.dpp.sharp_strength, - 1)); - } - if (mdp_rev > MDP_REV_41) { /* mdp chip select controller */ mask = 0; @@ -945,11 +970,16 @@ int mdp4_overlay_format2type(uint32 format) case MDP_ARGB_8888: case MDP_RGBA_8888: case MDP_BGRA_8888: + case MDP_BGRX_8888: case MDP_RGBX_8888: return OVERLAY_TYPE_RGB; + case MDP_YCBYCR_H2V1: case MDP_YCRYCB_H2V1: + case MDP_CBYCRY_H2V1: case MDP_Y_CRCB_H2V1: case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H1V2: + case MDP_Y_CBCR_H1V2: case MDP_Y_CRCB_H2V2: case MDP_Y_CBCR_H2V2: case MDP_Y_CBCR_H2V2_TILE: @@ -1113,6 +1143,24 @@ int mdp4_overlay_format2pipe(struct mdp4_overlay_pipe *pipe) pipe->element0 = C1_B_Cb; /* B */ pipe->bpp = 4; /* 4 bpp */ break; + case MDP_BGRX_8888: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 3; /* alpha, 4 bits */ + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 3; + pipe->element3 = C3_ALPHA; /* alpha */ + pipe->element2 = C2_R_Cr; /* R */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C1_B_Cb; /* B */ + pipe->bpp = 4; /* 4 bpp */ + break; + case MDP_YCBYCR_H2V1: case MDP_YCRYCB_H2V1: pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; @@ -1124,15 +1172,42 @@ int mdp4_overlay_format2pipe(struct mdp4_overlay_pipe *pipe) pipe->unpack_tight = 1; pipe->unpack_align_msb = 0; pipe->unpack_count = 3; - pipe->element3 = C0_G_Y; /* G */ - pipe->element2 = C2_R_Cr; /* R */ - pipe->element1 = C0_G_Y; /* G */ - pipe->element0 = C1_B_Cb; /* B */ + pipe->element3 = C1_B_Cb; /* B */ + pipe->element2 = C0_G_Y; /* G */ + pipe->element1 = C2_R_Cr; /* R */ + pipe->element0 = C0_G_Y; /* G */ + pipe->bpp = 2; /* 2 bpp */ + pipe->chroma_sample = MDP4_CHROMA_H2V1; + break; + case MDP_CBYCRY_H2V1: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 0; /* alpha, 4 bits */ + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 3; + if (pipe->src_format == MDP_YCRYCB_H2V1) { + pipe->element3 = C0_G_Y; /* G */ + pipe->element2 = C2_R_Cr; /* R */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C1_B_Cb; /* B */ + } else if (pipe->src_format == MDP_YCBYCR_H2V1) { + pipe->element3 = C0_G_Y; /* G */ + pipe->element2 = C1_B_Cb; /* B */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C2_R_Cr; /* R */ + } pipe->bpp = 2; /* 2 bpp */ pipe->chroma_sample = MDP4_CHROMA_H2V1; break; case MDP_Y_CRCB_H2V1: case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H1V2: + case MDP_Y_CBCR_H1V2: case MDP_Y_CRCB_H2V2: case MDP_Y_CBCR_H2V2: case MDP_Y_CRCB_H1V1: @@ -1169,6 +1244,14 @@ int mdp4_overlay_format2pipe(struct mdp4_overlay_pipe *pipe) pipe->chroma_sample = MDP4_CHROMA_H1V2; else pipe->chroma_sample = MDP4_CHROMA_RGB; + } else if (pipe->src_format == MDP_Y_CRCB_H1V2) { + pipe->element1 = C1_B_Cb; + pipe->element0 = C2_R_Cr; + pipe->chroma_sample = MDP4_CHROMA_H1V2; + } else if (pipe->src_format == MDP_Y_CBCR_H1V2) { + pipe->element1 = C2_R_Cr; + pipe->element0 = C1_B_Cb; + pipe->chroma_sample = MDP4_CHROMA_H1V2; } else if (pipe->src_format == MDP_Y_CRCB_H2V2) { pipe->element1 = C1_B_Cb; pipe->element0 = C2_R_Cr; @@ -1290,6 +1373,7 @@ void transp_color_key(int format, uint32 transp, case MDP_XRGB_8888: case MDP_ARGB_8888: case MDP_BGRA_8888: + case MDP_BGRX_8888: b_start = 0; g_start = 8; r_start = 16; @@ -1329,6 +1413,8 @@ void transp_color_key(int format, uint32 transp, case MDP_Y_CR_CB_GH2V2: case MDP_Y_CRCB_H2V2: case MDP_Y_CRCB_H2V1: + case MDP_Y_CRCB_H1V2: + case MDP_Y_CBCR_H1V2: case MDP_Y_CRCB_H1V1: case MDP_Y_CBCR_H1V1: case MDP_YCRCB_H1V1: @@ -1400,6 +1486,87 @@ uint32 mdp4_overlay_unpack_pattern(struct mdp4_overlay_pipe *pipe) (pipe->element1 << 8) | pipe->element0; } +static uint32 mdp4_overlayproc_cfg_wb_panel(struct mdp4_overlay_pipe *pipe, + char *overlay_base, uint32 curr) +{ + int off, bpp; + uint32 flag; + bool is_rgb = false; + struct mdp_mixer_cfg *mixer_cfg; + + off = 0; + mixer_cfg = &ctrl->mdp_mixer_cfg[MDP4_MIXER2]; + + switch (mixer_cfg->writeback_format) { + case WB_FORMAT_RGB_888: + bpp = 3; /* RGB888 */ + flag = 0x0; + is_rgb = true; + break; + case WB_FORMAT_RGB_565: + bpp = 2; /* RGB565 */ + flag = 0x1; + is_rgb = true; + break; + case WB_FORMAT_xRGB_8888: + bpp = 4; /* xRGB8888 */ + flag = 0x3; + is_rgb = true; + break; + case WB_FORMAT_ARGB_8888: + bpp = 4; /* ARGB8888 */ + flag = 0x80000003; + is_rgb = true; + break; + case WB_FORMAT_ARGB_8888_INPUT_ALPHA: + pr_warn("currently not supported ARGB_8888_INPUT_ALPHA\n"); + default: + bpp = 1; /* NV12 */ + is_rgb = false; + break; + } + + if (is_rgb == true) { + if (pipe->ov_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + + outpdw(overlay_base + 0x000c, pipe->ov_blt_addr + off); + /* overlay ouput is RGB888 */ + outpdw(overlay_base + 0x0010, pipe->src_width * bpp); + outpdw(overlay_base + 0x001c, pipe->ov_blt_addr + off); + /* MDDI - BLT + on demand */ + outpdw(overlay_base + 0x0004, 0x08); + + curr = inpdw(overlay_base + 0x0014); + curr &= 0x4; + + outpdw(overlay_base + 0x0014, curr | flag); + } else { + if (pipe->ov_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + + outpdw(overlay_base + 0x000c, pipe->ov_blt_addr + off); + /* overlay ouput is RGB888 */ + outpdw(overlay_base + 0x0010, ((pipe->src_width << 16) | + pipe->src_width)); + outpdw(overlay_base + 0x001c, pipe->ov_blt_addr + off); + off = pipe->src_height * pipe->src_width; + /* align chroma to 2k address */ + off = (off + 2047) & ~2047; + /* UV plane adress */ + outpdw(overlay_base + 0x0020, pipe->ov_blt_addr + off); + /* MDDI - BLT + on demand */ + outpdw(overlay_base + 0x0004, 0x08); + /* pseudo planar + writeback */ + curr = inpdw(overlay_base + 0x0014); + curr &= 0x4; + outpdw(overlay_base + 0x0014, curr | 0x012); + /* rgb->yuv */ + outpdw(overlay_base + 0x0200, 0x05); + } + return curr; +} + /* * mdp4_overlayproc_cfg: only be called from base layer */ @@ -1459,34 +1626,8 @@ void mdp4_overlayproc_cfg(struct mdp4_overlay_pipe *pipe) #endif } else if (pipe->mixer_num == MDP4_MIXER2) { if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) { - off = 0; - bpp = 1; - if (pipe->ov_cnt & 0x01) - off = pipe->src_height * - pipe->src_width * bpp; - - outpdw(overlay_base + 0x000c, - pipe->ov_blt_addr + off); - /* overlay ouput is RGB888 */ - outpdw(overlay_base + 0x0010, - ((pipe->src_width << 16) | - pipe->src_width)); - outpdw(overlay_base + 0x001c, - pipe->ov_blt_addr + off); - off = pipe->src_height * pipe->src_width; - /* align chroma to 2k address */ - off = (off + 2047) & ~2047; - /* UV plane adress */ - outpdw(overlay_base + 0x0020, - pipe->ov_blt_addr + off); - /* MDDI - BLT + on demand */ - outpdw(overlay_base + 0x0004, 0x08); - /* pseudo planar + writeback */ - curr = inpdw(overlay_base + 0x0014); - curr &= 0x4; - outpdw(overlay_base + 0x0014, curr | 0x012); - /* rgb->yuv */ - outpdw(overlay_base + 0x0200, 0x05); + curr = mdp4_overlayproc_cfg_wb_panel(pipe, + overlay_base, curr); } } } else { @@ -1519,36 +1660,19 @@ void mdp4_overlayproc_cfg(struct mdp4_overlay_pipe *pipe) mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); } -int mdp4_overlay_pipe_staged(int mixer) +int mdp4_overlay_pipe_staged(struct mdp4_overlay_pipe *pipe) { - uint32 data, mask, i, off; - int p1, p2; + uint32 data, mask; + int mixer; - if (mixer == MDP4_MIXER2) - off = 0x100F0; - else - off = 0x10100; + mixer = pipe->mixer_num; + data = ctrl->mixer_cfg[mixer]; - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - data = inpdw(MDP_BASE + off); - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); - p1 = 0; - p2 = 0; - for (i = 0; i < 8; i++) { - mask = data & 0x0f; - if (mask) { - if (mask <= 4) - p1++; - else - p2++; - } - data >>= 4; - } + mask = 0x0f; + mask <<= (4 * pipe->pipe_num); + data &= mask; - if (mixer) - return p2; - else - return p1; + return data; } int mdp4_mixer_info(int mixer_num, struct mdp_mixer_info *info) @@ -1578,6 +1702,35 @@ int mdp4_mixer_info(int mixer_num, struct mdp_mixer_info *info) return cnt; } +void mdp4_solidfill_commit(int mixer) +{ + struct blend_cfg bcfg; + struct mdp4_overlay_pipe *pp = NULL; + int i = 0; + + for (i = 0; i < OVERLAY_PIPE_MAX; i++) { + pp = &ctrl->sf_plist[mixer][i]; + if (pp->pipe_ndx && pp->solid_fill) { + bcfg.solidfill = 1; + bcfg.solidfill_pipe = pp; + mdp4_overlay_bg_solidfill(&bcfg); + mdp4_overlay_reg_flush(pp, 1); + mdp4_mixer_stage_up(pp, 0); + } + } + mdp4_mixer_stage_commit(MDP4_MIXER1); + + for (i = 0; i < OVERLAY_PIPE_MAX; i++) { + pp = &ctrl->sf_plist[mixer][i]; + if (pp->pipe_ndx && pp->solid_fill) { + mdp4_overlay_reg_flush(pp, 1); + mdp4_mixer_stage_down(pp, 0); + pp->solid_fill = 0; + } + } + mdp4_mixer_stage_commit(MDP4_MIXER1); +} + void mdp4_mixer_stage_commit(int mixer) { struct mdp4_overlay_pipe *pipe; @@ -1600,8 +1753,14 @@ void mdp4_mixer_stage_commit(int mixer) data |= stage; } + /* + * stage_commit may be called from overlay_unset + * for command panel, mdp clocks may be off at this time. + * so mdp clock enabled is necessary + */ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); mdp_clk_ctrl(1); + mdp4_mixer_blend_setup(mixer); off = 0; @@ -1669,7 +1828,8 @@ void mdp4_mixer_stage_down(struct mdp4_overlay_pipe *pipe, int commit) ctrl->stage[mixer][i] = NULL; /* clear it */ } - if (commit || (mixer > 0 && !hdmi_prim_display)) + if (commit || ((mixer == 1) && !hdmi_prim_display) || + (mixer == 2)) mdp4_mixer_stage_commit(mixer); } /* @@ -1739,6 +1899,7 @@ void mdp4_overlay_borderfill_stage_down(struct mdp4_overlay_pipe *pipe) struct mdp4_overlay_pipe *bspipe; int ptype, pnum, pndx, mixer; int format, alpha_enable, alpha; + struct mdp4_iommu_pipe_info iom; if (pipe->pipe_type != OVERLAY_TYPE_BF) return; @@ -1753,6 +1914,7 @@ void mdp4_overlay_borderfill_stage_down(struct mdp4_overlay_pipe *pipe) return; } + iom = bspipe->iommu; ptype = bspipe->pipe_type; pnum = bspipe->pipe_num; pndx = bspipe->pipe_ndx; @@ -1766,6 +1928,7 @@ void mdp4_overlay_borderfill_stage_down(struct mdp4_overlay_pipe *pipe) bspipe->src_format = format; bspipe->alpha_enable = alpha_enable; bspipe->alpha = alpha; + bspipe->iommu = iom; bspipe->pipe_used++; /* mark base layer pipe used */ @@ -1910,7 +2073,7 @@ void mdp4_mixer_blend_setup(int mixer) struct mdp4_overlay_pipe *d_pipe; struct mdp4_overlay_pipe *s_pipe; struct blend_cfg *blend; - int i, off, ptype, alpha_drop = 0; + int i, off, alpha_drop; int d_alpha, s_alpha; unsigned char *overlay_base; uint32 c0, c1, c2, base_premulti; @@ -1936,11 +2099,14 @@ void mdp4_mixer_blend_setup(int mixer) d_alpha = 0; continue; } + alpha_drop = 0; /* per stage */ /* alpha channel is lost on VG pipe when using QSEED or M/N */ if (s_pipe->pipe_type == OVERLAY_TYPE_VIDEO && + s_pipe->alpha_enable && ((s_pipe->op_mode & MDP4_OP_SCALEY_EN) || (s_pipe->op_mode & MDP4_OP_SCALEX_EN)) && - !(s_pipe->op_mode & MDP4_OP_SCALEY_PIXEL_RPT)) + !(s_pipe->op_mode & (MDP4_OP_SCALEX_PIXEL_RPT | + MDP4_OP_SCALEY_PIXEL_RPT))) alpha_drop = 1; d_pipe = mdp4_background_layer(mixer, s_pipe); @@ -1956,7 +2122,8 @@ void mdp4_mixer_blend_setup(int mixer) blend->bg_alpha = 0x0ff - s_pipe->alpha; blend->fg_alpha = s_pipe->alpha; blend->co3_sel = 1; /* use fg alpha */ - + pr_debug("%s: bg alpha %d, fg alpha %d\n", + __func__, blend->bg_alpha, blend->fg_alpha); if (s_pipe->is_fg) { if (s_pipe->alpha == 0xff) { blend->solidfill = 1; @@ -1972,30 +2139,21 @@ void mdp4_mixer_blend_setup(int mixer) MDP4_BLEND_FG_ALPHA_FG_PIXEL; else blend->fg_alpha = 0xff; + blend->op |= MDP4_BLEND_BG_INV_ALPHA; } else blend->op = MDP4_BLEND_BG_ALPHA_FG_CONST; - - blend->op |= MDP4_BLEND_BG_INV_ALPHA; } else if (d_alpha) { - ptype = mdp4_overlay_format2type(s_pipe->src_format); - if (ptype == OVERLAY_TYPE_VIDEO) { - blend->op = (MDP4_BLEND_FG_ALPHA_BG_PIXEL | - MDP4_BLEND_FG_INV_ALPHA); - if ((!(s_pipe->flags & MDP_BLEND_FG_PREMULT)) && - ((i != MDP4_MIXER_STAGE0) || - (!base_premulti))) - blend->op |= - MDP4_BLEND_BG_ALPHA_BG_PIXEL; - else - blend->fg_alpha = 0xff; + blend->op = (MDP4_BLEND_FG_ALPHA_BG_PIXEL | + MDP4_BLEND_FG_INV_ALPHA); + if ((!(d_pipe->flags & MDP_BLEND_FG_PREMULT)) && + ((i != MDP4_MIXER_STAGE0) || + (!base_premulti))) + blend->op |= + MDP4_BLEND_BG_ALPHA_BG_PIXEL; + else + blend->fg_alpha = 0xff; - blend->co3_sel = 0; /* use bg alpha */ - } else { - /* s_pipe is rgb without alpha */ - blend->op = (MDP4_BLEND_FG_ALPHA_FG_CONST | - MDP4_BLEND_BG_ALPHA_BG_CONST); - blend->bg_alpha = 0; - } + blend->co3_sel = 0; /* use bg alpha */ } if (s_pipe->transp != MDP_TRANSP_NOP) { @@ -2057,7 +2215,9 @@ void mdp4_mixer_blend_setup(int mixer) outpdw(overlay_base + off + 0x108, blend->fg_alpha); outpdw(overlay_base + off + 0x10c, blend->bg_alpha); - if (mdp_rev >= MDP_REV_42) + if (mdp_rev >= MDP_REV_42 || + ctrl->panel_mode & MDP4_PANEL_MDDI || + ctrl->panel_mode & MDP4_PANEL_DSI_CMD) outpdw(overlay_base + off + 0x104, blend->op); outpdw(overlay_base + (off << 5) + 0x1004, blend->co3_sel); @@ -2392,6 +2552,11 @@ static int mdp4_overlay_req2pipe(struct mdp_overlay *req, int mixer, * zorder 2 == stage 2 == 4 */ if (req->id == MSMFB_NEW_REQUEST) { /* new request */ + if (mdp4_overlay_pipe_staged(pipe)) { + pr_err("%s: ndx=%d still staged\n", __func__, + pipe->pipe_ndx); + return -EPERM; + } pipe->pipe_used++; pipe->mixer_num = mixer; pr_debug("%s: zorder=%d pipe ndx=%d num=%d\n", __func__, @@ -2453,26 +2618,13 @@ static int mdp4_calc_pipe_mdp_clk(struct msm_fb_data_type *mfd, if (!pipe) { pr_err("%s: pipe is null!\n", __func__); - pipe->req_bw = OVERLAY_PERF_LEVEL4; return ret; } if (!mfd) { pr_err("%s: mfd is null!\n", __func__); - pipe->req_bw = OVERLAY_PERF_LEVEL4; return ret; } - /* - * Serveral special cases require the max mdp clk but cannot - * be explained by mdp clk equation. - */ - if (pipe->flags & MDP_DEINTERLACE) { - pr_info("%s deinterlace requires max mdp clk.\n", - __func__); - pipe->req_clk = mdp_max_clk; - return 0; - } - pr_debug("%s: pipe sets: panel res(x,y)=(%d,%d)\n", __func__, mfd->panel_info.xres, mfd->panel_info.yres); pr_debug("%s: src(w,h)(%d,%d),src(x,y)(%d,%d)\n", @@ -2484,6 +2636,11 @@ static int mdp4_calc_pipe_mdp_clk(struct msm_fb_data_type *mfd, mfd->panel_info.type == MIPI_CMD_PANEL) ? mfd->panel_info.mipi.dsi_pclk_rate : mfd->panel_info.clk_rate; + + if (mfd->panel_info.type == LVDS_PANEL && + mfd->panel_info.lvds.channel_mode == LVDS_DUAL_CHANNEL_MODE) + pclk = pclk << 1; + if (!pclk) { pipe->req_clk = mdp_max_clk; pr_err("%s panel pixel clk is zero!\n", __func__); @@ -2590,7 +2747,8 @@ static int mdp4_calc_pipe_mdp_clk(struct msm_fb_data_type *mfd, * required(FIR). */ if ((mfd->panel_info.lcdc.v_back_porch <= 4) && - (pipe->src_h != pipe->dst_h)) { + (pipe->src_h != pipe->dst_h) && + (mfd->panel_info.lcdc.v_back_porch)) { u32 clk = 0; clk = 4 * (pclk >> shift) / mfd->panel_info.lcdc.v_back_porch; clk <<= shift; @@ -2611,6 +2769,16 @@ static int mdp4_calc_pipe_mdp_clk(struct msm_fb_data_type *mfd, __func__); } + /* + * Interlaced videos require the max mdp clk but cannot + * be explained by mdp clk equation. + */ + if (pipe->flags & MDP_DEINTERLACE) { + rst = (rst > mdp_max_clk) ? rst : mdp_max_clk; + pr_info("%s deinterlace requires max mdp clk.\n", + __func__); + } + pipe->req_clk = (u32) rst; pr_debug("%s: required mdp clk %d mixer %d pipe ndx %d\n", @@ -2619,18 +2787,13 @@ static int mdp4_calc_pipe_mdp_clk(struct msm_fb_data_type *mfd, return 0; } -#define OVERLAY_VGA_SIZE 0x04B000 -#define OVERLAY_720P_TILE_SIZE 0x0E6000 -#define OVERLAY_WSVGA_SIZE 0x98000 /* 1024x608, align 600 to 32bit */ - -#define OVERLAY_BUS_SCALE_TABLE_BASE 6 - - static int mdp4_calc_pipe_mdp_bw(struct msm_fb_data_type *mfd, - struct mdp4_overlay_pipe *pipe) + struct mdp4_overlay_pipe *pipe) { - u32 res; + u32 fps; int ret = -EINVAL; + u32 quota; + u32 shift = 16; if (!pipe) { pr_err("%s: pipe is null!\n", __func__); @@ -2641,28 +2804,79 @@ static int mdp4_calc_pipe_mdp_bw(struct msm_fb_data_type *mfd, return ret; } - if (pipe->flags & MDP_DEINTERLACE) { - pr_info("%s deinterlace requires max mdp bw.\n", - __func__); - pipe->req_bw = OVERLAY_PERF_LEVEL1; - return 0; + fps = mdp_get_panel_framerate(mfd); + quota = pipe->src_w * pipe->src_h * fps * pipe->bpp; + + quota >>= shift; + /* factor 1.15 for ab */ + pipe->bw_ab_quota = quota * MDP4_BW_AB_FACTOR / 100; + /* factor 1.25 for ib */ + pipe->bw_ib_quota = quota * MDP4_BW_IB_FACTOR / 100; + /* down scaling factor for ib */ + if ((!pipe->dst_h) && (!pipe->src_h) && + (pipe->src_h > pipe->dst_h)) { + u64 ib = quota; + ib *= pipe->src_h; + ib /= pipe->dst_h; + pipe->bw_ib_quota = max(ib, pipe->bw_ib_quota); + pr_debug("%s: src_h=%d dst_h=%d mdp ib %llu, ib_quota=%llu\n", + __func__, pipe->src_h, pipe->dst_h, + ib<bw_ib_quota<pipe_type == OVERLAY_TYPE_BF) { - pipe->req_bw = OVERLAY_PERF_LEVEL4; - return 0; + pipe->bw_ab_quota <<= shift; + pipe->bw_ib_quota <<= shift; + + pr_debug("%s: pipe ndx=%d src(h,w)(%d, %d) fps=%d bpp=%d\n", + __func__, pipe->pipe_ndx, pipe->src_h, pipe->src_w, + fps, pipe->bpp); + pr_debug("%s: ab_quota=%llu ib_quota=%llu\n", __func__, + pipe->bw_ab_quota, pipe->bw_ib_quota); + + return 0; +} + +int mdp4_calc_blt_mdp_bw(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + struct mdp4_overlay_perf *perf_req = &perf_request; + u32 fps; + int bpp; + int ret = -EINVAL; + u32 quota; + u32 shift = 16; + if (!pipe) { + pr_err("%s: pipe is null!\n", __func__); + return ret; + } + if (!mfd) { + pr_err("%s: mfd is null!\n", __func__); + return ret; } - res = pipe->src_w * pipe->src_h; + bpp = BLT_BPP; + fps = mdp_get_panel_framerate(mfd); - if (res <= OVERLAY_WSVGA_SIZE) - pipe->req_bw = OVERLAY_PERF_LEVEL4; - else if (res <= OVERLAY_VGA_SIZE) - pipe->req_bw = OVERLAY_PERF_LEVEL3; - else if (res <= OVERLAY_720P_TILE_SIZE) - pipe->req_bw = OVERLAY_PERF_LEVEL2; - else - pipe->req_bw = OVERLAY_PERF_LEVEL1; + /* read and write bw*/ + quota = pipe->dst_w * pipe->dst_h * fps * bpp * 2; + quota >>= shift; + + perf_req->mdp_ov_ab_bw[pipe->mixer_num] = + quota * MDP4_BW_AB_FACTOR / 100; + + perf_req->mdp_ov_ib_bw[pipe->mixer_num] = + quota * MDP4_BW_IB_FACTOR / 100; + + perf_req->mdp_ov_ab_bw[pipe->mixer_num] <<= shift; + perf_req->mdp_ov_ib_bw[pipe->mixer_num] <<= shift; + + pr_debug("%s: pipe ndx=%d dst(h,w)(%d, %d) fps=%d bpp=%d\n", + __func__, pipe->pipe_ndx, pipe->dst_h, pipe->dst_w, + fps, bpp); + pr_debug("%s: overlay=%d ab_bw=%llu ib_bw=%llu\n", __func__, + pipe->mixer_num, + perf_req->mdp_ov_ab_bw[pipe->mixer_num], + perf_req->mdp_ov_ib_bw[pipe->mixer_num]); return 0; } @@ -2671,12 +2885,12 @@ int mdp4_overlay_mdp_perf_req(struct msm_fb_data_type *mfd, struct mdp4_overlay_pipe *plist) { u32 worst_mdp_clk = 0; - u32 worst_mdp_bw = OVERLAY_PERF_LEVEL4; int i; struct mdp4_overlay_perf *perf_req = &perf_request; struct mdp4_overlay_pipe *pipe = plist; u32 cnt = 0; int ret = -EINVAL; + u64 ab_quota_total = 0, ib_quota_total = 0; if (!mfd) { pr_err("%s: mfd is null!\n", __func__); @@ -2688,8 +2902,8 @@ int mdp4_overlay_mdp_perf_req(struct msm_fb_data_type *mfd, return ret; } - perf_req->use_ov0_blt = 0; - perf_req->use_ov1_blt = 0; + for (i = 0; i < MDP4_MIXER_MAX; i++) + perf_req->use_ov_blt[i] = 0; for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) { @@ -2701,25 +2915,17 @@ int mdp4_overlay_mdp_perf_req(struct msm_fb_data_type *mfd, cnt++; if (worst_mdp_clk < pipe->req_clk) worst_mdp_clk = pipe->req_clk; - if (pipe->req_clk > mdp_max_clk) { - if (pipe->mixer_num == MDP4_MIXER0) - perf_req->use_ov0_blt = 1; - if (pipe->mixer_num == MDP4_MIXER1) - perf_req->use_ov1_blt = 1; - } - if (!pipe->req_bw) { - pr_err("%s mdp pipe bw request should not be zero!\n", - __func__); - pr_debug("%s %d pid %d num %d idx %d mix %d bw %d\n", - __func__, __LINE__, current->pid, - pipe->pipe_num, pipe->pipe_ndx, - pipe->mixer_num, pipe->req_bw); - pipe->req_bw = OVERLAY_PERF_LEVEL4; - } + if (pipe->req_clk > mdp_max_clk) + perf_req->use_ov_blt[pipe->mixer_num] = 1; - if (pipe->req_bw < worst_mdp_bw) - worst_mdp_bw = pipe->req_bw; + if (pipe->mixer_num == MDP4_MIXER2) + perf_req->use_ov_blt[MDP4_MIXER2] = 1; + + if (pipe->pipe_type != OVERLAY_TYPE_BF) { + ab_quota_total += pipe->bw_ab_quota; + ib_quota_total += pipe->bw_ib_quota; + } if (mfd->mdp_rev == MDP_REV_41) { /* @@ -2728,32 +2934,45 @@ int mdp4_overlay_mdp_perf_req(struct msm_fb_data_type *mfd, */ if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) { if (pipe->dst_x != 0) - perf_req->use_ov0_blt = 1; + perf_req->use_ov_blt[MDP4_MIXER0] = 1; } if ((mfd->panel_info.xres > 1280) && (mfd->panel_info.type != DTV_PANEL)) { - perf_req->use_ov0_blt = 1; + perf_req->use_ov_blt[MDP4_MIXER0] = 1; } } } - perf_req->mdp_clk_rate = worst_mdp_clk; - if (perf_req->mdp_clk_rate > mdp_max_clk) - perf_req->mdp_clk_rate = mdp_max_clk; - + perf_req->mdp_clk_rate = min(worst_mdp_clk, mdp_max_clk); perf_req->mdp_clk_rate = mdp_clk_round_rate(perf_req->mdp_clk_rate); - perf_req->mdp_bw = worst_mdp_bw; + for (i = 0; i < MDP4_MIXER_MAX; i++) { + if (perf_req->use_ov_blt[i]) { + ab_quota_total += perf_req->mdp_ov_ab_bw[i]; + ib_quota_total += perf_req->mdp_ov_ib_bw[i]; + } + } + + perf_req->mdp_ab_bw = roundup(ab_quota_total, MDP_BUS_SCALE_AB_STEP); + perf_req->mdp_ib_bw = roundup(ib_quota_total, MDP_BUS_SCALE_AB_STEP); + + pr_debug("%s %d: ab_quota_total=(%llu, %d) ib_quota_total=(%llu, %d)\n", + __func__, __LINE__, + ab_quota_total, perf_req->mdp_ab_bw, + ib_quota_total, perf_req->mdp_ib_bw); - if (cnt >= 3) - perf_req->mdp_bw = OVERLAY_PERF_LEVEL1; + if (ab_quota_total > mdp_max_bw) + pr_warn("%s: req ab bw=%llu is larger than max bw=%llu", + __func__, ab_quota_total, mdp_max_bw); + if (ib_quota_total > mdp_max_bw) + pr_warn("%s: req ib bw=%llu is larger than max bw=%llu", + __func__, ib_quota_total, mdp_max_bw); - pr_debug("%s %d pid %d cnt %d clk %d ov0_blt %d, ov1_blt %d bw %d\n", + pr_debug("%s %d: pid %d cnt %d clk %d ov0_blt %d, ov1_blt %d\n", __func__, __LINE__, current->pid, cnt, perf_req->mdp_clk_rate, - perf_req->use_ov0_blt, - perf_req->use_ov1_blt, - perf_req->mdp_bw); + perf_req->use_ov_blt[0], + perf_req->use_ov_blt[1]); return 0; } @@ -2789,7 +3008,7 @@ void mdp4_overlay_mdp_perf_upd(struct msm_fb_data_type *mfd, flag); if (!mdp4_extn_disp) - perf_cur->use_ov1_blt = 0; + perf_cur->use_ov_blt[1] = 0; if (flag) { if (perf_req->mdp_clk_rate > perf_cur->mdp_clk_rate) { @@ -2802,19 +3021,27 @@ void mdp4_overlay_mdp_perf_upd(struct msm_fb_data_type *mfd, perf_cur->mdp_clk_rate = perf_req->mdp_clk_rate; } - if (perf_req->mdp_bw < perf_cur->mdp_bw) { + if ((perf_req->mdp_ab_bw > perf_cur->mdp_ab_bw) || + (perf_req->mdp_ib_bw > perf_cur->mdp_ib_bw)) { mdp_bus_scale_update_request - (OVERLAY_BUS_SCALE_TABLE_BASE - - perf_req->mdp_bw); - pr_info("%s mdp bw is changed [%d] from %d to %d\n", + (perf_req->mdp_ab_bw, perf_req->mdp_ib_bw); + pr_debug("%s mdp ab_bw is changed [%d] from %d to %d\n", + __func__, + flag, + perf_cur->mdp_ab_bw, + perf_req->mdp_ab_bw); + pr_debug("%s mdp ib_bw is changed [%d] from %d to %d\n", __func__, flag, - perf_cur->mdp_bw, - perf_req->mdp_bw); - perf_cur->mdp_bw = perf_req->mdp_bw; + perf_cur->mdp_ib_bw, + perf_req->mdp_ib_bw); + perf_cur->mdp_ab_bw = perf_req->mdp_ab_bw; + perf_cur->mdp_ib_bw = perf_req->mdp_ib_bw; } - if (mfd->panel_info.pdest == DISPLAY_1 && - perf_req->use_ov0_blt && !perf_cur->use_ov0_blt) { + + if ((mfd->panel_info.pdest == DISPLAY_1 && + perf_req->use_ov_blt[0] && !perf_cur->use_ov_blt[0]) || + dbg_force_ov0_blt) { if (mfd->panel_info.type == LCDC_PANEL || mfd->panel_info.type == LVDS_PANEL) mdp4_lcdc_overlay_blt_start(mfd); @@ -2822,22 +3049,25 @@ void mdp4_overlay_mdp_perf_upd(struct msm_fb_data_type *mfd, mdp4_dsi_video_blt_start(mfd); else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) mdp4_dsi_cmd_blt_start(mfd); - pr_info("%s mixer0 start blt [%d] from %d to %d.\n", + else if (ctrl->panel_mode & MDP4_PANEL_MDDI) + mdp4_mddi_blt_start(mfd); + pr_debug("%s mixer0 start blt [%d] from %d to %d.\n", __func__, flag, - perf_cur->use_ov0_blt, - perf_req->use_ov0_blt); - perf_cur->use_ov0_blt = perf_req->use_ov0_blt; + perf_cur->use_ov_blt[0], + perf_req->use_ov_blt[0]); + perf_cur->use_ov_blt[0] = perf_req->use_ov_blt[0]; } - if (mfd->panel_info.pdest == DISPLAY_2 && - perf_req->use_ov1_blt && !perf_cur->use_ov1_blt) { + if ((mfd->panel_info.pdest == DISPLAY_2 && + perf_req->use_ov_blt[1] && !perf_cur->use_ov_blt[1]) || + dbg_force_ov1_blt) { mdp4_dtv_overlay_blt_start(mfd); - pr_info("%s mixer1 start blt [%d] from %d to %d.\n", + pr_debug("%s mixer1 start blt [%d] from %d to %d.\n", __func__, flag, - perf_cur->use_ov1_blt, - perf_req->use_ov1_blt); - perf_cur->use_ov1_blt = perf_req->use_ov1_blt; + perf_cur->use_ov_blt[1], + perf_req->use_ov_blt[1]); + perf_cur->use_ov_blt[1] = perf_req->use_ov_blt[1]; } } else { if (perf_req->mdp_clk_rate < perf_cur->mdp_clk_rate) { @@ -2850,19 +3080,26 @@ void mdp4_overlay_mdp_perf_upd(struct msm_fb_data_type *mfd, perf_cur->mdp_clk_rate = perf_req->mdp_clk_rate; } - if (perf_req->mdp_bw > perf_cur->mdp_bw) { - pr_info("%s mdp bw is changed [%d] from %d to %d\n", + if (perf_req->mdp_ab_bw < perf_cur->mdp_ab_bw || + perf_req->mdp_ib_bw < perf_cur->mdp_ib_bw) { + mdp_bus_scale_update_request + (perf_req->mdp_ab_bw, perf_req->mdp_ib_bw); + pr_debug("%s mdp ab bw is changed [%d] from %d to %d\n", __func__, flag, - perf_cur->mdp_bw, - perf_req->mdp_bw); - mdp_bus_scale_update_request - (OVERLAY_BUS_SCALE_TABLE_BASE - - perf_req->mdp_bw); - perf_cur->mdp_bw = perf_req->mdp_bw; + perf_cur->mdp_ab_bw, + perf_req->mdp_ab_bw); + pr_debug("%s mdp ib bw is changed [%d] from %d to %d\n", + __func__, + flag, + perf_cur->mdp_ib_bw, + perf_req->mdp_ib_bw); + perf_cur->mdp_ab_bw = perf_req->mdp_ab_bw; + perf_cur->mdp_ib_bw = perf_req->mdp_ib_bw; } - if (mfd->panel_info.pdest == DISPLAY_1 && - !perf_req->use_ov0_blt && perf_cur->use_ov0_blt) { + if ((mfd->panel_info.pdest == DISPLAY_1 && + !perf_req->use_ov_blt[0] && perf_cur->use_ov_blt[0]) || + dbg_force_ov0_blt) { if (mfd->panel_info.type == LCDC_PANEL || mfd->panel_info.type == LVDS_PANEL) mdp4_lcdc_overlay_blt_stop(mfd); @@ -2870,22 +3107,25 @@ void mdp4_overlay_mdp_perf_upd(struct msm_fb_data_type *mfd, mdp4_dsi_video_blt_stop(mfd); else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) mdp4_dsi_cmd_blt_stop(mfd); - pr_info("%s mixer0 stop blt [%d] from %d to %d.\n", + else if (ctrl->panel_mode & MDP4_PANEL_MDDI) + mdp4_mddi_blt_stop(mfd); + pr_debug("%s mixer0 stop blt [%d] from %d to %d.\n", __func__, flag, - perf_cur->use_ov0_blt, - perf_req->use_ov0_blt); - perf_cur->use_ov0_blt = perf_req->use_ov0_blt; + perf_cur->use_ov_blt[0], + perf_req->use_ov_blt[0]); + perf_cur->use_ov_blt[0] = perf_req->use_ov_blt[0]; } - if (mfd->panel_info.pdest == DISPLAY_2 && - !perf_req->use_ov1_blt && perf_cur->use_ov1_blt) { + if ((mfd->panel_info.pdest == DISPLAY_2 && + !perf_req->use_ov_blt[1] && perf_cur->use_ov_blt[1]) || + dbg_force_ov1_blt) { mdp4_dtv_overlay_blt_stop(mfd); - pr_info("%s mixer1 stop blt [%d] from %d to %d.\n", + pr_debug("%s mixer1 stop blt [%d] from %d to %d.\n", __func__, flag, - perf_cur->use_ov1_blt, - perf_req->use_ov1_blt); - perf_cur->use_ov1_blt = perf_req->use_ov1_blt; + perf_cur->use_ov_blt[1], + perf_req->use_ov_blt[1]); + perf_cur->use_ov_blt[1] = perf_req->use_ov_blt[1]; } } return; @@ -2898,9 +3138,6 @@ static int get_img(struct msmfb_data *img, struct fb_info *info, { struct file *file; int put_needed, ret = 0, fb_num; -#ifdef CONFIG_ANDROID_PMEM - unsigned long vstart; -#endif *p_need = 0; if (img->flags & MDP_BLIT_SRC_GEM) { @@ -2914,14 +3151,13 @@ static int get_img(struct msmfb_data *img, struct fb_info *info, if (file == NULL) return -EINVAL; + pipe->flags |= MDP_MEMORY_ID_TYPE_FB; if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) { fb_num = MINOR(file->f_dentry->d_inode->i_rdev); if (get_fb_phys_info(start, len, fb_num, DISPLAY_SUBSYSTEM_ID)) { ret = -1; } else { - pr_warn("%s: mdp4_overlay play with FB memory\n", - __func__); *srcp_file = file; *p_need = put_needed; } @@ -2936,13 +3172,6 @@ static int get_img(struct msmfb_data *img, struct fb_info *info, return mdp4_overlay_iommu_map_buf(img->memory_id, pipe, plane, start, len, srcp_ihdl); #endif -#ifdef CONFIG_ANDROID_PMEM - if (!get_pmem_file(img->memory_id, start, &vstart, - len, srcp_file)) - return 0; - else - return -EINVAL; -#endif } #ifdef CONFIG_FB_MSM_MIPI_DSI @@ -3054,22 +3283,22 @@ int mdp4_overlay_set(struct fb_info *info, struct mdp_overlay *req) pr_debug("pipe->flags 0x%x\n", pipe->flags); if (pipe->flags & MDP_SECURE_OVERLAY_SESSION) { mfd->mem_hid &= ~BIT(ION_IOMMU_HEAP_ID); - mfd->mem_hid |= ION_SECURE; + mfd->mem_hid |= ION_FLAG_SECURE; } else { mfd->mem_hid |= BIT(ION_IOMMU_HEAP_ID); - mfd->mem_hid &= ~ION_SECURE; + mfd->mem_hid &= ~ION_FLAG_SECURE; } } - if (pipe->flags & MDP_SHARPENING) { - bool test = ((pipe->req_data.dpp.sharp_strength > 0) && - ((req->src_rect.w > req->dst_rect.w) && - (req->src_rect.h > req->dst_rect.h))); - if (test) { - pr_debug("%s: No sharpening while downscaling.\n", + mdp4_stat.overlay_set[pipe->mixer_num]++; + + if (pipe->flags & MDP_OVERLAY_PP_CFG_EN) { + if (pipe->pipe_num <= OVERLAY_PIPE_VG2) + memcpy(&pipe->pp_cfg, &req->overlay_pp_cfg, + sizeof(struct mdp_overlay_pp_params)); + else + pr_debug("%s: RGB Pipes don't support CSC/QSEED\n", __func__); - pipe->flags &= ~MDP_SHARPENING; - } } mdp4_overlay_mdp_pipe_req(pipe, mfd); @@ -3108,8 +3337,6 @@ int mdp4_overlay_unset(struct fb_info *info, int ndx) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; struct mdp4_overlay_pipe *pipe; - struct dpp_ctrl dpp; - int i; if (mfd == NULL) return -ENODEV; @@ -3137,33 +3364,25 @@ int mdp4_overlay_unset(struct fb_info *info, int ndx) else { /* mixer 0 */ ctrl->mixer0_played = 0; - if (ctrl->panel_mode & MDP4_PANEL_MDDI) { - if (mfd->panel_power_on) - mdp4_mddi_blt_dmap_busy_wait(mfd); - } + } mdp4_overlay_reg_flush(pipe, 1); mdp4_mixer_stage_down(pipe, 0); if (pipe->mixer_num == MDP4_MIXER0) { - if (ctrl->panel_mode & MDP4_PANEL_MDDI) { - if (mfd->panel_power_on) - mdp4_mddi_overlay_restore(); - } } else { /* mixer1, DTV, ATV */ - if (ctrl->panel_mode & MDP4_PANEL_DTV) + if (ctrl->panel_mode & MDP4_PANEL_DTV) { + if (hdmi_prim_display) { + struct mdp4_overlay_pipe *pp; + pp = &ctrl->sf_plist[pipe->mixer_num] + [pipe->pipe_ndx - 1]; + *pp = *pipe; /* clone it */ + pp->solid_fill = 1; + } mdp4_overlay_dtv_unset(mfd, pipe); - } - - /* Reset any HSIC settings to default */ - if (pipe->flags & MDP_DPP_HSIC) { - for (i = 0; i < NUM_HSIC_PARAM; i++) - dpp.hsic_params[i] = 0; - - mdp4_hsic_set(pipe, &dpp); - mdp4_hsic_update(pipe); + } } mdp4_stat.overlay_unset[pipe->mixer_num]++; @@ -3184,6 +3403,8 @@ int mdp4_overlay_wait4vsync(struct fb_info *info, long long *vtime) mdp4_dsi_cmd_wait4vsync(0, vtime); else if (ctrl->panel_mode & MDP4_PANEL_LCDC) mdp4_lcdc_wait4vsync(0, vtime); + else if (ctrl->panel_mode & MDP4_PANEL_MDDI) + mdp4_mddi_wait4vsync(0, vtime); } else if (hdmi_prim_display || info->node == 1) { mdp4_dtv_wait4vsync(0, vtime); } @@ -3207,6 +3428,8 @@ int mdp4_overlay_vsync_ctrl(struct fb_info *info, int enable) mdp4_dsi_cmd_vsync_ctrl(info, cmd); else if (ctrl->panel_mode & MDP4_PANEL_LCDC) mdp4_lcdc_vsync_ctrl(info, cmd); + else if (ctrl->panel_mode & MDP4_PANEL_MDDI) + mdp4_mddi_vsync_ctrl(info, cmd); } else if (hdmi_prim_display || info->node == 1) mdp4_dtv_vsync_ctrl(info, cmd); @@ -3291,11 +3514,8 @@ int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req) struct mdp4_overlay_pipe *pipe; ulong start, addr; ulong len = 0; - struct file *srcp0_file = NULL; - struct file *srcp1_file = NULL, *srcp2_file = NULL; struct ion_handle *srcp0_ihdl = NULL; struct ion_handle *srcp1_ihdl = NULL, *srcp2_ihdl = NULL; - int ps0_need, p_need; uint32_t overlay_version = 0; int ret = 0; @@ -3320,8 +3540,8 @@ int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req) mutex_lock(&mfd->dma->ov_mutex); img = &req->data; - get_img(img, info, pipe, 0, &start, &len, &srcp0_file, - &ps0_need, &srcp0_ihdl); + get_img(img, info, pipe, 0, &start, &len, &pipe->srcp0_file, + &pipe->put0_need, &srcp0_ihdl); if (len == 0) { pr_err("%s: pmem Error\n", __func__); ret = -1; @@ -3343,8 +3563,9 @@ int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req) if (pipe->fetch_plane == OVERLAY_PLANE_PSEUDO_PLANAR) { if (overlay_version > 0) { img = &req->plane1_data; - get_img(img, info, pipe, 1, &start, &len, &srcp1_file, - &p_need, &srcp1_ihdl); + get_img(img, info, pipe, 1, &start, &len, + &pipe->srcp1_file, &pipe->put1_need, + &srcp1_ihdl); if (len == 0) { pr_err("%s: Error to get plane1\n", __func__); ret = -EINVAL; @@ -3363,7 +3584,9 @@ int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req) } pipe->srcp0_ystride = pipe->src_width; if ((pipe->src_format == MDP_Y_CRCB_H1V1) || - (pipe->src_format == MDP_Y_CBCR_H1V1)) { + (pipe->src_format == MDP_Y_CBCR_H1V1) || + (pipe->src_format == MDP_Y_CRCB_H1V2) || + (pipe->src_format == MDP_Y_CBCR_H1V2)) { if (pipe->src_width > YUV_444_MAX_WIDTH) pipe->srcp1_ystride = pipe->src_width << 2; else @@ -3374,8 +3597,9 @@ int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req) } else if (pipe->fetch_plane == OVERLAY_PLANE_PLANAR) { if (overlay_version > 0) { img = &req->plane1_data; - get_img(img, info, pipe, 1, &start, &len, &srcp1_file, - &p_need, &srcp1_ihdl); + get_img(img, info, pipe, 1, &start, &len, + &pipe->srcp1_file, &pipe->put1_need, + &srcp1_ihdl); if (len == 0) { pr_err("%s: Error to get plane1\n", __func__); ret = -EINVAL; @@ -3384,8 +3608,9 @@ int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req) pipe->srcp1_addr = start + img->offset; img = &req->plane2_data; - get_img(img, info, pipe, 2, &start, &len, &srcp2_file, - &p_need, &srcp2_ihdl); + get_img(img, info, pipe, 2, &start, &len, + &pipe->srcp2_file, &pipe->put2_need, + &srcp2_ihdl); if (len == 0) { pr_err("%s: Error to get plane2\n", __func__); ret = -EINVAL; @@ -3426,78 +3651,85 @@ int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req) mdp4_overlay_mdp_perf_req(mfd, ctrl->plist); - if (pipe->mixer_num == MDP4_MIXER2 || ctrl->panel_mode & MDP4_PANEL_MDDI) - goto mddi; - if (pipe->mixer_num == MDP4_MIXER0) { if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) { /* cndx = 0 */ mdp4_dsi_cmd_pipe_queue(0, pipe); - } - if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) { + } else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) { /* cndx = 0 */ mdp4_dsi_video_pipe_queue(0, pipe); } else if (ctrl->panel_mode & MDP4_PANEL_LCDC) { /* cndx = 0 */ mdp4_lcdc_pipe_queue(0, pipe); } + if (ctrl->panel_mode & MDP4_PANEL_MDDI) { + /* cndx = 0 */ + mdp4_mddi_pipe_queue(0, pipe); + } } else if (pipe->mixer_num == MDP4_MIXER1) { - if (ctrl->panel_mode & MDP4_PANEL_DTV) + if (ctrl->panel_mode & MDP4_PANEL_DTV) { mdp4_dtv_pipe_queue(0, pipe);/* cndx = 0 */ + mdp4_dtv_set_avparams(pipe, img->memory_id); + } + } else if (pipe->mixer_num == MDP4_MIXER2) { + ctrl->mixer2_played++; + if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) + mdp4_wfd_pipe_queue(0, pipe);/* cndx = 0 */ } mutex_unlock(&mfd->dma->ov_mutex); return ret; -mddi: - if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) { - mdp4_overlay_vg_setup(pipe); /* video/graphic pipe */ - } else { - mdp4_overlay_rgb_setup(pipe); /* rgb pipe */ - } +end: + mutex_unlock(&mfd->dma->ov_mutex); - mdp4_mixer_stage_up(pipe, 0); + return ret; +} - if (pipe->mixer_num == MDP4_MIXER2) { - ctrl->mixer2_played++; -#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL - if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) { - mdp4_writeback_dma_busy_wait(mfd); - mdp4_writeback_kickoff_video(mfd, pipe); - } -#endif - } else if (ctrl->panel_mode & MDP4_PANEL_MDDI) { - if (pipe->flags & MDP_OV_PLAY_NOWAIT) { - mdp4_stat.overlay_play[pipe->mixer_num]++; - mutex_unlock(&mfd->dma->ov_mutex); - goto end; - } - mdp4_mixer_stage_commit(pipe->mixer_num); - mdp4_mddi_dma_busy_wait(mfd); - mdp4_mddi_kickoff_video(mfd, pipe); +int mdp4_overlay_commit(struct fb_info *info) +{ + int ret = 0; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (mfd == NULL) + return -ENODEV; + + if (!mfd->panel_power_on) /* suspended */ + return -EINVAL; + + mutex_lock(&mfd->dma->ov_mutex); + + mdp4_overlay_mdp_perf_upd(mfd, 1); + + msm_fb_wait_for_fence(mfd); + + switch (mfd->panel.type) { + case MIPI_CMD_PANEL: + mdp4_dsi_cmd_pipe_commit(0, 1); + break; + case MIPI_VIDEO_PANEL: + mdp4_dsi_video_pipe_commit(0, 1); + break; + case LCDC_PANEL: + mdp4_lcdc_pipe_commit(0, 1); + break; + case DTV_PANEL: + mdp4_dtv_pipe_commit(0, 1); + break; + case WRITEBACK_PANEL: + mdp4_wfd_pipe_commit(mfd, 0, 1); + break; + default: + pr_err("Panel Not Supported for Commit"); + ret = -EINVAL; + break; } + msm_fb_signal_timeline(mfd); - /* write out DPP HSIC registers */ - if (pipe->flags & MDP_DPP_HSIC) - mdp4_hsic_update(pipe); - if (!(pipe->flags & MDP_OV_PLAY_NOWAIT)) - mdp4_iommu_unmap(pipe); - mdp4_stat.overlay_play[pipe->mixer_num]++; + mdp4_overlay_mdp_perf_upd(mfd, 0); -end: mutex_unlock(&mfd->dma->ov_mutex); -#ifdef CONFIG_ANDROID_PMEM - if (srcp0_file) - put_pmem_file(srcp0_file); - if (srcp1_file) - put_pmem_file(srcp1_file); - if (srcp2_file) - put_pmem_file(srcp2_file); -#endif - /* only source may use frame buffer */ - if (img->flags & MDP_MEMORY_ID_TYPE_FB) - fput_light(srcp0_file, ps0_need); return ret; } @@ -3730,3 +3962,58 @@ int mdp4_update_base_blend(struct msm_fb_data_type *mfd, } return ret; } + +int mdp4_update_writeback_format(struct msm_fb_data_type *mfd, + struct mdp_mixer_cfg *mdp_mixer_cfg) +{ + int ret = 0; + u32 mixer_num; + struct mdp_mixer_cfg *mixer; + + mixer_num = mdp4_get_mixer_num(mfd->panel_info.type); + if (!ctrl) { + pr_warn("mdp4_overlay_ctrl is NULL\n"); + return -EPERM; + } + mixer = &ctrl->mdp_mixer_cfg[mixer_num]; + + switch (mdp_mixer_cfg->writeback_format) { + case WB_FORMAT_RGB_888: + case WB_FORMAT_RGB_565: + case WB_FORMAT_NV12: + case WB_FORMAT_xRGB_8888: + case WB_FORMAT_ARGB_8888: + mixer->writeback_format = mdp_mixer_cfg->writeback_format; + break; + case WB_FORMAT_ARGB_8888_INPUT_ALPHA: + mixer->writeback_format = mdp_mixer_cfg->writeback_format; + mixer->alpha = mdp_mixer_cfg->alpha; + break; + default: + mixer->writeback_format = WB_FORMAT_NV12; + pr_warn("Unsupported format request, setting to NV12\n"); + ret = -EINVAL; + break; + } + return ret; +} + +int mdp4_update_base_blend(struct msm_fb_data_type *mfd, + struct mdp_blend_cfg *mdp_blend_cfg) +{ + int ret = 0; + u32 mixer_num; + struct blend_cfg *blend; + mixer_num = mdp4_get_mixer_num(mfd->panel_info.type); + if (!ctrl) + return -EPERM; + blend = &ctrl->blend[mixer_num][MDP4_MIXER_STAGE_BASE]; + if (mdp_blend_cfg->is_premultiplied) { + blend->bg_alpha = 0xFF; + blend->op = MDP4_BLEND_FG_ALPHA_BG_CONST; + } else { + blend->op = MDP4_BLEND_FG_ALPHA_FG_PIXEL; + blend->bg_alpha = 0; + } + return ret; +} diff --git a/drivers/video/msm/mdp4_overlay.c.save b/drivers/video/msm/mdp4_overlay.c.save new file mode 100644 index 00000000..0c755b3c --- /dev/null +++ b/drivers/video/msm/mdp4_overlay.c.save @@ -0,0 +1,3709 @@ +/* Copyright (c) 2009-2012, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#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 "mdp.h" +#include "msm_fb.h" +#include "mdp4.h" + +#define VERSION_KEY_MASK 0xFFFFFF00 + +struct mdp4_overlay_ctrl { + struct mdp4_overlay_pipe plist[OVERLAY_PIPE_MAX]; + struct mdp4_overlay_pipe *stage[MDP4_MIXER_MAX][MDP4_MIXER_STAGE_MAX]; + struct mdp4_overlay_pipe *baselayer[MDP4_MIXER_MAX]; + struct blend_cfg blend[MDP4_MIXER_MAX][MDP4_MIXER_STAGE_MAX]; + uint32 mixer_cfg[MDP4_MIXER_MAX]; + uint32 flush[MDP4_MIXER_MAX]; + struct iommu_free_list iommu_free[MDP4_MIXER_MAX]; + uint32 cs_controller; + uint32 hw_version; + uint32 panel_3d; + uint32 panel_mode; + uint32 mixer0_played; + uint32 mixer1_played; + uint32 mixer2_played; +} mdp4_overlay_db = { + .cs_controller = CS_CONTROLLER_0, + .plist = { + { + .pipe_type = OVERLAY_TYPE_RGB, + .pipe_num = OVERLAY_PIPE_RGB1, + .pipe_ndx = 1, + }, + { + .pipe_type = OVERLAY_TYPE_RGB, + .pipe_num = OVERLAY_PIPE_RGB2, + .pipe_ndx = 2, + }, + { + .pipe_type = OVERLAY_TYPE_VIDEO, + .pipe_num = OVERLAY_PIPE_VG1, + .pipe_ndx = 3, + }, + { + .pipe_type = OVERLAY_TYPE_VIDEO, + .pipe_num = OVERLAY_PIPE_VG2, + .pipe_ndx = 4, + }, + { + .pipe_type = OVERLAY_TYPE_BF, + .pipe_num = OVERLAY_PIPE_RGB3, + .pipe_ndx = 5, + .mixer_num = MDP4_MIXER0, + }, + { + .pipe_type = OVERLAY_TYPE_BF, + .pipe_num = OVERLAY_PIPE_VG3, + .pipe_ndx = 6, + .mixer_num = MDP4_MIXER1, + }, + { + .pipe_type = OVERLAY_TYPE_BF, + .pipe_num = OVERLAY_PIPE_VG4, + .pipe_ndx = 7, + .mixer_num = MDP4_MIXER2, + }, + }, +}; + +static DEFINE_MUTEX(iommu_mutex); +static struct mdp4_overlay_ctrl *ctrl = &mdp4_overlay_db; + +struct mdp4_overlay_perf { + u32 mdp_clk_rate; + u32 use_ov0_blt; + u32 use_ov1_blt; + u32 mdp_bw; +}; + +struct mdp4_overlay_perf perf_request = { + .mdp_bw = OVERLAY_PERF_LEVEL4, +}; +struct mdp4_overlay_perf perf_current = { + .mdp_bw = OVERLAY_PERF_LEVEL4, +}; + +static struct ion_client *display_iclient; + + +/* + * mdp4_overlay_iommu_unmap_freelist() + * mdp4_overlay_iommu_2freelist() + * mdp4_overlay_iommu_pipe_free() + * above three functiosns need to be called from same thread and + * in order so that no mutex are needed. + */ +void mdp4_overlay_iommu_unmap_freelist(int mixer) +{ + int i; + struct ion_handle *ihdl; + struct iommu_free_list *flist; + + mutex_lock(&iommu_mutex); + flist = &ctrl->iommu_free[mixer]; + if (flist->total == 0) { + mutex_unlock(&iommu_mutex); + return; + } + for (i = 0; i < IOMMU_FREE_LIST_MAX; i++) { + ihdl = flist->ihdl[i]; + if (ihdl == NULL) + continue; + pr_debug("%s: mixer=%d i=%d ihdl=0x%p\n", __func__, + mixer, i, ihdl); + ion_unmap_iommu(display_iclient, ihdl, DISPLAY_READ_DOMAIN, + GEN_POOL); + mdp4_stat.iommu_unmap++; + pr_debug("%s: map=%d unmap=%d drop=%d\n", __func__, + (int)mdp4_stat.iommu_map, (int)mdp4_stat.iommu_unmap, + (int)mdp4_stat.iommu_drop); + ion_free(display_iclient, ihdl); + flist->ihdl[i] = NULL; + } + + flist->fndx = 0; + flist->total = 0; + mutex_unlock(&iommu_mutex); +} + +void mdp4_overlay_iommu_2freelist(int mixer, struct ion_handle *ihdl) +{ + struct iommu_free_list *flist; + + flist = &ctrl->iommu_free[mixer]; + if (flist->fndx >= IOMMU_FREE_LIST_MAX) { + pr_err("%s: Error, mixer=%d iommu fndx=%d\n", + __func__, mixer, flist->fndx); + mdp4_stat.iommu_drop++; + return; + } + + pr_debug("%s: add mixer=%d fndx=%d ihdl=0x%p\n", __func__, + mixer, flist->fndx, ihdl); + + flist->total++; + flist->ihdl[flist->fndx++] = ihdl; +} + +void mdp4_overlay_iommu_pipe_free(int ndx, int all) +{ + struct mdp4_overlay_pipe *pipe; + struct mdp4_iommu_pipe_info *iom; + int plane, mixer; + + pipe = mdp4_overlay_ndx2pipe(ndx); + if (pipe == NULL) + return; + + mutex_lock(&iommu_mutex); + mixer = pipe->mixer_num; + iom = &pipe->iommu; + pr_debug("%s: mixer=%d ndx=%d all=%d\n", __func__, + mixer, pipe->pipe_ndx, all); + for (plane = 0; plane < MDP4_MAX_PLANE; plane++) { + if (iom->prev_ihdl[plane]) { + mdp4_overlay_iommu_2freelist(mixer, + iom->prev_ihdl[plane]); + iom->prev_ihdl[plane] = NULL; + } + if (all && iom->ihdl[plane]) { + mdp4_overlay_iommu_2freelist(mixer, iom->ihdl[plane]); + iom->ihdl[plane] = NULL; + } + } + mutex_unlock(&iommu_mutex); +} + +int mdp4_overlay_iommu_map_buf(int mem_id, + struct mdp4_overlay_pipe *pipe, unsigned int plane, + unsigned long *start, unsigned long *len, + struct ion_handle **srcp_ihdl) +{ + struct mdp4_iommu_pipe_info *iom; + + if (!display_iclient) + return -EINVAL; + + *srcp_ihdl = ion_import_dma_buf(display_iclient, mem_id); + if (IS_ERR_OR_NULL(*srcp_ihdl)) { + pr_err("ion_import_dma_buf() failed\n"); + return PTR_ERR(*srcp_ihdl); + } + pr_debug("%s(): ion_hdl %p, ion_buf %d\n", __func__, *srcp_ihdl, + ion_share_dma_buf(display_iclient, *srcp_ihdl)); + pr_debug("mixer %u, pipe %u, plane %u\n", pipe->mixer_num, + pipe->pipe_ndx, plane); + if (ion_map_iommu(display_iclient, *srcp_ihdl, + DISPLAY_READ_DOMAIN, GEN_POOL, SZ_4K, 0, start, + len, 0, ION_IOMMU_UNMAP_DELAYED)) { + ion_free(display_iclient, *srcp_ihdl); + pr_err("ion_map_iommu() failed\n"); + return -EINVAL; + } + + mutex_lock(&iommu_mutex); + iom = &pipe->iommu; + if (iom->prev_ihdl[plane]) { + mdp4_overlay_iommu_2freelist(pipe->mixer_num, + iom->prev_ihdl[plane]); + mdp4_stat.iommu_drop++; + pr_err("%s: dropped, ndx=%d plane=%d\n", __func__, + pipe->pipe_ndx, plane); + } + iom->prev_ihdl[plane] = iom->ihdl[plane]; + iom->ihdl[plane] = *srcp_ihdl; + mdp4_stat.iommu_map++; + + pr_debug("%s: ndx=%d plane=%d prev=0x%p cur=0x%p start=0x%lx len=%lx\n", + __func__, pipe->pipe_ndx, plane, iom->prev_ihdl[plane], + iom->ihdl[plane], *start, *len); + mutex_unlock(&iommu_mutex); + return 0; +} + +static struct mdp4_iommu_pipe_info mdp_iommu[MDP4_MIXER_MAX][OVERLAY_PIPE_MAX]; + +void mdp4_iommu_unmap(struct mdp4_overlay_pipe *pipe) +{ + struct mdp4_iommu_pipe_info *iom_pipe_info; + unsigned char i, j; + + if (!display_iclient) + return; + + for (j = 0; j < OVERLAY_PIPE_MAX; j++) { + iom_pipe_info = &mdp_iommu[pipe->mixer_num][j]; + for (i = 0; i < MDP4_MAX_PLANE; i++) { + if (iom_pipe_info->prev_ihdl[i]) { + pr_debug("%s(): mixer %u, pipe %u, plane %u, " + "prev_ihdl %p\n", __func__, + pipe->mixer_num, j + 1, i, + iom_pipe_info->prev_ihdl[i]); + ion_unmap_iommu(display_iclient, + iom_pipe_info->prev_ihdl[i], + DISPLAY_READ_DOMAIN, GEN_POOL); + ion_free(display_iclient, + iom_pipe_info->prev_ihdl[i]); + iom_pipe_info->prev_ihdl[i] = NULL; + } + + if (iom_pipe_info->mark_unmap) { + if (iom_pipe_info->ihdl[i]) { + pr_debug("%s(): MARK, mixer %u, pipe %u, plane %u, " + "ihdl %p\n", __func__, + pipe->mixer_num, j + 1, i, + iom_pipe_info->ihdl[i]); + ion_unmap_iommu(display_iclient, + iom_pipe_info->ihdl[i], + DISPLAY_READ_DOMAIN, GEN_POOL); + ion_free(display_iclient, + iom_pipe_info->ihdl[i]); + iom_pipe_info->ihdl[i] = NULL; + } + } + } + iom_pipe_info->mark_unmap = 0; + } +} + +int mdp4_overlay_mixer_play(int mixer_num) +{ + if (mixer_num == MDP4_MIXER2) + return ctrl->mixer2_played; + else if (mixer_num == MDP4_MIXER1) + return ctrl->mixer1_played; + else + return ctrl->mixer0_played; +} + +void mdp4_overlay_panel_3d(int mixer_num, uint32 panel_3d) +{ + ctrl->panel_3d = panel_3d; +} + +void mdp4_overlay_panel_mode(int mixer_num, uint32 mode) +{ + ctrl->panel_mode |= mode; +} + +void mdp4_overlay_panel_mode_unset(int mixer_num, uint32 mode) +{ + ctrl->panel_mode &= ~mode; +} + +uint32 mdp4_overlay_panel_list(void) +{ + return ctrl->panel_mode; +} + +void mdp4_overlay_cfg_init(void) +{ + if (ctrl->hw_version == 0) { + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + ctrl->hw_version = inpdw(MDP_BASE + 0x0); /* MDP_HW_VERSION */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + } + + if (ctrl->hw_version >= 0x0402030b) { + /* MDP_LAYERMIXER_IN_CFG_UPDATE_METHOD */ + outpdw(MDP_BASE + 0x100fc, 0x01); + } +} + +int mdp4_overlay_borderfill_supported(void) +{ + return (ctrl->hw_version >= 0x0402030b); +} + +void mdp4_overlay_dmae_cfg(struct msm_fb_data_type *mfd, int atv) +{ + uint32 dmae_cfg_reg; + + if (atv) + dmae_cfg_reg = DMA_DEFLKR_EN; + else + dmae_cfg_reg = 0; + + if (mfd->fb_imgType == MDP_BGR_565) + dmae_cfg_reg |= DMA_PACK_PATTERN_BGR; + else + dmae_cfg_reg |= DMA_PACK_PATTERN_RGB; + + + if (mfd->panel_info.bpp == 18) { + dmae_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */ + DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + } else if (mfd->panel_info.bpp == 16) { + dmae_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */ + DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + } else { + dmae_cfg_reg |= DMA_DSTC0G_8BITS | /* 888 16BPP */ + DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* dma2 config register */ + MDP_OUTP(MDP_BASE + 0xb0000, dmae_cfg_reg); + if (atv) { + MDP_OUTP(MDP_BASE + 0xb0070, 0xeb0010); + MDP_OUTP(MDP_BASE + 0xb0074, 0xf00010); + MDP_OUTP(MDP_BASE + 0xb0078, 0xf00010); + MDP_OUTP(MDP_BASE + 0xb3000, 0x80); + MDP_OUTP(MDP_BASE + 0xb3010, 0x1800040); + MDP_OUTP(MDP_BASE + 0xb3014, 0x1000080); + MDP_OUTP(MDP_BASE + 0xb4004, 0x67686970); + } else { + mdp_vid_quant_set(); + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +#ifdef CONFIG_FB_MSM_HDMI_3D +void unfill_black_screen(void) { return; } +#else +void unfill_black_screen(void) +{ + uint32 temp_src_format; + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + /* + * VG2 Constant Color + */ + temp_src_format = inpdw(MDP_BASE + 0x30050); + MDP_OUTP(MDP_BASE + 0x30050, temp_src_format&(~BIT(22))); + /* + * MDP_OVERLAY_REG_FLUSH + */ + MDP_OUTP(MDP_BASE + 0x18000, BIT(3)); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return; +} +#endif + +#ifdef CONFIG_FB_MSM_HDMI_3D +void fill_black_screen(void) { return; } +#else +void fill_black_screen(void) +{ + /*Black color*/ + uint32 color = 0x00000000; + uint32 temp_src_format; + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + /* + * VG2 Constant Color + */ + MDP_OUTP(MDP_BASE + 0x31008, color); + /* + * MDP_VG2_SRC_FORMAT + */ + temp_src_format = inpdw(MDP_BASE + 0x30050); + MDP_OUTP(MDP_BASE + 0x30050, temp_src_format | BIT(22)); + /* + * MDP_OVERLAY_REG_FLUSH + */ + MDP_OUTP(MDP_BASE + 0x18000, BIT(3)); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return; +} +#endif + +void mdp4_overlay_dmae_xy(struct mdp4_overlay_pipe *pipe) +{ + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + MDP_OUTP(MDP_BASE + 0xb0004, + (pipe->src_height << 16 | pipe->src_width)); + if (pipe->dma_blt_addr) { + uint32 off, bpp; +#ifdef BLT_RGB565 + bpp = 2; /* overlay ouput is RGB565 */ +#else + bpp = 3; /* overlay ouput is RGB888 */ +#endif + off = 0; + if (pipe->ov_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + MDP_OUTP(MDP_BASE + 0xb0008, pipe->dma_blt_addr + off); + /* RGB888, output of overlay blending */ + MDP_OUTP(MDP_BASE + 0xb000c, pipe->src_width * bpp); + } else { + /* dma_e source */ + MDP_OUTP(MDP_BASE + 0xb0008, pipe->srcp0_addr); + MDP_OUTP(MDP_BASE + 0xb000c, pipe->srcp0_ystride); + } + /* dma_e dest */ + MDP_OUTP(MDP_BASE + 0xb0010, (pipe->dst_y << 16 | pipe->dst_x)); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_overlay_dmap_cfg(struct msm_fb_data_type *mfd, int lcdc) +{ + uint32 dma2_cfg_reg; + uint32 mask, curr; + + dma2_cfg_reg = DMA_DITHER_EN; +#ifdef BLT_RGB565 + /* RGB888 is 0 */ + dma2_cfg_reg |= DMA_BUF_FORMAT_RGB565; /* blt only */ +#endif + + if (mfd->fb_imgType == MDP_BGR_565) + dma2_cfg_reg |= DMA_PACK_PATTERN_BGR; + else + dma2_cfg_reg |= DMA_PACK_PATTERN_RGB; + + + if (mfd->panel_info.bpp == 18) { + dma2_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */ + DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + } else if (mfd->panel_info.bpp == 16) { + dma2_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */ + DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + } else { + dma2_cfg_reg |= DMA_DSTC0G_8BITS | /* 888 16BPP */ + DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + +#ifndef CONFIG_FB_MSM_LCDC_CHIMEI_WXGA_PANEL + if (lcdc) + dma2_cfg_reg |= DMA_PACK_ALIGN_MSB; +#endif + + /* dma2 config register */ + curr = inpdw(MDP_BASE + 0x90000); + mask = 0x0FFFFFFF; + dma2_cfg_reg = (dma2_cfg_reg & mask) | (curr & ~mask); + MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +/* + * mdp4_overlay_dmap_xy: called form baselayer only + */ +void mdp4_overlay_dmap_xy(struct mdp4_overlay_pipe *pipe) +{ + uint32 off, bpp; + + if (!in_interrupt()) + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + if (pipe->dma_blt_addr) { +#ifdef BLT_RGB565 + bpp = 2; /* overlay ouput is RGB565 */ +#else + bpp = 3; /* overlay ouput is RGB888 */ +#endif + off = 0; + if (pipe->dmap_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + MDP_OUTP(MDP_BASE + 0x90008, pipe->dma_blt_addr + off); + /* RGB888, output of overlay blending */ + MDP_OUTP(MDP_BASE + 0x9000c, pipe->src_width * bpp); + } else { + MDP_OUTP(MDP_BASE + 0x90008, pipe->srcp0_addr); + MDP_OUTP(MDP_BASE + 0x9000c, pipe->srcp0_ystride); + } + /* dma_p source */ + MDP_OUTP(MDP_BASE + 0x90004, + (pipe->src_height << 16 | pipe->src_width)); + + /* dma_p dest */ + MDP_OUTP(MDP_BASE + 0x90010, (pipe->dst_y << 16 | pipe->dst_x)); + + if (!in_interrupt()) + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +#define MDP4_VG_PHASE_STEP_DEFAULT 0x20000000 +#define MDP4_VG_PHASE_STEP_SHIFT 29 + +static int mdp4_leading_0(uint32 num) +{ + uint32 bit = 0x80000000; + int i; + + for (i = 0; i < 32; i++) { + if (bit & num) + return i; + bit >>= 1; + } + + return i; +} + +static uint32 mdp4_scale_phase_step(int f_num, uint32 src, uint32 dst) +{ + uint32 val, s; + int n; + + n = mdp4_leading_0(src); + if (n > f_num) + n = f_num; + s = src << n; /* maximum to reduce lose of resolution */ + val = s / dst; + if (n < f_num) { + n = f_num - n; + val <<= n; + val |= ((s % dst) << n) / dst; + } + + return val; +} + +static void mdp4_scale_setup(struct mdp4_overlay_pipe *pipe) +{ + pipe->phasex_step = MDP4_VG_PHASE_STEP_DEFAULT; + pipe->phasey_step = MDP4_VG_PHASE_STEP_DEFAULT; + + if (pipe->dst_h && pipe->src_h != pipe->dst_h) { + u32 upscale_max; + upscale_max = (mdp_rev >= MDP_REV_41) ? + MDP4_REV41_OR_LATER_UP_SCALING_MAX : + MDP4_REV40_UP_SCALING_MAX; + if (pipe->dst_h > pipe->src_h * upscale_max) + return; + + pipe->op_mode |= MDP4_OP_SCALEY_EN; + + if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) { + if (pipe->flags & MDP_BACKEND_COMPOSITION && + pipe->alpha_enable && pipe->dst_h > pipe->src_h) + pipe->op_mode |= MDP4_OP_SCALEY_PIXEL_RPT; + else if (pipe->dst_h <= (pipe->src_h / 4)) + pipe->op_mode |= MDP4_OP_SCALEY_MN_PHASE; + else + pipe->op_mode |= MDP4_OP_SCALEY_FIR; + } else { /* RGB pipe */ + pipe->op_mode |= MDP4_OP_SCALE_RGB_ENHANCED | + MDP4_OP_SCALE_RGB_BILINEAR | + MDP4_OP_SCALE_ALPHA_BILINEAR; + } + + pipe->phasey_step = mdp4_scale_phase_step(29, + pipe->src_h, pipe->dst_h); + } + + if (pipe->dst_w && pipe->src_w != pipe->dst_w) { + u32 upscale_max; + upscale_max = (mdp_rev >= MDP_REV_41) ? + MDP4_REV41_OR_LATER_UP_SCALING_MAX : + MDP4_REV40_UP_SCALING_MAX; + + if (pipe->dst_w > pipe->src_w * upscale_max) + return; + pipe->op_mode |= MDP4_OP_SCALEX_EN; + if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) { + if (pipe->flags & MDP_BACKEND_COMPOSITION && + pipe->alpha_enable && pipe->dst_w > pipe->src_w) + pipe->op_mode |= MDP4_OP_SCALEX_PIXEL_RPT; + else if (pipe->dst_w <= (pipe->src_w / 4)) + pipe->op_mode |= MDP4_OP_SCALEX_MN_PHASE; + else + pipe->op_mode |= MDP4_OP_SCALEX_FIR; + } else { /* RGB pipe */ + pipe->op_mode |= MDP4_OP_SCALE_RGB_ENHANCED | + MDP4_OP_SCALE_RGB_BILINEAR | + MDP4_OP_SCALE_ALPHA_BILINEAR; + } + + pipe->phasex_step = mdp4_scale_phase_step(29, + pipe->src_w, pipe->dst_w); + } +} + +void mdp4_overlay_rgb_setup(struct mdp4_overlay_pipe *pipe) +{ + char *rgb_base; + uint32 src_size, src_xy, dst_size, dst_xy; + uint32 format, pattern; + uint32 curr, mask; + uint32 offset = 0; + int pnum; + + pnum = pipe->pipe_num - OVERLAY_PIPE_RGB1; /* start from 0 */ + rgb_base = MDP_BASE + MDP4_RGB_BASE; + rgb_base += (MDP4_RGB_OFF * pnum); + + src_size = ((pipe->src_h << 16) | pipe->src_w); + src_xy = ((pipe->src_y << 16) | pipe->src_x); + dst_size = ((pipe->dst_h << 16) | pipe->dst_w); + dst_xy = ((pipe->dst_y << 16) | pipe->dst_x); + + if ((pipe->src_x + pipe->src_w) > 0x7FF) { + offset += pipe->src_x * pipe->bpp; + src_xy &= 0xFFFF0000; + } + + if ((pipe->src_y + pipe->src_h) > 0x7FF) { + offset += pipe->src_y * pipe->src_width * pipe->bpp; + src_xy &= 0x0000FFFF; + } + + format = mdp4_overlay_format(pipe); + pattern = mdp4_overlay_unpack_pattern(pipe); + +#ifdef MDP4_IGC_LUT_ENABLE + pipe->op_mode |= MDP4_OP_IGC_LUT_EN; +#endif + + mdp4_scale_setup(pipe); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* Ensure proper covert matrix loaded when color space swaps */ + curr = inpdw(rgb_base + 0x0058); + /* Don't touch bits you don't want to configure*/ + mask = 0xFFFEFFFF; + pipe->op_mode = (pipe->op_mode & mask) | (curr & ~mask); + + outpdw(rgb_base + 0x0000, src_size); /* MDP_RGB_SRC_SIZE */ + outpdw(rgb_base + 0x0004, src_xy); /* MDP_RGB_SRC_XY */ + outpdw(rgb_base + 0x0008, dst_size); /* MDP_RGB_DST_SIZE */ + outpdw(rgb_base + 0x000c, dst_xy); /* MDP_RGB_DST_XY */ + + outpdw(rgb_base + 0x0010, pipe->srcp0_addr + offset); + outpdw(rgb_base + 0x0040, pipe->srcp0_ystride); + + outpdw(rgb_base + 0x0050, format);/* MDP_RGB_SRC_FORMAT */ + outpdw(rgb_base + 0x0054, pattern);/* MDP_RGB_SRC_UNPACK_PATTERN */ + if (format & MDP4_FORMAT_SOLID_FILL) { + u32 op_mode = pipe->op_mode; + op_mode &= ~(MDP4_OP_FLIP_LR + MDP4_OP_SCALEX_EN); + op_mode &= ~(MDP4_OP_FLIP_UD + MDP4_OP_SCALEY_EN); + outpdw(rgb_base + 0x0058, op_mode);/* MDP_RGB_OP_MODE */ + } else + outpdw(rgb_base + 0x0058, pipe->op_mode);/* MDP_RGB_OP_MODE */ + outpdw(rgb_base + 0x005c, pipe->phasex_step); + outpdw(rgb_base + 0x0060, pipe->phasey_step); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + mdp4_stat.pipe[pipe->pipe_num]++; +} + + +static void mdp4_overlay_vg_get_src_offset(struct mdp4_overlay_pipe *pipe, + char *vg_base, uint32 *luma_off, uint32 *chroma_off) +{ + uint32 src_xy; + *luma_off = 0; + *chroma_off = 0; + + if (pipe->src_x && (pipe->frame_format == + MDP4_FRAME_FORMAT_LINEAR)) { + src_xy = (pipe->src_y << 16) | pipe->src_x; + src_xy &= 0xffff0000; + outpdw(vg_base + 0x0004, src_xy); /* MDP_RGB_SRC_XY */ + + switch (pipe->src_format) { + case MDP_Y_CR_CB_H2V2: + case MDP_Y_CR_CB_GH2V2: + case MDP_Y_CB_CR_H2V2: + *luma_off = pipe->src_x; + *chroma_off = pipe->src_x/2; + break; + + case MDP_Y_CBCR_H2V2_TILE: + case MDP_Y_CRCB_H2V2_TILE: + case MDP_Y_CBCR_H2V2: + case MDP_Y_CRCB_H2V2: + case MDP_Y_CRCB_H1V1: + case MDP_Y_CBCR_H1V1: + case MDP_Y_CRCB_H2V1: + case MDP_Y_CBCR_H2V1: + *luma_off = pipe->src_x; + *chroma_off = pipe->src_x; + break; + + case MDP_YCRYCB_H2V1: + if (pipe->src_x & 0x1) + pipe->src_x += 1; + *luma_off += pipe->src_x * 2; + break; + + case MDP_ARGB_8888: + case MDP_RGBA_8888: + case MDP_BGRA_8888: + case MDP_RGBX_8888: + case MDP_RGB_565: + case MDP_BGR_565: + case MDP_XRGB_8888: + case MDP_RGB_888: + case MDP_YCBCR_H1V1: + case MDP_YCRCB_H1V1: + *luma_off = pipe->src_x * pipe->bpp; + break; + + default: + pr_err("%s: fmt %u not supported for adjustment\n", + __func__, pipe->src_format); + break; + } + } +} + +void mdp4_overlay_vg_setup(struct mdp4_overlay_pipe *pipe) +{ + char *vg_base; + uint32 frame_size, src_size, src_xy, dst_size, dst_xy; + uint32 format, pattern, luma_offset, chroma_offset; + uint32 mask, curr, addr; + int pnum, ptype; + + pnum = pipe->pipe_num - OVERLAY_PIPE_VG1; /* start from 0 */ + vg_base = MDP_BASE + MDP4_VIDEO_BASE; + vg_base += (MDP4_VIDEO_OFF * pnum); + + frame_size = ((pipe->src_height << 16) | pipe->src_width); + src_size = ((pipe->src_h << 16) | pipe->src_w); + src_xy = ((pipe->src_y << 16) | pipe->src_x); + dst_size = ((pipe->dst_h << 16) | pipe->dst_w); + dst_xy = ((pipe->dst_y << 16) | pipe->dst_x); + + ptype = mdp4_overlay_format2type(pipe->src_format); + format = mdp4_overlay_format(pipe); + pattern = mdp4_overlay_unpack_pattern(pipe); + + /* not RGB use VG pipe, pure VG pipe */ + pipe->op_mode |= MDP4_OP_CSC_EN; + if (ptype != OVERLAY_TYPE_RGB) + pipe->op_mode |= MDP4_OP_SRC_DATA_YCBCR; + +#ifdef MDP4_IGC_LUT_ENABLE + pipe->op_mode |= MDP4_OP_IGC_LUT_EN; +#endif + + mdp4_scale_setup(pipe); + + luma_offset = 0; + chroma_offset = 0; + + if (ptype == OVERLAY_TYPE_RGB) { + if ((pipe->src_y + pipe->src_h) > 0x7FF) { + luma_offset = pipe->src_y * pipe->src_width * pipe->bpp; + src_xy &= 0x0000FFFF; + } + + if ((pipe->src_x + pipe->src_w) > 0x7FF) { + luma_offset += pipe->src_x * pipe->bpp; + src_xy &= 0xFFFF0000; + } + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + outpdw(vg_base + 0x0000, src_size); /* MDP_RGB_SRC_SIZE */ + outpdw(vg_base + 0x0004, src_xy); /* MDP_RGB_SRC_XY */ + outpdw(vg_base + 0x0008, dst_size); /* MDP_RGB_DST_SIZE */ + outpdw(vg_base + 0x000c, dst_xy); /* MDP_RGB_DST_XY */ + + if (pipe->frame_format != MDP4_FRAME_FORMAT_LINEAR) + outpdw(vg_base + 0x0048, frame_size); /* TILE frame size */ + + /* + * Adjust src X offset to avoid MDP from overfetching pixels + * present before the offset. This is required for video + * frames coming with unused green pixels along the left margin + */ + /* not RGB use VG pipe, pure VG pipe */ + if (ptype != OVERLAY_TYPE_RGB) { + mdp4_overlay_vg_get_src_offset(pipe, vg_base, &luma_offset, + &chroma_offset); + } + + /* Ensure proper covert matrix loaded when color space swaps */ + curr = inpdw(vg_base + 0x0058); + mask = 0x600; + + if ((curr & mask) != (pipe->op_mode & mask)) { + addr = ((uint32_t)vg_base) + 0x4000; + if (ptype != OVERLAY_TYPE_RGB) + mdp4_csc_write(&(mdp_csc_convert[1]), addr); + else + mdp4_csc_write(&(mdp_csc_convert[0]), addr); + + mask = 0xFFFCFFFF; + } else { + /* Don't touch bits you don't want to configure*/ + mask = 0xFFFCF1FF; + } + pipe->op_mode = (pipe->op_mode & mask) | (curr & ~mask); + + /* luma component plane */ + outpdw(vg_base + 0x0010, pipe->srcp0_addr + luma_offset); + + /* chroma component plane or planar color 1 */ + outpdw(vg_base + 0x0014, pipe->srcp1_addr + chroma_offset); + + /* planar color 2 */ + outpdw(vg_base + 0x0018, pipe->srcp2_addr + chroma_offset); + + outpdw(vg_base + 0x0040, + pipe->srcp1_ystride << 16 | pipe->srcp0_ystride); + + outpdw(vg_base + 0x0044, + pipe->srcp3_ystride << 16 | pipe->srcp2_ystride); + + outpdw(vg_base + 0x0050, format); /* MDP_RGB_SRC_FORMAT */ + outpdw(vg_base + 0x0054, pattern); /* MDP_RGB_SRC_UNPACK_PATTERN */ + if (format & MDP4_FORMAT_SOLID_FILL) { + u32 op_mode = pipe->op_mode; + op_mode &= ~(MDP4_OP_FLIP_LR + MDP4_OP_SCALEX_EN); + op_mode &= ~(MDP4_OP_FLIP_UD + MDP4_OP_SCALEY_EN); + outpdw(vg_base + 0x0058, op_mode);/* MDP_RGB_OP_MODE */ + } else + outpdw(vg_base + 0x0058, pipe->op_mode);/* MDP_RGB_OP_MODE */ + outpdw(vg_base + 0x005c, pipe->phasex_step); + outpdw(vg_base + 0x0060, pipe->phasey_step); + + if (pipe->op_mode & MDP4_OP_DITHER_EN) { + outpdw(vg_base + 0x0068, + pipe->r_bit << 4 | pipe->b_bit << 2 | pipe->g_bit); + } + + if (pipe->flags & MDP_SHARPENING) { + outpdw(vg_base + 0x8200, + mdp4_ss_table_value(pipe->req_data.dpp.sharp_strength, + 0)); + outpdw(vg_base + 0x8204, + mdp4_ss_table_value(pipe->req_data.dpp.sharp_strength, + 1)); + } + + if (mdp_rev > MDP_REV_41) { + /* mdp chip select controller */ + mask = 0; + if (pipe->pipe_num == OVERLAY_PIPE_VG1) + mask = 0x020; /* bit 5 */ + else if (pipe->pipe_num == OVERLAY_PIPE_VG2) + mask = 0x02000; /* bit 13 */ + if (mask) { + if (pipe->op_mode & MDP4_OP_SCALEY_MN_PHASE) + ctrl->cs_controller &= ~mask; + else + ctrl->cs_controller |= mask; + /* NOT double buffered */ + outpdw(MDP_BASE + 0x00c0, ctrl->cs_controller); + } + } + + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + mdp4_stat.pipe[pipe->pipe_num]++; +} + +int mdp4_overlay_format2type(uint32 format) +{ + switch (format) { + case MDP_RGB_565: + case MDP_RGB_888: + case MDP_BGR_565: + case MDP_XRGB_8888: + case MDP_ARGB_8888: + case MDP_RGBA_8888: + case MDP_BGRA_8888: + case MDP_RGBX_8888: + return OVERLAY_TYPE_RGB; + case MDP_YCRYCB_H2V1: + case MDP_Y_CRCB_H2V1: + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V2: + case MDP_Y_CBCR_H2V2: + case MDP_Y_CBCR_H2V2_TILE: + case MDP_Y_CRCB_H2V2_TILE: + case MDP_Y_CR_CB_H2V2: + case MDP_Y_CR_CB_GH2V2: + case MDP_Y_CB_CR_H2V2: + case MDP_Y_CRCB_H1V1: + case MDP_Y_CBCR_H1V1: + case MDP_YCRCB_H1V1: + case MDP_YCBCR_H1V1: + return OVERLAY_TYPE_VIDEO; + case MDP_RGB_BORDERFILL: + return OVERLAY_TYPE_BF; + default: + mdp4_stat.err_format++; + return -ERANGE; + } + +} + +#define C3_ALPHA 3 /* alpha */ +#define C2_R_Cr 2 /* R/Cr */ +#define C1_B_Cb 1 /* B/Cb */ +#define C0_G_Y 0 /* G/luma */ +#define YUV_444_MAX_WIDTH 1280 /* Max width for YUV 444*/ + +int mdp4_overlay_format2pipe(struct mdp4_overlay_pipe *pipe) +{ + switch (pipe->src_format) { + case MDP_RGB_565: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 0; + pipe->r_bit = 1; /* R, 5 bits */ + pipe->b_bit = 1; /* B, 5 bits */ + pipe->g_bit = 2; /* G, 6 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 2; + pipe->element2 = C2_R_Cr; /* R */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C1_B_Cb; /* B */ + pipe->bpp = 2; /* 2 bpp */ + break; + case MDP_RGB_888: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 0; + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 2; + pipe->element2 = C1_B_Cb; /* B */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C2_R_Cr; /* R */ + pipe->bpp = 3; /* 3 bpp */ + break; + case MDP_BGR_565: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 0; + pipe->r_bit = 1; /* R, 5 bits */ + pipe->b_bit = 1; /* B, 5 bits */ + pipe->g_bit = 2; /* G, 6 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 2; + pipe->element2 = C1_B_Cb; /* B */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C2_R_Cr; /* R */ + pipe->bpp = 2; /* 2 bpp */ + break; + case MDP_XRGB_8888: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 3; /* alpha, 4 bits */ + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 3; + pipe->element3 = C1_B_Cb; /* B */ + pipe->element2 = C0_G_Y; /* G */ + pipe->element1 = C2_R_Cr; /* R */ + pipe->element0 = C3_ALPHA; /* alpha */ + pipe->bpp = 4; /* 4 bpp */ + break; + case MDP_ARGB_8888: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 3; /* alpha, 4 bits */ + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 1; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 3; + pipe->element3 = C1_B_Cb; /* B */ + pipe->element2 = C0_G_Y; /* G */ + pipe->element1 = C2_R_Cr; /* R */ + pipe->element0 = C3_ALPHA; /* alpha */ + pipe->bpp = 4; /* 4 bpp */ + break; + case MDP_RGBA_8888: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 3; /* alpha, 4 bits */ + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 1; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 3; + pipe->element3 = C3_ALPHA; /* alpha */ + pipe->element2 = C1_B_Cb; /* B */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C2_R_Cr; /* R */ + pipe->bpp = 4; /* 4 bpp */ + break; + case MDP_RGBX_8888: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 3; + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 3; + pipe->element3 = C3_ALPHA; /* alpha */ + pipe->element2 = C1_B_Cb; /* B */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C2_R_Cr; /* R */ + pipe->bpp = 4; /* 4 bpp */ + break; + case MDP_BGRA_8888: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 3; /* alpha, 4 bits */ + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 1; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 3; + pipe->element3 = C3_ALPHA; /* alpha */ + pipe->element2 = C2_R_Cr; /* R */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C1_B_Cb; /* B */ + pipe->bpp = 4; /* 4 bpp */ + break; + case MDP_YCRYCB_H2V1: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 0; /* alpha, 4 bits */ + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 3; + pipe->element3 = C0_G_Y; /* G */ + pipe->element2 = C2_R_Cr; /* R */ + pipe->element1 = C0_G_Y; /* G */ + pipe->element0 = C1_B_Cb; /* B */ + pipe->bpp = 2; /* 2 bpp */ + pipe->chroma_sample = MDP4_CHROMA_H2V1; + break; + case MDP_Y_CRCB_H2V1: + case MDP_Y_CBCR_H2V1: + case MDP_Y_CRCB_H2V2: + case MDP_Y_CBCR_H2V2: + case MDP_Y_CRCB_H1V1: + case MDP_Y_CBCR_H1V1: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_PSEUDO_PLANAR; + pipe->a_bit = 0; + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 1; /* 2 */ + if (pipe->src_format == MDP_Y_CRCB_H2V1) { + pipe->element1 = C1_B_Cb; + pipe->element0 = C2_R_Cr; + pipe->chroma_sample = MDP4_CHROMA_H2V1; + } else if (pipe->src_format == MDP_Y_CRCB_H1V1) { + pipe->element1 = C1_B_Cb; + pipe->element0 = C2_R_Cr; + if (pipe->src_width > YUV_444_MAX_WIDTH) + pipe->chroma_sample = MDP4_CHROMA_H1V2; + else + pipe->chroma_sample = MDP4_CHROMA_RGB; + } else if (pipe->src_format == MDP_Y_CBCR_H2V1) { + pipe->element1 = C2_R_Cr; + pipe->element0 = C1_B_Cb; + pipe->chroma_sample = MDP4_CHROMA_H2V1; + } else if (pipe->src_format == MDP_Y_CBCR_H1V1) { + pipe->element1 = C2_R_Cr; + pipe->element0 = C1_B_Cb; + if (pipe->src_width > YUV_444_MAX_WIDTH) + pipe->chroma_sample = MDP4_CHROMA_H1V2; + else + pipe->chroma_sample = MDP4_CHROMA_RGB; + } else if (pipe->src_format == MDP_Y_CRCB_H2V2) { + pipe->element1 = C1_B_Cb; + pipe->element0 = C2_R_Cr; + pipe->chroma_sample = MDP4_CHROMA_420; + } else if (pipe->src_format == MDP_Y_CBCR_H2V2) { + pipe->element1 = C2_R_Cr; + pipe->element0 = C1_B_Cb; + pipe->chroma_sample = MDP4_CHROMA_420; + } + pipe->bpp = 2; /* 2 bpp */ + break; + case MDP_Y_CBCR_H2V2_TILE: + case MDP_Y_CRCB_H2V2_TILE: + pipe->frame_format = MDP4_FRAME_FORMAT_VIDEO_SUPERTILE; + pipe->fetch_plane = OVERLAY_PLANE_PSEUDO_PLANAR; + pipe->a_bit = 0; + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 1; /* 2 */ + if (pipe->src_format == MDP_Y_CRCB_H2V2_TILE) { + pipe->element1 = C1_B_Cb; /* B */ + pipe->element0 = C2_R_Cr; /* R */ + pipe->chroma_sample = MDP4_CHROMA_420; + } else if (pipe->src_format == MDP_Y_CBCR_H2V2_TILE) { + pipe->element1 = C2_R_Cr; /* R */ + pipe->element0 = C1_B_Cb; /* B */ + pipe->chroma_sample = MDP4_CHROMA_420; + } + pipe->bpp = 2; /* 2 bpp */ + break; + case MDP_Y_CR_CB_H2V2: + case MDP_Y_CR_CB_GH2V2: + case MDP_Y_CB_CR_H2V2: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_PLANAR; + pipe->a_bit = 0; + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->chroma_sample = MDP4_CHROMA_420; + pipe->bpp = 2; /* 2 bpp */ + break; + case MDP_YCBCR_H1V1: + case MDP_YCRCB_H1V1: + pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR; + pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED; + pipe->a_bit = 0; + pipe->r_bit = 3; /* R, 8 bits */ + pipe->b_bit = 3; /* B, 8 bits */ + pipe->g_bit = 3; /* G, 8 bits */ + pipe->alpha_enable = 0; + pipe->unpack_tight = 1; + pipe->unpack_align_msb = 0; + pipe->unpack_count = 2; + pipe->element0 = C0_G_Y; /* G */ + if (pipe->src_format == MDP_YCRCB_H1V1) { + pipe->element1 = C2_R_Cr; /* R */ + pipe->element2 = C1_B_Cb; /* B */ + } else { + pipe->element1 = C1_B_Cb; /* B */ + pipe->element2 = C2_R_Cr; /* R */ + } + pipe->bpp = 3; /* 3 bpp */ + case MDP_RGB_BORDERFILL: + pipe->alpha_enable = 0; + pipe->alpha = 0; + break; + default: + /* not likely */ + mdp4_stat.err_format++; + return -ERANGE; + } + + return 0; +} + +/* + * color_key_convert: output with 12 bits color key + */ +static uint32 color_key_convert(int start, int num, uint32 color) +{ + uint32 data; + + data = (color >> start) & ((1 << num) - 1); + + /* convert to 8 bits */ + if (num == 5) + data = ((data << 3) | (data >> 2)); + else if (num == 6) + data = ((data << 2) | (data >> 4)); + + /* convert 8 bits to 12 bits */ + data = (data << 4) | (data >> 4); + + return data; +} + +void transp_color_key(int format, uint32 transp, + uint32 *c0, uint32 *c1, uint32 *c2) +{ + int b_start, g_start, r_start; + int b_num, g_num, r_num; + + switch (format) { + case MDP_RGB_565: + b_start = 0; + g_start = 5; + r_start = 11; + r_num = 5; + g_num = 6; + b_num = 5; + break; + case MDP_RGB_888: + case MDP_XRGB_8888: + case MDP_ARGB_8888: + case MDP_BGRA_8888: + b_start = 0; + g_start = 8; + r_start = 16; + r_num = 8; + g_num = 8; + b_num = 8; + break; + case MDP_RGBA_8888: + case MDP_RGBX_8888: + b_start = 16; + g_start = 8; + r_start = 0; + r_num = 8; + g_num = 8; + b_num = 8; + break; + case MDP_BGR_565: + b_start = 11; + g_start = 5; + r_start = 0; + r_num = 5; + g_num = 6; + b_num = 5; + break; + case MDP_Y_CB_CR_H2V2: + case MDP_Y_CBCR_H2V2: + case MDP_Y_CBCR_H2V1: + case MDP_YCBCR_H1V1: + b_start = 8; + g_start = 16; + r_start = 0; + r_num = 8; + g_num = 8; + b_num = 8; + break; + case MDP_Y_CR_CB_H2V2: + case MDP_Y_CR_CB_GH2V2: + case MDP_Y_CRCB_H2V2: + case MDP_Y_CRCB_H2V1: + case MDP_Y_CRCB_H1V1: + case MDP_Y_CBCR_H1V1: + case MDP_YCRCB_H1V1: + b_start = 0; + g_start = 16; + r_start = 8; + r_num = 8; + g_num = 8; + b_num = 8; + break; + default: + b_start = 0; + g_start = 8; + r_start = 16; + r_num = 8; + g_num = 8; + b_num = 8; + break; + } + + *c0 = color_key_convert(g_start, g_num, transp); + *c1 = color_key_convert(b_start, b_num, transp); + *c2 = color_key_convert(r_start, r_num, transp); +} + +uint32 mdp4_overlay_format(struct mdp4_overlay_pipe *pipe) +{ + uint32 format; + + format = 0; + + if (pipe->solid_fill) + format |= MDP4_FORMAT_SOLID_FILL; + + if (pipe->unpack_align_msb) + format |= MDP4_FORMAT_UNPACK_ALIGN_MSB; + + if (pipe->unpack_tight) + format |= MDP4_FORMAT_UNPACK_TIGHT; + + if (pipe->alpha_enable) + format |= MDP4_FORMAT_ALPHA_ENABLE; + + if (pipe->flags & MDP_SOURCE_ROTATED_90) + format |= MDP4_FORMAT_90_ROTATED; + format |= (pipe->unpack_count << 13); + format |= ((pipe->bpp - 1) << 9); + format |= (pipe->a_bit << 6); + format |= (pipe->r_bit << 4); + format |= (pipe->b_bit << 2); + format |= pipe->g_bit; + + format |= (pipe->frame_format << 29); + + if (pipe->fetch_plane == OVERLAY_PLANE_PSEUDO_PLANAR || + pipe->fetch_plane == OVERLAY_PLANE_PLANAR) { + /* video/graphic */ + format |= (pipe->fetch_plane << 19); + format |= (pipe->chroma_site << 28); + format |= (pipe->chroma_sample << 26); + } + + return format; +} + +uint32 mdp4_overlay_unpack_pattern(struct mdp4_overlay_pipe *pipe) +{ + return (pipe->element3 << 24) | (pipe->element2 << 16) | + (pipe->element1 << 8) | pipe->element0; +} + +/* + * mdp4_overlayproc_cfg: only be called from base layer + */ +void mdp4_overlayproc_cfg(struct mdp4_overlay_pipe *pipe) +{ + uint32 data, intf; + char *overlay_base; + uint32 curr; + + intf = 0; + if (pipe->mixer_num == MDP4_MIXER2) + overlay_base = MDP_BASE + MDP4_OVERLAYPROC2_BASE; + else if (pipe->mixer_num == MDP4_MIXER1) { + overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */ + intf = inpdw(MDP_BASE + 0x0038); /* MDP_DISP_INTF_SEL */ + intf >>= 4; + intf &= 0x03; + } else + overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */ + + if (!in_interrupt()) + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* + * BLT support both primary and external external + */ + if (pipe->ov_blt_addr) { + int off, bpp; +#ifdef BLT_RGB565 + bpp = 2; /* overlay ouput is RGB565 */ +#else + bpp = 3; /* overlay ouput is RGB888 */ +#endif + data = pipe->src_height; + data <<= 16; + data |= pipe->src_width; + outpdw(overlay_base + 0x0008, data); /* ROI, height + width */ + if (pipe->mixer_num == MDP4_MIXER0 || + pipe->mixer_num == MDP4_MIXER1) { + off = 0; + if (pipe->ov_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + + outpdw(overlay_base + 0x000c, pipe->ov_blt_addr + off); + /* overlay ouput is RGB888 */ + outpdw(overlay_base + 0x0010, pipe->src_width * bpp); + outpdw(overlay_base + 0x001c, pipe->ov_blt_addr + off); + /* MDDI - BLT + on demand */ + outpdw(overlay_base + 0x0004, 0x08); + + curr = inpdw(overlay_base + 0x0014); + curr &= 0x4; +#ifdef BLT_RGB565 + outpdw(overlay_base + 0x0014, curr | 0x1); /* RGB565 */ +#else + outpdw(overlay_base + 0x0014, curr | 0x0); /* RGB888 */ +#endif + } else if (pipe->mixer_num == MDP4_MIXER2) { + if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) { + off = 0; + bpp = 1; + if (pipe->ov_cnt & 0x01) + off = pipe->src_height * + pipe->src_width * bpp; + + outpdw(overlay_base + 0x000c, + pipe->ov_blt_addr + off); + /* overlay ouput is RGB888 */ + outpdw(overlay_base + 0x0010, + ((pipe->src_width << 16) | + pipe->src_width)); + outpdw(overlay_base + 0x001c, + pipe->ov_blt_addr + off); + off = pipe->src_height * pipe->src_width; + /* align chroma to 2k address */ + off = (off + 2047) & ~2047; + /* UV plane adress */ + outpdw(overlay_base + 0x0020, + pipe->ov_blt_addr + off); + /* MDDI - BLT + on demand */ + outpdw(overlay_base + 0x0004, 0x08); + /* pseudo planar + writeback */ + curr = inpdw(overlay_base + 0x0014); + curr &= 0x4; + outpdw(overlay_base + 0x0014, curr | 0x012); + /* rgb->yuv */ + outpdw(overlay_base + 0x0200, 0x05); + } + } + } else { + data = pipe->src_height; + data <<= 16; + data |= pipe->src_width; + outpdw(overlay_base + 0x0008, data); /* ROI, height + width */ + outpdw(overlay_base + 0x000c, pipe->srcp0_addr); + outpdw(overlay_base + 0x0010, pipe->srcp0_ystride); + outpdw(overlay_base + 0x0004, 0x01); /* directout */ + } + + if (pipe->mixer_num == MDP4_MIXER1) { + if (intf == TV_INTF) { + curr = inpdw(overlay_base + 0x0014); + curr &= 0x4; + outpdw(overlay_base + 0x0014, 0x02); /* yuv422 */ + /* overlay1 CSC config */ + outpdw(overlay_base + 0x0200, 0x05); /* rgb->yuv */ + } + } + +#ifdef MDP4_IGC_LUT_ENABLE + curr = inpdw(overlay_base + 0x0014); + curr &= ~0x4; + outpdw(overlay_base + 0x0014, curr | 0x4); /* GC_LUT_EN, 888 */ +#endif + + if (!in_interrupt()) + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +int mdp4_overlay_pipe_staged(int mixer) +{ + uint32 data, mask, i, off; + int p1, p2; + + if (mixer == MDP4_MIXER2) + off = 0x100F0; + else + off = 0x10100; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + data = inpdw(MDP_BASE + off); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + p1 = 0; + p2 = 0; + for (i = 0; i < 8; i++) { + mask = data & 0x0f; + if (mask) { + if (mask <= 4) + p1++; + else + p2++; + } + data >>= 4; + } + + if (mixer) + return p2; + else + return p1; +} + +int mdp4_mixer_info(int mixer_num, struct mdp_mixer_info *info) +{ + + int ndx, cnt; + struct mdp4_overlay_pipe *pipe; + + if (mixer_num > MDP4_MIXER_MAX) + return -ENODEV; + + cnt = 0; + ndx = MDP4_MIXER_STAGE_BASE; + for ( ; ndx < MDP4_MIXER_STAGE_MAX; ndx++) { + pipe = ctrl->stage[mixer_num][ndx]; + if (pipe == NULL) + continue; + info->z_order = pipe->mixer_stage - MDP4_MIXER_STAGE0; + /* z_order == -1, means base layer */ + info->ptype = pipe->pipe_type; + info->pnum = pipe->pipe_num; + info->pndx = pipe->pipe_ndx; + info->mixer_num = pipe->mixer_num; + info++; + cnt++; + } + return cnt; +} + +void mdp4_mixer_stage_commit(int mixer) +{ + struct mdp4_overlay_pipe *pipe; + int i, num; + u32 data, stage; + int off; + unsigned long flags; + + data = 0; + for (i = MDP4_MIXER_STAGE_BASE; i < MDP4_MIXER_STAGE_MAX; i++) { + pipe = ctrl->stage[mixer][i]; + if (pipe == NULL) + continue; + pr_debug("%s: mixer=%d ndx=%d stage=%d\n", __func__, + mixer, pipe->pipe_ndx, i); + stage = pipe->mixer_stage; + if (mixer >= MDP4_MIXER1) + stage += 8; + stage <<= (4 * pipe->pipe_num); + data |= stage; + } + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_clk_ctrl(1); + mdp4_mixer_blend_setup(mixer); + + off = 0; + if (data != ctrl->mixer_cfg[mixer]) { + ctrl->mixer_cfg[mixer] = data; + if (mixer >= MDP4_MIXER2) { + /* MDP_LAYERMIXER2_IN_CFG */ + off = 0x100f0; + } else { + /* mixer 0 or 1 */ + num = mixer + 1; + num &= 0x01; + data |= ctrl->mixer_cfg[num]; + off = 0x10100; + } + pr_debug("%s: mixer=%d data=%x flush=%x pid=%d\n", __func__, + mixer, data, ctrl->flush[mixer], current->pid); + } + + local_irq_save(flags); + if (off) + outpdw(MDP_BASE + off, data); + + if (ctrl->flush[mixer]) { + outpdw(MDP_BASE + 0x18000, ctrl->flush[mixer]); + ctrl->flush[mixer] = 0; + } + local_irq_restore(flags); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + mdp_clk_ctrl(0); +} + + +void mdp4_mixer_stage_up(struct mdp4_overlay_pipe *pipe, int commit) +{ + struct mdp4_overlay_pipe *pp; + int i, mixer; + + mixer = pipe->mixer_num; + + for (i = MDP4_MIXER_STAGE_BASE; i < MDP4_MIXER_STAGE_MAX; i++) { + pp = ctrl->stage[mixer][i]; + if (pp && pp->pipe_ndx == pipe->pipe_ndx) { + ctrl->stage[mixer][i] = NULL; + break; + } + } + + ctrl->stage[mixer][pipe->mixer_stage] = pipe; /* keep it */ + + if (commit) + mdp4_mixer_stage_commit(mixer); +} + +void mdp4_mixer_stage_down(struct mdp4_overlay_pipe *pipe, int commit) +{ + struct mdp4_overlay_pipe *pp; + int i, mixer; + + mixer = pipe->mixer_num; + + for (i = MDP4_MIXER_STAGE_BASE; i < MDP4_MIXER_STAGE_MAX; i++) { + pp = ctrl->stage[mixer][i]; + if (pp && pp->pipe_ndx == pipe->pipe_ndx) + ctrl->stage[mixer][i] = NULL; /* clear it */ + } + + if (commit || (mixer > 0 && !hdmi_prim_display)) + mdp4_mixer_stage_commit(mixer); +} +/* + * mixer0: rgb3: border color at register 0x15004, 0x15008 + * mixer1: vg3: border color at register 0x1D004, 0x1D008 + * mixer2: xxx: border color at register 0x8D004, 0x8D008 + */ +void mdp4_overlay_borderfill_stage_up(struct mdp4_overlay_pipe *pipe) +{ + struct mdp4_overlay_pipe *bspipe; + int ptype, pnum, pndx, mixer; + int format, alpha_enable, alpha; + + if (pipe->pipe_type != OVERLAY_TYPE_BF) + return; + + mixer = pipe->mixer_num; + + if (ctrl->baselayer[mixer]) + return; + + bspipe = ctrl->stage[mixer][MDP4_MIXER_STAGE_BASE]; + + /* + * bspipe is clone here + * get real pipe + */ + bspipe = mdp4_overlay_ndx2pipe(bspipe->pipe_ndx); + + /* save original base layer */ + ctrl->baselayer[mixer] = bspipe; + + pipe->alpha = 0; /* make sure bf pipe has alpha 0 */ + ptype = pipe->pipe_type; + pnum = pipe->pipe_num; + pndx = pipe->pipe_ndx; + format = pipe->src_format; + alpha_enable = pipe->alpha_enable; + alpha = pipe->alpha; + *pipe = *bspipe; /* keep base layer configuration */ + pipe->pipe_type = ptype; + pipe->pipe_num = pnum; + pipe->pipe_ndx = pndx; + pipe->src_format = format; + pipe->alpha_enable = alpha_enable; + pipe->alpha = alpha; + + /* free original base layer pipe to be sued as normal pipe */ + bspipe->pipe_used = 0; + + if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) + mdp4_dsi_video_base_swap(0, pipe); + else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) + mdp4_dsi_cmd_base_swap(0, pipe); + else if (ctrl->panel_mode & MDP4_PANEL_LCDC) + mdp4_lcdc_base_swap(0, pipe); + else if (ctrl->panel_mode & MDP4_PANEL_DTV) + mdp4_dtv_base_swap(0, pipe); + + mdp4_overlay_reg_flush(bspipe, 1); + /* borderfill pipe as base layer */ + mdp4_mixer_stage_up(pipe, 0); +} + +void mdp4_overlay_borderfill_stage_down(struct mdp4_overlay_pipe *pipe) +{ + struct mdp4_overlay_pipe *bspipe; + int ptype, pnum, pndx, mixer; + int format, alpha_enable, alpha; + + if (pipe->pipe_type != OVERLAY_TYPE_BF) + return; + + mixer = pipe->mixer_num; + + /* retrieve original base layer */ + bspipe = ctrl->baselayer[mixer]; + if (bspipe == NULL) { + pr_err("%s: no base layer at mixer=%d\n", + __func__, mixer); + return; + } + + ptype = bspipe->pipe_type; + pnum = bspipe->pipe_num; + pndx = bspipe->pipe_ndx; + format = bspipe->src_format; + alpha_enable = bspipe->alpha_enable; + alpha = bspipe->alpha; + *bspipe = *pipe; /* restore base layer configuration */ + bspipe->pipe_type = ptype; + bspipe->pipe_num = pnum; + bspipe->pipe_ndx = pndx; + bspipe->src_format = format; + bspipe->alpha_enable = alpha_enable; + bspipe->alpha = alpha; + + bspipe->pipe_used++; /* mark base layer pipe used */ + + ctrl->baselayer[mixer] = NULL; + + /* free borderfill pipe */ + pipe->pipe_used = 0; + + if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) + mdp4_dsi_video_base_swap(0, bspipe); + else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) + mdp4_dsi_cmd_base_swap(0, bspipe); + else if (ctrl->panel_mode & MDP4_PANEL_LCDC) + mdp4_lcdc_base_swap(0, bspipe); + else if (ctrl->panel_mode & MDP4_PANEL_DTV) + mdp4_dtv_base_swap(0, bspipe); + + /* free borderfill pipe */ + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_down(pipe, 0); /* commit will happen for bspipe up */ + mdp4_overlay_pipe_free(pipe); + + /* stage up base layer */ + mdp4_overlay_reg_flush(bspipe, 1); + /* restore original base layer */ + mdp4_mixer_stage_up(bspipe, 1); +} + + +static struct mdp4_overlay_pipe *mdp4_background_layer(int mixer, + struct mdp4_overlay_pipe *sp) +{ + struct mdp4_overlay_pipe *pp; + struct mdp4_overlay_pipe *kp; + int i; + + kp = ctrl->stage[mixer][MDP4_MIXER_STAGE_BASE]; + for (i = MDP4_MIXER_STAGE_BASE; i < MDP4_MIXER_STAGE_MAX; i++) { + pp = ctrl->stage[mixer][i]; + if (pp == NULL) + continue; + if (pp == sp) + break; + + if ((pp->dst_x <= sp->dst_x) && + ((pp->dst_x + pp->dst_w) >= (sp->dst_x + sp->dst_w))) { + if ((pp->dst_y <= sp->dst_y) && + ((pp->dst_y + pp->dst_h) >= + (sp->dst_y + sp->dst_h))) { + kp = pp; + } + } + } + return kp; +} + +static void mdp4_overlay_bg_solidfill(struct blend_cfg *blend) +{ + struct mdp4_overlay_pipe *pipe; + char *base; + u32 op_mode, format; + int pnum, ptype; + + pipe = blend->solidfill_pipe; + if (pipe == NULL) + return; + + if (pipe->pipe_type == OVERLAY_TYPE_BF) + return; + + ptype = mdp4_overlay_format2type(pipe->src_format); + if (ptype == OVERLAY_TYPE_RGB) { + pnum = pipe->pipe_num - OVERLAY_PIPE_RGB1; + base = MDP_BASE + MDP4_RGB_BASE; + base += MDP4_RGB_OFF * pnum; + } else { + pnum = pipe->pipe_num - OVERLAY_PIPE_VG1; + base = MDP_BASE + MDP4_VIDEO_BASE; + base += MDP4_VIDEO_OFF * pnum; + } + + format = inpdw(base + 0x50); + if (blend->solidfill) { + format |= MDP4_FORMAT_SOLID_FILL; + /* + * If solid fill is enabled, flip and scale + * have to be disabled. otherwise, h/w + * underruns. + */ + op_mode = inpdw(base + 0x0058); + op_mode &= ~(MDP4_OP_FLIP_LR + MDP4_OP_SCALEX_EN); + op_mode &= ~(MDP4_OP_FLIP_UD + MDP4_OP_SCALEY_EN); + outpdw(base + 0x0058, op_mode); + outpdw(base + 0x1008, 0); /* black */ + /* + * Set src size and dst size same to avoid underruns + */ + outpdw(base + 0x0000, inpdw(base + 0x0008)); + } else { + u32 src_size = ((pipe->src_h << 16) | pipe->src_w); + outpdw(base + 0x0000, src_size); + format &= ~MDP4_FORMAT_SOLID_FILL; + blend->solidfill_pipe = NULL; + } + + outpdw(base + 0x50, format); + + mdp4_overlay_reg_flush(pipe, 0); +} + +void mdp4_mixer_blend_cfg(int mixer) +{ + int i, off; + unsigned char *overlay_base; + struct blend_cfg *blend; + + if (mixer == MDP4_MIXER2) + overlay_base = MDP_BASE + MDP4_OVERLAYPROC2_BASE; + else if (mixer == MDP4_MIXER1) + overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE; + else + overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE; + + blend = &ctrl->blend[mixer][MDP4_MIXER_STAGE_BASE]; + blend++; /* stage0 */ + + for (i = MDP4_MIXER_STAGE0; i < MDP4_MIXER_STAGE_MAX; i++) { + off = 20 * i; + off = 0x20 * (i - MDP4_MIXER_STAGE0); + if (i == MDP4_MIXER_STAGE3) + off -= 4; + outpdw(overlay_base + off + 0x104, blend->op); + blend++; + } +} + +/* + * D(i+1) = Ks * S + Kd * D(i) + */ +void mdp4_mixer_blend_setup(int mixer) +{ + struct mdp4_overlay_pipe *d_pipe; + struct mdp4_overlay_pipe *s_pipe; + struct blend_cfg *blend; + int i, off, ptype, alpha_drop = 0; + int d_alpha, s_alpha; + unsigned char *overlay_base; + uint32 c0, c1, c2; + + + d_pipe = ctrl->stage[mixer][MDP4_MIXER_STAGE_BASE]; + if (d_pipe == NULL) { + pr_err("%s: Error: no bg_pipe at mixer=%d\n", __func__, mixer); + return; + } + + blend = &ctrl->blend[mixer][MDP4_MIXER_STAGE0]; + for (i = MDP4_MIXER_STAGE0; i < MDP4_MIXER_STAGE_MAX; i++) { + blend->solidfill = 0; + blend->op = (MDP4_BLEND_FG_ALPHA_FG_CONST | + MDP4_BLEND_BG_ALPHA_BG_CONST); + s_pipe = ctrl->stage[mixer][i]; + if (s_pipe == NULL) { + blend++; + d_pipe = NULL; + d_alpha = 0; + continue; + } + /* alpha channel is lost on VG pipe when using QSEED or M/N */ + if (s_pipe->pipe_type == OVERLAY_TYPE_VIDEO && + ((s_pipe->op_mode & MDP4_OP_SCALEY_EN) || + (s_pipe->op_mode & MDP4_OP_SCALEX_EN)) && + !(s_pipe->op_mode & MDP4_OP_SCALEY_PIXEL_RPT)) + alpha_drop = 1; + + d_pipe = mdp4_background_layer(mixer, s_pipe); + d_alpha = d_pipe->alpha_enable; + s_alpha = s_pipe->alpha_enable; + pr_debug("%s: stage=%d: bg: ndx=%d da=%d dalpha=%x " + "fg: ndx=%d sa=%d salpha=%x is_fg=%d alpha_drop=%d\n", + __func__, i-2, d_pipe->pipe_ndx, d_alpha, d_pipe->alpha, + s_pipe->pipe_ndx, s_alpha, s_pipe->alpha, s_pipe->is_fg, + alpha_drop); + + /* base on fg's alpha */ + blend->bg_alpha = 0x0ff - s_pipe->alpha; + blend->fg_alpha = s_pipe->alpha; + blend->co3_sel = 1; /* use fg alpha */ + + if (s_pipe->is_fg) { + if (s_pipe->alpha == 0xff) { + blend->solidfill = 1; + blend->solidfill_pipe = d_pipe; + } + } else if (s_alpha) { + if (!alpha_drop) { + blend->op = MDP4_BLEND_BG_ALPHA_FG_PIXEL; + if (!(s_pipe->flags & MDP_BLEND_FG_PREMULT)) + blend->op |= + MDP4_BLEND_FG_ALPHA_FG_PIXEL; + } else + blend->op = MDP4_BLEND_BG_ALPHA_FG_CONST; + + blend->op |= MDP4_BLEND_BG_INV_ALPHA; + } else if (d_alpha) { + ptype = mdp4_overlay_format2type(s_pipe->src_format); + if (ptype == OVERLAY_TYPE_VIDEO) { + blend->op = (MDP4_BLEND_FG_ALPHA_BG_PIXEL | + MDP4_BLEND_FG_INV_ALPHA); + if (!(s_pipe->flags & MDP_BLEND_FG_PREMULT)) + blend->op |= + MDP4_BLEND_BG_ALPHA_BG_PIXEL; + blend->co3_sel = 0; /* use bg alpha */ + } else { + /* s_pipe is rgb without alpha */ + blend->op = (MDP4_BLEND_FG_ALPHA_FG_CONST | + MDP4_BLEND_BG_ALPHA_BG_CONST); + blend->bg_alpha = 0; + } + } + + if (s_pipe->transp != MDP_TRANSP_NOP) { + if (s_pipe->is_fg) { + transp_color_key(s_pipe->src_format, + s_pipe->transp, &c0, &c1, &c2); + /* Fg blocked */ + blend->op |= MDP4_BLEND_FG_TRANSP_EN; + /* lower limit */ + blend->transp_low0 = (c1 << 16 | c0); + blend->transp_low1 = c2; + /* upper limit */ + blend->transp_high0 = (c1 << 16 | c0); + blend->transp_high1 = c2; + } else { + transp_color_key(d_pipe->src_format, + s_pipe->transp, &c0, &c1, &c2); + /* Fg blocked */ + blend->op |= MDP4_BLEND_BG_TRANSP_EN; + blend--; /* one stage back */ + /* lower limit */ + blend->transp_low0 = (c1 << 16 | c0); + blend->transp_low1 = c2; + /* upper limit */ + blend->transp_high0 = (c1 << 16 | c0); + blend->transp_high1 = c2; + blend++; /* back to original stage */ + } + } + blend++; + } + + /* mixer numer, /dev/fb0, /dev/fb1, /dev/fb2 */ + if (mixer == MDP4_MIXER2) + overlay_base = MDP_BASE + MDP4_OVERLAYPROC2_BASE;/* 0x88000 */ + else if (mixer == MDP4_MIXER1) + overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */ + else + overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */ + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + blend = &ctrl->blend[mixer][MDP4_MIXER_STAGE_BASE]; + /* lower limit */ + outpdw(overlay_base + 0x180, blend->transp_low0); + outpdw(overlay_base + 0x184, blend->transp_low1); + /* upper limit */ + outpdw(overlay_base + 0x188, blend->transp_high0); + outpdw(overlay_base + 0x18c, blend->transp_high1); + blend++; /* stage0 */ + for (i = MDP4_MIXER_STAGE0; i < MDP4_MIXER_STAGE_MAX; i++) { + off = 20 * i; + off = 0x20 * (i - MDP4_MIXER_STAGE0); + if (i == MDP4_MIXER_STAGE3) + off -= 4; + + if (blend->solidfill_pipe) + mdp4_overlay_bg_solidfill(blend); + + outpdw(overlay_base + off + 0x108, blend->fg_alpha); + outpdw(overlay_base + off + 0x10c, blend->bg_alpha); + + if (mdp_rev >= MDP_REV_42) + outpdw(overlay_base + off + 0x104, blend->op); + + outpdw(overlay_base + (off << 5) + 0x1004, blend->co3_sel); + outpdw(overlay_base + off + 0x110, blend->transp_low0);/* low */ + outpdw(overlay_base + off + 0x114, blend->transp_low1);/* low */ + /* upper limit */ + outpdw(overlay_base + off + 0x118, blend->transp_high0); + outpdw(overlay_base + off + 0x11c, blend->transp_high1); + blend++; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); +} + +void mdp4_overlay_reg_flush(struct mdp4_overlay_pipe *pipe, int all) +{ + int mixer; + uint32 *reg; + + mixer = pipe->mixer_num; + reg = &ctrl->flush[mixer]; + *reg |= (1 << (2 + pipe->pipe_num)); + + if (all) { + if (mixer == MDP4_MIXER0) + *reg |= 0x01; + else + *reg |= 0x02; + } +} + +void mdp4_overlay_flush_piggyback(int m0, int m1) +{ + u32 data; + + data = ctrl->flush[m0] | ctrl->flush[m1]; + ctrl->flush[m0] = data; +} + +void mdp4_overlay_reg_flush_reset(struct mdp4_overlay_pipe *pipe) +{ + int mixer; + + mixer = pipe->mixer_num; + ctrl->flush[mixer] = 0; +} + +struct mdp4_overlay_pipe *mdp4_overlay_stage_pipe(int mixer, int stage) +{ + return ctrl->stage[mixer][stage]; +} + +struct mdp4_overlay_pipe *mdp4_overlay_ndx2pipe(int ndx) +{ + struct mdp4_overlay_pipe *pipe; + + if (ndx <= 0 || ndx > OVERLAY_PIPE_MAX) + return NULL; + + pipe = &ctrl->plist[ndx - 1]; /* ndx start from 1 */ + + if (pipe->pipe_used == 0) + return NULL; + + return pipe; +} + +struct mdp4_overlay_pipe *mdp4_overlay_pipe_alloc(int ptype, int mixer) +{ + int i; + struct mdp4_overlay_pipe *pipe; + + if (ptype == OVERLAY_TYPE_BF) { + if (!mdp4_overlay_borderfill_supported()) + return NULL; + } + + for (i = 0; i < OVERLAY_PIPE_MAX; i++) { + pipe = &ctrl->plist[i]; + if ((pipe->pipe_used == 0) && ((pipe->pipe_type == ptype) || + (ptype == OVERLAY_TYPE_RGB && + pipe->pipe_type == OVERLAY_TYPE_VIDEO))) { + if (ptype == OVERLAY_TYPE_BF && + mixer != pipe->mixer_num) + continue; + init_completion(&pipe->comp); + init_completion(&pipe->dmas_comp); + pr_debug("%s: pipe=%x ndx=%d num=%d\n", __func__, + (int)pipe, pipe->pipe_ndx, pipe->pipe_num); + return pipe; + } + } + + pr_err("%s: ptype=%d FAILED\n", __func__, ptype); + + return NULL; +} + + +void mdp4_overlay_pipe_free(struct mdp4_overlay_pipe *pipe) +{ + uint32 ptype, num, ndx, mixer; + struct mdp4_iommu_pipe_info iom; + + pr_debug("%s: pipe=%x ndx=%d\n", __func__, (int)pipe, pipe->pipe_ndx); + + ptype = pipe->pipe_type; + num = pipe->pipe_num; + ndx = pipe->pipe_ndx; + mixer = pipe->mixer_num; + + mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0); + + iom = pipe->iommu; + + memset(pipe, 0, sizeof(*pipe)); + pipe->pipe_type = ptype; + pipe->pipe_num = num; + pipe->pipe_ndx = ndx; + pipe->mixer_num = mixer; + pipe->iommu = iom; + +} + +static int mdp4_overlay_req2pipe(struct mdp_overlay *req, int mixer, + struct mdp4_overlay_pipe **ppipe, + struct msm_fb_data_type *mfd) +{ + struct mdp4_overlay_pipe *pipe; + int ret, ptype; + + u32 upscale_max; + upscale_max = (mdp_rev >= MDP_REV_41) ? + MDP4_REV41_OR_LATER_UP_SCALING_MAX : + MDP4_REV40_UP_SCALING_MAX; + + if (mfd == NULL) { + pr_err("%s: mfd == NULL, -ENODEV\n", __func__); + return -ENODEV; + } + + if (mixer >= MDP4_MIXER_MAX) { + pr_err("%s: mixer out of range!\n", __func__); + mdp4_stat.err_mixer++; + return -ERANGE; + } + + if (req->z_order < 0 || req->z_order > 3) { + pr_err("%s: z_order=%d out of range!\n", __func__, + req->z_order); + mdp4_stat.err_zorder++; + return -ERANGE; + } + + if (req->src_rect.h > 0xFFF) { + pr_err("%s: src_h is out of range: 0X%x!\n", + __func__, req->src_rect.h); + mdp4_stat.err_size++; + return -EINVAL; + } + + if (req->src_rect.w > 0xFFF) { + pr_err("%s: src_w is out of range: 0X%x!\n", + __func__, req->src_rect.w); + mdp4_stat.err_size++; + return -EINVAL; + } + + if (req->src_rect.x > 0xFFF) { + pr_err("%s: src_x is out of range: 0X%x!\n", + __func__, req->src_rect.x); + mdp4_stat.err_size++; + return -EINVAL; + } + + if (req->src_rect.y > 0xFFF) { + pr_err("%s: src_y is out of range: 0X%x!\n", + __func__, req->src_rect.y); + mdp4_stat.err_size++; + return -EINVAL; + } + + if (req->dst_rect.h > 0xFFF) { + pr_err("%s: dst_h is out of range: 0X%x!\n", + __func__, req->dst_rect.h); + mdp4_stat.err_size++; + return -EINVAL; + } + + if (req->dst_rect.w > 0xFFF) { + pr_err("%s: dst_w is out of range: 0X%x!\n", + __func__, req->dst_rect.w); + mdp4_stat.err_size++; + return -EINVAL; + } + + if (req->dst_rect.x > 0xFFF) { + pr_err("%s: dst_x is out of range: 0X%x!\n", + __func__, req->dst_rect.x); + mdp4_stat.err_size++; + return -EINVAL; + } + + if (req->dst_rect.y > 0xFFF) { + pr_err("%s: dst_y is out of range: 0X%x!\n", + __func__, req->dst_rect.y); + mdp4_stat.err_size++; + return -EINVAL; + } + + if (req->src_rect.h == 0 || req->src_rect.w == 0) { + pr_err("%s: src img of zero size!\n", __func__); + mdp4_stat.err_size++; + return -EINVAL; + } + + if (req->dst_rect.h > (req->src_rect.h * upscale_max)) { + mdp4_stat.err_scale++; + pr_err("%s: scale up, too much (h)!\n", __func__); + return -ERANGE; + } + + if (req->src_rect.h > (req->dst_rect.h * 8)) { /* too little */ + mdp4_stat.err_scale++; + pr_err("%s: scale down, too little (h)!\n", __func__); + return -ERANGE; + } + + if (req->dst_rect.w > (req->src_rect.w * upscale_max)) { + mdp4_stat.err_scale++; + pr_err("%s: scale up, too much (w)!\n", __func__); + return -ERANGE; + } + + if (req->src_rect.w > (req->dst_rect.w * 8)) { /* too little */ + mdp4_stat.err_scale++; + pr_err("%s: scale down, too little (w)!\n", __func__); + return -ERANGE; + } + + if (mdp_rev <= MDP_REV_41) { + if ((mdp4_overlay_format2type(req->src.format) == + OVERLAY_TYPE_RGB) && + !(req->flags & MDP_OV_PIPE_SHARE) && + ((req->src_rect.w > req->dst_rect.w) || + (req->src_rect.h > req->dst_rect.h))) { + mdp4_stat.err_size++; + pr_err("%s: downscale on RGB pipe!\n", __func__); + return -EINVAL; + } + } + + if (mdp_hw_revision == MDP4_REVISION_V1) { + /* non integer down saceling ratio smaller than 1/4 + * is not supportted + */ + if (req->src_rect.h > (req->dst_rect.h * 4)) { + if (req->src_rect.h % req->dst_rect.h) { + mdp4_stat.err_scale++; + pr_err("%s: need integer (h)!\n", __func__); + return -ERANGE; + } + } + + if (req->src_rect.w > (req->dst_rect.w * 4)) { + if (req->src_rect.w % req->dst_rect.w) { + mdp4_stat.err_scale++; + pr_err("%s: need integer (w)!\n", __func__); + return -ERANGE; + } + } + } + + if (((req->src_rect.x + req->src_rect.w) > req->src.width) || + ((req->src_rect.y + req->src_rect.h) > req->src.height)) { + mdp4_stat.err_size++; + pr_err("%s invalid src rectangle\n", __func__); + return -ERANGE; + } + + if (ctrl->panel_3d != MDP4_3D_SIDE_BY_SIDE) { + int xres; + int yres; + + xres = mfd->panel_info.xres; + yres = mfd->panel_info.yres; + + if (((req->dst_rect.x + req->dst_rect.w) > xres) || + ((req->dst_rect.y + req->dst_rect.h) > yres)) { + mdp4_stat.err_size++; + pr_err("%s invalid dst rectangle\n", __func__); + return -ERANGE; + } + } + + ptype = mdp4_overlay_format2type(req->src.format); + if (ptype < 0) { + pr_err("%s: mdp4_overlay_format2type!\n", __func__); + return ptype; + } + + if (req->flags & MDP_OV_PIPE_SHARE) + ptype = OVERLAY_TYPE_VIDEO; /* VG pipe supports both RGB+YUV */ + + if (req->id == MSMFB_NEW_REQUEST) /* new request */ + pipe = mdp4_overlay_pipe_alloc(ptype, mixer); + else + pipe = mdp4_overlay_ndx2pipe(req->id); + + if (pipe == NULL) { + pr_err("%s: pipe == NULL!\n", __func__); + return -ENOMEM; + } + + if (!display_iclient && !IS_ERR_OR_NULL(mfd->iclient)) { + display_iclient = mfd->iclient; + pr_debug("%s(): display_iclient %p\n", __func__, + display_iclient); + } + + pipe->src_format = req->src.format; + ret = mdp4_overlay_format2pipe(pipe); + + if (ret < 0) { + pr_err("%s: mdp4_overlay_format2pipe!\n", __func__); + return ret; + } + + /* + * base layer == 1, reserved for frame buffer + * zorder 0 == stage 0 == 2 + * zorder 1 == stage 1 == 3 + * zorder 2 == stage 2 == 4 + */ + if (req->id == MSMFB_NEW_REQUEST) { /* new request */ + pipe->pipe_used++; + pipe->mixer_num = mixer; + pr_debug("%s: zorder=%d pipe ndx=%d num=%d\n", __func__, + req->z_order, pipe->pipe_ndx, pipe->pipe_num); + + } + + pipe->mixer_stage = req->z_order + MDP4_MIXER_STAGE0; + pipe->src_width = req->src.width & 0x1fff; /* source img width */ + pipe->src_height = req->src.height & 0x1fff; /* source img height */ + pipe->src_h = req->src_rect.h & 0x07ff; + pipe->src_w = req->src_rect.w & 0x07ff; + pipe->src_y = req->src_rect.y & 0x07ff; + pipe->src_x = req->src_rect.x & 0x07ff; + pipe->dst_h = req->dst_rect.h & 0x07ff; + pipe->dst_w = req->dst_rect.w & 0x07ff; + pipe->dst_y = req->dst_rect.y & 0x07ff; + pipe->dst_x = req->dst_rect.x & 0x07ff; + + pipe->op_mode = 0; + + if (req->flags & MDP_FLIP_LR) + pipe->op_mode |= MDP4_OP_FLIP_LR; + + if (req->flags & MDP_FLIP_UD) + pipe->op_mode |= MDP4_OP_FLIP_UD; + + if (req->flags & MDP_DITHER) + pipe->op_mode |= MDP4_OP_DITHER_EN; + + if (req->flags & MDP_DEINTERLACE) + pipe->op_mode |= MDP4_OP_DEINT_EN; + + if (req->flags & MDP_DEINTERLACE_ODD) + pipe->op_mode |= MDP4_OP_DEINT_ODD_REF; + + pipe->is_fg = req->is_fg;/* control alpha and color key */ + + pipe->alpha = req->alpha & 0x0ff; + + pipe->transp = req->transp_mask; + + pipe->flags = req->flags; + + *ppipe = pipe; + + return 0; +} + +static int mdp4_calc_pipe_mdp_clk(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + u32 pclk; + u32 xscale, yscale; + u32 hsync = 0; + u32 shift = 16; + u64 rst; + int ret = -EINVAL; + + if (!pipe) { + pr_err("%s: pipe is null!\n", __func__); + pipe->req_bw = OVERLAY_PERF_LEVEL4; + return ret; + } + if (!mfd) { + pr_err("%s: mfd is null!\n", __func__); + pipe->req_bw = OVERLAY_PERF_LEVEL4; + return ret; + } + + /* + * Serveral special cases require the max mdp clk but cannot + * be explained by mdp clk equation. + */ + if (pipe->flags & MDP_DEINTERLACE) { + pr_info("%s deinterlace requires max mdp clk.\n", + __func__); + pipe->req_clk = mdp_max_clk; + return 0; + } + + pr_debug("%s: pipe sets: panel res(x,y)=(%d,%d)\n", + __func__, mfd->panel_info.xres, mfd->panel_info.yres); + pr_debug("%s: src(w,h)(%d,%d),src(x,y)(%d,%d)\n", + __func__, pipe->src_w, pipe->src_h, pipe->src_x, pipe->src_y); + pr_debug("%s: dst(w,h)(%d,%d),dst(x,y)(%d,%d)\n", + __func__, pipe->dst_w, pipe->dst_h, pipe->dst_x, pipe->dst_y); + + pclk = (mfd->panel_info.type == MIPI_VIDEO_PANEL || + mfd->panel_info.type == MIPI_CMD_PANEL) ? + mfd->panel_info.mipi.dsi_pclk_rate : + mfd->panel_info.clk_rate; + if (!pclk) { + pipe->req_clk = mdp_max_clk; + pr_err("%s panel pixel clk is zero!\n", __func__); + return ret; + } + pr_debug("%s: mdp panel pixel clk is %d.\n", + __func__, pclk); + + if (!pipe->dst_h) { + pr_err("%s: pipe dst_h is zero!\n", __func__); + pipe->req_clk = mdp_max_clk; + return ret; + } + + if (!pipe->src_h) { + pr_err("%s: pipe src_h is zero!\n", __func__); + pipe->req_clk = mdp_max_clk; + return ret; + } + + if (!pipe->dst_w) { + pr_err("%s: pipe dst_w is zero!\n", __func__); + pipe->req_clk = mdp_max_clk; + return ret; + } + + if (!pipe->dst_h) { + pr_err("%s: pipe dst_h is zero!\n", __func__); + pipe->req_clk = mdp_max_clk; + return ret; + } + + /* + * For the scaling cases, make more margin by removing porch + * values and adding extra 20%. + */ + if ((pipe->src_h != pipe->dst_h) || + (pipe->src_w != pipe->dst_w)) { + hsync = mfd->panel_info.xres; + hsync *= 100; + hsync /= 120; + pr_debug("%s: panel hsync is %d. with scaling\n", + __func__, hsync); + + } else { + hsync = mfd->panel_info.lcdc.h_back_porch + + mfd->panel_info.lcdc.h_front_porch + + mfd->panel_info.lcdc.h_pulse_width + + mfd->panel_info.xres; + pr_debug("%s: panel hsync is %d.\n", + __func__, hsync); + } + + if (!hsync) { + pipe->req_clk = mdp_max_clk; + pr_err("%s: panel hsync is zero!\n", __func__); + return 0; + } + + xscale = mfd->panel_info.xres; + xscale += pipe->src_w; + + if (xscale < pipe->dst_w) { + pipe->req_clk = mdp_max_clk; + pr_err("%s: xres+src_w cannot be less than dst_w!\n", + __func__); + return ret; + } + + xscale -= pipe->dst_w; + xscale <<= shift; + xscale /= hsync; + pr_debug("%s: the right %d shifted xscale is %d.\n", + __func__, shift, xscale); + + if (pipe->src_h > pipe->dst_h) { + yscale = pipe->src_h; + yscale <<= shift; + yscale /= pipe->dst_h; + } else { /* upscale */ + yscale = pipe->dst_h; + yscale <<= shift; + yscale /= pipe->src_h; + } + + yscale *= pipe->src_w; + yscale /= hsync; + + pr_debug("%s: the right %d shifted yscale is %d.\n", + __func__, shift, yscale); + + rst = pclk; + if (yscale > xscale) + rst *= yscale; + else + rst *= xscale; + + rst >>= shift; + + /* + * There is one special case for the panels that have low + * v_back_porch (<=4), mdp clk should be fast enough to buffer + * 4 lines input during back porch time if scaling is + * required(FIR). + */ + if ((mfd->panel_info.lcdc.v_back_porch <= 4) && + (pipe->src_h != pipe->dst_h)) { + u32 clk = 0; + clk = 4 * (pclk >> shift) / mfd->panel_info.lcdc.v_back_porch; + clk <<= shift; + pr_debug("%s: mdp clk rate %d based on low vbp %d\n", + __func__, clk, mfd->panel_info.lcdc.v_back_porch); + rst = (rst > clk) ? rst : clk; + } + + /* + * If the calculated mdp clk is less than panel pixel clk, + * most likely due to upscaling, mdp clk rate will be set to + * greater than pclk. Now the driver uses 1.15 as the + * factor. Ideally this factor is passed from board file. + */ + if (rst < pclk) { + rst = ((pclk >> shift) * 23 / 20) << shift; + pr_debug("%s calculated mdp clk is less than pclk.\n", + __func__); + } + + pipe->req_clk = (u32) rst; + + pr_debug("%s: required mdp clk %d mixer %d pipe ndx %d\n", + __func__, pipe->req_clk, pipe->mixer_num, pipe->pipe_ndx); + + return 0; +} + +#define OVERLAY_VGA_SIZE 0x04B000 +#define OVERLAY_720P_TILE_SIZE 0x0E6000 +#define OVERLAY_WSVGA_SIZE 0x98000 /* 1024x608, align 600 to 32bit */ + +#define OVERLAY_BUS_SCALE_TABLE_BASE 6 + + +static int mdp4_calc_pipe_mdp_bw(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) +{ + u32 res; + int ret = -EINVAL; + + if (!pipe) { + pr_err("%s: pipe is null!\n", __func__); + return ret; + } + if (!mfd) { + pr_err("%s: mfd is null!\n", __func__); + return ret; + } + + if (pipe->flags & MDP_DEINTERLACE) { + pr_info("%s deinterlace requires max mdp bw.\n", + __func__); + pipe->req_bw = OVERLAY_PERF_LEVEL1; + return 0; + } + + if (pipe->pipe_type == OVERLAY_TYPE_BF) { + pipe->req_bw = OVERLAY_PERF_LEVEL4; + return 0; + } + + res = pipe->src_w * pipe->src_h; + + if (res <= OVERLAY_WSVGA_SIZE) + pipe->req_bw = OVERLAY_PERF_LEVEL4; + else if (res <= OVERLAY_VGA_SIZE) + pipe->req_bw = OVERLAY_PERF_LEVEL3; + else if (res <= OVERLAY_720P_TILE_SIZE) + pipe->req_bw = OVERLAY_PERF_LEVEL2; + else + pipe->req_bw = OVERLAY_PERF_LEVEL1; + + return 0; +} + +int mdp4_overlay_mdp_perf_req(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *plist) +{ + u32 worst_mdp_clk = 0; + u32 worst_mdp_bw = OVERLAY_PERF_LEVEL4; + int i; + struct mdp4_overlay_perf *perf_req = &perf_request; + struct mdp4_overlay_pipe *pipe = plist; + u32 cnt = 0; + int ret = -EINVAL; + + if (!mfd) { + pr_err("%s: mfd is null!\n", __func__); + return ret; + } + + if (!plist) { + pr_err("%s: plist is null!\n", __func__); + return ret; + } + + perf_req->use_ov0_blt = 0; + perf_req->use_ov1_blt = 0; + + for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) { + + if (!pipe) + return ret; + + if (!pipe->pipe_used) + continue; + cnt++; + if (worst_mdp_clk < pipe->req_clk) + worst_mdp_clk = pipe->req_clk; + if (pipe->req_clk > mdp_max_clk) { + if (pipe->mixer_num == MDP4_MIXER0) + perf_req->use_ov0_blt = 1; + if (pipe->mixer_num == MDP4_MIXER1) + perf_req->use_ov1_blt = 1; + } + + if (!pipe->req_bw) { + pr_err("%s mdp pipe bw request should not be zero!\n", + __func__); + pr_debug("%s %d pid %d num %d idx %d mix %d bw %d\n", + __func__, __LINE__, current->pid, + pipe->pipe_num, pipe->pipe_ndx, + pipe->mixer_num, pipe->req_bw); + pipe->req_bw = OVERLAY_PERF_LEVEL4; + } + + if (pipe->req_bw < worst_mdp_bw) + worst_mdp_bw = pipe->req_bw; + + if (mfd->mdp_rev == MDP_REV_41) { + /* + * writeback (blt) mode to provide work around + * for dsi cmd mode interface hardware bug. + */ + if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) { + if (pipe->dst_x != 0) + perf_req->use_ov0_blt = 1; + } + if ((mfd->panel_info.xres > 1280) && + (mfd->panel_info.type != DTV_PANEL)) { + perf_req->use_ov0_blt = 1; + } + } + } + + perf_req->mdp_clk_rate = worst_mdp_clk; + if (perf_req->mdp_clk_rate > mdp_max_clk) + perf_req->mdp_clk_rate = mdp_max_clk; + + perf_req->mdp_clk_rate = mdp_clk_round_rate(perf_req->mdp_clk_rate); + + perf_req->mdp_bw = worst_mdp_bw; + + if (cnt >= 3) + perf_req->mdp_bw = OVERLAY_PERF_LEVEL1; + + pr_debug("%s %d pid %d cnt %d clk %d ov0_blt %d, ov1_blt %d bw %d\n", + __func__, __LINE__, current->pid, cnt, + perf_req->mdp_clk_rate, + perf_req->use_ov0_blt, + perf_req->use_ov1_blt, + perf_req->mdp_bw); + + return 0; +} + +int mdp4_overlay_mdp_pipe_req(struct mdp4_overlay_pipe *pipe, + struct msm_fb_data_type *mfd) +{ + int ret = 0; + + if (mdp4_calc_pipe_mdp_clk(mfd, pipe)) { + pr_err("%s unable to calc mdp pipe clk rate ret=%d\n", + __func__, ret); + ret = -EINVAL; + } + if (mdp4_calc_pipe_mdp_bw(mfd, pipe)) { + pr_err("%s unable to calc mdp pipe bandwidth ret=%d\n", + __func__, ret); + ret = -EINVAL; + } + return ret; +} + +void mdp4_overlay_mdp_perf_upd(struct msm_fb_data_type *mfd, + int flag) +{ + struct mdp4_overlay_perf *perf_req = &perf_request; + struct mdp4_overlay_perf *perf_cur = &perf_current; + + pr_debug("%s %d: req mdp clk %d, cur mdp clk %d flag %d\n", + __func__, __LINE__, + perf_req->mdp_clk_rate, + perf_cur->mdp_clk_rate, + flag); + + if (!mdp4_extn_disp) + perf_cur->use_ov1_blt = 0; + + if (flag) { + if (perf_req->mdp_clk_rate > perf_cur->mdp_clk_rate) { + mdp_set_core_clk(perf_req->mdp_clk_rate); + pr_info("%s mdp clk is changed [%d] from %d to %d\n", + __func__, + flag, + perf_cur->mdp_clk_rate, + perf_req->mdp_clk_rate); + perf_cur->mdp_clk_rate = + perf_req->mdp_clk_rate; + } + if (perf_req->mdp_bw < perf_cur->mdp_bw) { + mdp_bus_scale_update_request + (OVERLAY_BUS_SCALE_TABLE_BASE - + perf_req->mdp_bw); + pr_info("%s mdp bw is changed [%d] from %d to %d\n", + __func__, + flag, + perf_cur->mdp_bw, + perf_req->mdp_bw); + perf_cur->mdp_bw = perf_req->mdp_bw; + } + if (mfd->panel_info.pdest == DISPLAY_1 && + perf_req->use_ov0_blt && !perf_cur->use_ov0_blt) { + if (mfd->panel_info.type == LCDC_PANEL || + mfd->panel_info.type == LVDS_PANEL) + mdp4_lcdc_overlay_blt_start(mfd); + else if (mfd->panel_info.type == MIPI_VIDEO_PANEL) + mdp4_dsi_video_blt_start(mfd); + else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) + mdp4_dsi_cmd_blt_start(mfd); + pr_info("%s mixer0 start blt [%d] from %d to %d.\n", + __func__, + flag, + perf_cur->use_ov0_blt, + perf_req->use_ov0_blt); + perf_cur->use_ov0_blt = perf_req->use_ov0_blt; + } + if (mfd->panel_info.pdest == DISPLAY_2 && + perf_req->use_ov1_blt && !perf_cur->use_ov1_blt) { + mdp4_dtv_overlay_blt_start(mfd); + pr_info("%s mixer1 start blt [%d] from %d to %d.\n", + __func__, + flag, + perf_cur->use_ov1_blt, + perf_req->use_ov1_blt); + perf_cur->use_ov1_blt = perf_req->use_ov1_blt; + } + } else { + if (perf_req->mdp_clk_rate < perf_cur->mdp_clk_rate) { + pr_info("%s mdp clk is changed [%d] from %d to %d\n", + __func__, + flag, + perf_cur->mdp_clk_rate, + perf_req->mdp_clk_rate); + mdp_set_core_clk(perf_req->mdp_clk_rate); + perf_cur->mdp_clk_rate = + perf_req->mdp_clk_rate; + } + if (perf_req->mdp_bw > perf_cur->mdp_bw) { + pr_info("%s mdp bw is changed [%d] from %d to %d\n", + __func__, + flag, + perf_cur->mdp_bw, + perf_req->mdp_bw); + mdp_bus_scale_update_request + (OVERLAY_BUS_SCALE_TABLE_BASE - + perf_req->mdp_bw); + perf_cur->mdp_bw = perf_req->mdp_bw; + } + if (mfd->panel_info.pdest == DISPLAY_1 && + !perf_req->use_ov0_blt && perf_cur->use_ov0_blt) { + if (mfd->panel_info.type == LCDC_PANEL || + mfd->panel_info.type == LVDS_PANEL) + mdp4_lcdc_overlay_blt_stop(mfd); + else if (mfd->panel_info.type == MIPI_VIDEO_PANEL) + mdp4_dsi_video_blt_stop(mfd); + else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) + mdp4_dsi_cmd_blt_stop(mfd); + pr_info("%s mixer0 stop blt [%d] from %d to %d.\n", + __func__, + flag, + perf_cur->use_ov0_blt, + perf_req->use_ov0_blt); + perf_cur->use_ov0_blt = perf_req->use_ov0_blt; + } + if (mfd->panel_info.pdest == DISPLAY_2 && + !perf_req->use_ov1_blt && perf_cur->use_ov1_blt) { + mdp4_dtv_overlay_blt_stop(mfd); + pr_info("%s mixer1 stop blt [%d] from %d to %d.\n", + __func__, + flag, + perf_cur->use_ov1_blt, + perf_req->use_ov1_blt); + perf_cur->use_ov1_blt = perf_req->use_ov1_blt; + } + } + return; +} + +static int get_img(struct msmfb_data *img, struct fb_info *info, + struct mdp4_overlay_pipe *pipe, unsigned int plane, + unsigned long *start, unsigned long *len, struct file **srcp_file, + int *p_need, struct ion_handle **srcp_ihdl) +{ + struct file *file; + int put_needed, ret = 0, fb_num; +#ifdef CONFIG_ANDROID_PMEM + unsigned long vstart; +#endif + *p_need = 0; + + if (img->flags & MDP_BLIT_SRC_GEM) { + *srcp_file = NULL; + return kgsl_gem_obj_addr(img->memory_id, (int) img->priv, + start, len); + } + + if (img->flags & MDP_MEMORY_ID_TYPE_FB) { + file = fget_light(img->memory_id, &put_needed); + if (file == NULL) + return -EINVAL; + + if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) { + fb_num = MINOR(file->f_dentry->d_inode->i_rdev); + if (get_fb_phys_info(start, len, fb_num, + DISPLAY_SUBSYSTEM_ID)) { + ret = -1; + } else { + pr_warn("%s: mdp4_overlay play with FB memory\n", + __func__); + *srcp_file = file; + *p_need = put_needed; + } + } else + ret = -1; + if (ret) + fput_light(file, put_needed); + return ret; + } + +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + return mdp4_overlay_iommu_map_buf(img->memory_id, pipe, plane, + start, len, srcp_ihdl); +#endif +#ifdef CONFIG_ANDROID_PMEM + if (!get_pmem_file(img->memory_id, start, &vstart, + len, srcp_file)) + return 0; + else + return -EINVAL; +#endif +} + +#ifdef CONFIG_FB_MSM_MIPI_DSI +int mdp4_overlay_3d_sbys(struct fb_info *info, struct msmfb_overlay_3d *req) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int ret = -EPERM; + + if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) + return -EINTR; + + if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD mdp4_dsi_cmd_3d_sbys(mfd, req); + ret = 0; + } else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) { + mdp4_dsi_video_3d_sbys(mfd, req);) { + mdp4_dsi_cmd_3d_sbys(mfd, req); + ret = 0; + } else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO mdp4_dsi_cmd_3d_sbys(mfd, req); + ret = 0; + } else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) { + mdp4_dsi_video_3d_sbys(mfd, req);) { + mdp4_dsi_video_3d_sbys(mfd, req); + ret = 0; + } + mutex_unlock(&mfd->dma->ov_mutex); + + return ret; +} +#else +int mdp4_overlay_3d_sbys(struct fb_info *info, struct msmfb_overlay_3d *req) +{ + /* do nothing */ + return -EPERM; +} +#endif + +int mdp4_overlay_blt(struct fb_info *info, struct msmfb_overlay_blt *req) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (mfd == NULL) + return -ENODEV; + + if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) + return -EINTR; + + if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) + mdp4_dsi_cmd_overlay_blt(mfd, req); + else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) + mdp4_dsi_video_overlay_blt(mfd, req); + else if (ctrl->panel_mode & MDP4_PANEL_LCDC) + mdp4_lcdc_overlay_blt(mfd, req); + else if (ctrl->panel_mode & MDP4_PANEL_MDDI) + mdp4_mddi_overlay_blt(mfd, req); + + mutex_unlock(&mfd->dma->ov_mutex); + + return 0; +} + +int mdp4_overlay_get(struct fb_info *info, struct mdp_overlay *req) +{ + struct mdp4_overlay_pipe *pipe; + + pipe = mdp4_overlay_ndx2pipe(req->id); + if (pipe == NULL) + return -ENODEV; + + *req = pipe->req_data; + + if (mdp4_overlay_borderfill_supported()) + req->flags |= MDP_BORDERFILL_SUPPORTED; + + return 0; +} + +int mdp4_overlay_set(struct fb_info *info, struct mdp_overlay *req) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int ret, mixer; + struct mdp4_overlay_pipe *pipe; + + if (mfd == NULL) { + pr_err("%s: mfd == NULL, -ENODEV\n", __func__); + return -ENODEV; + } + + if (info->node != 0 || mfd->cont_splash_done) /* primary */ + if (!mfd->panel_power_on) /* suspended */ + return -EPERM; + + if (req->src.format == MDP_FB_FORMAT) + req->src.format = mfd->fb_imgType; + + if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) { + pr_err("%s: mutex_lock_interruptible, -EINTR\n", __func__); + return -EINTR; + } + + mixer = mfd->panel_info.pdest; /* DISPLAY_1 or DISPLAY_2 */ + + ret = mdp4_overlay_req2pipe(req, mixer, &pipe, mfd); + + if (ret < 0) { + mutex_unlock(&mfd->dma->ov_mutex); + pr_err("%s: mdp4_overlay_req2pipe, ret=%d\n", __func__, ret); + return ret; + } + + /* return id back to user */ + req->id = pipe->pipe_ndx; /* pipe_ndx start from 1 */ + pipe->req_data = *req; /* keep original req */ + + if (!IS_ERR_OR_NULL(mfd->iclient)) { + pr_debug("pipe->flags 0x%x\n", pipe->flags); + if (pipe->flags & MDP_SECURE_OVERLAY_SESSION) { + mfd->mem_hid &= ~BIT(ION_IOMMU_HEAP_ID); + mfd->mem_hid |= ION_SECURE; + } else { + mfd->mem_hid |= BIT(ION_IOMMU_HEAP_ID); + mfd->mem_hid &= ~ION_SECURE; + } + } + + if (pipe->flags & MDP_SHARPENING) { + bool test = ((pipe->req_data.dpp.sharp_strength > 0) && + ((req->src_rect.w > req->dst_rect.w) && + (req->src_rect.h > req->dst_rect.h))); + if (test) { + pr_debug("%s: No sharpening while downscaling.\n", + __func__); + pipe->flags &= ~MDP_SHARPENING; + } + } + + mdp4_overlay_mdp_pipe_req(pipe, mfd); + + mutex_unlock(&mfd->dma->ov_mutex); + + return 0; +} + +int mdp4_overlay_unset_mixer(int mixer) +{ + struct mdp4_overlay_pipe *pipe; + struct mdp4_overlay_pipe *orgpipe; + int i, cnt = 0; + + /* free pipe besides base layer pipe */ + for (i = MDP4_MIXER_STAGE3; i > MDP4_MIXER_STAGE_BASE; i--) { + pipe = ctrl->stage[mixer][i]; + if (pipe == NULL) + continue; + pipe->flags &= ~MDP_OV_PLAY_NOWAIT; + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_down(pipe, 1); + mdp4_overlay_pipe_free(pipe); + /*Clear real pipe attributes as well */ + orgpipe = mdp4_overlay_ndx2pipe(pipe->pipe_ndx); + if (orgpipe != NULL) + orgpipe->pipe_used = 0; + cnt++; + } + + return cnt; +} + +int mdp4_overlay_unset(struct fb_info *info, int ndx) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct mdp4_overlay_pipe *pipe; + struct dpp_ctrl dpp; + int i; + + if (mfd == NULL) + return -ENODEV; + + if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) + return -EINTR; + + pipe = mdp4_overlay_ndx2pipe(ndx); + + if (pipe == NULL) { + mutex_unlock(&mfd->dma->ov_mutex); + return -ENODEV; + } + + if (pipe->pipe_type == OVERLAY_TYPE_BF) { + mdp4_overlay_borderfill_stage_down(pipe); + mutex_unlock(&mfd->dma->ov_mutex); + return 0; + } + + if (pipe->mixer_num == MDP4_MIXER2) + ctrl->mixer2_played = 0; + else if (pipe->mixer_num == MDP4_MIXER1) + ctrl->mixer1_played = 0; + else { + /* mixer 0 */ + ctrl->mixer0_played = 0; + if (ctrl->panel_mode & MDP4_PANEL_MDDI) { + if (mfd->panel_power_on) + mdp4_mddi_blt_dmap_busy_wait(mfd); + } + } + + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_down(pipe, 0); + + if (pipe->mixer_num == MDP4_MIXER0) { + if (ctrl->panel_mode & MDP4_PANEL_MDDI) { + if (mfd->panel_power_on) + mdp4_mddi_overlay_restore(); + } + + } else { /* mixer1, DTV, ATV */ + if (ctrl->panel_mode & MDP4_PANEL_DTV) + mdp4_overlay_dtv_unset(mfd, pipe); + } + + /* Reset any HSIC settings to default */ + if (pipe->flags & MDP_DPP_HSIC) { + for (i = 0; i < NUM_HSIC_PARAM; i++) + dpp.hsic_params[i] = 0; + + mdp4_hsic_set(pipe, &dpp); + mdp4_hsic_update(pipe); + } + + mdp4_stat.overlay_unset[pipe->mixer_num]++; + + mdp4_overlay_pipe_free(pipe); + + mutex_unlock(&mfd->dma->ov_mutex); + + return 0; +} + +int mdp4_overlay_wait4vsync(struct fb_info *info, long long *vtime) +{ + if (!hdmi_prim_display && info->node == 0) { + if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) + mdp4_dsi_video_wait4vsync(0, vtime); + else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) + mdp4_dsi_cmd_wait4vsync(0, vtime); + else if (ctrl->panel_mode & MDP4_PANEL_LCDC) + mdp4_lcdc_wait4vsync(0, vtime); + } else if (hdmi_prim_display || info->node == 1) { + mdp4_dtv_wait4vsync(0, vtime); + } + + return 0; +} + +int mdp4_overlay_vsync_ctrl(struct fb_info *info, int enable) +{ + int cmd; + + if (enable) + cmd = 1; + else + cmd = 0; + + if (!hdmi_prim_display && info->node == 0) { + if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) + mdp4_dsi_video_vsync_ctrl(info, cmd); + else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) + mdp4_dsi_cmd_vsync_ctrl(info, cmd); + else if (ctrl->panel_mode & MDP4_PANEL_LCDC) + mdp4_lcdc_vsync_ctrl(info, cmd); + } else if (hdmi_prim_display || info->node == 1) + mdp4_dtv_vsync_ctrl(info, cmd); + + return 0; +} + + +struct tile_desc { + uint32 width; /* tile's width */ + uint32 height; /* tile's height */ + uint32 row_tile_w; /* tiles per row's width */ + uint32 row_tile_h; /* tiles per row's height */ +}; + +void tile_samsung(struct tile_desc *tp) +{ + /* + * each row of samsung tile consists of two tiles in height + * and two tiles in width which means width should align to + * 64 x 2 bytes and height should align to 32 x 2 bytes. + * video decoder generate two tiles in width and one tile + * in height which ends up height align to 32 X 1 bytes. + */ + tp->width = 64; /* 64 bytes */ + tp->row_tile_w = 2; /* 2 tiles per row's width */ + tp->height = 32; /* 32 bytes */ + tp->row_tile_h = 1; /* 1 tiles per row's height */ +} + +uint32 tile_mem_size(struct mdp4_overlay_pipe *pipe, struct tile_desc *tp) +{ + uint32 tile_w, tile_h; + uint32 row_num_w, row_num_h; + + + tile_w = tp->width * tp->row_tile_w; + tile_h = tp->height * tp->row_tile_h; + + row_num_w = (pipe->src_width + tile_w - 1) / tile_w; + row_num_h = (pipe->src_height + tile_h - 1) / tile_h; + return ((row_num_w * row_num_h * tile_w * tile_h) + 8191) & ~8191; +} + +int mdp4_overlay_play_wait(struct fb_info *info, struct msmfb_overlay_data *req) +{ + return 0; +} + +/* + * mdp4_overlay_dma_commit: called from dma_done isr + * No mutex/sleep allowed + */ +void mdp4_overlay_dma_commit(int mixer) +{ + /* + * non double buffer register update here + * perf level, new clock rate should be done here + */ +} + +/* + * mdp4_overlay_vsync_commit: called from tasklet context + * No mutex/sleep allowed + */ +void mdp4_overlay_vsync_commit(struct mdp4_overlay_pipe *pipe) +{ + if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) + mdp4_overlay_vg_setup(pipe); /* video/graphic pipe */ + else + mdp4_overlay_rgb_setup(pipe); /* rgb pipe */ + + pr_debug("%s: pipe=%x ndx=%d num=%d used=%d\n", __func__, + (int) pipe, pipe->pipe_ndx, pipe->pipe_num, pipe->pipe_used); + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_up(pipe, 0); +} + +int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct msmfb_data *img; + struct mdp4_overlay_pipe *pipe; + ulong start, addr; + ulong len = 0; + struct file *srcp0_file = NULL; + struct file *srcp1_file = NULL, *srcp2_file = NULL; + struct ion_handle *srcp0_ihdl = NULL; + struct ion_handle *srcp1_ihdl = NULL, *srcp2_ihdl = NULL; + int ps0_need, p_need; + uint32_t overlay_version = 0; + int ret = 0; + + if (mfd == NULL) + return -ENODEV; + + if (!mfd->panel_power_on) /* suspended */ + return -EPERM; + + pipe = mdp4_overlay_ndx2pipe(req->id); + if (pipe == NULL) { + mdp4_stat.err_play++; + return -ENODEV; + } + + if (pipe->pipe_type == OVERLAY_TYPE_BF) { + mdp4_overlay_borderfill_stage_up(pipe); + mdp4_mixer_stage_commit(pipe->mixer_num); + return 0; + } + + mutex_lock(&mfd->dma->ov_mutex); + + img = &req->data; + get_img(img, info, pipe, 0, &start, &len, &srcp0_file, + &ps0_need, &srcp0_ihdl); + if (len == 0) { + pr_err("%s: pmem Error\n", __func__); + ret = -1; + goto end; + } + + addr = start + img->offset; + pipe->srcp0_addr = addr; + pipe->srcp0_ystride = pipe->src_width * pipe->bpp; + + + pr_debug("%s: mixer=%d ndx=%x addr=%x flags=%x pid=%d\n", __func__, + pipe->mixer_num, pipe->pipe_ndx, (int)addr, pipe->flags, + current->pid); + + if ((req->version_key & VERSION_KEY_MASK) == 0xF9E8D700) + overlay_version = (req->version_key & ~VERSION_KEY_MASK); + + if (pipe->fetch_plane == OVERLAY_PLANE_PSEUDO_PLANAR) { + if (overlay_version > 0) { + img = &req->plane1_data; + get_img(img, info, pipe, 1, &start, &len, &srcp1_file, + &p_need, &srcp1_ihdl); + if (len == 0) { + pr_err("%s: Error to get plane1\n", __func__); + ret = -EINVAL; + goto end; + } + pipe->srcp1_addr = start + img->offset; + } else if (pipe->frame_format == + MDP4_FRAME_FORMAT_VIDEO_SUPERTILE) { + struct tile_desc tile; + + tile_samsung(&tile); + pipe->srcp1_addr = addr + tile_mem_size(pipe, &tile); + } else { + pipe->srcp1_addr = addr + (pipe->src_width * + pipe->src_height); + } + pipe->srcp0_ystride = pipe->src_width; + if ((pipe->src_format == MDP_Y_CRCB_H1V1) || + (pipe->src_format == MDP_Y_CBCR_H1V1)) { + if (pipe->src_width > YUV_444_MAX_WIDTH) + pipe->srcp1_ystride = pipe->src_width << 2; + else + pipe->srcp1_ystride = pipe->src_width << 1; + } else + pipe->srcp1_ystride = pipe->src_width; + + } else if (pipe->fetch_plane == OVERLAY_PLANE_PLANAR) { + if (overlay_version > 0) { + img = &req->plane1_data; + get_img(img, info, pipe, 1, &start, &len, &srcp1_file, + &p_need, &srcp1_ihdl); + if (len == 0) { + pr_err("%s: Error to get plane1\n", __func__); + ret = -EINVAL; + goto end; + } + pipe->srcp1_addr = start + img->offset; + + img = &req->plane2_data; + get_img(img, info, pipe, 2, &start, &len, &srcp2_file, + &p_need, &srcp2_ihdl); + if (len == 0) { + pr_err("%s: Error to get plane2\n", __func__); + ret = -EINVAL; + goto end; + } + pipe->srcp2_addr = start + img->offset; + } else { + if (pipe->src_format == MDP_Y_CR_CB_GH2V2) { + addr += (ALIGN(pipe->src_width, 16) * + pipe->src_height); + pipe->srcp1_addr = addr; + addr += ((ALIGN((pipe->src_width / 2), 16)) * + (pipe->src_height / 2)); + pipe->srcp2_addr = addr; + } else { + addr += (pipe->src_width * pipe->src_height); + pipe->srcp1_addr = addr; + addr += ((pipe->src_width / 2) * + (pipe->src_height / 2)); + pipe->srcp2_addr = addr; + } + } + /* mdp planar format expects Cb in srcp1 and Cr in p2 */ + if ((pipe->src_format == MDP_Y_CR_CB_H2V2) || + (pipe->src_format == MDP_Y_CR_CB_GH2V2)) + swap(pipe->srcp1_addr, pipe->srcp2_addr); + + if (pipe->src_format == MDP_Y_CR_CB_GH2V2) { + pipe->srcp0_ystride = ALIGN(pipe->src_width, 16); + pipe->srcp1_ystride = ALIGN(pipe->src_width / 2, 16); + pipe->srcp2_ystride = ALIGN(pipe->src_width / 2, 16); + } else { + pipe->srcp0_ystride = pipe->src_width; + pipe->srcp1_ystride = pipe->src_width / 2; + pipe->srcp2_ystride = pipe->src_width / 2; + } + } + + mdp4_overlay_mdp_perf_req(mfd, ctrl->plist); + + if (pipe->mixer_num == MDP4_MIXER2 || ctrl->panel_mode & MDP4_PANEL_MDDI) + goto mddi; + + if (pipe->mixer_num == MDP4_MIXER0) { + if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) { + /* cndx = 0 */ + mdp4_dsi_cmd_pipe_queue(0, pipe); + } + if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) { + /* cndx = 0 */ + mdp4_dsi_video_pipe_queue(0, pipe); + } else if (ctrl->panel_mode & MDP4_PANEL_LCDC) { + /* cndx = 0 */ + mdp4_lcdc_pipe_queue(0, pipe); + } + } else if (pipe->mixer_num == MDP4_MIXER1) { + if (ctrl->panel_mode & MDP4_PANEL_DTV) + mdp4_dtv_pipe_queue(0, pipe);/* cndx = 0 */ + } + + mutex_unlock(&mfd->dma->ov_mutex); + return ret; + +mddi: + if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) { + mdp4_overlay_vg_setup(pipe); /* video/graphic pipe */ + } else { + mdp4_overlay_rgb_setup(pipe); /* rgb pipe */ + } + + mdp4_mixer_stage_up(pipe, 0); + + if (pipe->mixer_num == MDP4_MIXER2) { + ctrl->mixer2_played++; +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL + if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) { + mdp4_writeback_dma_busy_wait(mfd); + mdp4_writeback_kickoff_video(mfd, pipe); + } +#endif + } else if (ctrl->panel_mode & MDP4_PANEL_MDDI) { + if (pipe->flags & MDP_OV_PLAY_NOWAIT) { + mdp4_stat.overlay_play[pipe->mixer_num]++; + mutex_unlock(&mfd->dma->ov_mutex); + goto end; + } + mdp4_mixer_stage_commit(pipe->mixer_num); + mdp4_mddi_dma_busy_wait(mfd); + mdp4_mddi_kickoff_video(mfd, pipe); + } + + /* write out DPP HSIC registers */ + if (pipe->flags & MDP_DPP_HSIC) + mdp4_hsic_update(pipe); + if (!(pipe->flags & MDP_OV_PLAY_NOWAIT)) + mdp4_iommu_unmap(pipe); + mdp4_stat.overlay_play[pipe->mixer_num]++; + +end: + mutex_unlock(&mfd->dma->ov_mutex); + +#ifdef CONFIG_ANDROID_PMEM + if (srcp0_file) + put_pmem_file(srcp0_file); + if (srcp1_file) + put_pmem_file(srcp1_file); + if (srcp2_file) + put_pmem_file(srcp2_file); +#endif + /* only source may use frame buffer */ + if (img->flags & MDP_MEMORY_ID_TYPE_FB) + fput_light(srcp0_file, ps0_need); + return ret; +} + +struct msm_iommu_ctx { + char *name; + int domain; +}; + +static struct msm_iommu_ctx msm_iommu_ctx_names[] = { + /* Display read*/ + { + .name = "mdp_port0_cb0", + .domain = DISPLAY_READ_DOMAIN, + }, + /* Display read*/ + { + .name = "mdp_port0_cb1", + .domain = DISPLAY_READ_DOMAIN, + }, + /* Display write */ + { + .name = "mdp_port1_cb0", + .domain = DISPLAY_READ_DOMAIN, + }, + /* Display write */ + { + .name = "mdp_port1_cb1", + .domain = DISPLAY_READ_DOMAIN, + }, +}; + +static struct msm_iommu_ctx msm_iommu_split_ctx_names[] = { + /* Display read*/ + { + .name = "mdp_port0_cb0", + .domain = DISPLAY_READ_DOMAIN, + }, + /* Display read*/ + { + .name = "mdp_port0_cb1", + .domain = DISPLAY_WRITE_DOMAIN, + }, + /* Display write */ + { + .name = "mdp_port1_cb0", + .domain = DISPLAY_READ_DOMAIN, + }, + /* Display write */ + { + .name = "mdp_port1_cb1", + .domain = DISPLAY_WRITE_DOMAIN, + }, +}; + +void mdp4_iommu_attach(void) +{ + static int done; + struct msm_iommu_ctx *ctx_names; + struct iommu_domain *domain; + int i, arr_size; + + if (!done) { + if (mdp_iommu_split_domain) { + ctx_names = msm_iommu_split_ctx_names; + arr_size = ARRAY_SIZE(msm_iommu_split_ctx_names); + } else { + ctx_names = msm_iommu_ctx_names; + arr_size = ARRAY_SIZE(msm_iommu_ctx_names); + } + + for (i = 0; i < arr_size; i++) { + int domain_idx; + struct device *ctx = msm_iommu_get_ctx( + ctx_names[i].name); + + if (!ctx) + continue; + + domain_idx = ctx_names[i].domain; + + domain = msm_get_iommu_domain(domain_idx); + if (!domain) + continue; + + if (iommu_attach_device(domain, ctx)) { + WARN(1, "%s: could not attach domain %d to context %s." + " iommu programming will not occur.\n", + __func__, domain_idx, + ctx_names[i].name); + continue; + } + } + done = 1; + } +} + +int mdp4_v4l2_overlay_set(struct fb_info *info, struct mdp_overlay *req, + struct mdp4_overlay_pipe **ppipe) +{ + struct mdp4_overlay_pipe *pipe; + int err; + struct msm_fb_data_type *mfb = info->par; + + req->z_order = 0; + req->id = MSMFB_NEW_REQUEST; + req->is_fg = false; + req->alpha = 0xff; + err = mdp4_overlay_req2pipe(req, MDP4_MIXER0, &pipe, mfb); + if (err < 0) { + pr_err("%s:Could not allocate MDP overlay pipe\n", __func__); + return err; + } + msm_fb_signal_timeline(mfd); + + mdp4_mixer_blend_setup(pipe->mixer_num); + *ppipe = pipe; + + return 0; +} + +void mdp4_v4l2_overlay_clear(struct mdp4_overlay_pipe *pipe) +{ + mdp4_overlay_reg_flush(pipe, 1); + mdp4_mixer_stage_down(pipe, 1); + mdp4_overlay_pipe_free(pipe); +} + +int mdp4_v4l2_overlay_play(struct fb_info *info, struct mdp4_overlay_pipe *pipe, + unsigned long srcp0_addr, unsigned long srcp1_addr, + unsigned long srcp2_addr) +{ + struct msm_fb_data_type *mfd = info->par; + int err; + + if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) + return -EINTR; + + switch (pipe->src_format) { + case MDP_Y_CR_CB_H2V2: + /* YUV420 */ + pipe->srcp0_addr = srcp0_addr; + pipe->srcp0_ystride = pipe->src_width; + /* + * For YUV420, the luma plane is 1 byte per pixel times + * num of pixels in the image Also, the planes are + * switched in MDP, srcp2 is actually first chroma plane + */ + pipe->srcp2_addr = srcp1_addr ? srcp1_addr : + pipe->srcp0_addr + (pipe->src_width * pipe->src_height); + pipe->srcp2_ystride = pipe->src_width/2; + /* + * The chroma planes are half the size of the luma + * planes + */ + pipe->srcp1_addr = srcp2_addr ? srcp2_addr : + pipe->srcp2_addr + + (pipe->src_width * pipe->src_height / 4); + pipe->srcp1_ystride = pipe->src_width/2; + break; + case MDP_Y_CRCB_H2V2: + /* NV12 */ + pipe->srcp0_addr = srcp0_addr; + pipe->srcp0_ystride = pipe->src_width; + pipe->srcp1_addr = srcp1_addr ? srcp1_addr : + pipe->srcp0_addr + + (pipe->src_width * pipe->src_height); + pipe->srcp1_ystride = pipe->src_width; + break; + default: + pr_err("%s: format (%u) is not supported\n", __func__, + pipe->src_format); + err = -EINVAL; + goto done; + } + + pr_debug("%s: pipe ndx=%d stage=%d format=%x\n", __func__, + pipe->pipe_ndx, pipe->mixer_stage, pipe->src_format); + + if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) + mdp4_overlay_vg_setup(pipe); + else + mdp4_overlay_rgb_setup(pipe); + + if (ctrl->panel_mode & MDP4_PANEL_LCDC) + mdp4_overlay_reg_flush(pipe, 1); + + mdp4_mixer_stage_up(pipe, 0); /* mixer stage commit commits this */ + mdp4_mixer_stage_commit(pipe->mixer_num); + +#ifdef V4L2_VSYNC + /* + * TODO: incorporate v4l2 into vsycn driven mechanism + */ + if (ctrl->panel_mode & MDP4_PANEL_LCDC) { + mdp4_overlay_lcdc_vsync_push(mfd, pipe); + } else { +#ifdef CONFIG_FB_MSM_MIPI_DSI + if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) { + mdp4_dsi_cmd_dma_busy_wait(mfd); + mdp4_dsi_cmd_kickoff_video(mfd, pipe); + } +#else + if (ctrl->panel_mode & MDP4_PANEL_MDDI) { + mdp4_mddi_dma_busy_wait(mfd); + mdp4_mddi_kickoff_video(mfd, pipe); + } +#endif + } +#endif + +done: + mutex_unlock(&mfd->dma->ov_mutex); + return err; +} diff --git a/drivers/video/msm/mdp4_overlay_atv.c b/drivers/video/msm/mdp4_overlay_atv.c index 5f84662d..4cbac095 100644 --- a/drivers/video/msm/mdp4_overlay_atv.c +++ b/drivers/video/msm/mdp4_overlay_atv.c @@ -184,6 +184,8 @@ void mdp4_atv_overlay(struct msm_fb_data_type *mfd) } else { pipe->srcp0_addr = (uint32)(buf + buf_offset); } + mdp_update_pm(mfd, vsync_ctrl_db[0].vsync_time); + mdp4_overlay_mdp_perf_req(pipe, mfd); mdp4_overlay_mdp_perf_upd(mfd, 1); mdp4_overlay_rgb_setup(pipe); diff --git a/drivers/video/msm/mdp4_overlay_dsi_cmd.c b/drivers/video/msm/mdp4_overlay_dsi_cmd.c index 7a768788..1821cc0f 100644 --- a/drivers/video/msm/mdp4_overlay_dsi_cmd.c +++ b/drivers/video/msm/mdp4_overlay_dsi_cmd.c @@ -31,14 +31,14 @@ #include "mipi_dsi.h" #include "mdp4.h" -static int dsi_state; - -#define TOUT_PERIOD HZ /* 1 second */ -#define MS_100 (HZ/10) /* 100 ms */ - static int vsync_start_y_adjust = 4; #define MAX_CONTROLLER 1 + +/* + * VSYNC_EXPIRE_TICK == 0 means clock always on + * VSYNC_EXPIRE_TICK == 4 is recommended + */ #define VSYNC_EXPIRE_TICK 4 static struct vsycn_ctrl { @@ -51,10 +51,10 @@ static struct vsycn_ctrl { u32 ov_done; u32 dmap_koff; u32 dmap_done; + u32 pan_display; uint32 rdptr_intr_tot; uint32 rdptr_sirq_tot; atomic_t suspend; - atomic_t vsync_resume; int wait_vsync_cnt; int blt_change; int blt_free; @@ -70,8 +70,8 @@ static struct vsycn_ctrl { struct vsync_update vlist[2]; int vsync_enabled; int clk_enabled; - int clk_control; int new_update; + int clk_control; ktime_t vsync_time; struct work_struct clk_work; } vsync_ctrl_db[MAX_CONTROLLER]; @@ -155,7 +155,7 @@ static void mdp4_dsi_cmd_do_blt(struct msm_fb_data_type *mfd, int enable) int cndx = 0; struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; - int need_wait; + int need_wait = 0; vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; @@ -163,7 +163,7 @@ static void mdp4_dsi_cmd_do_blt(struct msm_fb_data_type *mfd, int enable) mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0); if (mfd->ov0_wb_buf->write_addr == 0) { - pr_info("%s: no blt_base assigned\n", __func__); + pr_err("%s: no blt_base assigned\n", __func__); return; } @@ -205,7 +205,7 @@ static void mdp4_dsi_cmd_do_blt(struct msm_fb_data_type *mfd, int enable) vctrl->blt_free = 4; /* 4 commits to free wb buf */ } - pr_info("%s: changed=%d enable=%d ov_blt_addr=%x\n", __func__, + pr_debug("%s: changed=%d enable=%d ov_blt_addr=%x\n", __func__, vctrl->blt_change, enable, (int)pipe->ov_blt_addr); spin_unlock_irqrestore(&vctrl->spin_lock, flags); @@ -229,8 +229,10 @@ void mdp4_dsi_cmd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe) vctrl = &vsync_ctrl_db[cndx]; - if (atomic_read(&vctrl->suspend) > 0) + if (atomic_read(&vctrl->suspend)) { + pr_err("%s: suspended, no more pipe queue\n", __func__); return; + } mutex_lock(&vctrl->update_lock); undx = vctrl->update_ndx; @@ -250,7 +252,7 @@ void mdp4_dsi_cmd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe) static void mdp4_dsi_cmd_blt_ov_update(struct mdp4_overlay_pipe *pipe); -int mdp4_dsi_cmd_pipe_commit(void) +int mdp4_dsi_cmd_pipe_commit(int cndx, int wait) { int i, undx; int mixer = 0; @@ -271,6 +273,8 @@ int mdp4_dsi_cmd_pipe_commit(void) pipe = vctrl->base_pipe; mixer = pipe->mixer_num; + mdp_update_pm(vctrl->mfd, vctrl->vsync_time); + if (vp->update_cnt == 0) { mutex_unlock(&vctrl->update_lock); return cnt; @@ -363,20 +367,28 @@ int mdp4_dsi_cmd_pipe_commit(void) mdp4_dsi_cmd_blt_ov_update(pipe); pipe->ov_cnt++; vctrl->ov_koff++; + INIT_COMPLETION(vctrl->ov_comp); vsync_irq_enable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM); } else { + INIT_COMPLETION(vctrl->dmap_comp); vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM); vctrl->dmap_koff++; } - pr_debug("%s: kickoff\n", __func__); + pr_debug("%s: kickoff, pid=%d\n", __func__, current->pid); /* kickoff overlay engine */ mdp4_stat.kickoff_ov0++; outpdw(MDP_BASE + 0x0004, 0); - mb(); + mb(); /* make sure kickoff ececuted */ spin_unlock_irqrestore(&vctrl->spin_lock, flags); mdp4_stat.overlay_commit[pipe->mixer_num]++; + if (wait) { + long long tick; + + mdp4_dsi_cmd_wait4vsync(cndx, &tick); + } + return cnt; } @@ -386,15 +398,14 @@ void mdp4_dsi_cmd_vsync_ctrl(struct fb_info *info, int enable) { struct vsycn_ctrl *vctrl; unsigned long flags; - int clk_set_on = 0; int cndx = 0; + int clk_set_on = 0; vctrl = &vsync_ctrl_db[cndx]; - pr_debug("%s: clk_enabled=%d vsycn_enabeld=%d req=%d\n", __func__, - vctrl->clk_enabled, vctrl->vsync_enabled, enable); - mutex_lock(&vctrl->update_lock); + pr_debug("%s: clk_enabled=%d vsync_enabled=%d req=%d\n", __func__, + vctrl->clk_enabled, vctrl->vsync_enabled, enable); if (vctrl->vsync_enabled == enable) { mutex_unlock(&vctrl->update_lock); @@ -404,33 +415,28 @@ void mdp4_dsi_cmd_vsync_ctrl(struct fb_info *info, int enable) vctrl->vsync_enabled = enable; if (enable) { + spin_lock_irqsave(&vctrl->spin_lock, flags); + vctrl->clk_control = 0; + vctrl->expire_tick = 0; + spin_unlock_irqrestore(&vctrl->spin_lock, flags); if (vctrl->clk_enabled == 0) { pr_debug("%s: SET_CLK_ON\n", __func__); mipi_dsi_clk_cfg(1); mdp_clk_ctrl(1); vctrl->clk_enabled = 1; + vctrl->new_update = 1; clk_set_on = 1; } - spin_lock_irqsave(&vctrl->spin_lock, flags); - vctrl->clk_control = 0; - vctrl->expire_tick = 0; - vctrl->new_update = 1; if (clk_set_on) { vsync_irq_enable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM); } - spin_unlock_irqrestore(&vctrl->spin_lock, flags); } else { spin_lock_irqsave(&vctrl->spin_lock, flags); - vctrl->clk_control = 1; - if (vctrl->clk_enabled) - vctrl->expire_tick = VSYNC_EXPIRE_TICK; + vctrl->expire_tick = VSYNC_EXPIRE_TICK; spin_unlock_irqrestore(&vctrl->spin_lock, flags); } mutex_unlock(&vctrl->update_lock); - - if (vctrl->vsync_enabled && atomic_read(&vctrl->suspend) == 0) - atomic_set(&vctrl->vsync_resume, 1); } void mdp4_dsi_cmd_wait4vsync(int cndx, long long *vtime) @@ -508,19 +514,33 @@ static void primary_rdptr_isr(int cndx) struct vsycn_ctrl *vctrl; vctrl = &vsync_ctrl_db[cndx]; - pr_debug("%s: ISR, cpu=%d\n", __func__, smp_processor_id()); + pr_debug("%s: ISR, tick=%d pan=%d cpu=%d\n", __func__, + vctrl->expire_tick, vctrl->pan_display, smp_processor_id()); vctrl->rdptr_intr_tot++; spin_lock(&vctrl->spin_lock); vctrl->vsync_time = ktime_get(); + if (vctrl->new_update) { + vctrl->new_update = 0; + spin_unlock(&vctrl->spin_lock); + return; + } + complete_all(&vctrl->vsync_comp); vctrl->wait_vsync_cnt = 0; if (vctrl->expire_tick) { vctrl->expire_tick--; - if (vctrl->expire_tick == 0) - schedule_work(&vctrl->clk_work); + if (vctrl->expire_tick == 0) { + if (vctrl->pan_display <= 0) { + vctrl->clk_control = 1; + schedule_work(&vctrl->clk_work); + } else { + /* wait one more vsycn */ + vctrl->expire_tick += 1; + } + } } spin_unlock(&vctrl->spin_lock); } @@ -533,16 +553,22 @@ void mdp4_dmap_done_dsi_cmd(int cndx) vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; + if (pipe == NULL) + return; /* blt enabled */ spin_lock(&vctrl->spin_lock); vsync_irq_disable(INTR_DMA_P_DONE, MDP_DMAP_TERM); vctrl->dmap_done++; + + if (vctrl->pan_display) + vctrl->pan_display--; + diff = vctrl->ov_done - vctrl->dmap_done; pr_debug("%s: ov_koff=%d ov_done=%d dmap_koff=%d dmap_done=%d cpu=%d\n", __func__, vctrl->ov_koff, vctrl->ov_done, vctrl->dmap_koff, vctrl->dmap_done, smp_processor_id()); - complete_all(&vctrl->dmap_comp); + complete(&vctrl->dmap_comp); if (diff <= 0) { if (vctrl->blt_wait) vctrl->blt_wait = 0; @@ -557,7 +583,7 @@ void mdp4_dmap_done_dsi_cmd(int cndx) vctrl->dmap_koff++; vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM); outpdw(MDP_BASE + 0x000c, 0); /* kickoff dmap engine */ - mb(); + mb(); /* make sure kickoff executed */ spin_unlock(&vctrl->spin_lock); } @@ -572,11 +598,13 @@ void mdp4_overlay0_done_dsi_cmd(int cndx) vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; + if (pipe == NULL) + return; spin_lock(&vctrl->spin_lock); vsync_irq_disable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM); vctrl->ov_done++; - complete_all(&vctrl->ov_comp); + complete(&vctrl->ov_comp); diff = vctrl->ov_done - vctrl->dmap_done; pr_debug("%s: ov_koff=%d ov_done=%d dmap_koff=%d dmap_done=%d cpu=%d\n", @@ -606,26 +634,30 @@ void mdp4_overlay0_done_dsi_cmd(int cndx) vctrl->dmap_koff++; vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM); outpdw(MDP_BASE + 0x000c, 0); /* kickoff dmap engine */ - mb(); + mb(); /* make sure kickoff executed */ spin_unlock(&vctrl->spin_lock); } static void clk_ctrl_work(struct work_struct *work) { + unsigned long flags; struct vsycn_ctrl *vctrl = container_of(work, typeof(*vctrl), clk_work); - unsigned long flags; mutex_lock(&vctrl->update_lock); + spin_lock_irqsave(&vctrl->spin_lock, flags); if (vctrl->clk_control && vctrl->clk_enabled) { - pr_debug("%s: SET_CLK_OFF\n", __func__); - mdp_clk_ctrl(0); - mipi_dsi_clk_cfg(0); - spin_lock_irqsave(&vctrl->spin_lock, flags); vsync_irq_disable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM); vctrl->clk_enabled = 0; vctrl->clk_control = 0; spin_unlock_irqrestore(&vctrl->spin_lock, flags); + /* make sure dsi link is idle */ + mipi_dsi_mdp_busy_wait(); + mipi_dsi_clk_cfg(0); + mdp_clk_ctrl(0); + pr_debug("%s: SET_CLK_OFF, pid=%d\n", __func__, current->pid); + } else { + spin_unlock_irqrestore(&vctrl->spin_lock, flags); } mutex_unlock(&vctrl->update_lock); } @@ -642,8 +674,7 @@ ssize_t mdp4_dsi_cmd_show_event(struct device *dev, cndx = 0; vctrl = &vsync_ctrl_db[0]; - if (atomic_read(&vctrl->suspend) > 0 || - atomic_read(&vctrl->vsync_resume) == 0) + if (atomic_read(&vctrl->suspend) > 0) return 0; spin_lock_irqsave(&vctrl->spin_lock, flags); @@ -656,7 +687,10 @@ ssize_t mdp4_dsi_cmd_show_event(struct device *dev, msecs_to_jiffies(VSYNC_PERIOD * 4)); if (ret <= 0) { vctrl->wait_vsync_cnt = 0; - return -EBUSY; + vsync_tick = ktime_to_ns(ktime_get()); + ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick); + buf[strlen(buf) + 1] = '\0'; + return ret; } spin_lock_irqsave(&vctrl->spin_lock, flags); @@ -664,6 +698,8 @@ ssize_t mdp4_dsi_cmd_show_event(struct device *dev, spin_unlock_irqrestore(&vctrl->spin_lock, flags); ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick); + pr_debug("%s: UEVENT\n", __func__); + buf[strlen(buf) + 1] = '\0'; return ret; } @@ -688,7 +724,6 @@ void mdp4_dsi_rdptr_init(int cndx) init_completion(&vctrl->dmap_comp); init_completion(&vctrl->vsync_comp); spin_lock_init(&vctrl->spin_lock); - atomic_set(&vctrl->vsync_resume, 1); atomic_set(&vctrl->suspend, 1); INIT_WORK(&vctrl->clk_work, clk_ctrl_work); } @@ -698,20 +733,6 @@ void mdp4_primary_rdptr(void) primary_rdptr_isr(0); } -void mdp4_overlay_dsi_state_set(int state) -{ - unsigned long flag; - - spin_lock_irqsave(&mdp_spin_lock, flag); - dsi_state = state; - spin_unlock_irqrestore(&mdp_spin_lock, flag); -} - -int mdp4_overlay_dsi_state_get(void) -{ - return dsi_state; -} - static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp) { /* @@ -849,6 +870,9 @@ static void mdp4_overlay_update_dsi_cmd(struct msm_fb_data_type *mfd) pipe = vctrl->base_pipe; } + /* TE enabled */ + mdp4_mipi_vsync_enable(mfd, pipe, 0); + MDP_OUTP(MDP_BASE + 0x021c, 10); /* read pointer */ /* @@ -936,6 +960,7 @@ void mdp4_dsi_cmd_3d_sbys(struct msm_fb_data_type *mfd, pipe->srcp0_addr = (uint32)src; mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_clk_ctrl(1); mdp4_overlay_rgb_setup(pipe); @@ -951,6 +976,7 @@ void mdp4_dsi_cmd_3d_sbys(struct msm_fb_data_type *mfd, mdp4_mixer_stage_commit(pipe->mixer_num); /* MDP cmd block disable */ + mdp_clk_ctrl(0); mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); } @@ -977,9 +1003,10 @@ int mdp4_dsi_cmd_on(struct platform_device *pdev) struct msm_fb_data_type *mfd; struct vsycn_ctrl *vctrl; - pr_debug("%s+:\n", __func__); + pr_debug("%s+: pid=%d\n", __func__, current->pid); mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + mfd->cont_splash_done = 1; vctrl = &vsync_ctrl_db[cndx]; vctrl->mfd = mfd; @@ -992,8 +1019,8 @@ int mdp4_dsi_cmd_on(struct platform_device *pdev) mdp4_iommu_attach(); atomic_set(&vctrl->suspend, 0); - pr_debug("%s-:\n", __func__); + pr_debug("%s-:\n", __func__); return ret; } @@ -1004,8 +1031,12 @@ int mdp4_dsi_cmd_off(struct platform_device *pdev) struct msm_fb_data_type *mfd; struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; + struct vsync_update *vp; + int undx; + int need_wait, cnt; + unsigned long flags; - pr_debug("%s+:\n", __func__); + pr_debug("%s+: pid=%d\n", __func__, current->pid); mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); @@ -1016,43 +1047,56 @@ int mdp4_dsi_cmd_off(struct platform_device *pdev) return ret; } + need_wait = 0; + mutex_lock(&vctrl->update_lock); atomic_set(&vctrl->suspend, 1); - atomic_set(&vctrl->vsync_resume, 0); complete_all(&vctrl->vsync_comp); + pr_debug("%s: clk=%d pan=%d\n", __func__, + vctrl->clk_enabled, vctrl->pan_display); + if (vctrl->clk_enabled) + need_wait = 1; + mutex_unlock(&vctrl->update_lock); + + cnt = 0; + if (need_wait) { + while (vctrl->clk_enabled) { + msleep(20); + cnt++; + if (cnt > 10) + break; + } + } + + if (cnt > 10) { + spin_lock_irqsave(&vctrl->spin_lock, flags); + vctrl->clk_control = 0; + vctrl->clk_enabled = 0; + vctrl->expire_tick = 0; + spin_unlock_irqrestore(&vctrl->spin_lock, flags); + mipi_dsi_clk_cfg(0); + mdp_clk_ctrl(0); + pr_err("%s: Error, SET_CLK_OFF by force\n", __func__); + } + /* sanity check, free pipes besides base layer */ mdp4_overlay_unset_mixer(pipe->mixer_num); mdp4_mixer_stage_down(pipe, 1); mdp4_overlay_pipe_free(pipe); vctrl->base_pipe = NULL; - if (vctrl->clk_enabled) { + undx = vctrl->update_ndx; + vp = &vctrl->vlist[undx]; + if (vp->update_cnt) { /* - * in case of suspend, vsycn_ctrl off is not - * received from frame work which left clock on - * then, clock need to be turned off here + * pipe's iommu will be freed at next overlay play + * and iommu_drop statistic will be increased by one */ - mdp_clk_ctrl(0); + vp->update_cnt = 0; /* empty queue */ } - vctrl->clk_enabled = 0; - vctrl->vsync_enabled = 0; - vctrl->clk_control = 0; - vctrl->expire_tick = 0; - - vsync_irq_disable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM); - - pr_debug("%s-:\n", __func__); - - /* - * footswitch off - * this will casue all mdp register - * to be reset to default - * after footswitch on later - */ - return ret; } @@ -1087,37 +1131,50 @@ void mdp4_dsi_cmd_overlay(struct msm_fb_data_type *mfd) struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; unsigned long flags; + int clk_set_on = 0; + mutex_lock(&mfd->dma->ov_mutex); vctrl = &vsync_ctrl_db[cndx]; - if (!mfd->panel_power_on) + if (!mfd->panel_power_on) { + mutex_unlock(&mfd->dma->ov_mutex); return; + } pipe = vctrl->base_pipe; if (pipe == NULL) { pr_err("%s: NO base pipe\n", __func__); + mutex_unlock(&mfd->dma->ov_mutex); return; } mutex_lock(&vctrl->update_lock); - if (!vctrl->clk_enabled) { - pr_err("%s: mdp clocks disabled\n", __func__); + if (atomic_read(&vctrl->suspend)) { mutex_unlock(&vctrl->update_lock); + mutex_unlock(&mfd->dma->ov_mutex); + pr_err("%s: suspended, no more pan display\n", __func__); return; - } - mutex_unlock(&vctrl->update_lock); spin_lock_irqsave(&vctrl->spin_lock, flags); - if (vctrl->expire_tick) { - /* - * in the middle of shutting clocks down - * delay to allow pan display to go through - */ + vctrl->clk_control = 0; + vctrl->pan_display++; + if (!vctrl->clk_enabled) { + clk_set_on = 1; + vctrl->clk_enabled = 1; vctrl->expire_tick = VSYNC_EXPIRE_TICK; } spin_unlock_irqrestore(&vctrl->spin_lock, flags); + if (clk_set_on) { + pr_debug("%s: SET_CLK_ON\n", __func__); + mipi_dsi_clk_cfg(1); + mdp_clk_ctrl(1); + vsync_irq_enable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM); + } + + mutex_unlock(&vctrl->update_lock); + if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE) { mdp4_mipi_vsync_enable(mfd, pipe, 0); mdp4_overlay_setup_pipe_addr(mfd, pipe); @@ -1125,10 +1182,7 @@ void mdp4_dsi_cmd_overlay(struct msm_fb_data_type *mfd) } mdp4_overlay_mdp_perf_upd(mfd, 1); - - mutex_lock(&mfd->dma->ov_mutex); - mdp4_dsi_cmd_pipe_commit(); - mutex_unlock(&mfd->dma->ov_mutex); - + mdp4_dsi_cmd_pipe_commit(cndx, 1); mdp4_overlay_mdp_perf_upd(mfd, 0); + mutex_unlock(&mfd->dma->ov_mutex); } diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c index 5025954c..72a53fcf 100644 --- a/drivers/video/msm/mdp4_overlay_dsi_video.c +++ b/drivers/video/msm/mdp4_overlay_dsi_video.c @@ -56,6 +56,8 @@ static struct vsycn_ctrl { int wait_vsync_cnt; int blt_change; int blt_free; + u32 blt_ctrl; + u32 blt_mode; int sysfs_created; struct mutex update_lock; struct completion ov_comp; @@ -147,7 +149,7 @@ static void mdp4_dsi_video_blt_ov_update(struct mdp4_overlay_pipe *pipe); static void mdp4_dsi_video_wait4dmap(int cndx); static void mdp4_dsi_video_wait4ov(int cndx); -int mdp4_dsi_video_pipe_commit(void) +int mdp4_dsi_video_pipe_commit(int cndx, int wait) { int i, undx; @@ -159,7 +161,7 @@ int mdp4_dsi_video_pipe_commit(void) unsigned long flags; int cnt = 0; - vctrl = &vsync_ctrl_db[0]; + vctrl = &vsync_ctrl_db[cndx]; mutex_lock(&vctrl->update_lock); undx = vctrl->update_ndx; @@ -167,6 +169,8 @@ int mdp4_dsi_video_pipe_commit(void) pipe = vctrl->base_pipe; mixer = pipe->mixer_num; + mdp_update_pm(vctrl->mfd, vctrl->vsync_time); + if (vp->update_cnt == 0) { mutex_unlock(&vctrl->update_lock); return cnt; @@ -194,20 +198,6 @@ int mdp4_dsi_video_pipe_commit(void) } spin_unlock_irqrestore(&vctrl->spin_lock, flags); - mdp4_overlay_mdp_perf_upd(vctrl->mfd, 1); - - if (vctrl->blt_change) { - pipe = vctrl->base_pipe; - spin_lock_irqsave(&vctrl->spin_lock, flags); - INIT_COMPLETION(vctrl->dmap_comp); - INIT_COMPLETION(vctrl->ov_comp); - vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM); - spin_unlock_irqrestore(&vctrl->spin_lock, flags); - mdp4_dsi_video_wait4dmap(0); - if (pipe->ov_blt_addr) - mdp4_dsi_video_wait4ov(0); - } - pipe = vp->plist; for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) { @@ -253,6 +243,13 @@ int mdp4_dsi_video_pipe_commit(void) mdp4_stat.overlay_commit[pipe->mixer_num]++; + if (wait) { + if (pipe->ov_blt_addr) + mdp4_dsi_video_wait4ov(cndx); + else + mdp4_dsi_video_wait4dmap(cndx); + } + return cnt; } @@ -348,7 +345,6 @@ static void mdp4_dsi_video_wait4dmap_done(int cndx) vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM); spin_unlock_irqrestore(&vctrl->spin_lock, flags); mdp4_dsi_video_wait4dmap(cndx); - vsync_irq_disable(INTR_DMA_P_DONE, MDP_DMAP_TERM); } @@ -385,9 +381,6 @@ ssize_t mdp4_dsi_video_show_event(struct device *dev, atomic_read(&vctrl->vsync_resume) == 0) return 0; - if (!dsi_video_enabled) - return 0; - spin_lock_irqsave(&vctrl->spin_lock, flags); if (vctrl->wait_vsync_cnt == 0) INIT_COMPLETION(vctrl->vsync_comp); @@ -397,7 +390,10 @@ ssize_t mdp4_dsi_video_show_event(struct device *dev, msecs_to_jiffies(VSYNC_PERIOD * 4)); if (ret <= 0) { vctrl->wait_vsync_cnt = 0; - return -EBUSY; + vsync_tick = ktime_to_ns(ktime_get()); + ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick); + buf[strlen(buf) + 1] = '\0'; + return ret; } spin_lock_irqsave(&vctrl->spin_lock, flags); @@ -490,6 +486,7 @@ int mdp4_dsi_video_on(struct platform_device *pdev) int ret = 0; int cndx = 0; struct vsycn_ctrl *vctrl; + struct msm_panel_info *pinfo; vctrl = &vsync_ctrl_db[cndx]; mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); @@ -502,6 +499,9 @@ int mdp4_dsi_video_on(struct platform_device *pdev) vctrl->mfd = mfd; vctrl->dev = mfd->fbi->dev; + pinfo = &mfd->panel_info; + vctrl->blt_ctrl = pinfo->lcd.blt_ctrl; + vctrl->blt_mode = pinfo->lcd.blt_mode; /* mdp clock on */ mdp_clk_ctrl(1); @@ -544,7 +544,11 @@ int mdp4_dsi_video_on(struct platform_device *pdev) mfd->cont_splash_done = 1; mdp4_dsi_video_wait4dmap_done(0); MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0); + dsi_video_enabled = 0; mipi_dsi_controller_cfg(0); + /* Clks are enabled in probe. + Disabling clocks now */ + mdp_clk_ctrl(0); } pipe->src_height = fbi->var.yres; @@ -567,6 +571,7 @@ int mdp4_dsi_video_on(struct platform_device *pdev) pipe->dst_w = fbi->var.xres; mdp4_overlay_mdp_pipe_req(pipe, mfd); + mdp4_calc_blt_mdp_bw(mfd, pipe); atomic_set(&vctrl->suspend, 0); @@ -668,8 +673,9 @@ int mdp4_dsi_video_off(struct platform_device *pdev) struct msm_fb_data_type *mfd; struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; + struct vsync_update *vp; unsigned long flags; - int need_wait = 0; + int undx, need_wait = 0; mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); vctrl = &vsync_ctrl_db[cndx]; @@ -697,6 +703,21 @@ int mdp4_dsi_video_off(struct platform_device *pdev) dsi_video_enabled = 0; + if (vctrl->vsync_irq_enabled) { + vctrl->vsync_irq_enabled = 0; + vsync_irq_disable(INTR_PRIMARY_VSYNC, MDP_PRIM_VSYNC_TERM); + } + + undx = vctrl->update_ndx; + vp = &vctrl->vlist[undx]; + if (vp->update_cnt) { + /* + * pipe's iommu will be freed at next overlay play + * and iommu_drop statistic will be increased by one + */ + vp->update_cnt = 0; /* empty queue */ + } + if (pipe) { /* sanity check, free pipes besides base layer */ mdp4_overlay_unset_mixer(pipe->mixer_num); @@ -906,26 +927,16 @@ void mdp4_dmap_done_dsi_video(int cndx) } vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; + if (pipe == NULL) + return; spin_lock(&vctrl->spin_lock); vsync_irq_disable(INTR_DMA_P_DONE, MDP_DMAP_TERM); if (vctrl->blt_change) { mdp4_overlayproc_cfg(pipe); mdp4_overlay_dmap_xy(pipe); - if (pipe->ov_blt_addr) { - mdp4_dsi_video_blt_ov_update(pipe); - pipe->ov_cnt++; - /* Prefill one frame */ - vsync_irq_enable(INTR_OVERLAY0_DONE, - MDP_OVERLAY0_TERM); - /* kickoff overlay0 engine */ - mdp4_stat.kickoff_ov0++; - vctrl->ov_koff++; /* make up for prefill */ - outpdw(MDP_BASE + 0x0004, 0); - } vctrl->blt_change = 0; } - complete_all(&vctrl->dmap_comp); mdp4_overlay_dma_commit(cndx); spin_unlock(&vctrl->spin_lock); @@ -941,6 +952,8 @@ void mdp4_overlay0_done_dsi_video(int cndx) vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; + if (pipe == NULL) + return; spin_lock(&vctrl->spin_lock); vsync_irq_disable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM); @@ -966,19 +979,44 @@ static void mdp4_dsi_video_do_blt(struct msm_fb_data_type *mfd, int enable) int cndx = 0; struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; + long long vtime; + u32 mode, ctrl; vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; - mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0); + mode = (dbg_force_ov0_blt & 0x0f) ? + (dbg_force_ov0_blt & 0x0f) : vctrl->blt_mode; + ctrl = (dbg_force_ov0_blt >> 4) ? + (dbg_force_ov0_blt >> 4) : vctrl->blt_ctrl; - if (mfd->ov0_wb_buf->write_addr == 0) { - pr_info("%s: no blt_base assigned\n", __func__); + pr_debug("%s: mode=%d, enable=%d ov_blt_addr=%x\n", + __func__, mode, enable, (int)pipe->ov_blt_addr); + + if ((mode == MDP4_OVERLAY_MODE_BLT_ALWAYS_OFF) && + !pipe->ov_blt_addr) + return; + else if ((mode == MDP4_OVERLAY_MODE_BLT_ALWAYS_ON) && + pipe->ov_blt_addr) return; + else if (enable && pipe->ov_blt_addr) + return; + else if (!enable && !pipe->ov_blt_addr) + return; + + if (pipe->ov_blt_addr == 0) { + mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0); + if (mfd->ov0_wb_buf->write_addr == 0) { + pr_warning("%s: no blt_base assigned\n", __func__); + return; + } } + pr_debug("%s: mode=%d, enable=%d ov_blt_addr=%x\n", + __func__, mode, enable, (int)pipe->ov_blt_addr); + spin_lock_irqsave(&vctrl->spin_lock, flag); - if (enable && pipe->ov_blt_addr == 0) { + if (pipe->ov_blt_addr == 0) { pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr; pipe->dma_blt_addr = mfd->ov0_wb_buf->read_addr; pipe->ov_cnt = 0; @@ -987,23 +1025,57 @@ static void mdp4_dsi_video_do_blt(struct msm_fb_data_type *mfd, int enable) vctrl->ov_done = 0; vctrl->blt_free = 0; mdp4_stat.blt_dsi_video++; - vctrl->blt_change++; - } else if (enable == 0 && pipe->ov_blt_addr) { + } else { pipe->ov_blt_addr = 0; pipe->dma_blt_addr = 0; vctrl->blt_free = 4; /* 4 commits to free wb buf */ - vctrl->blt_change++; } + spin_unlock_irqrestore(&vctrl->spin_lock, flag); - pr_info("%s: changed=%d enable=%d ov_blt_addr=%x\n", __func__, - vctrl->blt_change, enable, (int)pipe->ov_blt_addr); - - if (!vctrl->blt_change) { + if (ctrl == MDP4_OVERLAY_BLT_SWITCH_TG_ON) { + spin_lock_irqsave(&vctrl->spin_lock, flag); + if (!dsi_video_enabled) { + pr_debug("%s: blt switched not in ISR dsi_video_enabled=%d\n", + __func__, dsi_video_enabled); + mdp4_overlayproc_cfg(pipe); + mdp4_overlay_dmap_xy(pipe); + } else { + pr_debug("%s: blt switched in ISR dsi_video_enabled=%d\n", + __func__, dsi_video_enabled); + vctrl->blt_change++; + } spin_unlock_irqrestore(&vctrl->spin_lock, flag); - return; - } - - spin_unlock_irqrestore(&vctrl->spin_lock, flag); + if (dsi_video_enabled) + mdp4_dsi_video_wait4dmap_done(0); + } else if (ctrl == MDP4_OVERLAY_BLT_SWITCH_TG_OFF) { + pr_debug("%s: blt switched by turning TG off\n", __func__); + if (dsi_video_enabled) { + mdp4_dsi_video_wait4vsync(0, &vtime); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0); + mdp4_dsi_video_wait4dmap_done(0); + } + mdp4_overlayproc_cfg(pipe); + mdp4_overlay_dmap_xy(pipe); + if (dsi_video_enabled) { + /* + * need wait for more than 1 ms to + * make sure dsi lanes' fifo is empty and + * lanes in stop state befroe reset + * controller + */ + usleep(2000); + mipi_dsi_sw_reset(); + MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 1); + } + } else if (ctrl == MDP4_OVERLAY_BLT_SWITCH_POLL) { + pr_debug("%s: blt switched by polling mdp status\n", __func__); + if (dsi_video_enabled) + while (inpdw(MDP_BASE + 0x0018) & 0x05) + cpu_relax(); + mdp4_overlayproc_cfg(pipe); + mdp4_overlay_dmap_xy(pipe); + } else + pr_err("%s: ctrl=%d is not supported\n", __func__, ctrl); } void mdp4_dsi_video_overlay_blt(struct msm_fb_data_type *mfd, @@ -1028,15 +1100,19 @@ void mdp4_dsi_video_overlay(struct msm_fb_data_type *mfd) uint8 *buf; unsigned int buf_offset; int bpp; - int cndx = 0; + int cnt, cndx = 0; struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; + mutex_lock(&mfd->dma->ov_mutex); + vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; - if (!pipe || !mfd->panel_power_on) + if (!pipe || !mfd->panel_power_on) { + mutex_unlock(&mfd->dma->ov_mutex); return; + } pr_debug("%s: cpu=%d pid=%d\n", __func__, smp_processor_id(), current->pid); @@ -1053,17 +1129,16 @@ void mdp4_dsi_video_overlay(struct msm_fb_data_type *mfd) mdp4_dsi_video_pipe_queue(0, pipe); } - mutex_lock(&mfd->dma->ov_mutex); mdp4_overlay_mdp_perf_upd(mfd, 1); - mdp4_dsi_video_pipe_commit(); - mutex_unlock(&mfd->dma->ov_mutex); - - if (pipe->ov_blt_addr) - mdp4_dsi_video_wait4ov(0); - else - mdp4_dsi_video_wait4dmap(0); + cnt = mdp4_dsi_video_pipe_commit(cndx, 0); + if (cnt) { + if (pipe->ov_blt_addr) + mdp4_dsi_video_wait4ov(cndx); + else + mdp4_dsi_video_wait4dmap(cndx); + } mdp4_overlay_mdp_perf_upd(mfd, 0); + mutex_unlock(&mfd->dma->ov_mutex); } - diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c index ef661c98..8539c00b 100644 --- a/drivers/video/msm/mdp4_overlay_dtv.c +++ b/drivers/video/msm/mdp4_overlay_dtv.c @@ -33,7 +33,6 @@ #include "mdp4.h" #define DTV_BASE 0xD0000 - static int dtv_enabled; /*#define DEBUG*/ @@ -55,14 +54,6 @@ static void __mdp_outp(uint32 port, uint32 value) static int first_pixel_start_x; static int first_pixel_start_y; -void mdp4_dtv_base_swap(int cndx, struct mdp4_overlay_pipe *pipe) -{ -#ifdef BYPASS4 - if (hdmi_prim_display) - dtv_pipe = pipe; -#endif -} - #define MAX_CONTROLLER 1 static struct vsycn_ctrl { @@ -75,16 +66,24 @@ static struct vsycn_ctrl { int dmae_wait_cnt; int wait_vsync_cnt; int blt_change; + int blt_ctrl; + int blt_mode; + int blt_free; int sysfs_created; struct mutex update_lock; struct completion ov_comp; struct completion dmae_comp; struct completion vsync_comp; spinlock_t spin_lock; + struct msm_fb_data_type *mfd; struct mdp4_overlay_pipe *base_pipe; struct vsync_update vlist[2]; int vsync_irq_enabled; ktime_t vsync_time; + uint32 *avtimer; + int vg1fd; + int vg2fd; + unsigned long long avtimer_tick; } vsync_ctrl_db[MAX_CONTROLLER]; static void vsync_irq_enable(int intr, int term) @@ -163,9 +162,10 @@ void mdp4_dtv_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe) } static void mdp4_dtv_blt_ov_update(struct mdp4_overlay_pipe *pipe); +static void mdp4_dtv_wait4ov(int cndx); static void mdp4_dtv_wait4dmae(int cndx); -int mdp4_dtv_pipe_commit(void) +int mdp4_dtv_pipe_commit(int cndx, int wait) { int i, undx; @@ -177,7 +177,7 @@ int mdp4_dtv_pipe_commit(void) unsigned long flags; int cnt = 0; - vctrl = &vsync_ctrl_db[0]; + vctrl = &vsync_ctrl_db[cndx]; mutex_lock(&vctrl->update_lock); undx = vctrl->update_ndx; vp = &vctrl->vlist[undx]; @@ -185,6 +185,8 @@ int mdp4_dtv_pipe_commit(void) mixer = pipe->mixer_num; mdp4_overlay_iommu_unmap_freelist(mixer); + mdp_update_pm(vctrl->mfd, vctrl->vsync_time); + if (vp->update_cnt == 0) { mutex_unlock(&vctrl->update_lock); return 0; @@ -193,6 +195,13 @@ int mdp4_dtv_pipe_commit(void) vctrl->update_ndx++; vctrl->update_ndx &= 0x01; vp->update_cnt = 0; /* reset */ + + if (vctrl->blt_free) { + vctrl->blt_free--; + if (vctrl->blt_free == 0) + mdp4_free_writeback_buf(vctrl->mfd, mixer); + } + mutex_unlock(&vctrl->update_lock); pipe = vp->plist; @@ -222,6 +231,7 @@ int mdp4_dtv_pipe_commit(void) if (pipe->ov_blt_addr) { mdp4_dtv_blt_ov_update(pipe); pipe->blt_ov_done++; + INIT_COMPLETION(vctrl->ov_comp); vsync_irq_enable(INTR_OVERLAY1_DONE, MDP_OVERLAY1_TERM); mb(); pipe->blt_ov_koff++; @@ -236,6 +246,12 @@ int mdp4_dtv_pipe_commit(void) spin_unlock_irqrestore(&vctrl->spin_lock, flags); mdp4_stat.overlay_commit[pipe->mixer_num]++; + if (wait) { + if (pipe->ov_blt_addr) + mdp4_dtv_wait4ov(cndx); + else + mdp4_dtv_wait4dmae(cndx); + } return cnt; } @@ -295,6 +311,23 @@ void mdp4_dtv_wait4vsync(int cndx, long long *vtime) *vtime = ktime_to_ns(vctrl->vsync_time); } +static void mdp4_dtv_wait4ov(int cndx) +{ + struct vsycn_ctrl *vctrl; + + if (cndx >= MAX_CONTROLLER) { + pr_err("%s: out or range: cndx=%d\n", __func__, cndx); + return; + } + + vctrl = &vsync_ctrl_db[cndx]; + + if (atomic_read(&vctrl->suspend) > 0) + return; + + wait_for_completion(&vctrl->ov_comp); +} + static void mdp4_dtv_wait4dmae(int cndx) { struct vsycn_ctrl *vctrl; @@ -319,10 +352,14 @@ ssize_t mdp4_dtv_show_event(struct device *dev, struct vsycn_ctrl *vctrl; ssize_t ret = 0; unsigned long flags; - u64 vsync_tick; + char ch = '\0'; + int vg1fd = -1, vg2fd = -1; + unsigned long long avtimer_tick = 0; + u64 vsync_tick = 0; cndx = 0; vctrl = &vsync_ctrl_db[0]; + memset(buf, 0, 64); if (atomic_read(&vctrl->suspend) > 0 || !external_common_state->hpd_state || @@ -339,17 +376,54 @@ ssize_t mdp4_dtv_show_event(struct device *dev, msecs_to_jiffies(VSYNC_PERIOD * 4)); if (ret <= 0) { vctrl->wait_vsync_cnt = 0; - return -EBUSY; + vsync_tick = ktime_to_ns(ktime_get()); + ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick); + buf[strlen(buf) + 1] = '\0'; + return ret; } spin_lock_irqsave(&vctrl->spin_lock, flags); + vg1fd = vctrl->vg1fd; + vg2fd = vctrl->vg2fd; + avtimer_tick = vctrl->avtimer_tick; vsync_tick = ktime_to_ns(vctrl->vsync_time); spin_unlock_irqrestore(&vctrl->spin_lock, flags); - ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick); - buf[strlen(buf) + 1] = '\0'; + ret = snprintf(buf, PAGE_SIZE, + "VSYNC=%llu%c" + "AVSYNCTP=%llu%c" + "VG1MEMID=%d%c" + "VG2MEMID=%d", + vsync_tick, + ch, avtimer_tick, + ch, vg1fd, + ch, vg2fd); + return ret; } + +static void mdp4_dtv_wait4dmae_done(int cndx) +{ + unsigned long flags; + struct vsycn_ctrl *vctrl; + + if (cndx >= MAX_CONTROLLER) { + pr_err("%s: out or range: cndx=%d\n", __func__, cndx); + return; + } + + vctrl = &vsync_ctrl_db[cndx]; + + if (atomic_read(&vctrl->suspend) > 0) + return; + + spin_lock_irqsave(&vctrl->spin_lock, flags); + INIT_COMPLETION(vctrl->dmae_comp); + vsync_irq_enable(INTR_DMA_E_DONE, MDP_DMA_E_TERM); + spin_unlock_irqrestore(&vctrl->spin_lock, flags); + mdp4_dtv_wait4dmae(cndx); +} + void mdp4_dtv_vsync_init(int cndx) { struct vsycn_ctrl *vctrl; @@ -376,6 +450,24 @@ void mdp4_dtv_vsync_init(int cndx) spin_lock_init(&vctrl->spin_lock); } +void mdp4_dtv_base_swap(int cndx, struct mdp4_overlay_pipe *pipe) +{ + struct vsycn_ctrl *vctrl; + + if (!hdmi_prim_display) { + pr_err("%s: failed, hdmi is not primary\n", __func__); + return; + } + + if (cndx >= MAX_CONTROLLER) { + pr_err("%s: out or range: cndx=%d\n", __func__, cndx); + return; + } + + vctrl = &vsync_ctrl_db[cndx]; + vctrl->base_pipe = pipe; +} + static int mdp4_dtv_start(struct msm_fb_data_type *mfd) { int dtv_width; @@ -410,6 +502,9 @@ static int mdp4_dtv_start(struct msm_fb_data_type *mfd) int hsync_end_x; struct fb_info *fbi; struct fb_var_screeninfo *var; + struct vsycn_ctrl *vctrl; + + vctrl = &vsync_ctrl_db[0]; if (!mfd) return -ENODEV; @@ -420,6 +515,8 @@ static int mdp4_dtv_start(struct msm_fb_data_type *mfd) fbi = mfd->fbi; var = &fbi->var; + vctrl->mfd = mfd; + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); if (hdmi_prim_display) { if (is_mdp4_hw_reset()) { @@ -514,6 +611,7 @@ static int mdp4_dtv_start(struct msm_fb_data_type *mfd) /* enable DTV block */ MDP_OUTP(MDP_BASE + DTV_BASE, 1); + dtv_enabled = 1; return 0; } @@ -528,6 +626,7 @@ static int mdp4_dtv_stop(struct msm_fb_data_type *mfd) return -EINVAL; MDP_OUTP(MDP_BASE + DTV_BASE, 0); + dtv_enabled = 0; return 0; } @@ -538,6 +637,7 @@ int mdp4_dtv_on(struct platform_device *pdev) int ret = 0; int cndx = 0; struct vsycn_ctrl *vctrl; + struct msm_panel_info *pinfo; vctrl = &vsync_ctrl_db[cndx]; @@ -549,7 +649,12 @@ int mdp4_dtv_on(struct platform_device *pdev) if (mfd->key != MFD_KEY) return -EINVAL; + vctrl->mfd = mfd; vctrl->dev = mfd->fbi->dev; + pinfo = &mfd->panel_info; + + vctrl->blt_ctrl = pinfo->lcd.blt_ctrl; + vctrl->blt_mode = pinfo->lcd.blt_mode; mdp_footswitch_ctrl(TRUE); /* Mdp clock enable */ @@ -571,6 +676,12 @@ int mdp4_dtv_on(struct platform_device *pdev) pr_warn("%s: panel_next_on failed", __func__); atomic_set(&vctrl->suspend, 0); + if (mfd->avtimer_phy && (vctrl->avtimer == NULL)) { + vctrl->avtimer = (uint32 *)ioremap(mfd->avtimer_phy, 8); + if (vctrl->avtimer == NULL) + pr_err(" avtimer ioremap fail\n"); + } + pr_info("%s:\n", __func__); return ret; @@ -581,8 +692,10 @@ int mdp4_dtv_off(struct platform_device *pdev) struct msm_fb_data_type *mfd; int ret = 0; int cndx = 0; + int undx; struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; + struct vsync_update *vp; mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); @@ -591,10 +704,10 @@ int mdp4_dtv_off(struct platform_device *pdev) atomic_set(&vctrl->suspend, 1); atomic_set(&vctrl->vsync_resume, 0); - if (vctrl->vsync_irq_enabled) { - while (vctrl->wait_vsync_cnt) - msleep(20); /* >= 17 ms */ - } + /* wait for one vsycn time to make sure + * previous stage_commit had been kicked in + */ + msleep(20); /* >= 17 ms */ complete_all(&vctrl->vsync_comp); @@ -623,6 +736,26 @@ int mdp4_dtv_off(struct platform_device *pdev) mdp4_overlay_panel_mode_unset(MDP4_MIXER1, MDP4_PANEL_DTV); + if (vctrl->vsync_irq_enabled) { + vctrl->vsync_irq_enabled = 0; + vsync_irq_disable(INTR_EXTERNAL_VSYNC, MDP_EXTER_VSYNC_TERM); + } + + undx = vctrl->update_ndx; + vp = &vctrl->vlist[undx]; + if (vp->update_cnt) { + /* + * pipe's iommu will be freed at next overlay play + * and iommu_drop statistic will be increased by one + */ + vp->update_cnt = 0; /* empty queue */ + } + + if (vctrl->avtimer != NULL) { + iounmap(vctrl->avtimer); + vctrl->avtimer = NULL; + } + ret = panel_next_off(pdev); mdp_footswitch_ctrl(FALSE); @@ -680,11 +813,6 @@ static void mdp4_dtv_blt_dmae_update(struct mdp4_overlay_pipe *pipe) MDP_OUTP(MDP_BASE + 0xb0008, addr); } -void mdp4_overlay_dtv_set_perf(struct msm_fb_data_type *mfd) -{ - -} - static void mdp4_overlay_dtv_alloc_pipe(struct msm_fb_data_type *mfd, int32 ptype, struct vsycn_ctrl *vctrl) { @@ -744,6 +872,7 @@ static void mdp4_overlay_dtv_alloc_pipe(struct msm_fb_data_type *mfd, pipe->srcp0_ystride = fbi->fix.line_length; mdp4_overlay_mdp_pipe_req(pipe, mfd); + mdp4_calc_blt_mdp_bw(mfd, pipe); ret = mdp4_overlay_format2pipe(pipe); if (ret < 0) @@ -801,7 +930,7 @@ int mdp4_overlay_dtv_unset(struct msm_fb_data_type *mfd, struct vsycn_ctrl *vctrl; vctrl = &vsync_ctrl_db[cndx]; - if (vctrl->base_pipe != NULL) + if (vctrl->base_pipe == NULL) return 0; if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE && @@ -809,6 +938,12 @@ int mdp4_overlay_dtv_unset(struct msm_fb_data_type *mfd, result = mdp4_dtv_stop(mfd); vctrl->base_pipe = NULL; } + + if (pipe->pipe_num == OVERLAY_PIPE_VG1) + vctrl->vg1fd = -1; + else if (pipe->pipe_num == OVERLAY_PIPE_VG2) + vctrl->vg2fd = -1; + return result; } @@ -818,6 +953,7 @@ void mdp4_external_vsync_dtv(void) { int cndx; struct vsycn_ctrl *vctrl; + uint32 *tp, LSW; cndx = 0; vctrl = &vsync_ctrl_db[cndx]; @@ -825,6 +961,15 @@ void mdp4_external_vsync_dtv(void) spin_lock(&vctrl->spin_lock); vctrl->vsync_time = ktime_get(); + vctrl->avtimer_tick = 0; + + if (vctrl->avtimer && ((vctrl->vg1fd > 0) || (vctrl->vg2fd > 0))) { + tp = vctrl->avtimer; + LSW = inpdw(tp); + tp++; + vctrl->avtimer_tick = (unsigned long long) inpdw(tp); + vctrl->avtimer_tick = ((vctrl->avtimer_tick << 32) | LSW); + } if (vctrl->wait_vsync_cnt) { complete_all(&vctrl->vsync_comp); @@ -850,22 +995,14 @@ void mdp4_dmae_done_dtv(void) vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; + if (pipe == NULL) + return; pr_debug("%s: cpu=%d\n", __func__, smp_processor_id()); spin_lock(&vctrl->spin_lock); if (vctrl->blt_change) { - if (pipe->ov_blt_addr) { - mdp4_overlayproc_cfg(pipe); - mdp4_overlay_dmae_xy(pipe); - mdp4_dtv_blt_ov_update(pipe); - pipe->blt_ov_done++; - - /* Prefill one frame */ - vsync_irq_enable(INTR_OVERLAY1_DONE, MDP_OVERLAY1_TERM); - /* kickoff overlay1 engine */ - mdp4_stat.kickoff_ov1++; - outpdw(MDP_BASE + 0x0008, 0); - } + mdp4_overlayproc_cfg(pipe); + mdp4_overlay_dmae_xy(pipe); vctrl->blt_change = 0; } @@ -890,6 +1027,8 @@ void mdp4_overlay1_done_dtv(void) vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; + if (pipe == NULL) + return; spin_lock(&vctrl->spin_lock); if (pipe->ov_blt_addr == 0) { @@ -898,104 +1037,154 @@ void mdp4_overlay1_done_dtv(void) } mdp4_dtv_blt_dmae_update(pipe); + complete_all(&vctrl->ov_comp); pipe->blt_dmap_done++; vsync_irq_disable(INTR_OVERLAY1_DONE, MDP_OVERLAY1_TERM); spin_unlock(&vctrl->spin_lock); } -void mdp4_dtv_set_black_screen(void) +void mdp4_dtv_set_black_screen() { char *rgb_base; /*Black color*/ uint32 color = 0x00000000; uint32 temp_src_format; - int cndx = 0; + int commit = 1, cndx = 0; + int pipe_num = OVERLAY_PIPE_RGB1; struct vsycn_ctrl *vctrl; vctrl = &vsync_ctrl_db[cndx]; - if (vctrl->base_pipe == NULL || !hdmi_prim_display) { - pr_err("dtv_pipe is not configured yet\n"); + if (!hdmi_prim_display) return; - } - rgb_base = MDP_BASE + MDP4_RGB_BASE; - rgb_base += (MDP4_RGB_OFF * vctrl->base_pipe->pipe_num); - /* - * RGB Constant Color - */ + if (vctrl->base_pipe == NULL) + commit = 0; + else + pipe_num = vctrl->base_pipe->pipe_num; + + rgb_base = MDP_BASE; + rgb_base += (MDP4_RGB_OFF * (pipe_num + 2)); + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* RGB Constant Color */ MDP_OUTP(rgb_base + 0x1008, color); - /* - * MDP_RGB_SRC_FORMAT - */ + + /* MDP_RGB_SRC_FORMAT */ temp_src_format = inpdw(rgb_base + 0x0050); MDP_OUTP(rgb_base + 0x0050, temp_src_format | BIT(22)); - mdp4_overlay_reg_flush(vctrl->base_pipe, 1); - mdp4_mixer_stage_up(vctrl->base_pipe, 0); - mdp4_mixer_stage_commit(vctrl->base_pipe->mixer_num); - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + if (commit) { + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + mdp4_overlay_reg_flush(vctrl->base_pipe, 1); + mdp4_mixer_stage_up(vctrl->base_pipe, 0); + mdp4_mixer_stage_commit(vctrl->base_pipe->mixer_num); + } else { + /* MDP_OVERLAY_REG_FLUSH for pipe*/ + MDP_OUTP(MDP_BASE + 0x18000, + BIT(pipe_num + 2) | BIT(MDP4_MIXER1)); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + } } static void mdp4_dtv_do_blt(struct msm_fb_data_type *mfd, int enable) { unsigned long flag; - int data; int cndx = 0; struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; + u32 mode, ctrl; vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; - mdp4_allocate_writeback_buf(mfd, MDP4_MIXER1); + mode = (dbg_force_ov1_blt & 0x0f) ? + (dbg_force_ov1_blt & 0x0f) : vctrl->blt_mode; + ctrl = (dbg_force_ov1_blt >> 4) ? + (dbg_force_ov1_blt >> 4) : vctrl->blt_ctrl; + + pr_debug("%s: mode=%d, ctrl = %d, enable=%d ov_blt_addr=%x\n", + __func__, mode, ctrl, enable, (int)pipe->ov_blt_addr); - if (!mfd->ov1_wb_buf->write_addr) { - pr_info("%s: ctrl=%d blt_base NOT assigned\n", __func__, cndx); + if ((mode == MDP4_OVERLAY_MODE_BLT_ALWAYS_OFF) && + !pipe->ov_blt_addr) + return; + else if ((mode == MDP4_OVERLAY_MODE_BLT_ALWAYS_ON) && + pipe->ov_blt_addr) + return; + else if (enable && pipe->ov_blt_addr) + return; + else if (!enable && !pipe->ov_blt_addr) return; + + if (pipe->ov_blt_addr == 0) { + mdp4_allocate_writeback_buf(vctrl->mfd, MDP4_MIXER1); + if (!vctrl->mfd->ov1_wb_buf->write_addr) { + pr_warning("%s: ctrl=%d blt_base NOT assigned\n", + __func__, cndx); + return; + } } + pr_debug("%s: mode=%d, ctrl=%d, enable=%d ov_blt_addr=%x\n", + __func__, mode, ctrl, enable, (int)pipe->ov_blt_addr); + spin_lock_irqsave(&vctrl->spin_lock, flag); if (enable && pipe->ov_blt_addr == 0) { - pipe->ov_blt_addr = mfd->ov1_wb_buf->write_addr; - pipe->dma_blt_addr = mfd->ov1_wb_buf->read_addr; + pipe->ov_blt_addr = vctrl->mfd->ov1_wb_buf->write_addr; + pipe->dma_blt_addr = vctrl->mfd->ov1_wb_buf->read_addr; pipe->blt_cnt = 0; pipe->ov_cnt = 0; pipe->blt_dmap_done = 0; pipe->blt_ov_koff = 0; pipe->blt_ov_done = 0; mdp4_stat.blt_dtv++; - vctrl->blt_change++; + vctrl->blt_free = 0; } else if (enable == 0 && pipe->ov_blt_addr) { pipe->ov_blt_addr = 0; pipe->dma_blt_addr = 0; - vctrl->blt_change++; + vctrl->blt_free = 4; } - - pr_info("%s: enable=%d change=%d blt_addr=%x\n", __func__, - enable, vctrl->blt_change, (int)pipe->ov_blt_addr); - - if (!vctrl->blt_change) { - spin_unlock_irqrestore(&vctrl->spin_lock, flag); - return; - } - - atomic_set(&vctrl->suspend, 1); spin_unlock_irqrestore(&vctrl->spin_lock, flag); - data = inpdw(MDP_BASE + DTV_BASE); - data &= 0x01; - if (data) /* timing generator enabled */ - mdp4_dtv_wait4dmae(0); + if (ctrl == MDP4_OVERLAY_BLT_SWITCH_TG_ON) { + spin_lock_irqsave(&vctrl->spin_lock, flag); + if (!dtv_enabled) { + pr_debug("%s: blt switched not in isr dtv_enabled=%d\n", + __func__, dtv_enabled); + mdp4_overlayproc_cfg(pipe); + mdp4_overlay_dmae_xy(pipe); + } else { + pr_debug("%s: blt switched in ISR dtv_enabled=%d\n", + __func__, dtv_enabled); + vctrl->blt_change++; - if (pipe->ov_blt_addr == 0) { - MDP_OUTP(MDP_BASE + DTV_BASE, 0); /* stop dtv */ - msleep(20); + } + spin_unlock_irqrestore(&vctrl->spin_lock, flag); + if (dtv_enabled) + mdp4_dtv_wait4dmae_done(0); + } else if (ctrl == MDP4_OVERLAY_BLT_SWITCH_TG_OFF) { + pr_debug("%s: dtv blt switched by turning TG off\n", + __func__); + if (dtv_enabled) { + mdp4_dtv_wait4dmae_done(0); + MDP_OUTP(MDP_BASE + DTV_BASE, 0); + msleep(20); + } mdp4_overlayproc_cfg(pipe); mdp4_overlay_dmae_xy(pipe); - MDP_OUTP(MDP_BASE + DTV_BASE, 1); /* start dtv */ - } - - atomic_set(&vctrl->suspend, 0); + if (dtv_enabled) + MDP_OUTP(MDP_BASE + DTV_BASE, 1); + } else if (ctrl == MDP4_OVERLAY_BLT_SWITCH_POLL) { + pr_debug("%s: dtv blt change by polling status\n", + __func__); + while (inpdw(MDP_BASE + 0x0018) & 0x12) + cpu_relax(); + mdp4_overlayproc_cfg(pipe); + mdp4_overlay_dmae_xy(pipe); + } else + pr_err("%s: ctrl=%d is not supported\n", __func__, ctrl); } void mdp4_dtv_overlay_blt_start(struct msm_fb_data_type *mfd) @@ -1013,9 +1202,13 @@ void mdp4_dtv_overlay(struct msm_fb_data_type *mfd) int cndx = 0; struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; + int wait = 0; - if (!mfd->panel_power_on) + mutex_lock(&mfd->dma->ov_mutex); + if (!mfd->panel_power_on) { + mutex_unlock(&mfd->dma->ov_mutex); return; + } vctrl = &vsync_ctrl_db[cndx]; if (vctrl->base_pipe == NULL) @@ -1025,6 +1218,7 @@ void mdp4_dtv_overlay(struct msm_fb_data_type *mfd) if (pipe == NULL) { pr_warn("%s: dtv_pipe == NULL\n", __func__); + mutex_unlock(&mfd->dma->ov_mutex); return; } @@ -1040,9 +1234,27 @@ void mdp4_dtv_overlay(struct msm_fb_data_type *mfd) mdp4_dtv_pipe_queue(0, pipe); } - mutex_lock(&mfd->dma->ov_mutex); + if (hdmi_prim_display) + wait = 1; + mdp4_overlay_mdp_perf_upd(mfd, 1); - mdp4_dtv_pipe_commit(); + mdp4_dtv_pipe_commit(cndx, wait); mdp4_overlay_mdp_perf_upd(mfd, 0); mutex_unlock(&mfd->dma->ov_mutex); } + +void mdp4_dtv_set_avparams(struct mdp4_overlay_pipe *pipe, int id) +{ + struct vsycn_ctrl *vctrl; + + if (pipe == NULL) { + pr_warn("%s: dtv_pipe == NULL\n", __func__); + return; + } + vctrl = &vsync_ctrl_db[0]; + if (pipe->pipe_num == OVERLAY_PIPE_VG1) + vctrl->vg1fd = id; + else if (pipe->pipe_num == OVERLAY_PIPE_VG2) + vctrl->vg2fd = id; +} + diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c index 79c9e976..3702b59b 100644 --- a/drivers/video/msm/mdp4_overlay_lcdc.c +++ b/drivers/video/msm/mdp4_overlay_lcdc.c @@ -152,7 +152,7 @@ static void mdp4_lcdc_blt_ov_update(struct mdp4_overlay_pipe *pipe); static void mdp4_lcdc_wait4dmap(int cndx); static void mdp4_lcdc_wait4ov(int cndx); -int mdp4_lcdc_pipe_commit(void) +int mdp4_lcdc_pipe_commit(int cndx, int wait) { int i, undx; @@ -164,7 +164,7 @@ int mdp4_lcdc_pipe_commit(void) unsigned long flags; int cnt = 0; - vctrl = &vsync_ctrl_db[0]; + vctrl = &vsync_ctrl_db[cndx]; mutex_lock(&vctrl->update_lock); undx = vctrl->update_ndx; @@ -172,6 +172,8 @@ int mdp4_lcdc_pipe_commit(void) pipe = vctrl->base_pipe; mixer = pipe->mixer_num; + mdp_update_pm(vctrl->mfd, vctrl->vsync_time); + if (vp->update_cnt == 0) { mutex_unlock(&vctrl->update_lock); return 0; @@ -257,6 +259,13 @@ int mdp4_lcdc_pipe_commit(void) mdp4_stat.overlay_commit[pipe->mixer_num]++; + if (wait) { + if (pipe->ov_blt_addr) + mdp4_lcdc_wait4ov(cndx); + else + mdp4_lcdc_wait4dmap(cndx); + } + return cnt; } @@ -368,9 +377,6 @@ ssize_t mdp4_lcdc_show_event(struct device *dev, atomic_read(&vctrl->vsync_resume) == 0) return 0; - if (!lcdc_enabled) - return 0; - spin_lock_irqsave(&vctrl->spin_lock, flags); if (vctrl->wait_vsync_cnt == 0) INIT_COMPLETION(vctrl->vsync_comp); @@ -380,7 +386,10 @@ ssize_t mdp4_lcdc_show_event(struct device *dev, msecs_to_jiffies(VSYNC_PERIOD * 4)); if (ret <= 0) { vctrl->wait_vsync_cnt = 0; - return -EBUSY; + vsync_tick = ktime_to_ns(ktime_get()); + ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick); + buf[strlen(buf) + 1] = '\0'; + return ret; } spin_lock_irqsave(&vctrl->spin_lock, flags); @@ -540,6 +549,7 @@ int mdp4_lcdc_on(struct platform_device *pdev) pipe->bpp = bpp; mdp4_overlay_mdp_pipe_req(pipe, mfd); + mdp4_calc_blt_mdp_bw(mfd, pipe); atomic_set(&vctrl->suspend, 0); @@ -655,8 +665,9 @@ int mdp4_lcdc_off(struct platform_device *pdev) struct msm_fb_data_type *mfd; struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; + struct vsync_update *vp; unsigned long flags; - int need_wait = 0; + int undx, need_wait = 0; mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); vctrl = &vsync_ctrl_db[cndx]; @@ -684,6 +695,21 @@ int mdp4_lcdc_off(struct platform_device *pdev) lcdc_enabled = 0; + if (vctrl->vsync_irq_enabled) { + vctrl->vsync_irq_enabled = 0; + vsync_irq_disable(INTR_PRIMARY_VSYNC, MDP_PRIM_VSYNC_TERM); + } + + undx = vctrl->update_ndx; + vp = &vctrl->vlist[undx]; + if (vp->update_cnt) { + /* + * pipe's iommu will be freed at next overlay play + * and iommu_drop statistic will be increased by one + */ + vp->update_cnt = 0; /* empty queue */ + } + if (pipe) { /* sanity check, free pipes besides base layer */ mdp4_overlay_unset_mixer(pipe->mixer_num); @@ -796,6 +822,8 @@ void mdp4_dmap_done_lcdc(int cndx) } vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; + if (pipe == NULL) + return; spin_lock(&vctrl->spin_lock); vsync_irq_disable(INTR_DMA_P_DONE, MDP_DMAP_TERM); @@ -834,6 +862,8 @@ void mdp4_overlay0_done_lcdc(int cndx) vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; + if (pipe == NULL) + return; spin_lock(&vctrl->spin_lock); vsync_irq_disable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM); @@ -917,16 +947,19 @@ void mdp4_lcdc_overlay(struct msm_fb_data_type *mfd) uint8 *buf; unsigned int buf_offset; int bpp; - int cndx = 0; + int cnt, cndx = 0; struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; + mutex_lock(&mfd->dma->ov_mutex); vctrl = &vsync_ctrl_db[cndx]; pipe = vctrl->base_pipe; - if (!pipe || !mfd->panel_power_on) + if (!pipe || !mfd->panel_power_on) { + mutex_unlock(&mfd->dma->ov_mutex); return; + } pr_debug("%s: cpu=%d pid=%d\n", __func__, smp_processor_id(), current->pid); @@ -943,16 +976,16 @@ void mdp4_lcdc_overlay(struct msm_fb_data_type *mfd) mdp4_lcdc_pipe_queue(0, pipe); } - mutex_lock(&mfd->dma->ov_mutex); mdp4_overlay_mdp_perf_upd(mfd, 1); - mdp4_lcdc_pipe_commit(); - mutex_unlock(&mfd->dma->ov_mutex); - - if (pipe->ov_blt_addr) - mdp4_lcdc_wait4ov(0); - else - mdp4_lcdc_wait4dmap(0); + cnt = mdp4_lcdc_pipe_commit(cndx, 0); + if (cnt) { + if (pipe->ov_blt_addr) + mdp4_lcdc_wait4ov(cndx); + else + mdp4_lcdc_wait4dmap(cndx); + } mdp4_overlay_mdp_perf_upd(mfd, 0); + mutex_unlock(&mfd->dma->ov_mutex); } diff --git a/drivers/video/msm/mdp4_overlay_mddi.c b/drivers/video/msm/mdp4_overlay_mddi.c index 717bba9b..2bdbb1fe 100644 --- a/drivers/video/msm/mdp4_overlay_mddi.c +++ b/drivers/video/msm/mdp4_overlay_mddi.c @@ -17,704 +17,1043 @@ #include #include #include -#include #include -#include #include - -#include -#include #include #include - #include +#include +#include +#include #include "mdp.h" #include "msm_fb.h" #include "mdp4.h" -static struct mdp4_overlay_pipe *mddi_pipe; -static struct msm_fb_data_type *mddi_mfd; -static int busy_wait_cnt; +static int mddi_state; -static int vsync_start_y_adjust = 4; +#define TOUT_PERIOD HZ /* 1 second */ +#define MS_100 (HZ/10) /* 100 ms */ -static int dmap_vsync_enable; +static int vsync_start_y_adjust = 4; -void mdp_dmap_vsync_set(int enable) +#define MAX_CONTROLLER 1 +#define VSYNC_EXPIRE_TICK 8 + +static struct vsycn_ctrl { + struct device *dev; + int inited; + int update_ndx; + int expire_tick; + int blt_wait; + u32 ov_koff; + u32 ov_done; + u32 dmap_koff; + u32 dmap_done; + uint32 rdptr_intr_tot; + uint32 rdptr_sirq_tot; + atomic_t suspend; + int wait_vsync_cnt; + int blt_change; + int blt_free; + int blt_end; + int uevent; + struct mutex update_lock; + struct completion ov_comp; + struct completion dmap_comp; + struct completion vsync_comp; + spinlock_t spin_lock; + struct msm_fb_data_type *mfd; + struct mdp4_overlay_pipe *base_pipe; + struct vsync_update vlist[2]; + int vsync_enabled; + int clk_enabled; + int clk_control; + int new_update; + ktime_t vsync_time; + struct work_struct vsync_work; + struct work_struct clk_work; +} vsync_ctrl_db[MAX_CONTROLLER]; + +static void vsync_irq_enable(int intr, int term) { - dmap_vsync_enable = enable; + unsigned long flag; + + spin_lock_irqsave(&mdp_spin_lock, flag); + /* no need to clear other interrupts for comamnd mode */ + mdp_intr_mask |= intr; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + mdp_enable_irq(term); + spin_unlock_irqrestore(&mdp_spin_lock, flag); } -int mdp_dmap_vsync_get(void) +static void vsync_irq_disable(int intr, int term) { - return dmap_vsync_enable; + unsigned long flag; + + spin_lock_irqsave(&mdp_spin_lock, flag); + /* no need to clrear other interrupts for comamnd mode */ + mdp_intr_mask &= ~intr; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + mdp_disable_irq_nosync(term); + spin_unlock_irqrestore(&mdp_spin_lock, flag); } -void mdp4_mddi_vsync_enable(struct msm_fb_data_type *mfd, - struct mdp4_overlay_pipe *pipe, int which) +static void mdp4_mddi_blt_ov_update(struct mdp4_overlay_pipe *pipe) { - uint32 start_y, data, tear_en; + uint32 off, addr; + int bpp; + char *overlay_base; - tear_en = (1 << which); + if (pipe->ov_blt_addr == 0) + return; - if ((mfd->use_mdp_vsync) && (mfd->ibuf.vsync_enable) && - (mfd->panel_info.lcd.vsync_enable)) { + bpp = 3; /* overlay ouput is RGB888 */ + off = 0; + if (pipe->ov_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + addr = pipe->ov_blt_addr + off; + /* overlay 0 */ + overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */ + outpdw(overlay_base + 0x000c, addr); + outpdw(overlay_base + 0x001c, addr); +} - if (mdp_hw_revision < MDP4_REVISION_V2_1) { - /* need dmas dmap switch */ - if (which == 0 && dmap_vsync_enable == 0 && - mfd->panel_info.lcd.rev < 2) /* dma_p */ - return; - } +static void mdp4_mddi_blt_dmap_update(struct mdp4_overlay_pipe *pipe) +{ + uint32 off, addr; + int bpp; - if (vsync_start_y_adjust <= pipe->dst_y) - start_y = pipe->dst_y - vsync_start_y_adjust; - else - start_y = (mfd->total_lcd_lines - 1) - - (vsync_start_y_adjust - pipe->dst_y); - if (which == 0) - MDP_OUTP(MDP_BASE + 0x210, start_y); /* primary */ - else - MDP_OUTP(MDP_BASE + 0x214, start_y); /* secondary */ + if (pipe->ov_blt_addr == 0) + return; - data = inpdw(MDP_BASE + 0x20c); - data |= tear_en; - MDP_OUTP(MDP_BASE + 0x20c, data); - } else { - data = inpdw(MDP_BASE + 0x20c); - data &= ~tear_en; - MDP_OUTP(MDP_BASE + 0x20c, data); - } + bpp = 3; /* overlay ouput is RGB888 */ + off = 0; + if (pipe->dmap_cnt & 0x01) + off = pipe->src_height * pipe->src_width * bpp; + addr = pipe->dma_blt_addr + off; + + /* dmap */ + MDP_OUTP(MDP_BASE + 0x90008, addr); } -#define WHOLESCREEN +static void mdp4_mddi_wait4dmap(int cndx); +static void mdp4_mddi_wait4ov(int cndx); -void mdp4_overlay_update_lcd(struct msm_fb_data_type *mfd) +static void mdp4_mddi_do_blt(struct msm_fb_data_type *mfd, int enable) { - MDPIBUF *iBuf = &mfd->ibuf; - uint8 *src; - int ptype; - uint32 mddi_ld_param; - uint16 mddi_vdo_packet_reg; + unsigned long flags; + int cndx = 0; + struct vsycn_ctrl *vctrl; struct mdp4_overlay_pipe *pipe; - int ret; + int need_wait; - if (mfd->key != MFD_KEY) - return; + vctrl = &vsync_ctrl_db[cndx]; + pipe = vctrl->base_pipe; - mddi_mfd = mfd; /* keep it */ + mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0); - /* MDP cmd block enable */ - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + if (mfd->ov0_wb_buf->write_addr == 0) { + pr_err("%s: no blt_base assigned\n", __func__); + return; + } - if (mddi_pipe == NULL) { - ptype = mdp4_overlay_format2type(mfd->fb_imgType); - if (ptype < 0) - printk(KERN_INFO "%s: format2type failed\n", __func__); - pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER0); - if (pipe == NULL) - printk(KERN_INFO "%s: pipe_alloc failed\n", __func__); - pipe->pipe_used++; - pipe->mixer_num = MDP4_MIXER0; - pipe->src_format = mfd->fb_imgType; - mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_MDDI); - ret = mdp4_overlay_format2pipe(pipe); - if (ret < 0) - printk(KERN_INFO "%s: format2type failed\n", __func__); + spin_lock_irqsave(&vctrl->spin_lock, flags); + if (enable && pipe->ov_blt_addr == 0) { + vctrl->blt_change++; + if (vctrl->dmap_koff != vctrl->dmap_done) { + INIT_COMPLETION(vctrl->dmap_comp); + need_wait = 1; + } + } else if (enable == 0 && pipe->ov_blt_addr) { + vctrl->blt_change++; + if (vctrl->ov_koff != vctrl->dmap_done) { + INIT_COMPLETION(vctrl->dmap_comp); + need_wait = 1; + } + } + spin_unlock_irqrestore(&vctrl->spin_lock, flags); + + if (need_wait) + mdp4_mddi_wait4dmap(0); + + spin_lock_irqsave(&vctrl->spin_lock, flags); + if (enable && pipe->ov_blt_addr == 0) { + pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr; + pipe->dma_blt_addr = mfd->ov0_wb_buf->read_addr; + pipe->ov_cnt = 0; + pipe->dmap_cnt = 0; + vctrl->ov_koff = vctrl->dmap_koff; + vctrl->ov_done = vctrl->dmap_done; + vctrl->blt_free = 0; + vctrl->blt_wait = 0; + vctrl->blt_end = 0; + mdp4_stat.blt_mddi++; + } else if (enable == 0 && pipe->ov_blt_addr) { + pipe->ov_blt_addr = 0; + pipe->dma_blt_addr = 0; + vctrl->blt_end = 1; + vctrl->blt_free = 4; /* 4 commits to free wb buf */ + } - mddi_pipe = pipe; /* keep it */ - mddi_ld_param = 0; - mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt; + pr_debug("%s: changed=%d enable=%d ov_blt_addr=%x\n", __func__, + vctrl->blt_change, enable, (int)pipe->ov_blt_addr); - if (mdp_hw_revision == MDP4_REVISION_V2_1) { - uint32 data; + spin_unlock_irqrestore(&vctrl->spin_lock, flags); +} - data = inpdw(MDP_BASE + 0x0028); - data &= ~0x0300; /* bit 8, 9, MASTER4 */ - if (mfd->fbi->var.xres == 540) /* qHD, 540x960 */ - data |= 0x0200; - else - data |= 0x0100; +/* + * mdp4_mddi_do_update: + * called from thread context + */ +void mdp4_mddi_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe) +{ + struct vsycn_ctrl *vctrl; + struct vsync_update *vp; + struct mdp4_overlay_pipe *pp; + int undx; - MDP_OUTP(MDP_BASE + 0x00028, data); - } + if (cndx >= MAX_CONTROLLER) { + pr_err("%s: out or range: cndx=%d\n", __func__, cndx); + return; + } - if (mfd->panel_info.type == MDDI_PANEL) { - if (mfd->panel_info.pdest == DISPLAY_1) - mddi_ld_param = 0; - else - mddi_ld_param = 1; - } else { - mddi_ld_param = 2; - } + vctrl = &vsync_ctrl_db[cndx]; - MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param); + if (atomic_read(&vctrl->suspend) > 0) + return; - if (mfd->panel_info.bpp == 24) - MDP_OUTP(MDP_BASE + 0x00094, - (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg); - else if (mfd->panel_info.bpp == 16) - MDP_OUTP(MDP_BASE + 0x00094, - (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg); - else - MDP_OUTP(MDP_BASE + 0x00094, - (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg); + mutex_lock(&vctrl->update_lock); + undx = vctrl->update_ndx; + vp = &vctrl->vlist[undx]; - MDP_OUTP(MDP_BASE + 0x00098, 0x01); - mdp4_init_writeback_buf(mfd, MDP4_MIXER0); - pipe->ov_blt_addr = 0; - pipe->dma_blt_addr = 0; - } else { - pipe = mddi_pipe; - } + pp = &vp->plist[pipe->pipe_ndx - 1]; /* ndx start form 1 */ - /* 0 for dma_p, client_id = 0 */ - MDP_OUTP(MDP_BASE + 0x00090, 0); + pr_debug("%s: vndx=%d pipe_ndx=%d expire=%x pid=%d\n", __func__, + undx, pipe->pipe_ndx, vctrl->expire_tick, current->pid); + *pp = *pipe; /* clone it */ + vp->update_cnt++; - src = (uint8 *) iBuf->buf; + mutex_unlock(&vctrl->update_lock); + mdp4_stat.overlay_play[pipe->mixer_num]++; +} -#ifdef WHOLESCREEN +static void mdp4_mddi_blt_ov_update(struct mdp4_overlay_pipe *pipe); - { - struct fb_info *fbi; +int mdp4_mddi_pipe_commit(void) +{ + int i, undx; + int mixer = 0; + struct vsycn_ctrl *vctrl; + struct vsync_update *vp; + struct mdp4_overlay_pipe *pipe; + struct mdp4_overlay_pipe *real_pipe; + unsigned long flags; + int need_dmap_wait = 0; + int need_ov_wait = 0; + int cnt = 0; + + vctrl = &vsync_ctrl_db[0]; + + mutex_lock(&vctrl->update_lock); + undx = vctrl->update_ndx; + vp = &vctrl->vlist[undx]; + pipe = vctrl->base_pipe; + mixer = pipe->mixer_num; + + if (vp->update_cnt == 0) { + mutex_unlock(&vctrl->update_lock); + return cnt; + } - fbi = mfd->fbi; - pipe->src_height = fbi->var.yres; - pipe->src_width = fbi->var.xres; - pipe->src_h = fbi->var.yres; - pipe->src_w = fbi->var.xres; - pipe->src_y = 0; - pipe->src_x = 0; - pipe->dst_h = fbi->var.yres; - pipe->dst_w = fbi->var.xres; - pipe->dst_y = 0; - pipe->dst_x = 0; - pipe->srcp0_addr = (uint32)src; - pipe->srcp0_ystride = fbi->fix.line_length; + vctrl->update_ndx++; + vctrl->update_ndx &= 0x01; + vp->update_cnt = 0; /* reset */ + if (vctrl->blt_free) { + vctrl->blt_free--; + if (vctrl->blt_free == 0) + mdp4_free_writeback_buf(vctrl->mfd, mixer); } + mutex_unlock(&vctrl->update_lock); + + /* free previous committed iommu back to pool */ + mdp4_overlay_iommu_unmap_freelist(mixer); + + spin_lock_irqsave(&vctrl->spin_lock, flags); + if (pipe->ov_blt_addr) { + /* Blt */ + if (vctrl->blt_wait) + need_dmap_wait = 1; + if (vctrl->ov_koff != vctrl->ov_done) { + INIT_COMPLETION(vctrl->ov_comp); + need_ov_wait = 1; + } + } else { + /* direct out */ + if (vctrl->dmap_koff != vctrl->dmap_done) { + INIT_COMPLETION(vctrl->dmap_comp); + pr_debug("%s: wait, ok=%d od=%d dk=%d dd=%d cpu=%d\n", + __func__, vctrl->ov_koff, vctrl->ov_done, + vctrl->dmap_koff, vctrl->dmap_done, smp_processor_id()); + need_dmap_wait = 1; + } + } + spin_unlock_irqrestore(&vctrl->spin_lock, flags); -#else - if (mdp4_overlay_active(MDP4_MIXER0)) { - struct fb_info *fbi; + if (need_dmap_wait) { + pr_debug("%s: wait4dmap\n", __func__); + mdp4_mddi_wait4dmap(0); + } - fbi = mfd->fbi; - pipe->src_height = fbi->var.yres; - pipe->src_width = fbi->var.xres; - pipe->src_h = fbi->var.yres; - pipe->src_w = fbi->var.xres; - pipe->src_y = 0; - pipe->src_x = 0; - pipe->dst_h = fbi->var.yres; - pipe->dst_w = fbi->var.xres; - pipe->dst_y = 0; - pipe->dst_x = 0; - pipe->srcp0_addr = (uint32) src; - pipe->srcp0_ystride = fbi->fix.line_length; - } else { - /* starting input address */ - src += (iBuf->dma_x + iBuf->dma_y * iBuf->ibuf_width) - * iBuf->bpp; - - pipe->src_height = iBuf->dma_h; - pipe->src_width = iBuf->dma_w; - pipe->src_h = iBuf->dma_h; - pipe->src_w = iBuf->dma_w; - pipe->src_y = 0; - pipe->src_x = 0; - pipe->dst_h = iBuf->dma_h; - pipe->dst_w = iBuf->dma_w; - pipe->dst_y = iBuf->dma_y; - pipe->dst_x = iBuf->dma_x; - pipe->srcp0_addr = (uint32) src; - pipe->srcp0_ystride = iBuf->ibuf_width * iBuf->bpp; - } -#endif - - pipe->mixer_stage = MDP4_MIXER_STAGE_BASE; + if (need_ov_wait) { + pr_debug("%s: wait4ov\n", __func__); + mdp4_mddi_wait4ov(0); + } - mdp4_overlay_rgb_setup(pipe); + if (pipe->ov_blt_addr) { + if (vctrl->blt_end) { + vctrl->blt_end = 0; + pipe->ov_blt_addr = 0; + pipe->dma_blt_addr = 0; + } + } - mdp4_mixer_stage_up(pipe, 1); + if (vctrl->blt_change) { + mdp4_overlayproc_cfg(pipe); + mdp4_overlay_dmap_xy(pipe); + vctrl->blt_change = 0; + } - mdp4_overlayproc_cfg(pipe); + pipe = vp->plist; + for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) { + if (pipe->pipe_used) { + cnt++; + real_pipe = mdp4_overlay_ndx2pipe(pipe->pipe_ndx); + if (real_pipe && real_pipe->pipe_used) { + /* pipe not unset */ + mdp4_overlay_vsync_commit(pipe); + } + /* free previous iommu to freelist + * which will be freed at next + * pipe_commit + */ + mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0); + pipe->pipe_used = 0; /* clear */ + } + } - mdp4_overlay_dmap_xy(pipe); + mdp4_mixer_stage_commit(mixer); - mdp4_overlay_dmap_cfg(mfd, 0); - mdp4_mixer_stage_commit(pipe->mixer_num); - mdp4_mddi_vsync_enable(mfd, pipe, 0); + pipe = vctrl->base_pipe; + spin_lock_irqsave(&vctrl->spin_lock, flags); + if (pipe->ov_blt_addr) { + mdp4_mddi_blt_ov_update(pipe); + pipe->ov_cnt++; + vctrl->ov_koff++; + vsync_irq_enable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM); + } else { + vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM); + vctrl->dmap_koff++; + } + pr_debug("%s: kickoff\n", __func__); + /* kickoff overlay engine */ + mdp4_stat.kickoff_ov0++; + outpdw(MDP_BASE + 0x0004, 0); + mb(); /* make sure kickoff ececuted */ + spin_unlock_irqrestore(&vctrl->spin_lock, flags); + + mdp4_stat.overlay_commit[pipe->mixer_num]++; - /* MDP cmd block disable */ - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return cnt; } -int mdp4_mddi_overlay_blt_start(struct msm_fb_data_type *mfd) +void mdp4_mddi_vsync_ctrl(struct fb_info *info, int enable) { - unsigned long flag; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct vsycn_ctrl *vctrl; + unsigned long flags; + int clk_set_on = 0; + int cndx = 0; - pr_debug("%s: blt_end=%d blt_addr=%x pid=%d\n", - __func__, mddi_pipe->blt_end, - (int)mddi_pipe->ov_blt_addr, current->pid); + vctrl = &vsync_ctrl_db[cndx]; - mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0); + pr_debug("%s: clk_enabled=%d vsycn_enabeld=%d req=%d\n", __func__, + vctrl->clk_enabled, vctrl->vsync_enabled, enable); - if (mfd->ov0_wb_buf->write_addr == 0) { - pr_info("%s: no blt_base assigned\n", __func__); - return -EBUSY; - } - - if (mddi_pipe->ov_blt_addr == 0) { - mdp4_mddi_dma_busy_wait(mfd); - spin_lock_irqsave(&mdp_spin_lock, flag); - mddi_pipe->blt_end = 0; - mddi_pipe->blt_cnt = 0; - mddi_pipe->ov_cnt = 0; - mddi_pipe->dmap_cnt = 0; - mddi_pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr; - mddi_pipe->dma_blt_addr = mfd->ov0_wb_buf->write_addr; - mdp4_stat.blt_mddi++; - spin_unlock_irqrestore(&mdp_spin_lock, flag); - return 0; -} + mutex_lock(&vctrl->update_lock); - return -EBUSY; + if (vctrl->vsync_enabled == enable) { + mutex_unlock(&vctrl->update_lock); + return; + } + + vctrl->vsync_enabled = enable; + + if (enable) { + if (vctrl->clk_enabled == 0) { + pr_debug("%s: SET_CLK_ON\n", __func__); + mdp_clk_ctrl(1); + vctrl->clk_enabled = 1; + clk_set_on = 1; + } + spin_lock_irqsave(&vctrl->spin_lock, flags); + vctrl->clk_control = 0; + vctrl->expire_tick = 0; + vctrl->uevent = 1; + vctrl->new_update = 1; + if (clk_set_on) { + vsync_irq_enable(INTR_PRIMARY_RDPTR, + MDP_PRIM_RDPTR_TERM); + } + spin_unlock_irqrestore(&vctrl->spin_lock, flags); + + mdp4_overlay_update_mddi(mfd); + } else { + spin_lock_irqsave(&vctrl->spin_lock, flags); + vctrl->clk_control = 1; + vctrl->uevent = 0; + if (vctrl->clk_enabled) + vctrl->expire_tick = VSYNC_EXPIRE_TICK; + spin_unlock_irqrestore(&vctrl->spin_lock, flags); + } + mutex_unlock(&vctrl->update_lock); } -int mdp4_mddi_overlay_blt_stop(struct msm_fb_data_type *mfd) +void mdp4_mddi_wait4vsync(int cndx, long long *vtime) { - unsigned long flag; + struct vsycn_ctrl *vctrl; + struct mdp4_overlay_pipe *pipe; + unsigned long flags; - pr_debug("%s: blt_end=%d blt_addr=%x\n", - __func__, mddi_pipe->blt_end, (int)mddi_pipe->ov_blt_addr); + if (cndx >= MAX_CONTROLLER) { + pr_err("%s: out or range: cndx=%d\n", __func__, cndx); + return; + } - if ((mddi_pipe->blt_end == 0) && mddi_pipe->ov_blt_addr) { - spin_lock_irqsave(&mdp_spin_lock, flag); - mddi_pipe->blt_end = 1; /* mark as end */ - spin_unlock_irqrestore(&mdp_spin_lock, flag); - return 0; + vctrl = &vsync_ctrl_db[cndx]; + pipe = vctrl->base_pipe; + + if (atomic_read(&vctrl->suspend) > 0) { + *vtime = -1; + return; } - return -EBUSY; -} + spin_lock_irqsave(&vctrl->spin_lock, flags); + if (vctrl->wait_vsync_cnt == 0) + INIT_COMPLETION(vctrl->vsync_comp); + vctrl->wait_vsync_cnt++; + spin_unlock_irqrestore(&vctrl->spin_lock, flags); -int mdp4_mddi_overlay_blt_offset(struct msm_fb_data_type *mfd, - struct msmfb_overlay_blt *req) -{ - req->offset = 0; - req->width = mddi_pipe->src_width; - req->height = mddi_pipe->src_height; - req->bpp = mddi_pipe->bpp; + wait_for_completion(&vctrl->vsync_comp); + mdp4_stat.wait4vsync0++; - return sizeof(*req); + *vtime = ktime_to_ns(vctrl->vsync_time); } -void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd, - struct msmfb_overlay_blt *req) +static void mdp4_mddi_wait4dmap(int cndx) { - if (req->enable) - mdp4_mddi_overlay_blt_start(mfd); - else if (req->enable == 0) - mdp4_mddi_overlay_blt_stop(mfd); + struct vsycn_ctrl *vctrl; -} + if (cndx >= MAX_CONTROLLER) { + pr_err("%s: out or range: cndx=%d\n", __func__, cndx); + return; + } -void mdp4_blt_xy_update(struct mdp4_overlay_pipe *pipe) -{ - uint32 off, addr, addr2; - int bpp; - char *overlay_base; + vctrl = &vsync_ctrl_db[cndx]; - if (pipe->ov_blt_addr == 0) + if (atomic_read(&vctrl->suspend) > 0) return; + wait_for_completion(&vctrl->dmap_comp); +} -#ifdef BLT_RGB565 - bpp = 2; /* overlay ouput is RGB565 */ -#else - bpp = 3; /* overlay ouput is RGB888 */ -#endif - off = 0; - if (pipe->dmap_cnt & 0x01) - off = pipe->src_height * pipe->src_width * bpp; +static void mdp4_mddi_wait4ov(int cndx) +{ + struct vsycn_ctrl *vctrl; - addr = pipe->ov_blt_addr + off; + if (cndx >= MAX_CONTROLLER) { + pr_err("%s: out or range: cndx=%d\n", __func__, cndx); + return; + } - /* dmap */ - MDP_OUTP(MDP_BASE + 0x90008, addr); + vctrl = &vsync_ctrl_db[cndx]; - off = 0; - if (pipe->ov_cnt & 0x01) - off = pipe->src_height * pipe->src_width * bpp; - addr2 = pipe->ov_blt_addr + off; - /* overlay 0 */ - overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */ - outpdw(overlay_base + 0x000c, addr2); - outpdw(overlay_base + 0x001c, addr2); -} + if (atomic_read(&vctrl->suspend) > 0) + return; -void mdp4_primary_rdptr(void) -{ + wait_for_completion(&vctrl->ov_comp); } /* - * mdp4_dmap_done_mddi: called from isr + * primary_rdptr_isr: + * called from interrupt context */ -void mdp4_dma_p_done_mddi(struct mdp_dma_data *dma) +static void primary_rdptr_isr(int cndx) { - int diff; + struct vsycn_ctrl *vctrl; - mddi_pipe->dmap_cnt++; - diff = mddi_pipe->ov_cnt - mddi_pipe->dmap_cnt; - pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n", - __func__, mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt); + vctrl = &vsync_ctrl_db[cndx]; + pr_debug("%s: ISR, cpu=%d\n", __func__, smp_processor_id()); + vctrl->rdptr_intr_tot++; + vctrl->vsync_time = ktime_get(); - if (diff <= 0) { - spin_lock(&mdp_spin_lock); - dma->dmap_busy = FALSE; - complete(&dma->dmap_comp); - spin_unlock(&mdp_spin_lock); - - if (mddi_pipe->blt_end) { - mddi_pipe->blt_end = 0; - mddi_pipe->ov_blt_addr = 0; - mddi_pipe->dma_blt_addr = 0; - pr_debug("%s: END, ov_cnt=%d dmap_cnt=%d\n", __func__, - mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt); - mdp_intr_mask &= ~INTR_DMA_P_DONE; - outp32(MDP_INTR_ENABLE, mdp_intr_mask); - } + spin_lock(&vctrl->spin_lock); - mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE); - mdp_disable_irq_nosync(MDP_DMA2_TERM); /* disable intr */ - return; + if (vctrl->uevent) + schedule_work(&vctrl->vsync_work); + + if (vctrl->wait_vsync_cnt) { + complete(&vctrl->vsync_comp); + vctrl->wait_vsync_cnt = 0; } - spin_lock(&mdp_spin_lock); - dma->busy = FALSE; - spin_unlock(&mdp_spin_lock); - complete(&dma->comp); - if (busy_wait_cnt) - busy_wait_cnt--; + if (vctrl->expire_tick) { + vctrl->expire_tick--; + if (vctrl->expire_tick == 0) + schedule_work(&vctrl->clk_work); + } + spin_unlock(&vctrl->spin_lock); +} - pr_debug("%s: kickoff dmap\n", __func__); +void mdp4_dmap_done_mddi(int cndx) +{ + struct vsycn_ctrl *vctrl; + struct mdp4_overlay_pipe *pipe; + int diff; + + vctrl = &vsync_ctrl_db[cndx]; + pipe = vctrl->base_pipe; + + /* blt enabled */ + spin_lock(&vctrl->spin_lock); + vsync_irq_disable(INTR_DMA_P_DONE, MDP_DMAP_TERM); + vctrl->dmap_done++; + diff = vctrl->ov_done - vctrl->dmap_done; + pr_debug("%s: ov_koff=%d ov_done=%d dmap_koff=%d dmap_done=%d cpu=%d\n", + __func__, vctrl->ov_koff, vctrl->ov_done, vctrl->dmap_koff, + vctrl->dmap_done, smp_processor_id()); + complete_all(&vctrl->dmap_comp); + if (diff <= 0) { + if (vctrl->blt_wait) + vctrl->blt_wait = 0; + spin_unlock(&vctrl->spin_lock); + return; + } - mdp4_blt_xy_update(mddi_pipe); - /* kick off dmap */ - outpdw(MDP_BASE + 0x000c, 0x0); + /* kick dmap */ + mdp4_mddi_blt_dmap_update(pipe); + pipe->dmap_cnt++; mdp4_stat.kickoff_dmap++; - mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE); + vctrl->dmap_koff++; + vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM); + outpdw(MDP_BASE + 0x000c, 0); /* kickoff dmap engine */ + mb(); /* make sure kickoff executed */ + spin_unlock(&vctrl->spin_lock); } /* * mdp4_overlay0_done_mddi: called from isr */ -void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma) +void mdp4_overlay0_done_mddi(int cndx) { + struct vsycn_ctrl *vctrl; + struct mdp4_overlay_pipe *pipe; int diff; - if (mddi_pipe->ov_blt_addr == 0) { - mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE); - spin_lock(&mdp_spin_lock); - dma->busy = FALSE; - spin_unlock(&mdp_spin_lock); - complete(&dma->comp); + vctrl = &vsync_ctrl_db[cndx]; + pipe = vctrl->base_pipe; - if (busy_wait_cnt) - busy_wait_cnt--; - mdp_disable_irq_nosync(MDP_OVERLAY0_TERM); + spin_lock(&vctrl->spin_lock); + vsync_irq_disable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM); + vctrl->ov_done++; + complete_all(&vctrl->ov_comp); + diff = vctrl->ov_done - vctrl->dmap_done; - return; - } - - /* blt enabled */ - if (mddi_pipe->blt_end == 0) - mddi_pipe->ov_cnt++; - - pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n", - __func__, mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt); + pr_debug("%s: ov_koff=%d ov_done=%d dmap_koff=%d dmap_done=%d cpu=%d\n", + __func__, vctrl->ov_koff, vctrl->ov_done, vctrl->dmap_koff, + vctrl->dmap_done, smp_processor_id()); - if (mddi_pipe->blt_cnt == 0) { - /* first kickoff since blt enabled */ - mdp_intr_mask |= INTR_DMA_P_DONE; - outp32(MDP_INTR_ENABLE, mdp_intr_mask); + if (pipe->ov_blt_addr == 0) { + /* blt disabled */ + spin_unlock(&vctrl->spin_lock); + return; } - mddi_pipe->blt_cnt++; - - diff = mddi_pipe->ov_cnt - mddi_pipe->dmap_cnt; - if (diff >= 2) { - mdp_disable_irq_nosync(MDP_OVERLAY0_TERM); + if (diff > 1) { + /* + * two overlay_done and none dmap_done yet + * let dmap_done kickoff dmap + * and put pipe_commit to wait + */ + vctrl->blt_wait = 1; + pr_debug("%s: blt_wait set\n", __func__); + spin_unlock(&vctrl->spin_lock); return; } + mdp4_mddi_blt_dmap_update(pipe); + pipe->dmap_cnt++; + mdp4_stat.kickoff_dmap++; + vctrl->dmap_koff++; + vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM); + outpdw(MDP_BASE + 0x000c, 0); /* kickoff dmap engine */ + mb(); /* make sure kickoff executed */ + spin_unlock(&vctrl->spin_lock); +} - spin_lock(&mdp_spin_lock); - dma->busy = FALSE; - dma->dmap_busy = TRUE; - spin_unlock(&mdp_spin_lock); - complete(&dma->comp); - - if (busy_wait_cnt) - busy_wait_cnt--; - - pr_debug("%s: kickoff dmap\n", __func__); +static void clk_ctrl_work(struct work_struct *work) +{ + struct vsycn_ctrl *vctrl = + container_of(work, typeof(*vctrl), clk_work); + unsigned long flags; + + mutex_lock(&vctrl->update_lock); + if (vctrl->clk_control && vctrl->clk_enabled) { + pr_debug("%s: SET_CLK_OFF\n", __func__); + mdp_clk_ctrl(0); + spin_lock_irqsave(&vctrl->spin_lock, flags); + vsync_irq_disable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM); + vctrl->clk_enabled = 0; + vctrl->clk_control = 0; + spin_unlock_irqrestore(&vctrl->spin_lock, flags); + } + mutex_unlock(&vctrl->update_lock); +} - mdp4_blt_xy_update(mddi_pipe); - mdp_enable_irq(MDP_DMA2_TERM); /* enable intr */ - /* kick off dmap */ - outpdw(MDP_BASE + 0x000c, 0x0); - mdp4_stat.kickoff_dmap++; - mdp_disable_irq_nosync(MDP_OVERLAY0_TERM); +static void send_vsync_work(struct work_struct *work) +{ + struct vsycn_ctrl *vctrl = + container_of(work, typeof(*vctrl), vsync_work); + char buf[64]; + char *envp[2]; + + snprintf(buf, sizeof(buf), "VSYNC=%llu", + ktime_to_ns(vctrl->vsync_time)); + envp[0] = buf; + envp[1] = NULL; + kobject_uevent_env(&vctrl->dev->kobj, KOBJ_CHANGE, envp); } -void mdp4_mddi_overlay_restore(void) + +void mdp4_mddi_rdptr_init(int cndx) { - if (mddi_mfd == NULL) - return; + struct vsycn_ctrl *vctrl; - pr_debug("%s: resotre, pid=%d\n", __func__, current->pid); + if (cndx >= MAX_CONTROLLER) { + pr_err("%s: out or range: cndx=%d\n", __func__, cndx); + return; + } - if (mddi_mfd->panel_power_on == 0) + vctrl = &vsync_ctrl_db[cndx]; + if (vctrl->inited) return; - if (mddi_mfd && mddi_pipe) { - mdp4_mddi_dma_busy_wait(mddi_mfd); - mdp4_overlay_update_lcd(mddi_mfd); - if (mddi_pipe->ov_blt_addr) - mdp4_mddi_blt_dmap_busy_wait(mddi_mfd); - mdp4_mddi_overlay_kickoff(mddi_mfd, mddi_pipe); - mddi_mfd->dma_update_flag = 1; - } - if (mdp_hw_revision < MDP4_REVISION_V2_1) /* need dmas dmap switch */ - mdp4_mddi_overlay_dmas_restore(); + vctrl->inited = 1; + vctrl->update_ndx = 0; + mutex_init(&vctrl->update_lock); + init_completion(&vctrl->ov_comp); + init_completion(&vctrl->dmap_comp); + init_completion(&vctrl->vsync_comp); + spin_lock_init(&vctrl->spin_lock); + INIT_WORK(&vctrl->vsync_work, send_vsync_work); + INIT_WORK(&vctrl->clk_work, clk_ctrl_work); } -void mdp4_mddi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd) +void mdp4_primary_rdptr(void) { - unsigned long flag; - int need_wait = 0; - - spin_lock_irqsave(&mdp_spin_lock, flag); - if (mfd->dma->dmap_busy == TRUE) { - INIT_COMPLETION(mfd->dma->dmap_comp); - need_wait++; - } - spin_unlock_irqrestore(&mdp_spin_lock, flag); - - if (need_wait) { - /* wait until DMA finishes the current job */ - wait_for_completion(&mfd->dma->dmap_comp); - } + primary_rdptr_isr(0); } -/* - * mdp4_mddi_cmd_dma_busy_wait: check mddi link activity - * mddi link is a shared resource and it can only be used - * while it is in idle state. - * ov_mutex need to be acquired before call this function. - */ -void mdp4_mddi_dma_busy_wait(struct msm_fb_data_type *mfd) +void mdp4_overlay_mddi_state_set(int state) { unsigned long flag; - int need_wait = 0; - pr_debug("%s: START, pid=%d\n", __func__, current->pid); spin_lock_irqsave(&mdp_spin_lock, flag); - if (mfd->dma->busy == TRUE) { - if (busy_wait_cnt == 0) - INIT_COMPLETION(mfd->dma->comp); - busy_wait_cnt++; - need_wait++; - } + mddi_state = state; spin_unlock_irqrestore(&mdp_spin_lock, flag); +} - - if (need_wait) { - /* wait until DMA finishes the current job */ - pr_debug("%s: PENDING, pid=%d\n", __func__, current->pid); - wait_for_completion(&mfd->dma->comp); - } - pr_debug("%s: DONE, pid=%d\n", __func__, current->pid); +int mdp4_overlay_mddi_state_get(void) +{ + return mddi_state; } -void mdp4_mddi_kickoff_video(struct msm_fb_data_type *mfd, - struct mdp4_overlay_pipe *pipe) +static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp) { /* - * a video kickoff may happen before UI kickoff after - * blt enabled. mdp4_overlay_update_lcd() need - * to be called before kickoff. - * vice versa for blt disabled. + * The adreno GPU hardware requires that the pitch be aligned to + * 32 pixels for color buffers, so for the cases where the GPU + * is writing directly to fb0, the framebuffer pitch + * also needs to be 32 pixel aligned */ - if (mddi_pipe->ov_blt_addr && mddi_pipe->blt_cnt == 0) - mdp4_overlay_update_lcd(mfd); /* first time */ - else if (mddi_pipe->ov_blt_addr == 0 && mddi_pipe->blt_cnt) { - mdp4_overlay_update_lcd(mfd); /* last time */ - mddi_pipe->blt_cnt = 0; - } - pr_debug("%s: blt_addr=%d blt_cnt=%d\n", - __func__, (int)mddi_pipe->ov_blt_addr, mddi_pipe->blt_cnt); - - if (mddi_pipe->ov_blt_addr) - mdp4_mddi_blt_dmap_busy_wait(mddi_mfd); - mdp4_mddi_overlay_kickoff(mfd, pipe); + if (fb_index == 0) + return ALIGN(xres, 32) * bpp; + else + return xres * bpp; } -void mdp4_mddi_kickoff_ui(struct msm_fb_data_type *mfd, - struct mdp4_overlay_pipe *pipe) +void mdp4_mddi_vsync_enable(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe, int which) { - pr_debug("%s: pid=%d\n", __func__, current->pid); - mdp4_mddi_overlay_kickoff(mfd, pipe); -} + uint32 start_y, data, tear_en; + tear_en = (1 << which); + + if ((mfd->use_mdp_vsync) && (mfd->ibuf.vsync_enable) && + (mfd->panel_info.lcd.vsync_enable)) { + + if (vsync_start_y_adjust <= pipe->dst_y) + start_y = pipe->dst_y - vsync_start_y_adjust; + else + start_y = (mfd->total_lcd_lines - 1) - + (vsync_start_y_adjust - pipe->dst_y); + if (which == 0) + MDP_OUTP(MDP_BASE + 0x210, start_y); /* primary */ + else + MDP_OUTP(MDP_BASE + 0x214, start_y); /* secondary */ -void mdp4_mddi_overlay_kickoff(struct msm_fb_data_type *mfd, - struct mdp4_overlay_pipe *pipe) + data = inpdw(MDP_BASE + 0x20c); + data |= tear_en; + MDP_OUTP(MDP_BASE + 0x20c, data); + } else { + data = inpdw(MDP_BASE + 0x20c); + data &= ~tear_en; + MDP_OUTP(MDP_BASE + 0x20c, data); + } +} + +void mdp4_mddi_base_swap(int cndx, struct mdp4_overlay_pipe *pipe) { - unsigned long flag; + struct vsycn_ctrl *vctrl; - mdp_enable_irq(MDP_OVERLAY0_TERM); - spin_lock_irqsave(&mdp_spin_lock, flag); - mfd->dma->busy = TRUE; - if (mddi_pipe->ov_blt_addr) - mfd->dma->dmap_busy = TRUE; - spin_unlock_irqrestore(&mdp_spin_lock, flag); - /* start OVERLAY pipe */ - mdp_pipe_kickoff(MDP_OVERLAY0_TERM, mfd); - mdp4_stat.kickoff_ov0++; + if (cndx >= MAX_CONTROLLER) { + pr_err("%s: out or range: cndx=%d\n", __func__, cndx); + return; + } + + vctrl = &vsync_ctrl_db[cndx]; + vctrl->base_pipe = pipe; } -void mdp4_dma_s_update_lcd(struct msm_fb_data_type *mfd, - struct mdp4_overlay_pipe *pipe) +static void mdp4_overlay_setup_pipe_addr(struct msm_fb_data_type *mfd, + struct mdp4_overlay_pipe *pipe) { MDPIBUF *iBuf = &mfd->ibuf; - uint32 outBpp = iBuf->bpp; + struct fb_info *fbi; + int bpp; + uint8 *src; + + /* whole screen for base layer */ + src = (uint8 *) iBuf->buf; + fbi = mfd->fbi; + + if (pipe->is_3d) { + bpp = fbi->var.bits_per_pixel / 8; + pipe->src_height = pipe->src_height_3d; + pipe->src_width = pipe->src_width_3d; + pipe->src_h = pipe->src_height_3d; + pipe->src_w = pipe->src_width_3d; + pipe->dst_h = pipe->src_height_3d; + pipe->dst_w = pipe->src_width_3d; + pipe->srcp0_ystride = msm_fb_line_length(0, + pipe->src_width, bpp); + } else { + /* 2D */ + pipe->src_height = fbi->var.yres; + pipe->src_width = fbi->var.xres; + pipe->src_h = fbi->var.yres; + pipe->src_w = fbi->var.xres; + pipe->dst_h = fbi->var.yres; + pipe->dst_w = fbi->var.xres; + pipe->srcp0_ystride = fbi->fix.line_length; + } + pipe->src_y = 0; + pipe->src_x = 0; + pipe->dst_y = 0; + pipe->dst_x = 0; + pipe->srcp0_addr = (uint32)src; +} + +void mdp4_overlay_update_mddi(struct msm_fb_data_type *mfd) +{ + int ptype; + uint32 mddi_ld_param; uint16 mddi_vdo_packet_reg; - uint32 dma_s_cfg_reg; + struct mdp4_overlay_pipe *pipe; + uint32 data; + int ret; + int cndx = 0; + struct vsycn_ctrl *vctrl; - dma_s_cfg_reg = 0; + if (mfd->key != MFD_KEY) + return; - if (mfd->fb_imgType == MDP_RGBA_8888) - dma_s_cfg_reg |= DMA_PACK_PATTERN_BGR; /* on purpose */ - else if (mfd->fb_imgType == MDP_BGR_565) - dma_s_cfg_reg |= DMA_PACK_PATTERN_BGR; - else - dma_s_cfg_reg |= DMA_PACK_PATTERN_RGB; - - if (outBpp == 4) - dma_s_cfg_reg |= (1 << 26); /* xRGB8888 */ - else if (outBpp == 2) - dma_s_cfg_reg |= DMA_IBUF_FORMAT_RGB565; - - dma_s_cfg_reg |= DMA_DITHER_EN; - - /* MDP cmd block enable */ - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - /* PIXELSIZE */ - MDP_OUTP(MDP_BASE + 0xa0004, (pipe->dst_h << 16 | pipe->dst_w)); - MDP_OUTP(MDP_BASE + 0xa0008, pipe->srcp0_addr); /* ibuf address */ - MDP_OUTP(MDP_BASE + 0xa000c, pipe->srcp0_ystride);/* ystride */ - - if (mfd->panel_info.bpp == 24) { - dma_s_cfg_reg |= DMA_DSTC0G_8BITS | /* 666 18BPP */ - DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; - } else if (mfd->panel_info.bpp == 18) { - dma_s_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */ - DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + vctrl = &vsync_ctrl_db[cndx]; + + if (vctrl->base_pipe == NULL) { + ptype = mdp4_overlay_format2type(mfd->fb_imgType); + + if (ptype < 0) + pr_err("%s: format2type failed\n", __func__); + + pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER0); + if (pipe == NULL) { + pr_err("%s: pipe_alloc failed\n", __func__); + return; + } + pipe->pipe_used++; + pipe->mixer_stage = MDP4_MIXER_STAGE_BASE; + pipe->mixer_num = MDP4_MIXER0; + pipe->src_format = mfd->fb_imgType; + mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_MDDI); + ret = mdp4_overlay_format2pipe(pipe); + if (ret < 0) + pr_err("%s: format2type failed\n", __func__); + + vctrl->base_pipe = pipe; /* keep it */ + mdp4_init_writeback_buf(mfd, MDP4_MIXER0); + pipe->ov_blt_addr = 0; + pipe->dma_blt_addr = 0; } else { - dma_s_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */ - DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + pipe = vctrl->base_pipe; } - MDP_OUTP(MDP_BASE + 0xa0010, (pipe->dst_y << 16) | pipe->dst_x); - - /* 1 for dma_s, client_id = 0 */ - MDP_OUTP(MDP_BASE + 0x00090, 1); + MDP_OUTP(MDP_BASE + 0x021c, 10); /* read pointer */ + mddi_ld_param = 0; mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt; + if (mdp_hw_revision == MDP4_REVISION_V2_1) { + data = inpdw(MDP_BASE + 0x0028); + data &= ~0x0300; /* bit 8, 9, MASTER4 */ + if (mfd->fbi->var.xres == 540) /* qHD, 540x960 */ + data |= 0x0200; + else + data |= 0x0100; + + MDP_OUTP(MDP_BASE + 0x00028, data); + } + + if (mfd->panel_info.type == MDDI_PANEL) { + if (mfd->panel_info.pdest == DISPLAY_1) + mddi_ld_param = 0; + else + mddi_ld_param = 1; + } else { + mddi_ld_param = 2; + } + + MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param); + if (mfd->panel_info.bpp == 24) MDP_OUTP(MDP_BASE + 0x00094, - (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg); + (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg); else if (mfd->panel_info.bpp == 16) MDP_OUTP(MDP_BASE + 0x00094, - (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg); + (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg); else MDP_OUTP(MDP_BASE + 0x00094, - (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg); + (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg); + + MDP_OUTP(MDP_BASE + 0x00098, 0x01); + + + mdp4_overlay_setup_pipe_addr(mfd, pipe); + + mdp4_overlay_rgb_setup(pipe); + + mdp4_overlay_reg_flush(pipe, 1); + + mdp4_mixer_stage_up(pipe, 0); + + mdp4_overlayproc_cfg(pipe); + + mdp4_overlay_dmap_xy(pipe); + + mdp4_overlay_dmap_cfg(mfd, 0); + + mdp4_mixer_stage_commit(pipe->mixer_num); + + wmb(); +} + +void mdp4_mddi_blt_start(struct msm_fb_data_type *mfd) +{ + mdp4_mddi_do_blt(mfd, 1); +} + +void mdp4_mddi_blt_stop(struct msm_fb_data_type *mfd) +{ + mdp4_mddi_do_blt(mfd, 0); +} + +void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd, + struct msmfb_overlay_blt *req) +{ + mdp4_mddi_do_blt(mfd, req->enable); +} + +int mdp4_mddi_on(struct platform_device *pdev) +{ + int ret = 0; + int cndx = 0; + struct msm_fb_data_type *mfd; + struct vsycn_ctrl *vctrl; + + pr_debug("%s+:\n", __func__); + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + vctrl = &vsync_ctrl_db[cndx]; + vctrl->mfd = mfd; + vctrl->dev = mfd->fbi->dev; - MDP_OUTP(MDP_BASE + 0x00098, 0x01); + mdp_clk_ctrl(1); + mdp4_overlay_update_mddi(mfd); + mdp_clk_ctrl(0); - MDP_OUTP(MDP_BASE + 0xa0000, dma_s_cfg_reg); + mdp4_iommu_attach(); - mdp4_mddi_vsync_enable(mfd, pipe, 1); + atomic_set(&vctrl->suspend, 0); + pr_debug("%s-:\n", __func__); - /* MDP cmd block disable */ - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + return ret; } -void mdp4_mddi_dma_s_kickoff(struct msm_fb_data_type *mfd, - struct mdp4_overlay_pipe *pipe) +int mdp4_mddi_off(struct platform_device *pdev) { - mdp_enable_irq(MDP_DMA_S_TERM); + int ret = 0; + int cndx = 0; + struct msm_fb_data_type *mfd; + struct vsycn_ctrl *vctrl; + struct mdp4_overlay_pipe *pipe; + + pr_debug("%s+:\n", __func__); - if (mddi_pipe->ov_blt_addr == 0) - mfd->dma->busy = TRUE; + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); - mfd->ibuf_flushed = TRUE; - /* start dma_s pipe */ - mdp_pipe_kickoff(MDP_DMA_S_TERM, mfd); - mdp4_stat.kickoff_dmas++; + vctrl = &vsync_ctrl_db[cndx]; + pipe = vctrl->base_pipe; + if (pipe == NULL) { + pr_err("%s: NO base pipe\n", __func__); + return ret; + } - /* wait until DMA finishes the current job */ - wait_for_completion(&mfd->dma->comp); - mdp_disable_irq(MDP_DMA_S_TERM); + atomic_set(&vctrl->suspend, 1); + + /* sanity check, free pipes besides base layer */ + mdp4_overlay_unset_mixer(pipe->mixer_num); + mdp4_mixer_stage_down(pipe, 1); + mdp4_overlay_pipe_free(pipe); + vctrl->base_pipe = NULL; + + if (vctrl->clk_enabled) { + /* + * in case of suspend, vsycn_ctrl off is not + * received from frame work which left clock on + * then, clock need to be turned off here + */ + mdp_clk_ctrl(0); + } + + vctrl->clk_enabled = 0; + vctrl->vsync_enabled = 0; + vctrl->clk_control = 0; + vctrl->expire_tick = 0; + vctrl->uevent = 0; + + vsync_irq_disable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM); + + pr_debug("%s-:\n", __func__); + + /* + * footswitch off + * this will casue all mdp register + * to be reset to default + * after footswitch on later + */ + + return ret; } -void mdp4_mddi_overlay_dmas_restore(void) +void mdp_mddi_overlay_suspend(struct msm_fb_data_type *mfd) { - /* mutex held by caller */ - if (mddi_mfd && mddi_pipe) { - mdp4_mddi_dma_busy_wait(mddi_mfd); - mdp4_dma_s_update_lcd(mddi_mfd, mddi_pipe); - mdp4_mddi_dma_s_kickoff(mddi_mfd, mddi_pipe); - mddi_mfd->dma_update_flag = 1; + int cndx = 0; + struct vsycn_ctrl *vctrl; + struct mdp4_overlay_pipe *pipe; + + vctrl = &vsync_ctrl_db[cndx]; + pipe = vctrl->base_pipe; + /* dis-engage rgb0 from mixer0 */ + if (pipe) { + if (mfd->ref_cnt == 0) { + /* adb stop */ + if (pipe->pipe_type == OVERLAY_TYPE_BF) + mdp4_overlay_borderfill_stage_down(pipe); + + /* pipe == rgb1 */ + mdp4_overlay_unset_mixer(pipe->mixer_num); + vctrl->base_pipe = NULL; + } else { + mdp4_mixer_stage_down(pipe, 1); + mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 1); + } } } void mdp4_mddi_overlay(struct msm_fb_data_type *mfd) { - mutex_lock(&mfd->dma->ov_mutex); + int cndx = 0; + struct vsycn_ctrl *vctrl; + struct mdp4_overlay_pipe *pipe; + unsigned long flags; + long long xx; - if (mfd && mfd->panel_power_on) { - mdp4_mddi_dma_busy_wait(mfd); - - if (mddi_pipe && mddi_pipe->ov_blt_addr) - mdp4_mddi_blt_dmap_busy_wait(mfd); - mdp4_overlay_mdp_perf_upd(mfd, 0); - mdp4_overlay_update_lcd(mfd); - - mdp4_overlay_mdp_perf_upd(mfd, 1); - if (mdp_hw_revision < MDP4_REVISION_V2_1) { - /* dmas dmap switch */ - if (mdp4_overlay_mixer_play(mddi_pipe->mixer_num) - == 0) { - mdp4_dma_s_update_lcd(mfd, mddi_pipe); - mdp4_mddi_dma_s_kickoff(mfd, mddi_pipe); - } else - mdp4_mddi_kickoff_ui(mfd, mddi_pipe); - } else /* no dams dmap switch */ - mdp4_mddi_kickoff_ui(mfd, mddi_pipe); - - /* signal if pan function is waiting for the update completion */ - if (mfd->pan_waiting) { - mfd->pan_waiting = FALSE; - complete(&mfd->pan_comp); - } + vctrl = &vsync_ctrl_db[cndx]; + + if (!mfd->panel_power_on) + return; + + pipe = vctrl->base_pipe; + if (pipe == NULL) { + pr_err("%s: NO base pipe\n", __func__); + return; + } + + mutex_lock(&vctrl->update_lock); + if (!vctrl->clk_enabled) { + pr_err("%s: mdp clocks disabled\n", __func__); + mutex_unlock(&vctrl->update_lock); + return; + + } + mutex_unlock(&vctrl->update_lock); + + spin_lock_irqsave(&vctrl->spin_lock, flags); + if (vctrl->expire_tick) { + /* + * in the middle of shutting clocks down + * delay to allow pan display to go through + */ + vctrl->expire_tick = VSYNC_EXPIRE_TICK; } + spin_unlock_irqrestore(&vctrl->spin_lock, flags); + + if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE) { + mdp4_mddi_vsync_enable(mfd, pipe, 0); + mdp4_overlay_setup_pipe_addr(mfd, pipe); + mdp4_mddi_pipe_queue(0, pipe); + } + + mdp4_overlay_mdp_perf_upd(mfd, 1); + + mutex_lock(&mfd->dma->ov_mutex); + mdp4_mddi_pipe_commit(); mutex_unlock(&mfd->dma->ov_mutex); + mdp4_mddi_wait4vsync(0, &xx); + + mdp4_overlay_mdp_perf_upd(mfd, 0); } int mdp4_mddi_overlay_cursor(struct fb_info *info, struct fb_cursor *cursor) @@ -722,7 +1061,6 @@ int mdp4_mddi_overlay_cursor(struct fb_info *info, struct fb_cursor *cursor) struct msm_fb_data_type *mfd = info->par; mutex_lock(&mfd->dma->ov_mutex); if (mfd && mfd->panel_power_on) { - mdp4_mddi_dma_busy_wait(mfd); mdp_hw_cursor_update(info, cursor); } mutex_unlock(&mfd->dma->ov_mutex); diff --git a/drivers/video/msm/mdp4_overlay_writeback.c b/drivers/video/msm/mdp4_overlay_writeback.c index 43e8f568..93f0afea 100644 --- a/drivers/video/msm/mdp4_overlay_writeback.c +++ b/drivers/video/msm/mdp4_overlay_writeback.c @@ -45,9 +45,54 @@ enum { WITH_CLIENT }; -static struct mdp4_overlay_pipe *writeback_pipe; -static struct msm_fb_data_type *writeback_mfd; -static int busy_wait_cnt; +#define MAX_CONTROLLER 1 +#define VSYNC_EXPIRE_TICK 0 + +static struct vsycn_ctrl { + struct device *dev; + int inited; + int update_ndx; + u32 ov_koff; + u32 ov_done; + atomic_t suspend; + struct mutex update_lock; + struct completion ov_comp; + spinlock_t spin_lock; + struct msm_fb_data_type *mfd; + struct mdp4_overlay_pipe *base_pipe; + struct vsync_update vlist[2]; + struct work_struct clk_work; +} vsync_ctrl_db[MAX_CONTROLLER]; + +static void vsync_irq_enable(int intr, int term) +{ + unsigned long flag; + + spin_lock_irqsave(&mdp_spin_lock, flag); + /* no need to clrear other interrupts for comamnd mode */ + mdp_intr_mask |= intr; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + mdp_enable_irq(term); + spin_unlock_irqrestore(&mdp_spin_lock, flag); +} + +static void vsync_irq_disable(int intr, int term) +{ + unsigned long flag; + + spin_lock_irqsave(&mdp_spin_lock, flag); + /* no need to clrear other interrupts for comamnd mode */ + mdp_intr_mask &= ~intr; + outp32(MDP_INTR_ENABLE, mdp_intr_mask); + mdp_disable_irq_nosync(term); + spin_unlock_irqrestore(&mdp_spin_lock, flag); +} + +static int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd); +static void mdp4_wfd_queue_wakeup(struct msm_fb_data_type *mfd, + struct msmfb_writeback_data_list *node); +static void mdp4_wfd_dequeue_update(struct msm_fb_data_type *mfd, + struct msmfb_writeback_data_list **wfdnode); int mdp4_overlay_writeback_on(struct platform_device *pdev) { @@ -58,6 +103,8 @@ int mdp4_overlay_writeback_on(struct platform_device *pdev) int bpp; int ret; uint32 data; + struct vsycn_ctrl *vctrl; + int cndx = 0; mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); @@ -67,7 +114,9 @@ int mdp4_overlay_writeback_on(struct platform_device *pdev) if (mfd->key != MFD_KEY) return -EINVAL; - writeback_mfd = mfd; /* keep it */ + vctrl = &vsync_ctrl_db[cndx]; + vctrl->mfd = mfd; + vctrl->dev = mfd->fbi->dev; fbi = mfd->fbi; @@ -77,12 +126,14 @@ int mdp4_overlay_writeback_on(struct platform_device *pdev) fbi->var.yoffset * fbi->fix.line_length; /* MDP cmd block enable */ - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_clk_ctrl(1); - if (writeback_pipe == NULL) { + if (vctrl->base_pipe == NULL) { pipe = mdp4_overlay_pipe_alloc(OVERLAY_TYPE_BF, MDP4_MIXER2); - if (pipe == NULL) + if (pipe == NULL) { pr_info("%s: pipe_alloc failed\n", __func__); + return -EIO; + } pipe->pipe_used++; pipe->mixer_stage = MDP4_MIXER_STAGE_BASE; pipe->mixer_num = MDP4_MIXER2; @@ -92,11 +143,12 @@ int mdp4_overlay_writeback_on(struct platform_device *pdev) if (ret < 0) pr_info("%s: format2type failed\n", __func__); - writeback_pipe = pipe; /* keep it */ + vctrl->base_pipe = pipe; /* keep it */ } else { - pipe = writeback_pipe; + pipe = vctrl->base_pipe; } + ret = panel_next_on(pdev); /* MDP_LAYERMIXER_WB_MUX_SEL to use mixer1 axi for mixer2 writeback */ @@ -113,46 +165,80 @@ int mdp4_overlay_writeback_on(struct platform_device *pdev) MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5008, (0x0 & 0xFFF)); /* 12-bit R */ - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + mdp_clk_ctrl(0); return ret; } int mdp4_overlay_writeback_off(struct platform_device *pdev) { - int ret; - struct msm_fb_data_type *mfd = - (struct msm_fb_data_type *)platform_get_drvdata(pdev); - if (mfd && writeback_pipe) { - mdp4_writeback_dma_busy_wait(mfd); - mdp4_overlay_pipe_free(writeback_pipe); - mdp4_overlay_panel_mode_unset(writeback_pipe->mixer_num, - MDP4_PANEL_WRITEBACK); - writeback_pipe = NULL; + int cndx = 0; + struct msm_fb_data_type *mfd; + struct vsycn_ctrl *vctrl; + struct mdp4_overlay_pipe *pipe; + int ret = 0; + int undx; + struct vsync_update *vp; + + pr_debug("%s+:\n", __func__); + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + vctrl = &vsync_ctrl_db[cndx]; + pipe = vctrl->base_pipe; + if (pipe == NULL) { + pr_err("%s: NO base pipe\n", __func__); + return ret; + } + + /* sanity check, free pipes besides base layer */ + mdp4_overlay_unset_mixer(pipe->mixer_num); + mdp4_mixer_stage_down(pipe, 1); + mdp4_overlay_pipe_free(pipe); + vctrl->base_pipe = NULL; + + undx = vctrl->update_ndx; + vp = &vctrl->vlist[undx]; + if (vp->update_cnt) { + /* + * pipe's iommu will be freed at next overlay play + * and iommu_drop statistic will be increased by one + */ + vp->update_cnt = 0; /* empty queue */ } + ret = panel_next_off(pdev); - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + mdp_clk_ctrl(1); /* MDP_LAYERMIXER_WB_MUX_SEL to restore to default cfg*/ outpdw(MDP_BASE + 0x100F4, 0x0); - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + mdp_clk_ctrl(0); + pr_debug("%s-:\n", __func__); return ret; } -int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd) + +static int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd) { struct fb_info *fbi; uint8 *buf; unsigned int buf_offset; struct mdp4_overlay_pipe *pipe; int bpp; + int cndx = 0; + struct vsycn_ctrl *vctrl; if (mfd->key != MFD_KEY) return -ENODEV; - if (!writeback_pipe) - return -EINVAL; fbi = mfd->fbi; - pipe = writeback_pipe; + vctrl = &vsync_ctrl_db[cndx]; + + pipe = vctrl->base_pipe; + if (!pipe) { + pr_err("%s: no base layer pipe\n", __func__); + return -EINVAL; + } bpp = fbi->var.bits_per_pixel / 8; buf = (uint8 *) fbi->fix.smem_start; @@ -160,7 +246,7 @@ int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd) fbi->var.yoffset * fbi->fix.line_length; /* MDP cmd block enable */ - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + mdp_clk_ctrl(1); pipe->src_height = fbi->var.yres; pipe->src_width = fbi->var.xres; @@ -175,6 +261,7 @@ int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd) pipe->dst_x = 0; mdp4_overlay_mdp_pipe_req(pipe, mfd); + mdp4_calc_blt_mdp_bw(mfd, pipe); if (mfd->display_iova) pipe->srcp0_addr = mfd->display_iova + buf_offset; @@ -184,208 +271,229 @@ int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd) mdp4_mixer_stage_up(pipe, 0); mdp4_overlayproc_cfg(pipe); - mdp4_mixer_stage_commit(pipe->mixer_num); + + if (hdmi_prim_display) + outpdw(MDP_BASE + 0x100F4, 0x01); + else + outpdw(MDP_BASE + 0x100F4, 0x02); + /* MDP cmd block disable */ - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + mdp_clk_ctrl(0); wmb(); return 0; } -void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd) + +/* + * mdp4_wfd_piep_queue: + * called from thread context + */ +void mdp4_wfd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe) { - unsigned long flag; - int need_wait = 0; + struct vsycn_ctrl *vctrl; + struct vsync_update *vp; + struct mdp4_overlay_pipe *pp; + int undx; - spin_lock_irqsave(&mdp_spin_lock, flag); - if (mfd->dma->busy == TRUE) { - if (busy_wait_cnt == 0) - INIT_COMPLETION(mfd->dma->comp); - busy_wait_cnt = 1; - need_wait++; + if (cndx >= MAX_CONTROLLER) { + pr_err("%s: out or range: cndx=%d\n", __func__, cndx); + return; } - spin_unlock_irqrestore(&mdp_spin_lock, flag); - if (need_wait) { - /* wait until DMA finishes the current job */ - pr_debug("%s: pending pid=%d\n", - __func__, current->pid); - wait_for_completion(&mfd->dma->comp); - } -} + vctrl = &vsync_ctrl_db[cndx]; -void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma) -{ - spin_lock(&mdp_spin_lock); - dma->busy = FALSE; - if (busy_wait_cnt) - busy_wait_cnt = 0; - mdp_disable_irq_nosync(MDP_OVERLAY2_TERM); - spin_unlock(&mdp_spin_lock); - complete_all(&dma->comp); - pr_debug("%s ovdone interrupt\n", __func__); + if (atomic_read(&vctrl->suspend) > 0) + return; -} -void mdp4_writeback_overlay_kickoff(struct msm_fb_data_type *mfd, - struct mdp4_overlay_pipe *pipe) -{ - unsigned long flag; - spin_lock_irqsave(&mdp_spin_lock, flag); - mdp_enable_irq(MDP_OVERLAY2_TERM); + mutex_lock(&vctrl->update_lock); + undx = vctrl->update_ndx; + vp = &vctrl->vlist[undx]; - mfd->dma->busy = TRUE; - outp32(MDP_INTR_CLEAR, INTR_OVERLAY2_DONE); - mdp_intr_mask |= INTR_OVERLAY2_DONE; - outp32(MDP_INTR_ENABLE, mdp_intr_mask); + pp = &vp->plist[pipe->pipe_ndx - 1]; /* ndx start form 1 */ - wmb(); /* make sure all registers updated */ - spin_unlock_irqrestore(&mdp_spin_lock, flag); - /* start OVERLAY pipe */ - mdp_pipe_kickoff(MDP_OVERLAY2_TERM, mfd); - wmb(); - pr_debug("%s: before ov done interrupt\n", __func__); -} -void mdp4_writeback_dma_stop(struct msm_fb_data_type *mfd) -{ - /* mutex holded by caller */ - if (mfd && writeback_pipe) { - mdp4_writeback_dma_busy_wait(mfd); - mdp4_overlay_writeback_update(mfd); + pr_debug("%s: vndx=%d pipe_ndx=%d pid=%d\n", __func__, + undx, pipe->pipe_ndx, current->pid); - mdp4_writeback_overlay_kickoff(mfd, writeback_pipe); - } + *pp = *pipe; /* clone it */ + vp->update_cnt++; + + mutex_unlock(&vctrl->update_lock); + mdp4_stat.overlay_play[pipe->mixer_num]++; } -void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd, - struct mdp4_overlay_pipe *pipe) +static void mdp4_wfd_wait4ov(int cndx); + +int mdp4_wfd_pipe_commit(struct msm_fb_data_type *mfd, + int cndx, int wait) { + int i, undx; + int mixer = 0; + struct vsycn_ctrl *vctrl; + struct vsync_update *vp; + struct mdp4_overlay_pipe *pipe; + struct mdp4_overlay_pipe *real_pipe; + unsigned long flags; + int cnt = 0; struct msmfb_writeback_data_list *node = NULL; - mutex_lock(&mfd->unregister_mutex); - mutex_lock(&mfd->writeback_mutex); - if (!list_empty(&mfd->writeback_free_queue) - && mfd->writeback_state != WB_STOPING - && mfd->writeback_state != WB_STOP) { - node = list_first_entry(&mfd->writeback_free_queue, - struct msmfb_writeback_data_list, active_entry); + + vctrl = &vsync_ctrl_db[cndx]; + + mutex_lock(&vctrl->update_lock); + undx = vctrl->update_ndx; + vp = &vctrl->vlist[undx]; + pipe = vctrl->base_pipe; + mixer = pipe->mixer_num; + + if (vp->update_cnt == 0) { + mutex_unlock(&vctrl->update_lock); + return cnt; } - if (node) { - list_del(&(node->active_entry)); - node->state = IN_BUSY_QUEUE; - mfd->writeback_active_cnt++; + + vctrl->update_ndx++; + vctrl->update_ndx &= 0x01; + vp->update_cnt = 0; /* reset */ + mutex_unlock(&vctrl->update_lock); + + mdp4_wfd_dequeue_update(mfd, &node); + + /* free previous committed iommu back to pool */ + mdp4_overlay_iommu_unmap_freelist(mixer); + + pipe = vp->plist; + for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) { + if (pipe->pipe_used) { + cnt++; + real_pipe = mdp4_overlay_ndx2pipe(pipe->pipe_ndx); + if (real_pipe && real_pipe->pipe_used) { + /* pipe not unset */ + mdp4_overlay_vsync_commit(pipe); + } + /* free previous iommu to freelist + * which will be freed at next + * pipe_commit + */ + mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0); + pipe->pipe_used = 0; /* clear */ + } } - mutex_unlock(&mfd->writeback_mutex); - writeback_pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL); + mdp_clk_ctrl(1); - /* free previous iommu at freelist back to pool */ - mdp4_overlay_iommu_unmap_freelist(writeback_pipe->mixer_num); + mdp4_mixer_stage_commit(mixer); - if (!writeback_pipe->ov_blt_addr) { - pr_err("%s: no writeback buffer 0x%x, %p\n", __func__, - (unsigned int)writeback_pipe->ov_blt_addr, node); - mutex_unlock(&mfd->unregister_mutex); + pipe = vctrl->base_pipe; + spin_lock_irqsave(&vctrl->spin_lock, flags); + vctrl->ov_koff++; + INIT_COMPLETION(vctrl->ov_comp); + vsync_irq_enable(INTR_OVERLAY2_DONE, MDP_OVERLAY2_TERM); + pr_debug("%s: kickoff\n", __func__); + /* kickoff overlay engine */ + mdp4_stat.kickoff_ov2++; + outpdw(MDP_BASE + 0x00D0, 0); + mb(); /* make sure kickoff executed */ + spin_unlock_irqrestore(&vctrl->spin_lock, flags); + + mdp4_stat.overlay_commit[pipe->mixer_num]++; + + if (wait) + mdp4_wfd_wait4ov(cndx); + + mdp4_wfd_queue_wakeup(mfd, node); + + return cnt; +} + +static void clk_ctrl_work(struct work_struct *work) +{ + struct vsycn_ctrl *vctrl = + container_of(work, typeof(*vctrl), clk_work); + mdp_clk_ctrl(0); +} + +void mdp4_wfd_init(int cndx) +{ + struct vsycn_ctrl *vctrl; + + if (cndx >= MAX_CONTROLLER) { + pr_err("%s: out or range: cndx=%d\n", __func__, cndx); return; } - if (writeback_pipe->blt_cnt == 0) - mdp4_overlay_writeback_update(mfd); + vctrl = &vsync_ctrl_db[cndx]; + if (vctrl->inited) + return; - pr_debug("%s: pid=%d\n", __func__, current->pid); + vctrl->inited = 1; + vctrl->update_ndx = 0; + mutex_init(&vctrl->update_lock); + init_completion(&vctrl->ov_comp); + spin_lock_init(&vctrl->spin_lock); + INIT_WORK(&vctrl->clk_work, clk_ctrl_work); +} - mdp4_mixer_stage_commit(pipe->mixer_num); +static void mdp4_wfd_wait4ov(int cndx) +{ + struct vsycn_ctrl *vctrl; - mdp4_writeback_overlay_kickoff(mfd, pipe); - mdp4_writeback_dma_busy_wait(mfd); + if (cndx >= MAX_CONTROLLER) { + pr_err("%s: out or range: cndx=%d\n", __func__, cndx); + return; + } - /* move current committed iommu to freelist */ - mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0); + vctrl = &vsync_ctrl_db[cndx]; - mutex_lock(&mfd->writeback_mutex); - list_add_tail(&node->active_entry, &mfd->writeback_busy_queue); - mutex_unlock(&mfd->writeback_mutex); - mfd->writeback_active_cnt--; - mutex_unlock(&mfd->unregister_mutex); - wake_up(&mfd->wait_q); + if (atomic_read(&vctrl->suspend) > 0) + return; + + wait_for_completion(&vctrl->ov_comp); } -void mdp4_writeback_kickoff_ui(struct msm_fb_data_type *mfd, - struct mdp4_overlay_pipe *pipe) + +void mdp4_overlay2_done_wfd(struct mdp_dma_data *dma) { - mdp4_mixer_stage_commit(pipe->mixer_num); + struct vsycn_ctrl *vctrl; + struct mdp4_overlay_pipe *pipe; + int cndx = 0; - pr_debug("%s: pid=%d\n", __func__, current->pid); - mdp4_writeback_overlay_kickoff(mfd, pipe); + vctrl = &vsync_ctrl_db[cndx]; + pipe = vctrl->base_pipe; + + spin_lock(&vctrl->spin_lock); + vsync_irq_disable(INTR_OVERLAY2_DONE, MDP_OVERLAY2_TERM); + vctrl->ov_done++; + complete(&vctrl->ov_comp); + schedule_work(&vctrl->clk_work); + pr_debug("%s ovdone interrupt\n", __func__); + spin_unlock(&vctrl->spin_lock); } void mdp4_writeback_overlay(struct msm_fb_data_type *mfd) { - int ret = 0; - struct msmfb_writeback_data_list *node = NULL; + struct vsycn_ctrl *vctrl; + struct mdp4_overlay_pipe *pipe; - mutex_lock(&mfd->unregister_mutex); - mutex_lock(&mfd->writeback_mutex); - if (!list_empty(&mfd->writeback_free_queue) - && mfd->writeback_state != WB_STOPING - && mfd->writeback_state != WB_STOP) { - node = list_first_entry(&mfd->writeback_free_queue, - struct msmfb_writeback_data_list, active_entry); - } - if (node) { - list_del(&(node->active_entry)); - node->state = IN_BUSY_QUEUE; - mfd->writeback_active_cnt++; - } - mutex_unlock(&mfd->writeback_mutex); + if (mfd && !mfd->panel_power_on) + return; + + pr_debug("%s:+ mfd=%x\n", __func__, (int)mfd); - writeback_pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL); + vctrl = &vsync_ctrl_db[0]; + pipe = vctrl->base_pipe; mutex_lock(&mfd->dma->ov_mutex); - pr_debug("%s in writeback\n", __func__); - if (writeback_pipe && !writeback_pipe->ov_blt_addr) { - pr_err("%s: no writeback buffer 0x%x\n", __func__, - (unsigned int)writeback_pipe->ov_blt_addr); - ret = mdp4_overlay_writeback_update(mfd); - if (ret) - pr_err("%s: update failed writeback pipe NULL\n", - __func__); - goto fail_no_blt_addr; - } - if (mfd && mfd->panel_power_on) { - pr_debug("%s in before busy wait\n", __func__); - mdp4_writeback_dma_busy_wait(mfd); + if (pipe->pipe_type == OVERLAY_TYPE_RGB) + mdp4_wfd_pipe_queue(0, pipe); - pr_debug("%s in before update\n", __func__); - ret = mdp4_overlay_writeback_update(mfd); - if (ret) { - pr_err("%s: update failed writeback pipe NULL\n", - __func__); - goto fail_no_blt_addr; - } + mdp4_overlay_mdp_perf_upd(mfd, 1); - pr_debug("%s: in writeback pan display 0x%x\n", __func__, - (unsigned int)writeback_pipe->ov_blt_addr); - mdp4_writeback_kickoff_ui(mfd, writeback_pipe); - mdp4_iommu_unmap(writeback_pipe); + mdp4_wfd_pipe_commit(mfd, 0, 1); - /* signal if pan function is waiting for the - * update completion */ - if (mfd->pan_waiting) { - mfd->pan_waiting = FALSE; - complete(&mfd->pan_comp); - } - } + mdp4_overlay_mdp_perf_upd(mfd, 0); - mutex_lock(&mfd->writeback_mutex); - list_add_tail(&node->active_entry, &mfd->writeback_busy_queue); - mfd->writeback_active_cnt--; - mutex_unlock(&mfd->writeback_mutex); - wake_up(&mfd->wait_q); -fail_no_blt_addr: - /*NOTE: This api was removed - mdp4_overlay_resource_release();*/ mutex_unlock(&mfd->dma->ov_mutex); - mutex_unlock(&mfd->unregister_mutex); } + static int mdp4_overlay_writeback_register_buffer( struct msm_fb_data_type *mfd, struct msmfb_writeback_data_list *node) { @@ -634,3 +742,88 @@ int mdp4_writeback_terminate(struct fb_info *info) mutex_unlock(&mfd->unregister_mutex); return rc; } + +static void mdp4_wfd_dequeue_update(struct msm_fb_data_type *mfd, + struct msmfb_writeback_data_list **wfdnode) +{ + struct vsycn_ctrl *vctrl; + struct mdp4_overlay_pipe *pipe; + struct msmfb_writeback_data_list *node = NULL; + + if (mfd && !mfd->panel_power_on) + return; + + pr_debug("%s:+ mfd=%x\n", __func__, (int)mfd); + + vctrl = &vsync_ctrl_db[0]; + pipe = vctrl->base_pipe; + + mutex_lock(&mfd->unregister_mutex); + mutex_lock(&mfd->writeback_mutex); + if (!list_empty(&mfd->writeback_free_queue) + && mfd->writeback_state != WB_STOPING + && mfd->writeback_state != WB_STOP) { + node = list_first_entry(&mfd->writeback_free_queue, + struct msmfb_writeback_data_list, active_entry); + } + if (node) { + list_del(&(node->active_entry)); + node->state = IN_BUSY_QUEUE; + mfd->writeback_active_cnt++; + } + mutex_unlock(&mfd->writeback_mutex); + + pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL); + + if (!pipe->ov_blt_addr) { + pr_err("%s: no writeback buffer 0x%x, %p\n", __func__, + (unsigned int)pipe->ov_blt_addr, node); + mutex_unlock(&mfd->unregister_mutex); + return; + } + + mdp4_overlay_writeback_update(mfd); + + *wfdnode = node; + + mutex_unlock(&mfd->unregister_mutex); +} + +static void mdp4_wfd_queue_wakeup(struct msm_fb_data_type *mfd, + struct msmfb_writeback_data_list *node) +{ + + if (mfd && !mfd->panel_power_on) + return; + + if (node == NULL) + return; + + pr_debug("%s: mfd=%x node: %p", __func__, (int)mfd, node); + + mutex_lock(&mfd->writeback_mutex); + list_add_tail(&node->active_entry, &mfd->writeback_busy_queue); + mfd->writeback_active_cnt--; + mutex_unlock(&mfd->writeback_mutex); + wake_up(&mfd->wait_q); +} + +int mdp4_writeback_set_mirroring_hint(struct fb_info *info, int hint) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (mfd->panel.type != WRITEBACK_PANEL) + return -ENOTSUPP; + + switch (hint) { + case MDP_WRITEBACK_MIRROR_ON: + case MDP_WRITEBACK_MIRROR_PAUSE: + case MDP_WRITEBACK_MIRROR_RESUME: + case MDP_WRITEBACK_MIRROR_OFF: + pr_info("wfd state switched to %d\n", hint); + switch_set_state(&mfd->writeback_sdev, hint); + return 0; + default: + return -EINVAL; + } +} diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c index 469e5bcd..a66f5c97 100644 --- a/drivers/video/msm/mdp4_util.c +++ b/drivers/video/msm/mdp4_util.c @@ -35,6 +35,149 @@ struct mdp4_statistic mdp4_stat; +struct mdp_csc_cfg_data csc_cfg_matrix[CSC_MAX_BLOCKS] = { + { + .block = MDP_BLOCK_VG_1, + .csc_data = { + (MDP_CSC_FLAG_YUV_OUT), + { + 0x0254, 0x0000, 0x0331, + 0x0254, 0xff37, 0xfe60, + 0x0254, 0x0409, 0x0000, + }, + { + 0xfff0, 0xff80, 0xff80, + }, + { + 0, 0, 0, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + }, + }, + { + .block = MDP_BLOCK_VG_2, + .csc_data = { + (MDP_CSC_FLAG_YUV_OUT), + { + 0x0254, 0x0000, 0x0331, + 0x0254, 0xff37, 0xfe60, + 0x0254, 0x0409, 0x0000, + }, + { + 0xfff0, 0xff80, 0xff80, + }, + { + 0, 0, 0, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + }, + }, + { + .block = MDP_BLOCK_DMA_P, + .csc_data = { + (0), + { + 0x0200, 0x0000, 0x0000, + 0x0000, 0x0200, 0x0000, + 0x0000, 0x0000, 0x0200, + }, + { + 0x0, 0x0, 0x0, + }, + { + 0, 0, 0, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + }, + }, + { + .block = MDP_BLOCK_OVERLAY_1, + .csc_data = { + (0), + { + 0x0083, 0x0102, 0x0032, + 0x1fb5, 0x1f6c, 0x00e1, + 0x00e1, 0x1f45, 0x1fdc, + }, + { + 0x0, 0x0, 0x0, + }, + { + 0x0010, 0x0080, 0x0080, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + { + 0x0010, 0x00eb, 0x0010, + 0x00f0, 0x0010, 0x00f0, + }, + }, + }, + { + .block = MDP_BLOCK_OVERLAY_2, + .csc_data = { + (0), + { + 0x0083, 0x0102, 0x0032, + 0x1fb5, 0x1f6c, 0x00e1, + 0x00e1, 0x1f45, 0x1fdc, + }, + { + 0x0, 0x0, 0x0, + }, + { + 0x0010, 0x0080, 0x0080, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + { + 0x0010, 0x00eb, 0x0010, + 0x00f0, 0x0010, 0x00f0, + }, + }, + }, + { + .block = MDP_BLOCK_DMA_S, + .csc_data = { + (0), + { + 0x0200, 0x0000, 0x0000, + 0x0000, 0x0200, 0x0000, + 0x0000, 0x0000, 0x0200, + }, + { + 0x0, 0x0, 0x0, + }, + { + 0, 0, 0, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + { + 0, 0xff, 0, 0xff, 0, 0xff, + }, + }, + }, +}; + unsigned is_mdp4_hw_reset(void) { unsigned hw_reset = 0; @@ -245,11 +388,12 @@ void mdp4_hw_init(void) { ulong bits; uint32 clk_rate; - + int i; /* MDP cmd block enable */ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - mdp_bus_scale_update_request(5); + mdp_bus_scale_update_request + (MDP_BUS_SCALE_INIT, MDP_BUS_SCALE_INIT); #ifdef MDP4_ERROR /* @@ -273,11 +417,8 @@ void mdp4_hw_init(void) mdp4_vg_qseed_init(0); mdp4_vg_qseed_init(1); - mdp4_vg_csc_setup(0); - mdp4_vg_csc_setup(1); - mdp4_mixer_csc_setup(1); - mdp4_mixer_csc_setup(2); - mdp4_dmap_csc_setup(); + for (i = 0; i < CSC_MAX_BLOCKS; i++) + mdp4_csc_config(&csc_cfg_matrix[i]); if (mdp_rev <= MDP_REV_41) { mdp4_mixer_gc_lut_setup(0); @@ -314,8 +455,6 @@ void mdp4_hw_init(void) clk_rate = mdp_get_core_clk(); mdp4_fetch_cfg(clk_rate); - mdp4_overlay_cfg_init(); - /* Mark hardware as initialized. Only revisions > v2.1 have a register * for tracking core reset status. */ if (mdp_hw_revision > MDP4_REVISION_V2_1) @@ -417,7 +556,7 @@ irqreturn_t mdp4_isr(int irq, void *ptr) mdp4_dmap_done_dsi_cmd(0); #else else { /* MDDI */ - mdp4_dma_p_done_mddi(dma); + mdp4_dmap_done_mddi(0); mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_OFF, TRUE); complete(&dma->comp); @@ -468,7 +607,7 @@ irqreturn_t mdp4_isr(int irq, void *ptr) mdp4_overlay0_done_dsi_cmd(0); #else if (panel & MDP4_PANEL_MDDI) - mdp4_overlay0_done_mddi(dma); + mdp4_overlay0_done_mddi(0); #endif } mdp_hw_cursor_done(); @@ -490,19 +629,15 @@ irqreturn_t mdp4_isr(int irq, void *ptr) if (panel & MDP4_PANEL_ATV) mdp4_overlay1_done_atv(); #endif + mdp_hw_cursor_done(); } #if defined(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) if (isr & INTR_OVERLAY2_DONE) { mdp4_stat.intr_overlay2++; + mdp_pipe_ctrl(MDP_OVERLAY2_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); /* disable DTV interrupt */ - dma = &dma_wb_data; - spin_lock(&mdp_spin_lock); - mdp_intr_mask &= ~INTR_OVERLAY2_DONE; - outp32(MDP_INTR_ENABLE, mdp_intr_mask); - dma->waiting = FALSE; - spin_unlock(&mdp_spin_lock); if (panel & MDP4_PANEL_WRITEBACK) - mdp4_overlay1_done_writeback(dma); + mdp4_overlay2_done_wfd(&dma_wb_data); } #endif #endif /* OVERLAY */ @@ -1252,422 +1387,46 @@ struct mdp_csc_cfg mdp_csc_convert[4] = { }, }; -struct mdp_csc_cfg csc_matrix[3] = { - { - (MDP_CSC_FLAG_YUV_OUT), - { - 0x0254, 0x0000, 0x0331, - 0x0254, 0xff37, 0xfe60, - 0x0254, 0x0409, 0x0000, - }, - { - 0xfff0, 0xff80, 0xff80, - }, - { - 0, 0, 0, - }, - { - 0, 0xff, 0, 0xff, 0, 0xff, - }, - { - 0, 0xff, 0, 0xff, 0, 0xff, - }, - }, - { - (MDP_CSC_FLAG_YUV_OUT), - { - 0x0254, 0x0000, 0x0331, - 0x0254, 0xff37, 0xfe60, - 0x0254, 0x0409, 0x0000, - }, - { - 0xfff0, 0xff80, 0xff80, - }, - { - 0, 0, 0, - }, - { - 0, 0xff, 0, 0xff, 0, 0xff, - }, - { - 0, 0xff, 0, 0xff, 0, 0xff, - }, - }, - { - (0), - { - 0x0200, 0x0000, 0x0000, - 0x0000, 0x0200, 0x0000, - 0x0000, 0x0000, 0x0200, - }, - { - 0x0, 0x0, 0x0, - }, - { - 0, 0, 0, - }, - { - 0, 0xff, 0, 0xff, 0, 0xff, - }, - { - 0, 0xff, 0, 0xff, 0, 0xff, - }, - }, -}; - - -#define MDP4_CSC_MV_OFF 0x4400 -#define MDP4_CSC_PRE_BV_OFF 0x4500 -#define MDP4_CSC_POST_BV_OFF 0x4580 -#define MDP4_CSC_PRE_LV_OFF 0x4600 -#define MDP4_CSC_POST_LV_OFF 0x4680 - -void mdp4_vg_csc_mv_setup(int vp_num) -{ - uint32 *off; - int i, voff; - - voff = MDP4_VIDEO_OFF * vp_num; - off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff + - MDP4_CSC_MV_OFF); - - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 9; i++) { - outpdw(off, csc_matrix[vp_num].csc_mv[i]); - off++; - } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} - -void mdp4_vg_csc_pre_bv_setup(int vp_num) -{ - uint32 *off; - int i, voff; - - voff = MDP4_VIDEO_OFF * vp_num; - off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff + - MDP4_CSC_PRE_BV_OFF); - - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 3; i++) { - outpdw(off, csc_matrix[vp_num].csc_pre_bv[i]); - off++; - } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} - -void mdp4_vg_csc_post_bv_setup(int vp_num) -{ - uint32 *off; - int i, voff; - - voff = MDP4_VIDEO_OFF * vp_num; - off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff + - MDP4_CSC_POST_BV_OFF); - - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 3; i++) { - outpdw(off, csc_matrix[vp_num].csc_post_bv[i]); - off++; - } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} - -void mdp4_vg_csc_pre_lv_setup(int vp_num) -{ - uint32 *off; - int i, voff; - - voff = MDP4_VIDEO_OFF * vp_num; - off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff + - MDP4_CSC_PRE_LV_OFF); - - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 6; i++) { - outpdw(off, csc_matrix[vp_num].csc_pre_lv[i]); - off++; - } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} - -void mdp4_vg_csc_post_lv_setup(int vp_num) -{ - uint32 *off; - int i, voff; - - voff = MDP4_VIDEO_OFF * vp_num; - off = (uint32 *)(MDP_BASE + MDP4_VIDEO_BASE + voff + - MDP4_CSC_POST_LV_OFF); - - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 6; i++) { - outpdw(off, csc_matrix[vp_num].csc_post_lv[i]); - off++; - } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} - -void mdp4_vg_csc_convert_setup(int vp_num) -{ - struct mdp_csc_cfg_data cfg; - - switch (vp_num) { - case 0: - cfg.block = MDP_BLOCK_VG_1; - break; - case 1: - cfg.block = MDP_BLOCK_VG_2; - break; - default: - pr_err("%s - invalid vp_num = %d", __func__, vp_num); - return; - } - cfg.csc_data = csc_matrix[vp_num]; - mdp4_csc_enable(&cfg); -} - -void mdp4_vg_csc_setup(int vp_num) -{ - /* yuv2rgb */ - mdp4_vg_csc_mv_setup(vp_num); - mdp4_vg_csc_pre_bv_setup(vp_num); - mdp4_vg_csc_post_bv_setup(vp_num); - mdp4_vg_csc_pre_lv_setup(vp_num); - mdp4_vg_csc_post_lv_setup(vp_num); - mdp4_vg_csc_convert_setup(vp_num); -} void mdp4_vg_csc_update(struct mdp_csc *p) { struct mdp4_overlay_pipe *pipe; - int vp_num; + uint32_t block = 0; + int i = 0; pipe = mdp4_overlay_ndx2pipe(p->id); if (pipe == NULL) { pr_err("%s: p->id = %d Error\n", __func__, p->id); return; } - - vp_num = pipe->pipe_num - OVERLAY_PIPE_VG1; - - if (vp_num == 0 || vp_num == 1) { - memcpy(csc_matrix[vp_num].csc_mv, p->csc_mv, sizeof(p->csc_mv)); - memcpy(csc_matrix[vp_num].csc_pre_bv, p->csc_pre_bv, - sizeof(p->csc_pre_bv)); - memcpy(csc_matrix[vp_num].csc_post_bv, p->csc_post_bv, - sizeof(p->csc_post_bv)); - memcpy(csc_matrix[vp_num].csc_pre_lv, p->csc_pre_lv, - sizeof(p->csc_pre_lv)); - memcpy(csc_matrix[vp_num].csc_post_lv, p->csc_post_lv, - sizeof(p->csc_post_lv)); - mdp4_vg_csc_setup(vp_num); - } -} -static uint32 csc_rgb2yuv_matrix_tab[9] = { - 0x0083, 0x0102, 0x0032, - 0x1fb5, 0x1f6c, 0x00e1, - 0x00e1, 0x1f45, 0x1fdc -}; - -static uint32 csc_rgb2yuv_pre_bv_tab[3] = {0, 0, 0}; - -static uint32 csc_rgb2yuv_post_bv_tab[3] = {0x0010, 0x0080, 0x0080}; - -static uint32 csc_rgb2yuv_pre_lv_tab[6] = { - 0x00, 0xff, 0x00, - 0xff, 0x00, 0xff -}; - -static uint32 csc_rgb2yuv_post_lv_tab[6] = { - 0x0010, 0x00eb, 0x0010, - 0x00f0, 0x0010, 0x00f0 -}; - -void mdp4_mixer_csc_mv_setup(uint32 mixer) -{ - uint32 *off; - int i; - - if (mixer == MDP4_MIXER1) - off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2400); - else - off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2400); - - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 9; i++) { - outpdw(off, csc_rgb2yuv_matrix_tab[i]); - off++; - } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} - -void mdp4_mixer_csc_pre_bv_setup(uint32 mixer) -{ - uint32 *off; - int i; - - if (mixer == MDP4_MIXER1) - off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2500); + if (pipe->pipe_num == OVERLAY_PIPE_VG1) + block = MDP_BLOCK_VG_1; + else if (pipe->pipe_num == OVERLAY_PIPE_VG2) + block = MDP_BLOCK_VG_2; else - off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2500); - - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 3; i++) { - outpdw(off, csc_rgb2yuv_pre_bv_tab[i]); - off++; - } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} - -void mdp4_mixer_csc_post_bv_setup(uint32 mixer) -{ - uint32 *off; - int i; - - if (mixer == MDP4_MIXER1) - off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2580); - else - off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2580); - - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 3; i++) { - outpdw(off, csc_rgb2yuv_post_bv_tab[i]); - off++; - } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} - -void mdp4_mixer_csc_pre_lv_setup(uint32 mixer) -{ - uint32 *off; - int i; - - if (mixer == MDP4_MIXER1) - off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2600); - else - off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2600); - - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 6; i++) { - outpdw(off, csc_rgb2yuv_pre_lv_tab[i]); - off++; - } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} - -void mdp4_mixer_csc_post_lv_setup(uint32 mixer) -{ - uint32 *off; - int i; - - if (mixer == MDP4_MIXER1) - off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x2680); - else - off = (uint32 *)(MDP_BASE + MDP4_OVERLAYPROC2_BASE + 0x2680); - - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 6; i++) { - outpdw(off, csc_rgb2yuv_post_lv_tab[i]); - off++; - } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} - -void mdp4_mixer_csc_setup(uint32 mixer) -{ - if (mixer >= MDP4_MIXER1) { - /* rgb2yuv */ - mdp4_mixer_csc_mv_setup(mixer); - mdp4_mixer_csc_pre_bv_setup(mixer); - mdp4_mixer_csc_post_bv_setup(mixer); - mdp4_mixer_csc_pre_lv_setup(mixer); - mdp4_mixer_csc_post_lv_setup(mixer); - } -} - -#define DMA_P_BASE 0x90000 -void mdp4_dmap_csc_mv_setup(void) -{ - uint32 *off; - int i; - - off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3400); - - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 9; i++) { - outpdw(off, csc_matrix[2].csc_mv[i]); - off++; - } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} - -void mdp4_dmap_csc_pre_bv_setup(void) -{ - uint32 *off; - int i; - - off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3500); - - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 3; i++) { - outpdw(off, csc_matrix[2].csc_pre_bv[i]); - off++; - } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} - -void mdp4_dmap_csc_post_bv_setup(void) -{ - uint32 *off; - int i; - - off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3580); - - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 3; i++) { - outpdw(off, csc_matrix[2].csc_post_bv[i]); - off++; - } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} - -void mdp4_dmap_csc_pre_lv_setup(void) -{ - uint32 *off; - int i; - - off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3600); + return; - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 6; i++) { - outpdw(off, csc_matrix[2].csc_pre_lv[i]); - off++; + for (i = 0; i < CSC_MAX_BLOCKS; i++) { + if (csc_cfg_matrix[i].block == block) + break; } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} - -void mdp4_dmap_csc_post_lv_setup(void) -{ - uint32 *off; - int i; + if (i == CSC_MAX_BLOCKS) + return; - off = (uint32 *)(MDP_BASE + DMA_P_BASE + 0x3680); + memcpy(&csc_cfg_matrix[i].csc_data.csc_mv, p->csc_mv, + sizeof(p->csc_mv)); + memcpy(&csc_cfg_matrix[i].csc_data.csc_pre_bv, p->csc_pre_bv, + sizeof(p->csc_pre_bv)); + memcpy(&csc_cfg_matrix[i].csc_data.csc_post_bv, p->csc_post_bv, + sizeof(p->csc_post_bv)); + memcpy(&csc_cfg_matrix[i].csc_data.csc_pre_lv, p->csc_pre_lv, + sizeof(p->csc_pre_lv)); + memcpy(&csc_cfg_matrix[i].csc_data.csc_post_lv, p->csc_post_lv, + sizeof(p->csc_post_lv)); + csc_cfg_matrix[i].csc_data.flags = MDP_CSC_FLAG_YUV_OUT; - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - for (i = 0; i < 6; i++) { - outpdw(off, csc_matrix[2].csc_post_lv[i]); - off++; - } - mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); -} + mdp4_csc_config(&csc_cfg_matrix[i]); -void mdp4_dmap_csc_setup(void) -{ - mdp4_dmap_csc_mv_setup(); - mdp4_dmap_csc_pre_bv_setup(); - mdp4_dmap_csc_post_bv_setup(); - mdp4_dmap_csc_pre_lv_setup(); - mdp4_dmap_csc_post_lv_setup(); } char gc_lut[] = { @@ -2375,7 +2134,7 @@ static uint32_t mdp4_csc_block2base(uint32_t block) base = 0x1A000; break; case MDP_BLOCK_OVERLAY_2: - base = (mdp_rev >= MDP_REV_44) ? 0x8A000 : 0x0; + base = (mdp_rev >= MDP_REV_43) ? 0x8A000 : 0x0; break; case MDP_BLOCK_VG_1: base = 0x24000; @@ -2546,7 +2305,7 @@ u32 mdp4_allocate_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num) pr_info("%s:%d ion based allocation mfd->mem_hid 0x%x\n", __func__, __LINE__, mfd->mem_hid); buf->ihdl = ion_alloc(mfd->iclient, buffer_size, SZ_4K, - mfd->mem_hid); + mfd->mem_hid, 0); if (!IS_ERR_OR_NULL(buf->ihdl)) { if (mdp_iommu_split_domain) { if (ion_map_iommu(mfd->iclient, buf->ihdl, @@ -2555,7 +2314,7 @@ u32 mdp4_allocate_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num) pr_err("ion_map_iommu() read failed\n"); return -ENOMEM; } - if (mfd->mem_hid & ION_SECURE) { + if (mfd->mem_hid & ION_FLAG_SECURE) { if (ion_phys(mfd->iclient, buf->ihdl, &addr, (size_t *)&len)) { pr_err("%s:%d: ion_phys map failed\n", @@ -2618,7 +2377,7 @@ void mdp4_free_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num) if (!IS_ERR_OR_NULL(mfd->iclient)) { if (!IS_ERR_OR_NULL(buf->ihdl)) { if (mdp_iommu_split_domain) { - if (!(mfd->mem_hid & ION_SECURE)) + if (!(mfd->mem_hid & ION_FLAG_SECURE)) ion_unmap_iommu(mfd->iclient, buf->ihdl, DISPLAY_WRITE_DOMAIN, GEN_POOL); ion_unmap_iommu(mfd->iclient, buf->ihdl, @@ -2895,7 +2654,7 @@ static uint32_t mdp_pp_block2argc(uint32_t block) break; case MDP_BLOCK_OVERLAY_2: - valid = (mdp_rev >= MDP_REV_44) ? 1 : 0; + valid = (mdp_rev >= MDP_REV_43) ? 1 : 0; break; default: @@ -3232,75 +2991,196 @@ static uint32_t mdp4_pp_block2qseed(uint32_t block) return valid; } -static int mdp4_qseed_write_cfg(struct mdp_qseed_cfg_data *cfg) +int mdp4_qseed_access_cfg(struct mdp_qseed_cfg *config, uint32_t base) { int i, ret = 0; - uint32_t base = (uint32_t) (MDP_BASE + mdp_block2base(cfg->block)); uint32_t *values; - if ((cfg->table_num != 1) && (cfg->table_num != 2)) { + if ((config->table_num != 1) && (config->table_num != 2)) { ret = -ENOTTY; goto error; } - if (((cfg->table_num == 1) && (cfg->len != QSEED_TABLE_1_COUNT)) || - ((cfg->table_num == 2) && (cfg->len != QSEED_TABLE_2_COUNT))) { + if (((config->table_num == 1) && (config->len != QSEED_TABLE_1_COUNT)) + || ((config->table_num == 2) && + (config->len != QSEED_TABLE_2_COUNT))) { ret = -EINVAL; goto error; } - values = kmalloc(cfg->len * sizeof(uint32_t), GFP_KERNEL); + values = kmalloc(config->len * sizeof(uint32_t), GFP_KERNEL); if (!values) { ret = -ENOMEM; goto error; } - ret = copy_from_user(values, cfg->data, sizeof(uint32_t) * cfg->len); + base += (config->table_num == 1) ? MDP4_QSEED_TABLE1_OFF : + MDP4_QSEED_TABLE2_OFF; - base += (cfg->table_num == 1) ? MDP4_QSEED_TABLE1_OFF : - MDP4_QSEED_TABLE2_OFF; - for (i = 0; i < cfg->len; i++) { - MDP_OUTP(base , values[i]); - base += sizeof(uint32_t); + if (config->ops & MDP_PP_OPS_WRITE) { + ret = copy_from_user(values, config->data, + sizeof(uint32_t) * config->len); + if (ret) { + pr_warn("%s: Error copying from user, %d", __func__, + ret); + ret = -EINVAL; + goto err_mem; + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < config->len; i++) { + if (!(base & 0x3FF)) + wmb(); + MDP_OUTP(base , values[i]); + base += sizeof(uint32_t); + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + } else if (config->ops & MDP_PP_OPS_READ) { + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + for (i = 0; i < config->len; i++) { + values[i] = inpdw(base); + if (!(base & 0x3FF)) + rmb(); + base += sizeof(uint32_t); + } + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + ret = copy_to_user(config->data, values, + sizeof(uint32_t) * config->len); + if (ret) { + pr_warn("%s: Error copying to user, %d", __func__, ret); + ret = -EINVAL; + goto err_mem; + } } +err_mem: kfree(values); error: return ret; } -int mdp4_qseed_cfg(struct mdp_qseed_cfg_data *cfg) +int mdp4_qseed_cfg(struct mdp_qseed_cfg_data *config) { int ret = 0; + struct mdp_qseed_cfg *cfg = &config->qseed_data; + uint32_t base; - if (!mdp4_pp_block2qseed(cfg->block)) { + if (!mdp4_pp_block2qseed(config->block)) { ret = -ENOTTY; goto error; } - if (cfg->table_num != 1) { - ret = -ENOTTY; - pr_info("%s: Only QSEED table1 supported.\n", __func__); + if ((cfg->ops & MDP_PP_OPS_READ) && (cfg->ops & MDP_PP_OPS_WRITE)) { + ret = -EPERM; + pr_warn("%s: Cannot read and write on the same request\n", + __func__); goto error; } + base = (uint32_t) (MDP_BASE + mdp_block2base(config->block)); + ret = mdp4_qseed_access_cfg(cfg, base); - switch ((cfg->ops & 0x6) >> 1) { - case 0x1: - pr_info("%s: QSEED read not supported\n", __func__); - ret = -ENOTTY; - break; - case 0x2: - ret = mdp4_qseed_write_cfg(cfg); - if (ret) - goto error; - break; - default: - break; +error: + return ret; +} + +static int is_valid_calib_addr(void *addr) +{ + int ret = 0; + unsigned int ptr; + + ptr = (unsigned int) addr; + + if (mdp_rev >= MDP_REV_30 && mdp_rev < MDP_REV_40) { + /* if request is outside the MDP reg-map or is not aligned 4 */ + if (ptr == 0x0 || ptr > 0xF0600 || ptr % 0x4) + goto end; + + if (ptr >= 0x90000 && ptr < 0x94000) { + if (ptr == 0x90000 || ptr == 0x90070) + ret = 1; + else if (ptr >= 0x93400 && ptr <= 0x93420) + ret = 1; + else if (ptr >= 0x93500 && ptr <= 0x93508) + ret = 1; + else if (ptr >= 0x93580 && ptr <= 0x93588) + ret = 1; + else if (ptr >= 0x93600 && ptr <= 0x93614) + ret = 1; + else if (ptr >= 0x93680 && ptr <= 0x93694) + ret = 1; + else if (ptr >= 0x93800 && ptr <= 0x93BFC) + ret = 1; + } + } else if (mdp_rev >= MDP_REV_40 && mdp_rev <= MDP_REV_44) { + /* if request is outside the MDP reg-map or is not aligned 4 */ + if (ptr > 0xF0600 || ptr % 0x4) + goto end; + + if (ptr < 0x90000) { + if (ptr == 0x0 || ptr == 0x4 || ptr == 0x28200 || + ptr == 0x28204) + ret = 1; + } else if (ptr < 0x95000) { + if (ptr == 0x90000 || ptr == 0x90070) + ret = 1; + else if (ptr >= 0x93400 && ptr <= 0x93420) + ret = 1; + else if (ptr >= 0x93500 && ptr <= 0x93508) + ret = 1; + else if (ptr >= 0x93580 && ptr <= 0x93588) + ret = 1; + else if (ptr >= 0x93600 && ptr <= 0x93614) + ret = 1; + else if (ptr >= 0x93680 && ptr <= 0x93694) + ret = 1; + else if (ptr >= 0x94800 && ptr <= 0x94BFC) + ret = 1; + } else if (ptr < 0x9A000) { + if (ptr >= 0x98800 && ptr <= 0x9883C) + ret = 1; + else if (ptr >= 0x98880 && ptr <= 0x988AC) + ret = 1; + else if (ptr >= 0x98900 && ptr <= 0x9893C) + ret = 1; + else if (ptr >= 0x98980 && ptr <= 0x989BC) + ret = 1; + else if (ptr >= 0x98A00 && ptr <= 0x98A3C) + ret = 1; + else if (ptr >= 0x98A80 && ptr <= 0x98ABC) + ret = 1; + else if (ptr >= 0x99000 && ptr <= 0x993FC) + ret = 1; + else if (ptr >= 0x99800 && ptr <= 0x99BFC) + ret = 1; + } else if (ptr >= 0x9A000 && ptr <= 0x9a08c) { + ret = 1; + } } +end: + return ret; +} -error: +int mdp4_calib_config(struct mdp_calib_config_data *cfg) +{ + int ret = -1; + void *ptr = (void *) cfg->addr; + + if (is_valid_calib_addr(ptr)) + ret = 0; + else + return ret; + + ptr = (void *)(((unsigned int) ptr) + MDP_BASE); + mdp_clk_ctrl(1); + if (cfg->ops & MDP_PP_OPS_READ) { + cfg->data = inpdw(ptr); + ret = 1; + } else if (cfg->ops & MDP_PP_OPS_WRITE) { + outpdw(ptr, cfg->data); + } + mdp_clk_ctrl(0); return ret; } + u32 mdp4_get_mixer_num(u32 panel_type) { u32 mixer_num; diff --git a/drivers/video/msm/mdp4_wfd_writeback.c b/drivers/video/msm/mdp4_wfd_writeback.c index d96fc7d6..7876f9ae 100644 --- a/drivers/video/msm/mdp4_wfd_writeback.c +++ b/drivers/video/msm/mdp4_wfd_writeback.c @@ -75,6 +75,13 @@ static int writeback_probe(struct platform_device *pdev) platform_set_drvdata(mdp_dev, mfd); + mfd->writeback_sdev.name = "wfd"; + rc = switch_dev_register(&mfd->writeback_sdev); + if (rc) { + pr_err("Failed to setup switch dev for writeback panel"); + return rc; + } + rc = platform_device_add(mdp_dev); if (rc) { WRITEBACK_MSG_ERR("failed to add device"); @@ -84,8 +91,16 @@ static int writeback_probe(struct platform_device *pdev) return rc; } +static int writeback_remove(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + switch_dev_unregister(&mfd->writeback_sdev); + return 0; +} + static struct platform_driver writeback_driver = { .probe = writeback_probe, + .remove = writeback_remove, .driver = { .name = "writeback", }, diff --git a/drivers/video/msm/mdp_cursor.c b/drivers/video/msm/mdp_cursor.c index 93ff3882..a3920af5 100644 --- a/drivers/video/msm/mdp_cursor.c +++ b/drivers/video/msm/mdp_cursor.c @@ -52,7 +52,11 @@ void mdp_cursor_ctrl_workqueue_handler(struct work_struct *work) /* disable vsync */ spin_lock_irqsave(&mdp_spin_lock, flag); - mdp_disable_irq(MDP_OVERLAY0_TERM); + if (hdmi_prim_display) + mdp_disable_irq(MDP_OVERLAY1_TERM); + else + mdp_disable_irq(MDP_OVERLAY0_TERM); + spin_unlock_irqrestore(&mdp_spin_lock, flag); } @@ -78,29 +82,37 @@ void mdp_hw_cursor_done(void) * * Moving this code out of the ISR will cause the MDP to underrun! */ + uint32_t base = 0; + + if (hdmi_prim_display) + base = ((uint32_t)(MDP_BASE + 0xB0000)); + else + base = ((uint32_t)(MDP_BASE + 0x90000)); + + spin_lock(&mdp_spin_lock); if (sync_disabled) { spin_unlock(&mdp_spin_lock); return; } - MDP_OUTP(MDP_BASE + 0x90044, (height << 16) | width); - MDP_OUTP(MDP_BASE + 0x90048, cursor_buf_phys); + MDP_OUTP(base + 0x44, (height << 16) | width); + MDP_OUTP(base + 0x48, cursor_buf_phys); - MDP_OUTP(MDP_BASE + 0x90060, + MDP_OUTP(base + 0x60, (transp_en << 3) | (calpha_en << 1) | - (inp32(MDP_BASE + 0x90060) & 0x1)); + (inp32(base + 0x60) & 0x1)); - MDP_OUTP(MDP_BASE + 0x90064, (alpha << 24)); - MDP_OUTP(MDP_BASE + 0x90068, (0xffffff & bg_color)); - MDP_OUTP(MDP_BASE + 0x9006C, (0xffffff & bg_color)); + MDP_OUTP(base + 0x64, (alpha << 24)); + MDP_OUTP(base + 0x68, (0xffffff & bg_color)); + MDP_OUTP(base + 0x6C, (0xffffff & bg_color)); /* enable/disable the cursor as per the last request */ - if (cursor_enabled && !(inp32(MDP_BASE + 0x90060) & (0x1))) - MDP_OUTP(MDP_BASE + 0x90060, inp32(MDP_BASE + 0x90060) | 0x1); - else if (!cursor_enabled && (inp32(MDP_BASE + 0x90060) & (0x1))) - MDP_OUTP(MDP_BASE + 0x90060, - inp32(MDP_BASE + 0x90060) & (~0x1)); + if (cursor_enabled && !(inp32(base + 0x60) & (0x1))) + MDP_OUTP(base + 0x60, inp32(base + 0x60) | 0x1); + else if (!cursor_enabled && (inp32(base + 0x60) & (0x1))) + MDP_OUTP(base + 0x60, + inp32(base + 0x60) & (~0x1)); /* enqueue the task to disable MDP interrupts */ queue_work(mdp_cursor_ctrl_wq, &mdp_cursor_ctrl_worker); @@ -119,17 +131,26 @@ static void mdp_hw_cursor_enable_vsync(void) if (sync_disabled) { /* cancel pending task to disable MDP interrupts */ - if (work_pending(&mdp_cursor_ctrl_worker)) + if (work_pending(&mdp_cursor_ctrl_worker)) { cancel_work_sync(&mdp_cursor_ctrl_worker); - else + } else { /* enable irq */ - mdp_enable_irq(MDP_OVERLAY0_TERM); + if (hdmi_prim_display) + mdp_enable_irq(MDP_OVERLAY1_TERM); + else + mdp_enable_irq(MDP_OVERLAY0_TERM); + } sync_disabled = 0; /* enable vsync intr */ - outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE); - mdp_intr_mask |= INTR_OVERLAY0_DONE; + if (hdmi_prim_display) { + outp32(MDP_INTR_CLEAR, INTR_OVERLAY1_DONE); + mdp_intr_mask |= INTR_OVERLAY1_DONE; + } else { + outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE); + mdp_intr_mask |= INTR_OVERLAY0_DONE; + } outp32(MDP_INTR_ENABLE, mdp_intr_mask); } } @@ -140,14 +161,20 @@ int mdp_hw_cursor_sync_update(struct fb_info *info, struct fb_cursor *cursor) struct fb_image *img = &cursor->image; unsigned long flag; int sync_needed = 0, ret = 0; + uint32_t base = 0; if ((img->width > MDP_CURSOR_WIDTH) || (img->height > MDP_CURSOR_HEIGHT) || (img->depth != 32)) return -EINVAL; + if (hdmi_prim_display) + base = ((uint32_t)(MDP_BASE + 0xB0000)); + else + base = ((uint32_t)(MDP_BASE + 0x90000)); + if (cursor->set & FB_CUR_SETPOS) - MDP_OUTP(MDP_BASE + 0x9004c, (img->dy << 16) | img->dx); + MDP_OUTP(base + 0x4c, (img->dy << 16) | img->dx); if (cursor->set & FB_CUR_SETIMAGE) { ret = copy_from_user(mfd->cursor_buf, img->data, diff --git a/drivers/video/msm/mdp_debugfs.c b/drivers/video/msm/mdp_debugfs.c index 68727f4b..d3e0c8dd 100644 --- a/drivers/video/msm/mdp_debugfs.c +++ b/drivers/video/msm/mdp_debugfs.c @@ -719,84 +719,6 @@ static const struct file_operations pmdh_fops = { .write = pmdh_reg_write, }; - - -#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI) -static int vsync_reg_open(struct inode *inode, struct file *file) -{ - /* non-seekable */ - file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); - return 0; -} - -static int vsync_reg_release(struct inode *inode, struct file *file) -{ - return 0; -} - -static ssize_t vsync_reg_write( - struct file *file, - const char __user *buff, - size_t count, - loff_t *ppos) -{ - uint32 enable; - int cnt; - - if (count >= sizeof(debug_buf)) - return -EFAULT; - - if (copy_from_user(debug_buf, buff, count)) - return -EFAULT; - - debug_buf[count] = 0; /* end of string */ - - cnt = sscanf(debug_buf, "%x", &enable); - - mdp_dmap_vsync_set(enable); - - return count; -} - -static ssize_t vsync_reg_read( - struct file *file, - char __user *buff, - size_t count, - loff_t *ppos) -{ - char *bp; - int len = 0; - int tot = 0; - int dlen; - - if (*ppos) - return 0; /* the end */ - - bp = debug_buf; - dlen = sizeof(debug_buf); - len = snprintf(bp, dlen, "%x\n", mdp_dmap_vsync_get()); - tot += len; - bp += len; - *bp = 0; - tot++; - - if (copy_to_user(buff, debug_buf, tot)) - return -EFAULT; - - *ppos += tot; /* increase offset */ - - return tot; -} - - -static const struct file_operations vsync_fops = { - .open = vsync_reg_open, - .release = vsync_reg_release, - .read = vsync_reg_read, - .write = vsync_reg_write, -}; -#endif - static ssize_t emdh_reg_write( struct file *file, const char __user *buff, @@ -1107,6 +1029,135 @@ static const struct file_operations dbg_reg_fops = { .write = dbg_reg_write, }; +u32 dbg_force_ov0_blt; +u32 dbg_force_ov1_blt; + +static ssize_t dbg_force_ov0_blt_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) { + int len; + + if (*ppos) + return 0; + + len = snprintf(debug_buf, sizeof(debug_buf), + "%d\n", dbg_force_ov0_blt); + + if (len < 0) + return 0; + + if (copy_to_user(buff, debug_buf, len)) + return -EFAULT; + + *ppos += len; + + return len; +} + +static ssize_t dbg_force_ov0_blt_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + u32 cnt; + + if (count >= sizeof(debug_buf)) + return -EFAULT; + + if (copy_from_user(debug_buf, buff, count)) + return -EFAULT; + + debug_buf[count] = 0; /* end of string */ + + cnt = sscanf(debug_buf, "%x", &dbg_force_ov0_blt); + + pr_info("%s: dbg_force_ov0_blt = %x\n", + __func__, dbg_force_ov0_blt); + + if ((dbg_force_ov0_blt & 0x0f) > 2) + pr_err("%s: invalid dbg_force_ov0_blt = %d\n", + __func__, dbg_force_ov0_blt); + + if ((dbg_force_ov0_blt >> 4) > 2) + pr_err("%s: invalid dbg_force_ov0_blt = %d\n", + __func__, dbg_force_ov0_blt); + + return count; +} + +static const struct file_operations dbg_force_ov0_blt_fops = { + .open = dbg_open, + .release = dbg_release, + .read = dbg_force_ov0_blt_read, + .write = dbg_force_ov0_blt_write, +}; + +static ssize_t dbg_force_ov1_blt_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) { + int len; + + if (*ppos) + return 0; + + len = snprintf(debug_buf, sizeof(debug_buf), + "%x\n", dbg_force_ov1_blt); + + if (len < 0) + return 0; + + if (copy_to_user(buff, debug_buf, len)) + return -EFAULT; + + *ppos += len; + + return len; +} + +static ssize_t dbg_force_ov1_blt_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + u32 cnt; + + if (count >= sizeof(debug_buf)) + return -EFAULT; + + if (copy_from_user(debug_buf, buff, count)) + return -EFAULT; + + debug_buf[count] = 0; /* end of string */ + + cnt = sscanf(debug_buf, "%x", &dbg_force_ov1_blt); + + pr_info("%s: dbg_force_ov1_blt = %x\n", + __func__, dbg_force_ov1_blt); + + if ((dbg_force_ov1_blt & 0x0f) > 2) + pr_err("%s: invalid dbg_force_ov1_blt = %x\n", + __func__, dbg_force_ov1_blt); + + if ((dbg_force_ov1_blt >> 4) > 2) + pr_err("%s: invalid dbg_force_ov1_blt = %d\n", + __func__, dbg_force_ov1_blt); + + return count; +} + +static const struct file_operations dbg_force_ov1_blt_fops = { + .open = dbg_open, + .release = dbg_release, + .read = dbg_force_ov1_blt_read, + .write = dbg_force_ov1_blt_write, +}; + #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL static uint32 hdmi_offset; static uint32 hdmi_count; @@ -1327,6 +1378,22 @@ int mdp_debugfs_init(void) } #endif + if (debugfs_create_file("force_ov0_blt", 0644, dent, 0, + &dbg_force_ov0_blt_fops) + == NULL) { + pr_err("%s(%d): debugfs_create_file: debug fail\n", + __FILE__, __LINE__); + return -EFAULT; + } + + if (debugfs_create_file("force_ov1_blt", 0644, dent, 0, + &dbg_force_ov1_blt_fops) + == NULL) { + pr_err("%s(%d): debugfs_create_file: debug fail\n", + __FILE__, __LINE__); + return -EFAULT; + } + dent = debugfs_create_dir("mddi", NULL); if (IS_ERR(dent)) { @@ -1342,15 +1409,6 @@ int mdp_debugfs_init(void) return -1; } -#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI) - if (debugfs_create_file("vsync", 0644, dent, 0, &vsync_fops) - == NULL) { - printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n", - __FILE__, __LINE__); - return -1; - } -#endif - dent = debugfs_create_dir("emdh", NULL); if (IS_ERR(dent)) { diff --git a/drivers/video/msm/mdp_dma.c b/drivers/video/msm/mdp_dma.c old mode 100644 new mode 100755 diff --git a/drivers/video/msm/mdp_dma_dsi_video.c b/drivers/video/msm/mdp_dma_dsi_video.c index 12ea0b4b..bf00a8c6 100644 --- a/drivers/video/msm/mdp_dma_dsi_video.c +++ b/drivers/video/msm/mdp_dma_dsi_video.c @@ -34,7 +34,7 @@ static int first_pixel_start_x; static int first_pixel_start_y; -static ssize_t vsync_show_event(struct device *dev, +ssize_t mdp_dma_video_show_event(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret = 0; @@ -47,20 +47,11 @@ static ssize_t vsync_show_event(struct device *dev, wait_for_completion(&vsync_cntrl.vsync_wait); ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", - ktime_to_ns(vsync_cntrl.vsync_time)); + ktime_to_ns(vsync_cntrl.vsync_time)); buf[strlen(buf) + 1] = '\0'; return ret; } -static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL); -static struct attribute *vsync_fs_attrs[] = { - &dev_attr_vsync_event.attr, - NULL, -}; -static struct attribute_group vsync_fs_attr_group = { - .attrs = vsync_fs_attrs, -}; - int mdp_dsi_video_on(struct platform_device *pdev) { int dsi_width; @@ -254,28 +245,12 @@ int mdp_dsi_video_on(struct platform_device *pdev) /* MDP cmd block disable */ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); - if (!vsync_cntrl.sysfs_created) { - ret = sysfs_create_group(&vsync_cntrl.dev->kobj, - &vsync_fs_attr_group); - if (ret) { - pr_err("%s: sysfs creation failed, ret=%d\n", - __func__, ret); - return ret; - } - - kobject_uevent(&vsync_cntrl.dev->kobj, KOBJ_ADD); - pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__); - vsync_cntrl.sysfs_created = 1; - } - mdp_histogram_ctrl_all(TRUE); - return ret; } int mdp_dsi_video_off(struct platform_device *pdev) { int ret = 0; - mdp_histogram_ctrl_all(FALSE); /* MDP cmd block enable */ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0); @@ -295,6 +270,7 @@ int mdp_dsi_video_off(struct platform_device *pdev) return ret; } +/* merge qcom patch to solve blue screen when power on */ void mdp_dma_video_vsync_ctrl(int enable) { unsigned long flag; @@ -307,22 +283,20 @@ void mdp_dma_video_vsync_ctrl(int enable) INIT_COMPLETION(vsync_cntrl.vsync_wait); vsync_cntrl.vsync_irq_enabled = enable; + if (!enable) + vsync_cntrl.disabled_clocks = 0; disabled_clocks = vsync_cntrl.disabled_clocks; spin_unlock_irqrestore(&mdp_spin_lock, flag); - if (enable && disabled_clocks) + if (enable && disabled_clocks) { mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - - spin_lock_irqsave(&mdp_spin_lock, flag); - if (enable && vsync_cntrl.disabled_clocks) { + spin_lock_irqsave(&mdp_spin_lock, flag); outp32(MDP_INTR_CLEAR, LCDC_FRAME_START); mdp_intr_mask |= LCDC_FRAME_START; outp32(MDP_INTR_ENABLE, mdp_intr_mask); mdp_enable_irq(MDP_VSYNC_TERM); - vsync_cntrl.disabled_clocks = 0; + spin_unlock_irqrestore(&mdp_spin_lock, flag); } - spin_unlock_irqrestore(&mdp_spin_lock, flag); - if (vsync_cntrl.vsync_irq_enabled && atomic_read(&vsync_cntrl.suspend) == 0) atomic_set(&vsync_cntrl.vsync_resume, 1); diff --git a/drivers/video/msm/mdp_dma_lcdc.c b/drivers/video/msm/mdp_dma_lcdc.c index e213528b..04d8b017 100644 --- a/drivers/video/msm/mdp_dma_lcdc.c +++ b/drivers/video/msm/mdp_dma_lcdc.c @@ -51,7 +51,7 @@ extern uint32 mdp_intr_mask; int first_pixel_start_x; int first_pixel_start_y; -static ssize_t vsync_show_event(struct device *dev, +ssize_t mdp_dma_lcdc_show_event(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret = 0; @@ -64,20 +64,11 @@ static ssize_t vsync_show_event(struct device *dev, wait_for_completion(&vsync_cntrl.vsync_wait); ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", - ktime_to_ns(vsync_cntrl.vsync_time)); + ktime_to_ns(vsync_cntrl.vsync_time)); buf[strlen(buf) + 1] = '\0'; return ret; } -static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL); -static struct attribute *vsync_fs_attrs[] = { - &dev_attr_vsync_event.attr, - NULL, -}; -static struct attribute_group vsync_fs_attr_group = { - .attrs = vsync_fs_attrs, -}; - int mdp_lcdc_on(struct platform_device *pdev) { int lcdc_width; @@ -320,21 +311,6 @@ int mdp_lcdc_on(struct platform_device *pdev) /* MDP cmd block disable */ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); - if (!vsync_cntrl.sysfs_created) { - ret = sysfs_create_group(&vsync_cntrl.dev->kobj, - &vsync_fs_attr_group); - if (ret) { - pr_err("%s: sysfs creation failed, ret=%d\n", - __func__, ret); - return ret; - } - - kobject_uevent(&vsync_cntrl.dev->kobj, KOBJ_ADD); - pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__); - vsync_cntrl.sysfs_created = 1; - } - mdp_histogram_ctrl_all(TRUE); - return ret; } @@ -353,7 +329,6 @@ int mdp_lcdc_off(struct platform_device *pdev) timer_base = DTV_BASE; } #endif - mdp_histogram_ctrl_all(FALSE); down(&mfd->dma->mutex); /* MDP cmd block enable */ @@ -387,21 +362,20 @@ void mdp_dma_lcdc_vsync_ctrl(int enable) INIT_COMPLETION(vsync_cntrl.vsync_wait); vsync_cntrl.vsync_irq_enabled = enable; + if (!enable) + vsync_cntrl.disabled_clocks = 0; disabled_clocks = vsync_cntrl.disabled_clocks; spin_unlock_irqrestore(&mdp_spin_lock, flag); - if (enable && disabled_clocks) + if (enable && disabled_clocks) { mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); - - spin_lock_irqsave(&mdp_spin_lock, flag); - if (enable && vsync_cntrl.disabled_clocks) { + spin_lock_irqsave(&mdp_spin_lock, flag); outp32(MDP_INTR_CLEAR, LCDC_FRAME_START); mdp_intr_mask |= LCDC_FRAME_START; outp32(MDP_INTR_ENABLE, mdp_intr_mask); mdp_enable_irq(MDP_VSYNC_TERM); - vsync_cntrl.disabled_clocks = 0; + spin_unlock_irqrestore(&mdp_spin_lock, flag); } - spin_unlock_irqrestore(&mdp_spin_lock, flag); if (vsync_cntrl.vsync_irq_enabled && atomic_read(&vsync_cntrl.suspend) == 0) diff --git a/drivers/video/msm/mdp_hw_init.c b/drivers/video/msm/mdp_hw_init.c index e937dbea..446f76a1 100644 --- a/drivers/video/msm/mdp_hw_init.c +++ b/drivers/video/msm/mdp_hw_init.c @@ -584,7 +584,9 @@ static void mdp_load_lut_param(void) #define IRQ_EN_1__MDP_IRQ___M 0x00000800 -void mdp_hw_init(int cont_splash) + +void mdp_hw_init(void) + { int i; @@ -631,17 +633,7 @@ void mdp_hw_init(int cont_splash) MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0, 0); MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4, 0); -#ifndef CONFIG_FB_MSM_MDP22 - /* Make sure to disable TG if continuous splash screen - * is not enabled on kernel side. This avoids issues - * when multiple panels are supported on same board - * and all the panels do not have splash screen/continuous - * splash support on the boot loader side. - */ - if (!cont_splash) { - MDP_OUTP(MDP_BASE + 0xE0000, 0); - MDP_OUTP(MDP_BASE + 0xF0000, 0); - } +#ifndef CONFIG_FB_MSM_MDP22 MDP_OUTP(MDP_BASE + 0x100, 0xffffffff); MDP_OUTP(MDP_BASE + 0x90070, 0); #endif diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index b2b66f90..ba18b078 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "linux/proc_fs.h" @@ -548,40 +547,8 @@ static void mdp_ppp_setbg(MDPIBUF *iBuf) format == MDP_Y_CRCB_H2V2) ? 2 : (format == MDP_Y_CBCR_H2V1 || \ format == MDP_Y_CRCB_H2V1) ? 1 : 1) -#ifdef CONFIG_ANDROID_PMEM -static void get_len(struct mdp_img *img, struct mdp_rect *rect, uint32_t bpp, - uint32_t *len0, uint32_t *len1) -{ - *len0 = IMG_LEN(rect->h, img->width, rect->w, bpp); - if (IS_PSEUDOPLNR(img->format)) - *len1 = *len0/Y_TO_CRCB_RATIO(img->format); - else - *len1 = 0; -} - -static void flush_imgs(struct mdp_blit_req *req, int src_bpp, int dst_bpp, - struct file *p_src_file, struct file *p_dst_file) -{ - uint32_t src0_len, src1_len; - - if (!(req->flags & MDP_BLIT_NON_CACHED)) { - /* flush src images to memory before dma to mdp */ - get_len(&req->src, &req->src_rect, src_bpp, - &src0_len, &src1_len); - - flush_pmem_file(p_src_file, - req->src.offset, src0_len); - - if (IS_PSEUDOPLNR(req->src.format)) - flush_pmem_file(p_src_file, - req->src.offset + src0_len, src1_len); - } - -} -#else static void flush_imgs(struct mdp_blit_req *req, int src_bpp, int dst_bpp, struct file *p_src_file, struct file *p_dst_file) { } -#endif static void mdp_start_ppp(struct msm_fb_data_type *mfd, MDPIBUF *iBuf, struct mdp_blit_req *req, struct file *p_src_file, struct file *p_dst_file) @@ -1286,9 +1253,6 @@ int get_img(struct mdp_img *img, struct mdp_blit_req *req, #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; #endif -#ifdef CONFIG_ANDROID_PMEM - unsigned long vstart; -#endif if (req->flags & MDP_MEMORY_ID_TYPE_FB) { file = fget_light(img->memory_id, &put_needed); @@ -1321,21 +1285,10 @@ int get_img(struct mdp_img *img, struct mdp_blit_req *req, return -EINVAL; #endif -#ifdef CONFIG_ANDROID_PMEM - if (!get_pmem_file(img->memory_id, start, &vstart, len, srcp_file)) - return ret; - else - return -EINVAL; -#endif } void put_img(struct file *p_src_file, struct ion_handle *p_ihdl) { -#ifdef CONFIG_ANDROID_PMEM - if (p_src_file) - put_pmem_file(p_src_file); -#endif - #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION if (!IS_ERR_OR_NULL(p_ihdl)) ion_free(ppp_display_iclient, p_ihdl); @@ -1401,6 +1354,9 @@ static int mdp_ppp_blit_addr(struct fb_info *info, struct mdp_blit_req *req, iBuf.mdpImg.mdpOp = MDPOP_NOP; + if (req->flags & MDP_IS_FG) + iBuf.mdpImg.mdpOp |= MDPOP_LAYER_IS_FG; + if (req->flags & MDP_IS_FG) iBuf.mdpImg.mdpOp |= MDPOP_LAYER_IS_FG; @@ -1701,12 +1657,19 @@ int mdp_ppp_v4l2_overlay_clear(void) return 0; } -int mdp_ppp_v4l2_overlay_play(struct fb_info *info, +int mdp_ppp_v4l2_overlay_play(struct fb_info *info, bool bUserPtr, unsigned long srcp0_addr, unsigned long srcp0_size, unsigned long srcp1_addr, unsigned long srcp1_size) { int ret; + unsigned long srcp0_start = 0, srcp1_start = 0; + unsigned long srcp0_len = 0, srcp1_len = 0; + struct ion_handle *srcp0_ihdl = NULL; + struct ion_handle *srcp1_ihdl = NULL; + struct msm_fb_data_type *mfd = info->par; + + ppp_display_iclient = mfd->iclient; if (!mdp_overlay_req_set) { pr_err("mdp_ppp:v4l2:No overlay set, ignore play req\n"); return -EINVAL; @@ -1715,6 +1678,31 @@ int mdp_ppp_v4l2_overlay_play(struct fb_info *info, overlay_req.dst.width = info->var.xres; overlay_req.dst.height = info->var.yres; + if (bUserPtr) { + overlay_req.src.memory_id = srcp0_addr; + get_img(&overlay_req.src, &overlay_req, info, &srcp0_start, + &srcp0_len, NULL, &srcp0_ihdl); + if (srcp0_len == 0) { + pr_err("%s: could not retrieve source image0" + , __func__); + return -EINVAL; + } + srcp0_addr = srcp0_start + srcp0_size; + srcp0_size = srcp0_len; + + if (srcp1_addr) { + overlay_req.src.memory_id = srcp1_addr; + get_img(&overlay_req.src, &overlay_req, info, + &srcp1_start, &srcp1_len, NULL, &srcp1_ihdl); + if (srcp1_len == 0) { + pr_err("%s: could not retrieve source image1" + , __func__); + return -EINVAL; + } + srcp1_addr = srcp1_start + srcp1_size; + srcp1_size = srcp1_len; + } + } ret = mdp_ppp_blit_addr(info, &overlay_req, srcp0_addr, srcp0_size, srcp1_addr, srcp1_size, info->fix.smem_start, info->fix.smem_len, NULL, NULL, diff --git a/drivers/video/msm/mipi_byd_otm9608a_wg451v.c b/drivers/video/msm/mipi_byd_otm9608a_wg451v.c old mode 100644 new mode 100755 diff --git "a/drivers/video/msm/mipi_div4_nt35510_cmd_wvga (\320\272\320\276\320\277\320\270.c" "b/drivers/video/msm/mipi_div4_nt35510_cmd_wvga (\320\272\320\276\320\277\320\270.c" new file mode 100755 index 00000000..3b0b49c2 --- /dev/null +++ "b/drivers/video/msm/mipi_div4_nt35510_cmd_wvga (\320\272\320\276\320\277\320\270.c" @@ -0,0 +1,1183 @@ +#include +#include "msm_fb.h" +#include "mipi_dsi.h" +#include "mdp4.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum +{ + NT35510_STATUS_LCD_SLEEP, + NT35510_STATUS_LCD_ON, + NT35510_STATUS_LCD_GLANSCREEN_ON, + NT35510_STATUS_LCD_GLANSCREEN_OFF, +}; +#define SN3228B_MAX_BACKLIGHT 14 + +#define GP_CLK_M 1 +#define GP_CLK_N 150 +#define LCM_RESET_GPIO_PIN85 85 +//pjn add for l1ED control +#define LCM_1LED_CTRL_GPIO_PIN121 121 +#define LCD_LOW_POWER_DETECT 1 +#define DSI_BIT_CLK_500M 1 +#define LCM_ID_GPIO_PIN120 120 + +#define MIPI_SEND_DATA_TYPE DTYPE_GEN_LWRITE //DTYPE_DCS_LWRITE // + +DEFINE_SEMAPHORE(nt35510_sem); + +static void nt35510_backlight_set(int level); + +static void mipi_nt35510_gs_set_backlight(int level); + +struct nt35510_state_type +{ + bool disp_initialized;/* disp initialized */ + bool display_on; /* lcd is on */ + uint8 disp_powered_up;/* lcd power and lcd bl power on */ + bool bk_init; + bool gp_clk_flag; + +}; + +typedef struct nt35510_state_type nt35510_state_t; +static nt35510_state_t nt35510_state= { 0 }; +static uint32 mddi_current_bl = 255;//added by zxd to ignore the same level. +static struct msm_panel_info pinfo; +static struct msm_panel_common_pdata *mipi_nt35510_pdata; +static struct dsi_buf nt35510_tx_buf; +static struct dsi_buf nt35510_rx_buf; + +//pjn add for low power glance screen,it specifies lcd state switch +int glancescreen_flag = 0; + +//add for panel module vender detect +extern int ODMM_PANEL_ID; +extern char panel_detect_desc[16]; + +//extern void lcd_lowpower_mode(void); +extern bool glanceview_suspend_statues(void); +extern bool glanceview_resume_statues(void); +extern void mipi_dsi_te_ctrl(struct msm_fb_data_type *mfd, bool flag); +extern void glanceview_oneled_state_ctrl(bool flag); + +static char exit_sleep[1] = {0x11}; +static char display_on[1] = {0x29}; +static char write_ram[1] = {0x2c}; /* write ram */ +static char enter_sleep[2] = {0x10, 0x00}; +static char display_off[2] = {0x28, 0x00}; +//static char STBYB_in[2] = {0x4F, 0x01}; + +#if 0 +static char idle_off[2] = {0x38, 0x00}; +static struct dsi_cmd_desc nt35510_ilde_mode_off_cmds[] = { + {DTYPE_DCS_WRITE, 1, 0, 0, 0, sizeof(idle_off), idle_off}, +}; +#else +static char idle_on[2] = {0x39, 0x00}; +static struct dsi_cmd_desc nt35510_ilde_mode_on_cmds[] = { + {DTYPE_DCS_WRITE, 1, 0, 0, 0, sizeof(idle_on), idle_on}, +}; +#endif + +#if 0 //use platform tear ctrl code +static char set_tear_on[2] = {0x35, 0x00}; +static struct dsi_cmd_desc dsi_tear_on_cmd = { + DTYPE_DCS_WRITE1, 1, 0, 0, 0, sizeof(set_tear_on), set_tear_on}; + + +static char set_tear_off[2] = {0x34, 0x00}; +static struct dsi_cmd_desc dsi_tear_off_cmd = { + DTYPE_DCS_WRITE, 1, 0, 0, 0, sizeof(set_tear_off), set_tear_off}; +#endif + +static struct dsi_cmd_desc nt35510_sleep_in_cmds[] = { + //{DTYPE_DCS_WRITE, 1, 0, 0, 0, sizeof(set_tear_off), set_tear_off}, + {DTYPE_DCS_WRITE, 1, 0, 0, 20, sizeof(display_off), display_off}, + {DTYPE_DCS_WRITE, 1, 0, 0, 120, sizeof(enter_sleep), enter_sleep}, +// {DTYPE_DCS_WRITE, 1, 0, 0, 5, sizeof(write_ram), write_ram}, +// {DTYPE_DCS_WRITE1, 1, 0, 0, 5, sizeof(STBYB_in), STBYB_in}, +}; + +#if 0 +//static char nt35510_cmd_00[1] = {0x01}; +static char nt35510_cmd_01[5] = { + 0xFF, 0xAA, 0x55, 0x25, + 0x01}; +static char nt35510_cmd_02[9] = { + 0xF3, 0x00, 0x32, 0x00, + 0x38, 0x31, 0x08, 0x11, + 0x00}; +//page 1 +static char nt35510_cmd_03[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x01}; +static char nt35510_cmd_04[4] = {0xB0, 0x05, 0x05, 0x05}; +static char nt35510_cmd_05[4] = {0xB6, 0x44, 0x44, 0x44}; +static char nt35510_cmd_06[4] = {0xB1, 0x05, 0x05, 0x05}; +static char nt35510_cmd_07[4] = {0xB7, 0x34, 0x34, 0x34}; +static char nt35510_cmd_08[4] = {0xB2, 0x03, 0x03, 0x03}; +static char nt35510_cmd_09[4] = {0xB3, 0x0B, 0x0B, 0x0B}; +static char nt35510_cmd_10[4] = {0xB8, 0x25, 0x25, 0x25}; + +static char nt35510_cmd_11[4] = {0xB9, 0x34, 0x34, 0x34}; +static char nt35510_cmd_12[2] = {0xBF, 0x01}; +static char nt35510_cmd_13[4] = {0xB4, 0x2D, 0x2D, 0x2D}; +static char nt35510_cmd_14[4] = {0xB5, 0x08, 0x08, 0x08}; +static char nt35510_cmd_15[4] = {0xBA, 0x24, 0x24, 0x24}; + +static char nt35510_cmd_16[4] = {0xBC, 0x00, 0xc8, 0x00};//{0xBC, 0x00, 0x68, 0x00}; +static char nt35510_cmd_17[4] = {0xBD, 0x00, 0xc8, 0x00};//{0xBD, 0x00, 0x7C, 0x00}; +static char nt35510_cmd_18[3] = {0xBE, 0x00, 0x78};//{0xBE, 0x00, 0x45}; +//gamma 2.2 +static char nt35510_cmd_19[53] = { + 0xd1, 0x00, 0x37, 0x00, + 0xa3, 0x00, 0xc8, 0x00, + 0xe4, 0x00, 0xf8, 0x01, + 0x0c, 0x01, 0x35, 0x01, + 0x61, 0x01, 0x84, 0x01, + 0xb8, 0x01, 0xe0, 0x02, + 0x22, 0x02, 0x57, 0x02, + 0x59, 0x02, 0x87, 0x02, + 0xBa, 0x02, 0xd9, 0x03, + 0x03, 0x03, 0x20, 0x03, + 0x49, 0x03, 0x62, 0x03, + 0x87, 0x03, 0x9e, 0x03, + 0xbc, 0x03, 0xea, 0x03, + 0xf1}; +static char nt35510_cmd_20[53] = { + 0xd2, 0x00, 0x37, 0x00, + 0xa3, 0x00, 0xc8, 0x00, + 0xe4, 0x00, 0xf8, 0x01, + 0x0c, 0x01, 0x35, 0x01, + 0x61, 0x01, 0x84, 0x01, + 0xb8, 0x01, 0xe0, 0x02, + 0x22, 0x02, 0x57, 0x02, + 0x59, 0x02, 0x87, 0x02, + 0xBa, 0x02, 0xd9, 0x03, + 0x03, 0x03, 0x20, 0x03, + 0x49, 0x03, 0x62, 0x03, + 0x87, 0x03, 0x9e, 0x03, + 0xbc, 0x03, 0xea, 0x03, + 0xf1}; +static char nt35510_cmd_21[53] = { + 0xd3, 0x00, 0x37, 0x00, + 0xa3, 0x00, 0xc8, 0x00, + 0xe4, 0x00, 0xf8, 0x01, + 0x0c, 0x01, 0x35, 0x01, + 0x61, 0x01, 0x84, 0x01, + 0xb8, 0x01, 0xe0, 0x02, + 0x22, 0x02, 0x57, 0x02, + 0x59, 0x02, 0x87, 0x02, + 0xBa, 0x02, 0xd9, 0x03, + 0x03, 0x03, 0x20, 0x03, + 0x49, 0x03, 0x62, 0x03, + 0x87, 0x03, 0x9e, 0x03, + 0xbc, 0x03, 0xea, 0x03, + 0xf1}; +static char nt35510_cmd_22[53] = { + 0xd4, 0x00, 0x37, 0x00, + 0xa3, 0x00, 0xc8, 0x00, + 0xe4, 0x00, 0xf8, 0x01, + 0x0c, 0x01, 0x35, 0x01, + 0x61, 0x01, 0x84, 0x01, + 0xb8, 0x01, 0xe0, 0x02, + 0x22, 0x02, 0x57, 0x02, + 0x59, 0x02, 0x87, 0x02, + 0xBa, 0x02, 0xd9, 0x03, + 0x03, 0x03, 0x20, 0x03, + 0x49, 0x03, 0x62, 0x03, + 0x87, 0x03, 0x9e, 0x03, + 0xbc, 0x03, 0xea, 0x03, + 0xf1}; +static char nt35510_cmd_23[53] = { + 0xd5, 0x00, 0x37, 0x00, + 0xa3, 0x00, 0xc8, 0x00, + 0xe4, 0x00, 0xf8, 0x01, + 0x0c, 0x01, 0x35, 0x01, + 0x61, 0x01, 0x84, 0x01, + 0xb8, 0x01, 0xe0, 0x02, + 0x22, 0x02, 0x57, 0x02, + 0x59, 0x02, 0x87, 0x02, + 0xBa, 0x02, 0xd9, 0x03, + 0x03, 0x03, 0x20, 0x03, + 0x49, 0x03, 0x62, 0x03, + 0x87, 0x03, 0x9e, 0x03, + 0xbc, 0x03, 0xea, 0x03, + 0xf1}; +static char nt35510_cmd_24[53] = { + 0xd6, 0x00, 0x37, 0x00, + 0xa3, 0x00, 0xc8, 0x00, + 0xe4, 0x00, 0xf8, 0x01, + 0x0c, 0x01, 0x35, 0x01, + 0x61, 0x01, 0x84, 0x01, + 0xb8, 0x01, 0xe0, 0x02, + 0x22, 0x02, 0x57, 0x02, + 0x59, 0x02, 0x87, 0x02, + 0xBa, 0x02, 0xd9, 0x03, + 0x03, 0x03, 0x20, 0x03, + 0x49, 0x03, 0x62, 0x03, + 0x87, 0x03, 0x9e, 0x03, + 0xbc, 0x03, 0xea, 0x03, + 0xf1}; +//page 0 +static char nt35510_cmd_25[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x00}; +/* + static char nt35510_cmd_26[6] = { + 0xB0, 0x00, 0x05, 0x02, + 0x05, 0x02};*/ +static char nt35510_cmd_27[3] = {0xB1, 0xEC, 0x00};//{0xB1, 0xCC, 0x00}; +static char nt35510_cmd_28[2] = {0xB3, 0x00}; +static char nt35510_cmd_29[2] = {0xB6, 0x03}; +static char nt35510_cmd_30[3] = {0xB7, 0x00, 0x00}; +static char nt35510_cmd_31[5] = { + 0xB8, 0x00, 0x06, 0x06, + 0x06}; +static char nt35510_cmd_32[2] = {0xBa, 0x01}; +static char nt35510_cmd_33[4] = {0xBC, 0x00, 0x00, 0x00}; +static char nt35510_cmd_34[6] = { + 0xBD, 0x01, 0x78, 0x06, + 0x50, 0x00}; +static char nt35510_cmd_35[4] = {0xCC, 0x03, 0x01, 0x06}; + + +static char nt35510_cmd_36[2] = {0x36, 0x00}; +static char nt35510_cmd_37[2] = {0x3a, 0x77}; +//pjn add vivid color +static char nt35510_cmd_38[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x00}; +static char nt35510_cmd_39[2] = {0xb4, 0x10}; +static char nt35510_cmd_40[5] = { + 0xFF, 0xAA, 0x55, 0x25, + 0x01}; +//light +/* +static char nt35510_cmd_41[12] = { + 0xF9, 0x14, 0x00, 0x05, + 0x09, 0x0d, 0x11, 0x14, + 0x18, 0x1c, 0x20, 0x24};*/ +//medium + +static char nt35510_cmd_41[12] = { + 0xF9, 0x14, 0x00, 0x0a, + 0x11, 0x17, 0x1d, 0x24, + 0x2a, 0x31, 0x37, 0x3d}; +//high +/* +static char nt35510_cmd_41[12] = { + 0xF9, 0x14, 0x00, 0x0d, + 0x1a, 0x26, 0x33, 0x40, + 0x4d, 0x5a, 0x66, 0x73};*/ + +static struct dsi_cmd_desc nt35510_cmd_on_cmds[] = { +// {MIPI_SEND_DATA_TYPE, 1, 0, 0, 120, sizeof(nt35510_cmd_00), nt35510_cmd_00}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_01), nt35510_cmd_01}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_02), nt35510_cmd_02}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_03), nt35510_cmd_03}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_04), nt35510_cmd_04}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_05), nt35510_cmd_05}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_06), nt35510_cmd_06}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_07), nt35510_cmd_07}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_08), nt35510_cmd_08}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_09), nt35510_cmd_09}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_10), nt35510_cmd_10}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_11), nt35510_cmd_11}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_12), nt35510_cmd_12}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_13), nt35510_cmd_13}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_14), nt35510_cmd_14}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_15), nt35510_cmd_15}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_16), nt35510_cmd_16}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_17), nt35510_cmd_17}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_18), nt35510_cmd_18}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_19), nt35510_cmd_19}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_20), nt35510_cmd_20}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_21), nt35510_cmd_21}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_22), nt35510_cmd_22}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_23), nt35510_cmd_23}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_24), nt35510_cmd_24}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_25), nt35510_cmd_25}, + //{MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_26), nt35510_cmd_26}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_27), nt35510_cmd_27}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_28), nt35510_cmd_28}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_29), nt35510_cmd_29}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_30), nt35510_cmd_30}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_31), nt35510_cmd_31}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_32), nt35510_cmd_32}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_33), nt35510_cmd_33}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_34), nt35510_cmd_34}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_35), nt35510_cmd_35}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_36), nt35510_cmd_36}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_37), nt35510_cmd_37}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_38), nt35510_cmd_38}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_39), nt35510_cmd_39}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_40), nt35510_cmd_40}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_41), nt35510_cmd_41}, + + {DTYPE_DCS_WRITE, 1, 0, 0, 120, sizeof(exit_sleep), exit_sleep}, + {DTYPE_DCS_WRITE, 1, 0, 0, 1, sizeof(set_tear_on), set_tear_on}, + {DTYPE_DCS_WRITE, 1, 0, 0, 20, sizeof(display_on), display_on}, + + {DTYPE_DCS_WRITE, 1, 0, 0, 5, sizeof(write_ram), write_ram}, +}; +#endif + +//pjn add low power glance screen +#if 1 +//static char nt35510_cmd_00[1] = {0x01}; +static char nt35510_cmd_01[5] = { + 0xFF, 0xAA, 0x55, 0x25, + 0x01}; +static char nt35510_cmd_02[9] = { + 0xF3, 0x00, 0x32, 0x00, + 0x38, 0x31, 0x08, 0x11, + 0x00}; +//page 1 +static char nt35510_cmd_03[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x01}; +#if 0 +static char nt35510_cmd_04[4] = {0xB0, 0x05, 0x0C, 0x05}; +static char nt35510_cmd_05[4] = {0xB6, 0x44, 0x14, 0x44}; +static char nt35510_cmd_06[4] = {0xB1, 0x05, 0x0C, 0x05}; +static char nt35510_cmd_07[4] = {0xB7, 0x34, 0x14, 0x34}; +static char nt35510_cmd_08[4] = {0xB2, 0x03, 0x03, 0x03}; +static char nt35510_cmd_09[4] = {0xB3, 0x0B, 0x08, 0x0B}; +static char nt35510_cmd_10[4] = {0xB8, 0x25, 0x25, 0x25}; + +static char nt35510_cmd_11[4] = {0xB9, 0x34, 0x14, 0x34}; +static char nt35510_cmd_12[2] = {0xBF, 0x01}; +static char nt35510_cmd_13[4] = {0xB4, 0x2D, 0x2D, 0x2D}; +static char nt35510_cmd_14[4] = {0xB5, 0x08, 0x08, 0x08}; +static char nt35510_cmd_15[4] = {0xBA, 0x24, 0x24, 0x24}; + +static char nt35510_cmd_16[4] = {0xBC, 0x00, 0x80, 0x00};//{0xBC, 0x00, 0x68, 0x00}; +static char nt35510_cmd_17[4] = {0xBD, 0x00, 0x80, 0x00};//{0xBD, 0x00, 0x7C, 0x00}; +static char nt35510_cmd_18[3] = {0xBE, 0x00, 0x78};//{0xBE, 0x00, 0x45}; + +static char nt35510_cmd_04[4] = {0xB0, 0x05, 0x05, 0x05}; +static char nt35510_cmd_05[4] = {0xB6, 0x44, 0x44, 0x44}; +static char nt35510_cmd_06[4] = {0xB1, 0x05, 0x05, 0x05}; +static char nt35510_cmd_07[4] = {0xB7, 0x34, 0x34, 0x34}; +static char nt35510_cmd_08[4] = {0xB2, 0x03, 0x03, 0x03}; +static char nt35510_cmd_09[4] = {0xB3, 0x0B, 0x0B, 0x0B}; +static char nt35510_cmd_10[4] = {0xB8, 0x25, 0x25, 0x25}; + +static char nt35510_cmd_11[4] = {0xB9, 0x34, 0x34, 0x34}; +static char nt35510_cmd_12[2] = {0xBF, 0x01}; +static char nt35510_cmd_13[4] = {0xB4, 0x2D, 0x2D, 0x2D}; +static char nt35510_cmd_14[4] = {0xB5, 0x08, 0x08, 0x08}; +static char nt35510_cmd_15[4] = {0xBA, 0x24, 0x24, 0x24}; + +static char nt35510_cmd_16[4] = {0xBC, 0x00, 0xc8, 0x00};//{0xBC, 0x00, 0x68, 0x00}; +static char nt35510_cmd_17[4] = {0xBD, 0x00, 0xc8, 0x00};//{0xBD, 0x00, 0x7C, 0x00}; +static char nt35510_cmd_18[3] = {0xBE, 0x00, 0x78};//{0xBE, 0x00, 0x45}; +#else +static char nt35510_cmd_04[4] = {0xB0, 0x05, 0x0C, 0x05}; +static char nt35510_cmd_05[4] = {0xB6, 0x44, 0x34, 0x44}; +static char nt35510_cmd_06[4] = {0xB1, 0x05, 0x0C, 0x05}; +static char nt35510_cmd_07[4] = {0xB7, 0x34, 0x24, 0x34}; +static char nt35510_cmd_08[4] = {0xB2, 0x03, 0x00, 0x03}; +static char nt35510_cmd_09[4] = {0xB3, 0x0B, 0x08, 0x0B}; +static char nt35510_cmd_10[4] = {0xB8, 0x25, 0x25, 0x25}; + +static char nt35510_cmd_11[4] = {0xB9, 0x34, 0x14, 0x34}; +static char nt35510_cmd_12[2] = {0xBF, 0x01}; +static char nt35510_cmd_13[4] = {0xB4, 0x2D, 0x2D, 0x2D}; +static char nt35510_cmd_14[4] = {0xB5, 0x08, 0x08, 0x08}; +static char nt35510_cmd_15[4] = {0xBA, 0x24, 0x14, 0x24}; + +static char nt35510_cmd_16[4] = {0xBC, 0x00, 0x60, 0x00};//{0xBC, 0x00, 0x68, 0x00}; +static char nt35510_cmd_17[4] = {0xBD, 0x00, 0x60, 0x00};//{0xBD, 0x00, 0x7C, 0x00}; +static char nt35510_cmd_18[3] = {0xBE, 0x00, 0x52};//{0xBE, 0x00, 0x45}; +#endif +//gamma 2.2 +static char nt35510_cmd_19[53] = { + 0xD1, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x33, 0x00, + 0x4b, 0x00, 0x5e, 0x00, + 0x7f, 0x00, 0x9f, 0x00, + 0xCb, 0x00, 0xee, 0x01, + 0x2a, 0x01, 0x5f, 0x01, + 0xAf, 0x01, 0xea, 0x01, + 0xEc, 0x02, 0x27, 0x02, + 0x68, 0x02, 0x92, 0x02, + 0xc8, 0x02, 0xe8, 0x03, + 0x25, 0x03, 0x4a, 0x03, + 0x85, 0x03, 0x95, 0x03, + 0xa3, 0x03, 0xD0, 0x03, + 0xFF}; +static char nt35510_cmd_20[53] = { + 0xD2, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x33, 0x00, + 0x4b, 0x00, 0x5e, 0x00, + 0x7f, 0x00, 0x9f, 0x00, + 0xCb, 0x00, 0xee, 0x01, + 0x2a, 0x01, 0x5f, 0x01, + 0xAf, 0x01, 0xea, 0x01, + 0xEc, 0x02, 0x27, 0x02, + 0x68, 0x02, 0x92, 0x02, + 0xc8, 0x02, 0xe8, 0x03, + 0x25, 0x03, 0x4a, 0x03, + 0x85, 0x03, 0x95, 0x03, + 0xa3, 0x03, 0xD0, 0x03, + 0xFF}; +static char nt35510_cmd_21[53] = { + 0xD3, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x33, 0x00, + 0x4b, 0x00, 0x5e, 0x00, + 0x7f, 0x00, 0x9f, 0x00, + 0xCb, 0x00, 0xee, 0x01, + 0x2a, 0x01, 0x5f, 0x01, + 0xAf, 0x01, 0xea, 0x01, + 0xEc, 0x02, 0x27, 0x02, + 0x68, 0x02, 0x92, 0x02, + 0xc8, 0x02, 0xe8, 0x03, + 0x25, 0x03, 0x4a, 0x03, + 0x85, 0x03, 0x95, 0x03, + 0xa3, 0x03, 0xD0, 0x03, + 0xFF}; +static char nt35510_cmd_22[53] = { + 0xD4, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x33, 0x00, + 0x4b, 0x00, 0x5e, 0x00, + 0x7f, 0x00, 0x9f, 0x00, + 0xCb, 0x00, 0xee, 0x01, + 0x2a, 0x01, 0x5f, 0x01, + 0xAf, 0x01, 0xea, 0x01, + 0xEc, 0x02, 0x27, 0x02, + 0x68, 0x02, 0x92, 0x02, + 0xc8, 0x02, 0xe8, 0x03, + 0x25, 0x03, 0x4a, 0x03, + 0x85, 0x03, 0x95, 0x03, + 0xa3, 0x03, 0xD0, 0x03, + 0xFF}; +static char nt35510_cmd_23[53] = { + 0xD5, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x33, 0x00, + 0x4b, 0x00, 0x5e, 0x00, + 0x7f, 0x00, 0x9f, 0x00, + 0xCb, 0x00, 0xee, 0x01, + 0x2a, 0x01, 0x5f, 0x01, + 0xAf, 0x01, 0xea, 0x01, + 0xEc, 0x02, 0x27, 0x02, + 0x68, 0x02, 0x92, 0x02, + 0xc8, 0x02, 0xe8, 0x03, + 0x25, 0x03, 0x4a, 0x03, + 0x85, 0x03, 0x95, 0x03, + 0xa3, 0x03, 0xD0, 0x03, + 0xFF}; +static char nt35510_cmd_24[53] = { + 0xD6, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x33, 0x00, + 0x4b, 0x00, 0x5e, 0x00, + 0x7f, 0x00, 0x9f, 0x00, + 0xCb, 0x00, 0xee, 0x01, + 0x2a, 0x01, 0x5f, 0x01, + 0xAf, 0x01, 0xea, 0x01, + 0xEc, 0x02, 0x27, 0x02, + 0x68, 0x02, 0x92, 0x02, + 0xc8, 0x02, 0xe8, 0x03, + 0x25, 0x03, 0x4a, 0x03, + 0x85, 0x03, 0x95, 0x03, + 0xa3, 0x03, 0xD0, 0x03, + 0xFF}; +//page 0 +static char nt35510_cmd_25[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x00}; +/* + static char nt35510_cmd_26[6] = { + 0xB0, 0x00, 0x05, 0x02, + 0x05, 0x02};*/ +static char nt35510_cmd_27[3] = {0xB1, 0xEC, 0x00};//{0xB1, 0xCC, 0x00}; +static char nt35510_cmd_28[2] = {0xB3, 0x00}; +static char nt35510_cmd_29[2] = {0xB6, 0x03}; +static char nt35510_cmd_30[3] = {0xB7, 0x00, 0x00}; +static char nt35510_cmd_31[5] = { + 0xB8, 0x00, 0x06, 0x06, + 0x06}; +static char nt35510_cmd_32[2] = {0xBa, 0x01}; +static char nt35510_cmd_33[4] = {0xBC, 0x00, 0x00, 0x00}; +static char nt35510_cmd_34[6] = { + 0xBE, 0x01, 0x84, 0x1C, + 0x1C, 0x01}; +static char nt35510_cmd_35[4] = {0xCC, 0x03, 0x01, 0x06}; + + +static char nt35510_cmd_36[2] = {0x36, 0x00}; +static char nt35510_cmd_37[2] = {0x3a, 0x77}; +//pjn add vivid color +static char nt35510_cmd_38[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x00}; +static char nt35510_cmd_39[2] = {0xb4, 0x10}; +static char nt35510_cmd_40[5] = { + 0xFF, 0xAA, 0x55, 0x25, + 0x01}; +//light +/* +static char nt35510_cmd_41[12] = { + 0xF9, 0x14, 0x00, 0x05, + 0x09, 0x0d, 0x11, 0x14, + 0x18, 0x1c, 0x20, 0x24};*/ +//medium + +static char nt35510_cmd_41[12] = { + 0xF9, 0x14, 0x00, 0x0a, + 0x11, 0x17, 0x1d, 0x24, + 0x2a, 0x31, 0x37, 0x3d}; +//high +/* +static char nt35510_cmd_41[12] = { + 0xF9, 0x14, 0x00, 0x0d, + 0x1a, 0x26, 0x33, 0x40, + 0x4d, 0x5a, 0x66, 0x73};*/ + +static struct dsi_cmd_desc nt35510_cmd_on_cmds[] = { +// {MIPI_SEND_DATA_TYPE, 1, 0, 0, 120, sizeof(nt35510_cmd_00), nt35510_cmd_00}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_01), nt35510_cmd_01}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_02), nt35510_cmd_02}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_03), nt35510_cmd_03}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_04), nt35510_cmd_04}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_05), nt35510_cmd_05}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_06), nt35510_cmd_06}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_07), nt35510_cmd_07}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_08), nt35510_cmd_08}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_09), nt35510_cmd_09}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_10), nt35510_cmd_10}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_11), nt35510_cmd_11}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_12), nt35510_cmd_12}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_13), nt35510_cmd_13}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_14), nt35510_cmd_14}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_15), nt35510_cmd_15}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_16), nt35510_cmd_16}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_17), nt35510_cmd_17}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_18), nt35510_cmd_18}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_19), nt35510_cmd_19}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_20), nt35510_cmd_20}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_21), nt35510_cmd_21}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_22), nt35510_cmd_22}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_23), nt35510_cmd_23}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_24), nt35510_cmd_24}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_25), nt35510_cmd_25}, + //{MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_26), nt35510_cmd_26}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_27), nt35510_cmd_27}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_28), nt35510_cmd_28}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_29), nt35510_cmd_29}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_30), nt35510_cmd_30}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_31), nt35510_cmd_31}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_32), nt35510_cmd_32}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_33), nt35510_cmd_33}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_34), nt35510_cmd_34}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_35), nt35510_cmd_35}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_36), nt35510_cmd_36}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_37), nt35510_cmd_37}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_38), nt35510_cmd_38}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_39), nt35510_cmd_39}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_40), nt35510_cmd_40}, + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_41), nt35510_cmd_41}, + + {DTYPE_DCS_WRITE, 1, 0, 0, 120, sizeof(exit_sleep), exit_sleep}, +}; + +static struct dsi_cmd_desc nt35510_cmd_on_cmds_1[] = { + {DTYPE_DCS_WRITE, 1, 0, 0, 5, sizeof(display_on), display_on}, + //{DTYPE_DCS_WRITE, 1, 0, 0, 1, sizeof(set_tear_on), set_tear_on}, + {DTYPE_DCS_WRITE, 1, 0, 0, 5, sizeof(write_ram), write_ram}, +}; +#endif + +//pjn end +static struct mipi_dsi_phy_ctrl dsi_cmd_mode_phy_db = { +#if 0 + /* DSI_BIT_CLK at 350MHz, 2 lane, RGB888 */ + {0x03, 0x01, 0x01, 0x00}, + /* timing */ + /*{0x69, 0x2B, 0x0D, 0x00, 0x37, 0x3C, 0x12, 0x2E, + 0x10, 0x03, 0x04},*/ + {0x69, 0x2B, 0x0D, 0x00, 0x37, 0x3C, 0x1f, 0x35, + 0x10, 0x07, 0x04}, //pjn modify + /* phy ctrl */ + {0x7f, 0x00, 0x00, 0x00}, + /* strength */ + {0xbb, 0x02, 0x06, 0x00}, + /* pll control */ + {0x01, 0x59, 0x31, 0xD2, 0x00, 0x40, 0x37, 0x62, + 0x01, 0x0F, 0x07, + 0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0}, +#else + /* DSI_BIT_CLK at 460MHz, 2 lane, RGB888 */ + {0x03, 0x01, 0x01, 0x00}, + /* timing */ + /*{0x69, 0x2B, 0x0D, 0x00, 0x37, 0x3C, 0x12, 0x2E, + 0x10, 0x03, 0x04},*/ + {0x82, 0x31, 0x12, 0x00, 0x41, 0x4A, 0x16, 0x35, + 0x16, 0x03, 0x04}, + /* phy ctrl */ + {0x7f, 0x00, 0x00, 0x00}, + /* strength */ + {0xbb, 0x02, 0x06, 0x00}, + /* pll control */ + {0x01, 0xC6, 0x31, 0xD2, 0x00, 0x40, 0x37, 0x62, + 0x01, 0x0F, 0x07, + 0x05, 0x14, 0x03, 0x0, 0x0, 0x0, 0x20, 0x0, 0x02, 0x0}, +#endif +}; + +//pjn add read 0x0a register + +static char nt35510_cmd_page1[6] = { + 0xF0, 0x55, 0xAA, 0x52, + 0x08, 0x01}; + +static struct dsi_cmd_desc nt35510_change_page[] = { + {MIPI_SEND_DATA_TYPE, 1, 0, 0, 0, sizeof(nt35510_cmd_page1), nt35510_cmd_page1}, +}; + +static u32 mipi_nt35510_read_reg(struct msm_fb_data_type *mfd, u16 reg, int page) +{ + u32 data; + int len = 4; + + struct dsi_cmd_desc cmd_read_reg = { + DTYPE_GEN_READ2, 1, 0, 1, 0, /* cmd 0x24 */ + sizeof(reg), (char *) ®}; + + mipi_dsi_buf_init(&nt35510_tx_buf); + mipi_dsi_buf_init(&nt35510_rx_buf); + + if(page) + /* mutex had been acquired at mipi_dsi_on */ + mipi_dsi_cmds_tx(&nt35510_tx_buf, nt35510_change_page, ARRAY_SIZE(nt35510_change_page)); + + len = mipi_dsi_cmds_rx(mfd, &nt35510_tx_buf, &nt35510_rx_buf, + &cmd_read_reg, len); + + data = *(u32 *)nt35510_rx_buf.data; + + printk("[==wxj==]%s: reg=0x%x.data=0x%08x\n", __func__, reg, data); + + return data; +} + +static int msm_lcm_send_init_code(void) +{ + int ret = 0; + my_prepare_enable_clocks(); + msleep(80); + ret = mipi_dsi_cmds_tx(&nt35510_tx_buf, nt35510_cmd_on_cmds, + ARRAY_SIZE(nt35510_cmd_on_cmds)); + my_unprepare_disable_clocks(); + msleep(16); + + my_prepare_enable_clocks(); + msleep(80); + ret = mipi_dsi_cmds_tx(&nt35510_tx_buf, nt35510_cmd_on_cmds_1, + ARRAY_SIZE(nt35510_cmd_on_cmds_1)); + msleep(16); + my_unprepare_disable_clocks(); + return ret; +} + +static void msm_lcm_reset(void) +{ + printk(KERN_INFO "[DISPLAY] begain to reset panel\n"); + gpio_direction_output(LCM_RESET_GPIO_PIN85, 1); + msleep(1); + gpio_direction_output(LCM_RESET_GPIO_PIN85, 0); + msleep(3); + gpio_direction_output(LCM_RESET_GPIO_PIN85, 1); + msleep(30); +} + +#define POWER_ON_NOT_BOOTLOGO 1 +static int mipi_nt35510_gpio_init(void) +{ + int ret; + { + printk(KERN_INFO"[DISPLAY] %s called\n",__func__); + + ret = gpio_request(LCM_RESET_GPIO_PIN85, "odmm_gpio_reset_en"); + if(ret < 0) + printk(KERN_ERR" LCM_RESET_GPIO_PIN85 request failed\n"); + + ret=gpio_tlmm_config( + GPIO_CFG( + LCM_RESET_GPIO_PIN85, + 0, + GPIO_CFG_OUTPUT, //LCDC DEN + GPIO_CFG_PULL_UP, + GPIO_CFG_4MA), + GPIO_CFG_ENABLE); + + if(ret) + { + printk(KERN_DEBUG"Failure to set LCM_RESET_GPIO_PIN85 config\n"); + return -1; + } + } + + ret = gpio_request(LCM_1LED_CTRL_GPIO_PIN121,"lcm_gpio_1led_en"); + if (ret < 0){ + printk(KERN_INFO" pjn=== GPIO121 request failed\n"); + return ret; + } + + ret = gpio_tlmm_config( + GPIO_CFG( + 121, + 0, + GPIO_CFG_OUTPUT, //LCDC DEN + GPIO_CFG_PULL_UP,//GPIO_CFG_PULL_UP,//GPIO_CFG_PULL_UP, + GPIO_CFG_4MA), + GPIO_CFG_ENABLE); + if(ret) + { + printk(KERN_DEBUG"Failure to set LCM_1LED_CTRL_GPIO_PIN121 config\n"); + return -1; + } + + gpio_request(LCM_ID_GPIO_PIN120, "odmm_panel_id"); + gpio_tlmm_config(GPIO_CFG(LCM_ID_GPIO_PIN120, 0, + GPIO_CFG_INPUT, + GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), + GPIO_CFG_ENABLE); + + gpio_export(LCM_RESET_GPIO_PIN85, 0); + gpio_export(LCM_1LED_CTRL_GPIO_PIN121, 0); + gpio_export(LCM_ID_GPIO_PIN120, 0); + + gpio_direction_output(LCM_1LED_CTRL_GPIO_PIN121, 0); + + return 0; +} + +static int mipi_nt35510_lcd_on(struct platform_device *pdev) +{ + + struct msm_fb_data_type *mfd; + + MSM_DRM_DEBUG("start. \n"); + + mfd = platform_get_drvdata(pdev); + if (!mfd) + { + printk("%s [DISPLAY:mfd] \n", __func__); + return -ENODEV; + } + if (!mfd->cont_splash_done) + { + mfd->cont_splash_done = 1; + printk(" %s [DISPLAY:cont_splash_done] \n", __func__); + return 0; + } + + pr_debug("platform_get_drvdata success \n"); + if (mfd->key != MFD_KEY) + { + printk(" %s [DISPLAY:MFD_KEY] \n", __func__); + return -EINVAL; + } + MSM_DRM_DEBUG("MFD_KEY Okay\n"); + + down(&nt35510_sem); + if (glanceview_resume_statues()) + { + printk("%s(glance 3)=%d skip init\n",__func__,nt35510_state.disp_powered_up); + nt35510_state.disp_powered_up = NT35510_STATUS_LCD_GLANSCREEN_ON; + } + else + { + if(nt35510_state.disp_powered_up == NT35510_STATUS_LCD_SLEEP) + { + printk("%s(0)=%d deep sleep to normal\n",__func__,nt35510_state.disp_powered_up); + + msm_lcm_reset(); + //mipi_set_tx_power_mode(1); +#if 0 + mipi_dsi_cmds_tx(&nt35510_tx_buf, nt35510_cmd_on_cmds, + ARRAY_SIZE(nt35510_cmd_on_cmds)); + mipi_dsi_cmds_tx(&nt35510_tx_buf, nt35510_cmd_on_cmds_1, + ARRAY_SIZE(nt35510_cmd_on_cmds_1)); +#endif + msm_lcm_send_init_code(); + //mipi_set_tx_power_mode(0); + } + + else if(nt35510_state.disp_powered_up == NT35510_STATUS_LCD_GLANSCREEN_OFF) + { + printk("%s(3)=%d idle mode to normal\n",__func__,nt35510_state.disp_powered_up); + //nt35510_backlight_set(0); + //gpio_direction_output(LCM_1LED_CTRL_GPIO_PIN121, 0); + glanceview_oneled_state_ctrl(0); + //pjn add lcd reset + msm_lcm_reset(); + + msm_lcm_send_init_code(); + } + else + { + printk("%s(error)=%d\n",__func__,nt35510_state.disp_powered_up); + + //gpio_direction_output(LCM_1LED_CTRL_GPIO_PIN121, 0); + glanceview_oneled_state_ctrl(0); + + msm_lcm_reset(); + //mipi_set_tx_power_mode(1); + msm_lcm_send_init_code(); + + //mipi_set_tx_power_mode(0); + } + nt35510_state.disp_powered_up = NT35510_STATUS_LCD_ON; + + mipi_dsi_te_ctrl(mfd, 1); + } + + up(&nt35510_sem); + + MSM_DRM_DEBUG("end.\n"); + return 0; +} + +//pjn add for glance scrren +int mipi_nt35510_glance_screen(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + int gpio121_val = 2; + mfd = platform_get_drvdata(pdev); + + MSM_DRM_DEBUG("start.\n"); + if (!mfd) + { + printk("%s mfd == NULL\n",__func__); + return -ENODEV; + } + + if (glancescreen_flag) { + gpio121_val = gpio_get_value_cansleep(LCM_1LED_CTRL_GPIO_PIN121); + if((!gpio121_val) && glancescreen_suspend_flag){ + gpio_set_value_cansleep(LCM_1LED_CTRL_GPIO_PIN121, 1); + } + return 0; + } + + down(&nt35510_sem); + nt35510_backlight_set(0); + msm_lcm_reset(); + //mipi_dsi_cmds_tx(&nt35510_tx_buf, nt35510_lp_cmd_on_cmds, + //ARRAY_SIZE(nt35510_lp_cmd_on_cmds)); + glancescreen_flag = 1; + nt35510_state.disp_powered_up = NT35510_STATUS_LCD_GLANSCREEN_OFF; + up(&nt35510_sem); + + MSM_DRM_DEBUG(" end.\n"); + return 0; +} + +static int mipi_nt35510_lcd_off(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + //int gpio121_val = 2; + + mfd = platform_get_drvdata(pdev); + + MSM_DRM_DEBUG(" start.\n"); + + down(&nt35510_sem); + + mipi_dsi_te_ctrl(mfd, 0); + + if (glanceview_suspend_statues()) + { + if (nt35510_state.disp_powered_up == NT35510_STATUS_LCD_GLANSCREEN_ON) + { + printk("%s(glance 2)=%d \n", __func__,nt35510_state.disp_powered_up); + + if (glancescreen_flag == 1) + { + //move oneled control owner to OS, it will be much more better + //msleep(2); + //gpio_set_value_cansleep(LCM_1LED_CTRL_GPIO_PIN121, 1); + glancescreen_flag = 2; + printk("%s(glance backlight)=%d \n", __func__,nt35510_state.disp_powered_up); + } + } + else + { + printk("%s(glance other)=%d,\n", __func__,nt35510_state.disp_powered_up); + mipi_nt35510_gs_set_backlight(0); + + mipi_dsi_cmds_tx(&nt35510_tx_buf, nt35510_ilde_mode_on_cmds, + ARRAY_SIZE(nt35510_ilde_mode_on_cmds)); + + glancescreen_flag = 1; + } + nt35510_state.disp_powered_up = NT35510_STATUS_LCD_GLANSCREEN_OFF; + + } + else + { + if(nt35510_state.disp_powered_up == NT35510_STATUS_LCD_ON ) + { + printk("%s(1)=%d\n",__func__,nt35510_state.disp_powered_up); + + //mipi_set_tx_power_mode(1); + + mipi_dsi_cmds_tx(&nt35510_tx_buf, nt35510_sleep_in_cmds, + ARRAY_SIZE(nt35510_sleep_in_cmds)); + + //mipi_set_tx_power_mode(0); + gpio_direction_output(LCM_RESET_GPIO_PIN85, 0); + } + + else if(nt35510_state.disp_powered_up == NT35510_STATUS_LCD_GLANSCREEN_ON) + { + printk("%s(2)=%d\n",__func__,nt35510_state.disp_powered_up); + //gpio_direction_output(LCM_1LED_CTRL_GPIO_PIN121, 0); + glanceview_oneled_state_ctrl(0); + + //mipi_set_tx_power_mode(1); + + mipi_dsi_cmds_tx(&nt35510_tx_buf, nt35510_sleep_in_cmds, + ARRAY_SIZE(nt35510_sleep_in_cmds)); + + //mipi_set_tx_power_mode(0); + gpio_direction_output(LCM_RESET_GPIO_PIN85, 0); + } + else + { + printk("%s(error)=%d\n",__func__,nt35510_state.disp_powered_up); + //gpio_direction_output(LCM_1LED_CTRL_GPIO_PIN121, 0); + glanceview_oneled_state_ctrl(0); + + //mipi_set_tx_power_mode(1); + + mipi_dsi_cmds_tx(&nt35510_tx_buf, nt35510_sleep_in_cmds, + ARRAY_SIZE(nt35510_sleep_in_cmds)); + + //mipi_set_tx_power_mode(0); + gpio_direction_output(LCM_RESET_GPIO_PIN85, 0); + + } + nt35510_state.disp_powered_up = NT35510_STATUS_LCD_SLEEP; + } + + up(&nt35510_sem); + MSM_DRM_DEBUG(" end.\n"); + return 0; +} + +#define PERPH_WEB_BLOCK_ADDR (0xA9D00040) +#define PDM0_CTL_OFFSET (0x04) +#define SIZE_8B (0x08) +#define PDM_DUTY_MAXVAL BIT(16) +#define PDM_DUTY_REFVAL BIT(15) +#define LED_FULL 255 + +static void nt35510_backlight_set(int level) +{ + int ret = 0; + if(unlikely(!nt35510_state.bk_init)){ + pmapp_disp_backlight_init(); + nt35510_state.bk_init = TRUE; + } + + if(level > 0) + gpio_direction_output(LCM_1LED_CTRL_GPIO_PIN121, 0); + + ret = pmapp_disp_backlight_set_brightness(level*255/255); + if (ret) + printk(KERN_ERR" can't set backlight level=%d\n",level); +} + +static void mipi_nt35510_set_backlight(struct msm_fb_data_type *mfd) +{ + + int level; + + level = mfd->bl_level; + + printk(KERN_INFO"[DISPLAY] current_bl=%d; target_bl=%d\n", mddi_current_bl, level); +// if(mddi_current_bl == level) +// return; + + nt35510_backlight_set(level); + mddi_current_bl = level; + + return; +} + +static void mipi_nt35510_gs_set_backlight(int level) +{ + printk("mipi_nt35510_gs_set_backlight()=%d\n",level); + nt35510_backlight_set(level); +} + + +static int __devinit mipi_nt35510_lcd_probe(struct platform_device *pdev) +{ + printk(KERN_INFO "[DISPLAY] %s is called\n", __func__); + if (pdev->id == 0) + { + mipi_nt35510_pdata = pdev->dev.platform_data; + return 0; + } + + nt35510_state.disp_initialized = TRUE; + nt35510_state.disp_powered_up = NT35510_STATUS_LCD_ON;// first poweron don't reset lcd + + msm_fb_add_device(pdev); + + mipi_nt35510_gpio_init(); + + return 0; +} + + +static void mipi_nt35510_shutdown(struct platform_device *pdev) +{ + printk(KERN_INFO "[DISPLAY] %s is called\n", __func__); + nt35510_backlight_set(0); + gpio_direction_output(LCM_RESET_GPIO_PIN85, 0); + gpio_free(LCM_RESET_GPIO_PIN85); + msleep(120); +} + +static struct platform_driver this_driver = { + .probe = mipi_nt35510_lcd_probe, + .shutdown = mipi_nt35510_shutdown, + .driver = { + .name = "mipi_nt35510", + }, +}; + +static struct msm_fb_panel_data nt35510_panel_data = { + .on = mipi_nt35510_lcd_on, + .off = mipi_nt35510_lcd_off, + .glance_screen = mipi_nt35510_glance_screen, + .set_backlight = mipi_nt35510_set_backlight, +}; + +static struct platform_device this_device = { + .name = "mipi_nt35510", + .id = 2, + .dev = { + .platform_data = &nt35510_panel_data, + } +}; + +static int __init mipi_div4_nt35510_lcd_init(void) +{ + int ret; + + #if 1 + gpio_request(LCM_ID_GPIO_PIN120, "odmm_panel_id"); + gpio_tlmm_config(GPIO_CFG(LCM_ID_GPIO_PIN120, 0, + GPIO_CFG_INPUT, + GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), + GPIO_CFG_ENABLE); + + ODMM_PANEL_ID = gpio_get_value_cansleep(LCM_ID_GPIO_PIN120); + if(ODMM_PANEL_ID == 0){ + strncpy(panel_detect_desc, "N1 DIV4 CMD", 16); + printk(KERN_INFO "[DISPLAY]Detect Device Used DIV4 Panel as ODMM_PANEL_ID = %d\n", ODMM_PANEL_ID); + }else{ + strncpy(panel_detect_desc, "N1 TRUST CMD", 16); + printk(KERN_INFO "[DISPLAY]Detect Device Used TRUST Panel as ODMM_PANEL_ID = %d\n", ODMM_PANEL_ID); + } + gpio_free(LCM_ID_GPIO_PIN120); + #endif + + //if(odmm_nt35510_lcd_id != ODMM_35516_nt35510_ID) + // return 0; + + mipi_dsi_buf_alloc(&nt35510_tx_buf, DSI_BUF_SIZE);//mipi cmd buffer + mipi_dsi_buf_alloc(&nt35510_rx_buf, DSI_BUF_SIZE); + + ret = platform_driver_register(&this_driver); + if (ret) + { + printk(KERN_ERR"[DISPLAY] %s platform_driver_register FAILED\n", __func__); + return ret; + } + + pinfo.xres = 480; + pinfo.yres = 800; + pinfo.type = MIPI_CMD_PANEL; + pinfo.pdest = DISPLAY_1; + pinfo.wait_cycle = 0; + pinfo.bpp = 24; + + pinfo.lcdc.h_back_porch = 29; + pinfo.lcdc.h_front_porch = 7; + pinfo.lcdc.h_pulse_width = 3;//8; + pinfo.lcdc.v_back_porch = 12; + pinfo.lcdc.v_front_porch = 16; + pinfo.lcdc.v_pulse_width = 5;////1; + + pinfo.lcdc.border_clr = 0; /* blk */ + pinfo.lcdc.underflow_clr = 0xff; /* blue */ + pinfo.lcdc.hsync_skew = 0; + pinfo.bl_max = 255;//16; + pinfo.bl_min = 1; + pinfo.fb_num = 2; + + pinfo.clk_rate = 350000000; + + pinfo.lcd.vsync_enable = TRUE; + pinfo.lcd.hw_vsync_mode = TRUE; + pinfo.lcd.refx100 = 6200; //6000; //6200/* adjust refx100 to prevent tearing */ + pinfo.lcd.v_back_porch = 12; + pinfo.lcd.v_front_porch = 16; + pinfo.lcd.v_pulse_width = 5;////1; + + pinfo.mipi.mode = DSI_CMD_MODE; + pinfo.mipi.dst_format = DSI_CMD_DST_FORMAT_RGB888; + pinfo.mipi.vc = 0; + pinfo.mipi.rgb_swap = DSI_RGB_SWAP_BGR;//DSI_RGB_SWAP_RGB; + pinfo.mipi.data_lane0 = TRUE; + pinfo.mipi.data_lane1 = TRUE; + pinfo.mipi.t_clk_post = 0x20; + pinfo.mipi.t_clk_pre = 0x2F; + pinfo.mipi.stream = 0; /* dma_p */ + pinfo.mipi.mdp_trigger = DSI_CMD_TRIGGER_SW_TE;//DSI_CMD_TRIGGER_SW_TE; + pinfo.mipi.dma_trigger = DSI_CMD_TRIGGER_SW; + pinfo.mipi.te_sel = 1; /* TE from vsync gpio */ + pinfo.mipi.interleave_max = 1; + pinfo.mipi.insert_dcs_cmd = TRUE; + pinfo.mipi.wr_mem_continue = 0x3c; + pinfo.mipi.wr_mem_start = 0x2c; + pinfo.mipi.dsi_phy_db = &dsi_cmd_mode_phy_db; + pinfo.mipi.tx_eot_append = 0x01; + pinfo.mipi.rx_eot_ignore = 0x0; + pinfo.mipi.dlane_swap = 0x01; + + nt35510_panel_data.panel_info = pinfo; + + ret = platform_device_register(&this_device); + if (ret) + platform_driver_unregister(&this_driver); + + pmapp_disp_backlight_init(); + + return ret; +} +struct dsi_buf * get_lcd_mipi_tx_buf(void) +{ + return &nt35510_tx_buf; +} +module_init(mipi_div4_nt35510_lcd_init); diff --git a/drivers/video/msm/mipi_div4_nt35510_cmd_wvga.c b/drivers/video/msm/mipi_div4_nt35510_cmd_wvga.c old mode 100644 new mode 100755 diff --git a/drivers/video/msm/mipi_dsi.c b/drivers/video/msm/mipi_dsi.c index 7848dbcb..d34fa560 100644 --- a/drivers/video/msm/mipi_dsi.c +++ b/drivers/video/msm/mipi_dsi.c @@ -100,6 +100,8 @@ static int mipi_dsi_off(struct platform_device *pdev) struct msm_fb_data_type *mfd; struct msm_panel_info *pinfo; + pr_debug("%s+:\n", __func__); + mfd = platform_get_drvdata(pdev); pinfo = &mfd->panel_info; @@ -113,7 +115,7 @@ static int mipi_dsi_off(struct platform_device *pdev) /* make sure dsi clk is on so that * dcs commands can be sent */ - mipi_dsi_clk_cfg(1); + mipi_dsi_clk_cfg(1); /* make sure dsi_cmd_mdp is idle */ mipi_dsi_cmd_mdp_busy(); @@ -130,13 +132,11 @@ static int mipi_dsi_off(struct platform_device *pdev) if (MDP_REV_303 != mdp_rev) gpio_free(vsync_gpio); } - //ensure platform TE off modify TE IRQ GPIO to output 0 - //mipi_dsi_set_tear_off(mfd); + mipi_dsi_set_tear_off(mfd); } } ret = panel_next_off(pdev); - #ifdef CONFIG_MSM_BUS_SCALING mdp_bus_scale_update_request(0); #endif @@ -152,7 +152,7 @@ static int mipi_dsi_off(struct platform_device *pdev) mipi_dsi_ahb_ctrl(0); spin_unlock_bh(&dsi_clk_lock); - mipi_dsi_unprepare_clocks(); + mipi_dsi_unprepare_clocks(); if (mipi_dsi_pdata && mipi_dsi_pdata->dsi_power_save) mipi_dsi_pdata->dsi_power_save(0); @@ -180,6 +180,8 @@ static int mipi_dsi_on(struct platform_device *pdev) u32 dummy_xres, dummy_yres; int target_type = 0; + pr_debug("%s+:\n", __func__); + mfd = platform_get_drvdata(pdev); fbi = mfd->fbi; var = &fbi->var; @@ -236,8 +238,7 @@ static int mipi_dsi_on(struct platform_device *pdev) width + dummy_xres + hfp - 1)); } else { /* DSI_LAN_SWAP_CTRL */ - MIPI_OUTP(MIPI_DSI_BASE + 0x00ac, - mipi_dsi_pdata->dlane_swap); + MIPI_OUTP(MIPI_DSI_BASE + 0x00ac, mipi->dlane_swap); MIPI_OUTP(MIPI_DSI_BASE + 0x20, ((hbp + width + dummy_xres) << 16 | (hbp))); @@ -275,7 +276,7 @@ static int mipi_dsi_on(struct platform_device *pdev) MIPI_OUTP(MIPI_DSI_BASE + 0x58, data); } - mipi_dsi_host_init(mipi, mipi_dsi_pdata->dlane_swap); + mipi_dsi_host_init(mipi); if (mipi->force_clk_lane_hs) { u32 tmp; @@ -290,8 +291,9 @@ static int mipi_dsi_on(struct platform_device *pdev) mutex_lock(&mfd->dma->ov_mutex); else down(&mfd->dma->mutex); - - ret = panel_next_on(pdev); + + if (mfd->op_enable) + ret = panel_next_on(pdev); mipi_dsi_op_mode_config(mipi->mode); @@ -325,7 +327,9 @@ static int mipi_dsi_on(struct platform_device *pdev) __func__, vsync_gpio); } tlmm_settings = TRUE; - gpio_direction_input(vsync_gpio); + + gpio_direction_input( + vsync_gpio); } else { if (!tlmm_settings) { pr_err( @@ -336,16 +340,10 @@ static int mipi_dsi_on(struct platform_device *pdev) } } } - //this is the platform base logic, the tear will turn on every wake up - //LCM driver will do this so skip it here - //mipi_dsi_set_tear_on(mfd); + mipi_dsi_set_tear_on(mfd); } } -#ifdef CONFIG_MSM_BUS_SCALING - mdp_bus_scale_update_request(2); -#endif - mdp4_overlay_dsi_state_set(ST_DSI_RESUME); if (mdp_rev >= MDP_REV_41) @@ -359,6 +357,12 @@ static int mipi_dsi_on(struct platform_device *pdev) } +static int mipi_dsi_late_init(struct platform_device *pdev) +{ + return panel_next_late_init(pdev); +} + + static int mipi_dsi_resource_initialized; static int mipi_dsi_probe(struct platform_device *pdev) @@ -505,6 +509,7 @@ static int mipi_dsi_probe(struct platform_device *pdev) pdata = mdp_dev->dev.platform_data; pdata->on = mipi_dsi_on; pdata->off = mipi_dsi_off; + pdata->late_init = mipi_dsi_late_init; pdata->next = pdev; /* diff --git a/drivers/video/msm/mipi_dsi.h b/drivers/video/msm/mipi_dsi.h index ef6bddff..38e31763 100644 --- a/drivers/video/msm/mipi_dsi.h +++ b/drivers/video/msm/mipi_dsi.h @@ -117,6 +117,7 @@ enum dsi_trigger_type { #define DSI_INTR_CMD_DMA_DONE_MASK BIT(1) #define DSI_INTR_CMD_DMA_DONE BIT(0) +#define DSI_VIDEO_TERM BIT(16) #define DSI_MDP_TERM BIT(8) #define DSI_CMD_TERM BIT(0) @@ -188,7 +189,7 @@ struct dsi_clk_desc { #define DSI_HDR_DATA1(data) ((data) & 0x0ff) #define DSI_HDR_WC(wc) ((wc) & 0x0ffff) -#define DSI_BUF_SIZE 64 +#define DSI_BUF_SIZE 1024 #define MIPI_DSI_MRPS 0x04 /* Maximum Return Packet Size */ #define MIPI_DSI_LEN 8 /* 4 x 4 - 6 - 2, bytes dcs header+crc-align */ @@ -298,7 +299,7 @@ int mipi_dsi_cmds_rx(struct msm_fb_data_type *mfd, struct dsi_buf *tp, struct dsi_buf *rp, struct dsi_cmd_desc *cmds, int len); int mipi_dsi_cmd_dma_rx(struct dsi_buf *tp, int rlen); -void mipi_dsi_host_init(struct mipi_panel_info *pinfo, char dlane_swap); +void mipi_dsi_host_init(struct mipi_panel_info *pinfo); void mipi_dsi_op_mode_config(int mode); void mipi_dsi_cmd_mode_ctrl(int enable); void mdp4_dsi_cmd_trigger(void); @@ -347,6 +348,8 @@ int mipi_dsi_cmdlist_put(struct dcs_cmd_req *cmdreq); struct dcs_cmd_req *mipi_dsi_cmdlist_get(void); void mipi_dsi_cmdlist_commit(int from_mdp); void mipi_dsi_cmd_mdp_busy(void); +void mipi_dsi_configure_fb_divider(u32 fps_level); +void mipi_dsi_wait4video_done(void); #ifdef CONFIG_FB_MSM_MDP303 void update_lane_config(struct msm_panel_info *pinfo); diff --git a/drivers/video/msm/mipi_dsi_host.c b/drivers/video/msm/mipi_dsi_host.c index 94c0df76..3e11c844 100644 --- a/drivers/video/msm/mipi_dsi_host.c +++ b/drivers/video/msm/mipi_dsi_host.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,7 @@ static struct completion dsi_dma_comp; static struct completion dsi_mdp_comp; +static struct completion dsi_video_comp; static struct dsi_buf dsi_tx_buf; static struct dsi_buf dsi_rx_buf; static spinlock_t dsi_irq_lock; @@ -92,6 +94,7 @@ void mipi_dsi_init(void) { init_completion(&dsi_dma_comp); init_completion(&dsi_mdp_comp); + init_completion(&dsi_video_comp); mipi_dsi_buf_alloc(&dsi_tx_buf, DSI_BUF_SIZE); mipi_dsi_buf_alloc(&dsi_rx_buf, DSI_BUF_SIZE); spin_lock_init(&dsi_irq_lock); @@ -183,6 +186,8 @@ void mipi_dsi_clk_cfg(int on) } } } + pr_debug("%s: on=%d clk_cnt=%d pid=%d\n", __func__, ++ on, dsi_clk_cnt, current->pid); spin_unlock_irqrestore(&mdp_spin_lock, flags); } @@ -814,7 +819,7 @@ static int mipi_dsi_long_read_resp(struct dsi_buf *rp) return len; } -void mipi_dsi_host_init(struct mipi_panel_info *pinfo, char dlane_swap) +void mipi_dsi_host_init(struct mipi_panel_info *pinfo) { uint32 dsi_ctrl, intr_ctrl; uint32 data; @@ -905,7 +910,7 @@ void mipi_dsi_host_init(struct mipi_panel_info *pinfo, char dlane_swap) MIPI_OUTP(MIPI_DSI_BASE + 0x0080, data); /* DSI_TRIG_CTRL */ /* DSI_LAN_SWAP_CTRL */ - MIPI_OUTP(MIPI_DSI_BASE + 0x00ac, dlane_swap); + MIPI_OUTP(MIPI_DSI_BASE + 0x00ac, pinfo->dlane_swap); /* clock out ctrl */ data = pinfo->t_clk_post & 0x3f; /* 6 bits */ @@ -964,31 +969,30 @@ void mipi_dsi_controller_cfg(int enable) uint32 dsi_ctrl; uint32 status; - int cnt; - - cnt = 16; - while (cnt--) { - status = MIPI_INP(MIPI_DSI_BASE + 0x0004); - status &= 0x02; /* CMD_MODE_DMA_BUSY */ - if (status == 0) - break; - usleep(1000); - } - if (cnt == 0) + u32 sleep_us = 1000; + u32 timeout_us = 16000; + + /* Check for CMD_MODE_DMA_BUSY */ + if (readl_poll_timeout((MIPI_DSI_BASE + 0x0004), + status, + ((status & 0x02) == 0), + sleep_us, timeout_us)) pr_info("%s: DSI status=%x failed\n", __func__, status); - cnt = 16; - while (cnt--) { - status = MIPI_INP(MIPI_DSI_BASE + 0x0008); - status &= 0x11111000; /* x_HS_FIFO_EMPTY */ - if (status == 0x11111000) /* all empty */ - break; - usleep(1000); - } - - if (cnt == 0) + /* Check for x_HS_FIFO_EMPTY */ + if (readl_poll_timeout((MIPI_DSI_BASE + 0x0008), + status, + ((status & 0x11111000) == 0x11111000), + sleep_us, timeout_us)) pr_info("%s: FIFO status=%x failed\n", __func__, status); + /* Check for VIDEO_MODE_ENGINE_BUSY */ + if (readl_poll_timeout((MIPI_DSI_BASE + 0x0004), + status, + ((status & 0x08) == 0), + sleep_us, timeout_us)) + pr_info("%s: DSI status=%x failed\n", __func__, status); + dsi_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0000); if (enable) dsi_ctrl |= 0x01; @@ -1008,7 +1012,8 @@ void mipi_dsi_op_mode_config(int mode) dsi_ctrl &= ~0x07; if (mode == DSI_VIDEO_MODE) { dsi_ctrl |= 0x03; - intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK; + intr_ctrl = (DSI_INTR_CMD_DMA_DONE_MASK | + DSI_INTR_VIDEO_DONE_MASK); } else { /* command mode */ dsi_ctrl |= 0x05; intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_ERROR_MASK | @@ -1021,6 +1026,18 @@ void mipi_dsi_op_mode_config(int mode) MIPI_OUTP(MIPI_DSI_BASE + 0x0000, dsi_ctrl); wmb(); } +void mipi_dsi_wait4video_done(void) +{ + unsigned long flag; + + spin_lock_irqsave(&dsi_mdp_lock, flag); + INIT_COMPLETION(dsi_video_comp); + mipi_dsi_enable_irq(DSI_VIDEO_TERM); + spin_unlock_irqrestore(&dsi_mdp_lock, flag); + + wait_for_completion_timeout(&dsi_video_comp, + msecs_to_jiffies(VSYNC_PERIOD * 4)); +} void mipi_dsi_mdp_busy_wait(struct msm_fb_data_type *mfd) { @@ -1056,6 +1073,7 @@ void mipi_dsi_cmd_mdp_start(void) spin_lock_irqsave(&dsi_mdp_lock, flag); mipi_dsi_enable_irq(DSI_MDP_TERM); dsi_mdp_busy = TRUE; + INIT_COMPLETION(dsi_mdp_comp); spin_unlock_irqrestore(&dsi_mdp_lock, flag); } @@ -1458,8 +1476,7 @@ int mipi_dsi_cmd_dma_tx(struct dsi_buf *tp) { unsigned long flags; - unsigned long timeout; - + #ifdef DSI_HOST_DEBUG int i; char *bp; @@ -1495,10 +1512,7 @@ int mipi_dsi_cmd_dma_tx(struct dsi_buf *tp) wmb(); spin_unlock_irqrestore(&dsi_mdp_lock, flags); - //wait_for_completion(&dsi_dma_comp); - timeout = wait_for_completion_timeout(&dsi_dma_comp, msecs_to_jiffies(200)); - if(!timeout) - printk(KERN_INFO "%s[MIPI]dsi send data timeout!\n", __func__); + wait_for_completion(&dsi_dma_comp); dma_unmap_single(&dsi_dev, tp->dmap, tp->len, DMA_TO_DEVICE); tp->dmap = 0; @@ -1532,28 +1546,35 @@ int mipi_dsi_cmd_dma_rx(struct dsi_buf *rp, int rlen) return rlen; } +static void mipi_dsi_wait4video_eng_busy(void) +{ + mipi_dsi_wait4video_done(); + /* delay 4 ms to skip BLLP */ + usleep(4000); +} + + void mipi_dsi_cmd_mdp_busy(void) { - u32 status; unsigned long flags; int need_wait = 0; + pr_debug("%s: start pid=%d\n", + __func__, current->pid); + spin_lock_irqsave(&dsi_mdp_lock, flags); - status = MIPI_INP(MIPI_DSI_BASE + 0x0004);/* DSI_STATUS */ - if (status & 0x04) { /* MDP BUSY */ - INIT_COMPLETION(dsi_mdp_comp); - need_wait = 1; - pr_debug("%s: status=%x need_wait\n", __func__, (int)status); - mipi_dsi_enable_irq(DSI_MDP_TERM); - } + if (dsi_mdp_busy == TRUE) + need_wait++; spin_unlock_irqrestore(&dsi_mdp_lock, flags); - if (need_wait){ -// wait_for_completion(&dsi_mdp_comp); - if (!wait_for_completion_timeout(&dsi_mdp_comp, msecs_to_jiffies(200))) { - pr_err("%s: dma timeout error\n", __func__); - } + if (need_wait) { + /* wait until DMA finishes the current job */ + pr_debug("%s: pending pid=%d\n", + __func__, current->pid); + wait_for_completion(&dsi_mdp_comp); } + pr_debug("%s: done pid=%d\n", + __func__, current->pid); } /* @@ -1610,16 +1631,32 @@ void mipi_dsi_cmdlist_rx(struct dcs_cmd_req *req) void mipi_dsi_cmdlist_commit(int from_mdp) { struct dcs_cmd_req *req; + u32 dsi_ctrl; mutex_lock(&cmd_mutex); req = mipi_dsi_cmdlist_get(); - if (req == NULL) { - mutex_unlock(&cmd_mutex); - return; - } +/* make sure dsi_cmd_mdp is idle */ + mipi_dsi_cmd_mdp_busy(); + + if (req == NULL) + goto need_lock; pr_debug("%s: from_mdp=%d pid=%d\n", __func__, from_mdp, current->pid); + dsi_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0000); + if (dsi_ctrl & 0x02) { + /* video mode, make sure video engine is busy + * so dcs command will be sent at start of BLLP + */ + mipi_dsi_wait4video_eng_busy(); + } else { + /* command mode */ + if (!from_mdp) { /* cmdlist_put */ + /* make sure dsi_cmd_mdp is idle */ + mipi_dsi_cmd_mdp_busy(); + } + } + if (!from_mdp) { /* from put */ /* make sure dsi_cmd_mdp is idle */ mipi_dsi_cmd_mdp_busy(); @@ -1632,6 +1669,11 @@ void mipi_dsi_cmdlist_commit(int from_mdp) mipi_dsi_cmdlist_tx(req); mipi_dsi_clk_cfg(0); + need_lock: + + if (from_mdp) /* from pipe_commit */ + mipi_dsi_cmd_mdp_start(); + mutex_unlock(&cmd_mutex); } @@ -1765,23 +1807,23 @@ irqreturn_t mipi_dsi_isr(int irq, void *ptr) } if (isr & DSI_INTR_VIDEO_DONE) { - /* - * do something here - */ + spin_lock(&dsi_mdp_lock); + mipi_dsi_disable_irq_nosync(DSI_VIDEO_TERM); + complete(&dsi_video_comp); + spin_unlock(&dsi_mdp_lock); } if (isr & DSI_INTR_CMD_DMA_DONE) { - //printk("%s():isr:0x%x,interrupt dma done\n",__func__,isr); mipi_dsi_mdp_stat_inc(STAT_DSI_CMD); spin_lock(&dsi_mdp_lock); complete(&dsi_dma_comp); dsi_ctrl_lock = FALSE; + dsi_mdp_busy = FALSE; mipi_dsi_disable_irq_nosync(DSI_CMD_TERM); spin_unlock(&dsi_mdp_lock); } if (isr & DSI_INTR_CMD_MDP_DONE) { - //printk("%s():isr:0x%x,interrupt mdp done\n",__func__,isr); mipi_dsi_mdp_stat_inc(STAT_DSI_MDP); spin_lock(&dsi_mdp_lock); dsi_ctrl_lock = FALSE; diff --git a/drivers/video/msm/mipi_hx8389b_video_qhd_pt.c b/drivers/video/msm/mipi_hx8389b_video_qhd_pt.c old mode 100644 new mode 100755 diff --git a/drivers/video/msm/mipi_novatek.c b/drivers/video/msm/mipi_novatek.c index 5cd42004..60d09440 100644 --- a/drivers/video/msm/mipi_novatek.c +++ b/drivers/video/msm/mipi_novatek.c @@ -302,19 +302,31 @@ static char manufacture_id[2] = {0x04, 0x00}; /* DTYPE_DCS_READ */ static struct dsi_cmd_desc novatek_manufacture_id_cmd = { DTYPE_DCS_READ, 1, 0, 1, 5, sizeof(manufacture_id), manufacture_id}; +static u32 manu_id; + +static void mipi_novatek_manufacture_cb(u32 data) +{ + manu_id = data; + pr_info("%s: manufacture_id=%x\n", __func__, manu_id); +} + static uint32 mipi_novatek_manufacture_id(struct msm_fb_data_type *mfd) { - struct dsi_buf *rp, *tp; - struct dsi_cmd_desc *cmd; - uint32 *lp; - - tp = &novatek_tx_buf; - rp = &novatek_rx_buf; - cmd = &novatek_manufacture_id_cmd; - mipi_dsi_cmds_rx(mfd, tp, rp, cmd, 3); - lp = (uint32 *)rp->data; - pr_info("%s: manufacture_id=%x\n", __func__, *lp); - return *lp; + struct dcs_cmd_req cmdreq; + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = &novatek_manufacture_id_cmd; + cmdreq.cmds_cnt = 1; + cmdreq.flags = CMD_REQ_RX | CMD_REQ_COMMIT; + cmdreq.rlen = 3; + cmdreq.rbuf = NULL; + cmdreq.cb = mipi_novatek_manufacture_cb; /* call back */ + mipi_dsi_cmdlist_put(&cmdreq); + /* + * blocked here, untill call back called + */ + + return manu_id; } static int fpga_addr; @@ -380,6 +392,7 @@ static int mipi_novatek_lcd_on(struct platform_device *pdev) struct msm_fb_data_type *mfd; struct mipi_panel_info *mipi; struct msm_panel_info *pinfo; + struct dcs_cmd_req cmdreq; mfd = platform_get_drvdata(pdev); if (!mfd) @@ -394,23 +407,31 @@ static int mipi_novatek_lcd_on(struct platform_device *pdev) mipi = &mfd->panel_info.mipi; if (mipi->mode == DSI_VIDEO_MODE) { - mipi_dsi_cmds_tx(&novatek_tx_buf, novatek_video_on_cmds, - ARRAY_SIZE(novatek_video_on_cmds)); + cmdreq.cmds = novatek_video_on_cmds; + cmdreq.cmds_cnt = ARRAY_SIZE(novatek_video_on_cmds); + cmdreq.flags = CMD_REQ_COMMIT; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + mipi_dsi_cmdlist_put(&cmdreq); } else { - mipi_dsi_cmds_tx(&novatek_tx_buf, novatek_cmd_on_cmds, - ARRAY_SIZE(novatek_cmd_on_cmds)); + cmdreq.cmds = novatek_cmd_on_cmds; + cmdreq.cmds_cnt = ARRAY_SIZE(novatek_cmd_on_cmds); + cmdreq.flags = CMD_REQ_COMMIT; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + mipi_dsi_cmdlist_put(&cmdreq); /* clean up ack_err_status */ mipi_dsi_cmd_bta_sw_trigger(); mipi_novatek_manufacture_id(mfd); } - return 0; } static int mipi_novatek_lcd_off(struct platform_device *pdev) { struct msm_fb_data_type *mfd; + struct dcs_cmd_req cmdreq; mfd = platform_get_drvdata(pdev); @@ -419,9 +440,19 @@ static int mipi_novatek_lcd_off(struct platform_device *pdev) if (mfd->key != MFD_KEY) return -EINVAL; - mipi_dsi_cmds_tx(&novatek_tx_buf, novatek_display_off_cmds, - ARRAY_SIZE(novatek_display_off_cmds)); + cmdreq.cmds = novatek_display_off_cmds; + cmdreq.cmds_cnt = ARRAY_SIZE(novatek_display_off_cmds); + cmdreq.flags = CMD_REQ_COMMIT; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + + mipi_dsi_cmdlist_put(&cmdreq); + + return 0; +} +static int mipi_novatek_lcd_late_init(struct platform_device *pdev) +{ return 0; } @@ -431,10 +462,16 @@ static char led_pwm1[2] = {0x51, 0x0}; /* DTYPE_DCS_WRITE1 */ static struct dsi_cmd_desc backlight_cmd = { DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(led_pwm1), led_pwm1}; -struct dcs_cmd_req cmdreq; static void mipi_novatek_set_backlight(struct msm_fb_data_type *mfd) { + struct dcs_cmd_req cmdreq; + + if (mipi_novatek_pdata && + mipi_novatek_pdata->gpio_set_backlight) { + mipi_novatek_pdata->gpio_set_backlight(mfd->bl_level); + return; + } if ((mipi_novatek_pdata->enable_wled_bl_ctrl) && (wled_trigger_initialized)) { @@ -446,7 +483,7 @@ static void mipi_novatek_set_backlight(struct msm_fb_data_type *mfd) cmdreq.cmds = &backlight_cmd; cmdreq.cmds_cnt = 1; - cmdreq.flags = 0; + cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL; cmdreq.rlen = 0; cmdreq.cb = NULL; @@ -462,6 +499,7 @@ static int __devinit mipi_novatek_lcd_probe(struct platform_device *pdev) struct mipi_panel_info *mipi; struct platform_device *current_pdev; static struct mipi_dsi_phy_ctrl *phy_settings; + static char dlane_swap; if (pdev->id == 0) { mipi_novatek_pdata = pdev->dev.platform_data; @@ -471,6 +509,11 @@ static int __devinit mipi_novatek_lcd_probe(struct platform_device *pdev) phy_settings = (mipi_novatek_pdata->phy_ctrl_settings); } + if (mipi_novatek_pdata + && mipi_novatek_pdata->dlane_swap) { + dlane_swap = (mipi_novatek_pdata->dlane_swap); + } + if (mipi_novatek_pdata && mipi_novatek_pdata->fpga_3d_config_addr) mipi_novatek_3d_init(mipi_novatek_pdata @@ -500,6 +543,9 @@ static int __devinit mipi_novatek_lcd_probe(struct platform_device *pdev) if (phy_settings != NULL) mipi->dsi_phy_db = phy_settings; + + if (dlane_swap) + mipi->dlane_swap = dlane_swap; } return 0; } @@ -514,6 +560,7 @@ static struct platform_driver this_driver = { static struct msm_fb_panel_data novatek_panel_data = { .on = mipi_novatek_lcd_on, .off = mipi_novatek_lcd_off, + .late_init = mipi_novatek_lcd_late_init, .set_backlight = mipi_novatek_set_backlight, }; diff --git a/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c b/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c index 616622ec..d2820a80 100644 --- a/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c +++ b/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c @@ -64,7 +64,7 @@ static int __init mipi_cmd_novatek_blue_qhd_pt_init(void) pinfo.is_3d_panel = FB_TYPE_3D_PANEL; pinfo.lcd.vsync_enable = TRUE; pinfo.lcd.hw_vsync_mode = TRUE; - pinfo.lcd.refx100 = 6000; /* adjust refx100 to prevent tearing */ + pinfo.lcd.refx100 = 6200; /* adjust refx100 to prevent tearing */ pinfo.lcd.v_back_porch = 11; pinfo.lcd.v_front_porch = 10; pinfo.lcd.v_pulse_width = 5; diff --git a/drivers/video/msm/mipi_nt35516_e808t.c b/drivers/video/msm/mipi_nt35516_e808t.c old mode 100644 new mode 100755 diff --git a/drivers/video/msm/mipi_otm9605a.c b/drivers/video/msm/mipi_otm9605a.c old mode 100644 new mode 100755 diff --git a/drivers/video/msm/mipi_yassy_otm9608a_wg451v.c b/drivers/video/msm/mipi_yassy_otm9608a_wg451v.c old mode 100644 new mode 100755 diff --git a/drivers/video/msm/msm_dss_io_7x27a.c b/drivers/video/msm/msm_dss_io_7x27a.c old mode 100644 new mode 100755 diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index e81f0398..89c6e3e0 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -3,7 +3,7 @@ * Core MSM framebuffer driver. * * Copyright (C) 2007 Google Incorporated - * Copyright (c) 2013 The Linux Foundation. All Rights Reserved. + * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -38,10 +38,11 @@ #include #include #include -#include #include #include -#include +#include +#include +#include #define MSM_FB_C #include "msm_fb.h" @@ -63,12 +64,17 @@ static unsigned char *fbram; static unsigned char *fbram_phys; static int fbram_size; static boolean bf_supported; + /* Set backlight on resume after 50 ms after first * pan display on the panel. This is to avoid panel specific * transients during resume. */ unsigned long backlight_duration = (HZ/20); +#ifdef CONFIG_FB_MSM_ALIGN_BUFFER +static bool align_buffer = false; +#endif + static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; static int pdev_list_cnt; @@ -121,12 +127,27 @@ static int msm_fb_mmap(struct fb_info *info, struct vm_area_struct * vma); static int mdp_bl_scale_config(struct msm_fb_data_type *mfd, struct mdp_bl_scale_data *data); static void msm_fb_scale_bl(__u32 *bl_lvl); +static void msm_fb_commit_wq_handler(struct work_struct *work); +static int msm_fb_pan_idle(struct msm_fb_data_type *mfd); +static int hack_lcd = 1; +boolean lcd_have_resume = FALSE; +boolean last_backlight_setting = FALSE; +int last_backlight_level = 0; + + #ifdef MSM_FB_ENABLE_DBGFS #define MSM_FB_MAX_DBGFS 1024 #define MAX_BACKLIGHT_BRIGHTNESS 255 +#define WAIT_FENCE_FIRST_TIMEOUT (3 * MSEC_PER_SEC) +#define WAIT_FENCE_FINAL_TIMEOUT (10 * MSEC_PER_SEC) +/* Display op timeout should be greater than total timeout */ +#define WAIT_DISP_OP_TIMEOUT (WAIT_FENCE_FIRST_TIMEOUT +\ + WAIT_FENCE_FINAL_TIMEOUT) * MDP_MAX_FENCE_FD +#define MAX_TIMELINE_NAME_LEN 16 + int msm_fb_debugfs_file_index; struct dentry *msm_fb_debugfs_root; struct dentry *msm_fb_debugfs_file[MSM_FB_MAX_DBGFS]; @@ -186,15 +207,25 @@ static void msm_fb_set_bl_brightness(struct led_classdev *led_cdev, /* This maps android backlight level 0 to 255 into driver backlight level 0 to bl_max with rounding */ - bl_lvl = (2 * value * mfd->panel_info.bl_max + MAX_BACKLIGHT_BRIGHTNESS) - /(2 * MAX_BACKLIGHT_BRIGHTNESS); + bl_lvl = value; if (!bl_lvl && value) bl_lvl = 1; - - down(&mfd->sem); - msm_fb_set_backlight(mfd, bl_lvl); - up(&mfd->sem); +/* Remove qcom backlight mechanism,user our own */ +/* If LCD don't resume ,don't turn on LCD backlight */ + if (hack_lcd) { + if(FALSE == lcd_have_resume) { + last_backlight_level = bl_lvl; + last_backlight_setting = TRUE; + } else { + msm_fb_set_backlight(mfd, bl_lvl); + last_backlight_setting = FALSE; + } + } else { + down(&mfd->sem); + msm_fb_set_backlight(mfd, bl_lvl); + up(&mfd->sem); + } } static struct led_classdev backlight_led = { @@ -344,8 +375,9 @@ static void msm_fb_remove_sysfs(struct platform_device *pdev) } static void bl_workqueue_handler(struct work_struct *work); + static ssize_t msm_fb_xres(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { ssize_t ret = strnlen(buf, PAGE_SIZE); struct fb_info *fbi = dev_get_drvdata(dev); @@ -386,34 +418,34 @@ static struct attribute_group yres_fs_attr_group = { static int msm_fb_resolution_sysfs(struct platform_device *pdev) { - int rc; - struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + int rc; + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); - if (!mfd) { + if (!mfd) { pr_err("%s: mfd not found\n", __func__); return -ENODEV; - } - if (!mfd->fbi) { + } + if (!mfd->fbi) { pr_err("%s: mfd->fbi not found\n", __func__); return -ENODEV; - } - if (!mfd->fbi->dev) { + } + if (!mfd->fbi->dev) { pr_err("%s: mfd->fbi->dev not found\n", __func__); return -ENODEV; - } - rc = sysfs_create_group(&mfd->fbi->dev->kobj, + } + rc = sysfs_create_group(&mfd->fbi->dev->kobj, &xres_fs_attr_group); if (rc) { pr_err("%s: sysfs group creation failed, rc=%d\n", __func__, rc); return rc; - } + } rc = sysfs_create_group(&mfd->fbi->dev->kobj, - &yres_fs_attr_group); + &yres_fs_attr_group); if (rc) { pr_err("%s: sysfs group creation failed, rc=%d\n", - __func__, rc); + __func__, rc); return rc; } return 0; @@ -500,7 +532,21 @@ static int msm_fb_probe(struct platform_device *pdev) pdev_list[pdev_list_cnt++] = pdev; msm_fb_create_sysfs(pdev); - msm_fb_resolution_sysfs(pdev); + msm_fb_resolution_sysfs(pdev); + + if (mfd->timeline == NULL) { + char timeline_name[MAX_TIMELINE_NAME_LEN]; + snprintf(timeline_name, sizeof(timeline_name), + "mdp_fb_%d", mfd->index); + mfd->timeline = sw_sync_timeline_create(timeline_name); + if (mfd->timeline == NULL) { + pr_err("%s: cannot create time line", __func__); + return -ENOMEM; + } else { + mfd->timeline_value = 0; + } + } + return 0; } @@ -512,6 +558,8 @@ static int msm_fb_remove(struct platform_device *pdev) mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + msm_fb_pan_idle(mfd); + msm_fb_remove_sysfs(pdev); pm_runtime_disable(mfd->fbi->dev); @@ -572,15 +620,18 @@ static int msm_fb_suspend(struct platform_device *pdev, pm_message_t state) struct msm_fb_data_type *mfd; int ret = 0; - MSM_FB_DEBUG("Enter\n"); + MSM_FB_DEBUG("msm_fb_suspend\n"); mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); if ((!mfd) || (mfd->key != MFD_KEY)) return 0; + msm_fb_pan_idle(mfd); + console_lock(); fb_set_suspend(mfd->fbi, FBINFO_STATE_SUSPENDED); + ret = msm_fb_suspend_sub(mfd); if (ret != 0) { printk(KERN_ERR "msm_fb: failed to suspend! %d\n", ret); @@ -674,6 +725,9 @@ static int msm_fb_resume_sub(struct msm_fb_data_type *mfd) mfd->suspend.op_enable,mfd->suspend.panel_power_on); if (mfd->suspend.panel_power_on) { + if (mfd->panel_driver_on == FALSE) + msm_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi, + mfd->op_enable); ret = msm_fb_blank_sub(FB_BLANK_UNBLANK, mfd->fbi, mfd->op_enable); @@ -681,6 +735,8 @@ static int msm_fb_resume_sub(struct msm_fb_data_type *mfd) MSM_FB_INFO("msm_fb_resume: can't turn on display!\n"); } + mfd->suspend.op_suspend = false; + return ret; } #endif @@ -693,13 +749,15 @@ static int msm_fb_resume(struct platform_device *pdev) int ret = 0; struct msm_fb_data_type *mfd; - MSM_FB_DEBUG("Enter\n"); + MSM_FB_DEBUG("msm_fb_resume\n"); mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); if ((!mfd) || (mfd->key != MFD_KEY)) return 0; + msm_fb_pan_idle(mfd); + console_lock(); ret = msm_fb_resume_sub(mfd); pdev->dev.power.power_state = PMSG_ON; @@ -737,14 +795,9 @@ static int msm_fb_ext_suspend(struct device *dev) struct msm_fb_panel_data *pdata = NULL; int ret = 0; - if (hdmi_prim_display) { - MSM_FB_INFO("%s: hdmi primary handles early suspend only\n", - __func__); - return 0; - } - if ((!mfd) || (mfd->key != MFD_KEY)) return 0; + msm_fb_pan_idle(mfd); pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; if (mfd->panel_info.type == HDMI_PANEL || @@ -768,15 +821,9 @@ static int msm_fb_ext_resume(struct device *dev) struct msm_fb_panel_data *pdata = NULL; int ret = 0; - if (hdmi_prim_display) { - MSM_FB_INFO("%s: hdmi primary handles early resume only\n", - __func__); - return 0; - } - if ((!mfd) || (mfd->key != MFD_KEY)) return 0; - + msm_fb_pan_idle(mfd); pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; if (mfd->panel_info.type == HDMI_PANEL || mfd->panel_info.type == DTV_PANEL) { @@ -877,8 +924,7 @@ static struct dev_pm_ops msm_fb_dev_pm_ops = { .runtime_suspend = msm_fb_runtime_suspend, .runtime_resume = msm_fb_runtime_resume, .runtime_idle = msm_fb_runtime_idle, -#if (defined(CONFIG_SUSPEND) && defined(CONFIG_FB_MSM_HDMI_MSM_PANEL) && \ - !defined(CONFIG_FB_MSM_HDMI_AS_PRIMARY)) +#if (defined(CONFIG_SUSPEND) && defined(CONFIG_FB_MSM_HDMI_MSM_PANEL)) .suspend = msm_fb_ext_suspend, .resume = msm_fb_ext_resume, #endif @@ -903,49 +949,23 @@ static struct platform_driver msm_fb_driver = { static void msmfb_early_suspend(struct early_suspend *h) { struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, - early_suspend); - struct msm_fb_panel_data *pdata = NULL; - MSM_DRM_DEBUG("start\n"); + early_suspend); - - msm_fb_suspend_sub(mfd); + msm_fb_pan_idle(mfd); - pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; - if (hdmi_prim_display && - (mfd->panel_info.type == HDMI_PANEL || - mfd->panel_info.type == DTV_PANEL)) { - /* Turn off the HPD circuitry */ - if (pdata->power_ctrl) { - MSM_FB_INFO("%s: Turning off HPD circuitry\n", - __func__); - pdata->power_ctrl(FALSE); - } - } + msm_fb_suspend_sub(mfd); } static void msmfb_early_resume(struct early_suspend *h) { struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, - early_suspend); - struct msm_fb_panel_data *pdata = NULL; - MSM_DRM_DEBUG("start\n"); - pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; - - if (hdmi_prim_display && - (mfd->panel_info.type == HDMI_PANEL || - mfd->panel_info.type == DTV_PANEL)) { - /* Turn on the HPD circuitry */ - if (pdata->power_ctrl) { - MSM_FB_INFO("%s: Turning on HPD circuitry\n", __func__); - pdata->power_ctrl(TRUE); - } - } - msm_fb_resume_sub(mfd); + early_suspend); + msm_fb_resume_sub(mfd); } #endif static int unset_bl_level, bl_updated; -static int bl_level_old, pan_display_flag; +static int bl_level_old; static int mdp_bl_scale_config(struct msm_fb_data_type *mfd, struct mdp_bl_scale_data *data) { @@ -959,15 +979,16 @@ static int mdp_bl_scale_config(struct msm_fb_data_type *mfd, bl_min_lvl); /* update current backlight to use new scaling*/ - if (mfd->panel_power_on && bl_updated) - msm_fb_set_backlight(mfd, curr_bl); + msm_fb_set_backlight(mfd, curr_bl); up(&mfd->sem); + return ret; } static void msm_fb_scale_bl(__u32 *bl_lvl) { __u32 temp = *bl_lvl; + pr_debug("%s: input = %d, scale = %d", __func__, temp, bl_scale); if (temp >= bl_min_lvl) { /* bl_scale is the numerator of scaling fraction (x/1024)*/ temp = ((*bl_lvl) * bl_scale) / 1024; @@ -976,43 +997,55 @@ static void msm_fb_scale_bl(__u32 *bl_lvl) if (temp < bl_min_lvl) temp = bl_min_lvl; } + pr_debug("%s: output = %d", __func__, temp); (*bl_lvl) = temp; } +/*must call this function from within mfd->sem*/ void msm_fb_set_backlight(struct msm_fb_data_type *mfd, __u32 bkl_lvl) { struct msm_fb_panel_data *pdata; __u32 temp = bkl_lvl; - - if(gv_dev.state==1){ - unset_bl_level = 0; - MSM_DRM_DEBUG(":unset_bl_level = %d\n", unset_bl_level); - return; - } + +/* Remove qcom backlight mechanism,user our own */ + if (!hack_lcd) { if (!mfd->panel_power_on || !bl_updated) { unset_bl_level = bkl_lvl; -//Normandy_P010154 remove to fix flash screen in charger mode by wang.xiaojun3@byd.com 131219 - if((unset_bl_level > 10) && !bl_updated && pan_display_flag) - schedule_delayed_work(&mfd->backlight_worker, backlight_duration); - MSM_DRM_DEBUG("bl_updated = %d,power_on=%d,level=%d\n", bl_updated,mfd->panel_power_on, bkl_lvl); return; } else { unset_bl_level = 0; } - - MSM_DRM_DEBUG(" %d\n", unset_bl_level); + } + + msm_fb_scale_bl(&temp); + msm_fb_pan_idle(mfd); pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; - if ((pdata) && (pdata->set_backlight)) { - msm_fb_scale_bl(&temp); + if (!pdata) + return; + if (!pdata->set_backlight) + return; + + if (!hack_lcd) { + if (bl_level_old == temp) { + return; + } mfd->bl_level = temp; pdata->set_backlight(mfd); mfd->bl_level = bkl_lvl; bl_level_old = temp; - } + } else { + if (bl_level_old == bkl_lvl) { + return; + } + down(&mfd->dma->mutex); + mfd->bl_level = bkl_lvl; + pdata->set_backlight(mfd); + up(&mfd->dma->mutex); + bl_level_old = mfd->bl_level; + } } - static int msm_fb_blank_sub(int blank_mode, struct fb_info *info, boolean op_enable) { @@ -1035,24 +1068,22 @@ static int msm_fb_blank_sub(int blank_mode, struct fb_info *info, msleep(16); ret = pdata->on(mfd->pdev); if (ret == 0) { - down(&mfd->sem); mfd->panel_power_on = TRUE; - up(&mfd->sem); - -/* ToDo: possible conflict with android which doesn't expect sw refresher */ -/* - if (!mfd->hw_refresh) - { - if ((ret = msm_fb_resume_sw_refresher(mfd)) != 0) - { - MSM_FB_INFO("msm_fb_blank_sub: msm_fb_resume_sw_refresher failed = %d!\n",ret); - } - } -*/ + mfd->panel_driver_on = mfd->op_enable; } +/* LCD resume completed ,then turn on lcd backlight */ + if (ret == 0 && hack_lcd) { + lcd_have_resume = TRUE; + /* Remove qcom backlight mechanism,user our own */ + if(TRUE == last_backlight_setting) + { + msm_fb_set_backlight(mfd,last_backlight_level); + last_backlight_setting = FALSE; + printk("%s:Waiting for LCD resume ,then set backlight level=%d\n",__func__,last_backlight_level); + } } + } break; - case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_NORMAL: @@ -1061,22 +1092,24 @@ static int msm_fb_blank_sub(int blank_mode, struct fb_info *info, if (mfd->panel_power_on) { int curr_pwr_state; + if (hack_lcd) + lcd_have_resume = FALSE; + mfd->op_enable = FALSE; curr_pwr_state = mfd->panel_power_on; - - down(&mfd->sem); - msm_fb_set_backlight(mfd, 0); mfd->panel_power_on = FALSE; - bl_updated = 0; - pan_display_flag = 0; - up(&mfd->sem); - if (delayed_work_pending(&mfd->backlight_worker)) + + if (!hack_lcd) { /* Remove qcom backlight mechanism,user our own */ cancel_delayed_work_sync(&mfd->backlight_worker); + bl_updated = 0; + } + msleep(16); ret = pdata->off(mfd->pdev); if (ret) mfd->panel_power_on = curr_pwr_state; + msm_fb_release_timeline(mfd); mfd->op_enable = TRUE; } break; @@ -1090,6 +1123,13 @@ int calc_fb_offset(struct msm_fb_data_type *mfd, struct fb_info *fbi, int bpp) struct msm_panel_info *panel_info = &mfd->panel_info; int remainder, yres, offset; +#ifdef CONFIG_FB_MSM_ALIGN_BUFFER + if (!align_buffer) + { + return fbi->var.xoffset * bpp + fbi->var.yoffset * fbi->fix.line_length; + } +#endif + if (panel_info->mode2_yres != 0) { yres = panel_info->mode2_yres; remainder = (fbi->fix.line_length*yres) & (PAGE_SIZE - 1); @@ -1119,7 +1159,7 @@ static void msm_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; - + msm_fb_pan_idle(mfd); cfb_fillrect(info, rect); if (!mfd->hw_refresh && (info->var.yoffset == 0) && !mfd->sw_currently_refreshing) { @@ -1140,6 +1180,7 @@ static void msm_fb_copyarea(struct fb_info *info, { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + msm_fb_pan_idle(mfd); cfb_copyarea(info, area); if (!mfd->hw_refresh && (info->var.yoffset == 0) && !mfd->sw_currently_refreshing) { @@ -1159,6 +1200,7 @@ static void msm_fb_imageblit(struct fb_info *info, const struct fb_image *image) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + msm_fb_pan_idle(mfd); cfb_imageblit(info, image); if (!mfd->hw_refresh && (info->var.yoffset == 0) && !mfd->sw_currently_refreshing) { @@ -1177,6 +1219,13 @@ static void msm_fb_imageblit(struct fb_info *info, const struct fb_image *image) static int msm_fb_blank(int blank_mode, struct fb_info *info) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + msm_fb_pan_idle(mfd); + if (mfd->op_enable == 0) { + if (blank_mode == FB_BLANK_UNBLANK) + mfd->suspend.panel_power_on = TRUE; + else + mfd->suspend.panel_power_on = FALSE; + } return msm_fb_blank_sub(blank_mode, info, mfd->op_enable); } @@ -1203,6 +1252,7 @@ static int msm_fb_mmap(struct fb_info *info, struct vm_area_struct * vma) u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); unsigned long off = vma->vm_pgoff << PAGE_SHIFT; struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + msm_fb_pan_idle(mfd); if (off >= len) { /* memory mapped io */ off -= len; @@ -1395,6 +1445,26 @@ static int msm_fb_register(struct msm_fb_data_type *mfd) bpp = 4; break; + case MDP_BGRA_8888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 0; + var->green.offset = 8; + var->red.offset = 16; + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 24; + var->transp.length = 8; + bpp = 4; + break; + + case MDP_YCRYCB_H2V1: /* ToDo: need to check TV-Out YUV422i framebuffer format */ /* we might need to create new type define */ @@ -1475,27 +1545,6 @@ static int msm_fb_register(struct msm_fb_data_type *mfd) var->yres_virtual = panel_info->yres * mfd->fb_page + ((PAGE_SIZE - remainder)/fix->line_length) * mfd->fb_page; var->bits_per_pixel = bpp * 8; /* FrameBuffer color depth */ - if (mfd->dest == DISPLAY_LCD) { - if (panel_info->type == MDDI_PANEL && panel_info->mddi.is_type1) - var->reserved[4] = panel_info->lcd.refx100 / (100 * 2); - else - var->reserved[4] = 60;//panel_info->lcd.refx100 / 100; - } else { - if (panel_info->type == MIPI_VIDEO_PANEL) { - var->reserved[4] = panel_info->mipi.frame_rate; - } else { - var->reserved[4] = panel_info->clk_rate / - ((panel_info->lcdc.h_back_porch + - panel_info->lcdc.h_front_porch + - panel_info->lcdc.h_pulse_width + - panel_info->xres) * - (panel_info->lcdc.v_back_porch + - panel_info->lcdc.v_front_porch + - panel_info->lcdc.v_pulse_width + - panel_info->yres)); - } - } - pr_debug("reserved[4] %u\n", var->reserved[4]); /* * id field for fb app @@ -1558,7 +1607,15 @@ static int msm_fb_register(struct msm_fb_data_type *mfd) mfd->msmfb_no_update_notify_timer.data = (unsigned long)mfd; init_completion(&mfd->msmfb_update_notify); init_completion(&mfd->msmfb_no_update_notify); - + init_completion(&mfd->commit_comp); + mutex_init(&mfd->sync_mutex); + INIT_WORK(&mfd->commit_work, msm_fb_commit_wq_handler); + mfd->msm_fb_backup = kzalloc(sizeof(struct msm_fb_backup_type), + GFP_KERNEL); + if (mfd->msm_fb_backup == 0) { + pr_err("error: not enough memory!\n"); + return -ENOMEM; + } fbram_offset = PAGE_ALIGN((int)fbram)-(int)fbram; fbram += fbram_offset; fbram_phys += fbram_offset; @@ -1572,6 +1629,16 @@ static int msm_fb_register(struct msm_fb_data_type *mfd) fbi->screen_base = fbram; fbi->fix.smem_start = (unsigned long)fbram_phys; + + memset(fbi->screen_base, 0x0, fix->smem_len); + + msm_iommu_map_contig_buffer(fbi->fix.smem_start, + DISPLAY_WRITE_DOMAIN, + GEN_POOL, + fbi->fix.smem_len, + SZ_4K, + 0, + &(mfd->display_iova)); msm_iommu_map_contig_buffer(fbi->fix.smem_start, DISPLAY_READ_DOMAIN, @@ -1597,10 +1664,22 @@ static int msm_fb_register(struct msm_fb_data_type *mfd) /* cursor memory allocation */ if (mfd->cursor_update) { + unsigned long cursor_buf_iommu = 0; mfd->cursor_buf = dma_alloc_coherent(NULL, MDP_CURSOR_SIZE, (dma_addr_t *) &mfd->cursor_buf_phys, GFP_KERNEL); + + msm_iommu_map_contig_buffer((unsigned long)mfd->cursor_buf_phys, + DISPLAY_READ_DOMAIN, + GEN_POOL, + MDP_CURSOR_SIZE, + SZ_4K, + 0, + &cursor_buf_iommu); + if (cursor_buf_iommu) + mfd->cursor_buf_phys = (void *)cursor_buf_iommu; + if (!mfd->cursor_buf) mfd->cursor_update = 0; } @@ -1642,7 +1721,7 @@ static int msm_fb_register(struct msm_fb_data_type *mfd) ret = 0; #ifdef CONFIG_HAS_EARLYSUSPEND - if (hdmi_prim_display || mfd->panel_info.type != DTV_PANEL) { + if (mfd->panel_info.type != DTV_PANEL) { mfd->early_suspend.suspend = msmfb_early_suspend; mfd->early_suspend.resume = msmfb_early_resume; mfd->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 2; @@ -1787,6 +1866,7 @@ glanceview_register(gv_device_id_lcd, &gv_dev, &mfd->pdev->dev); static int msm_fb_open(struct fb_info *info, int user) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + bool unblank = true; int result; result = pm_runtime_get_sync(info->dev); @@ -1808,9 +1888,16 @@ static int msm_fb_open(struct fb_info *info, int user) pr_debug("%s:%d no mdp_set_dma_pan_info %d\n", __func__, __LINE__, info->node); - if (msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable)) { - printk(KERN_ERR "msm_fb_open: can't turn on display!\n"); - return -1; + if (mfd->is_panel_ready && !mfd->is_panel_ready()) + unblank = false; + + if (unblank) { + if (msm_fb_blank_sub(FB_BLANK_UNBLANK, + info, TRUE)) { + MSM_FB_ERR("%s: can't turn on display!\n", + __func__); + return -EPERM; + } } } @@ -1828,10 +1915,10 @@ static int msm_fb_release(struct fb_info *info, int user) mfd->index); return -EINVAL; } - + msm_fb_pan_idle(mfd); mfd->ref_cnt--; - if (!mfd->ref_cnt) { + if ((!mfd->ref_cnt) && (mfd->op_enable)) { if ((ret = msm_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable)) != 0) { @@ -1844,74 +1931,199 @@ static int msm_fb_release(struct fb_info *info, int user) return ret; } -DEFINE_SEMAPHORE(msm_fb_pan_sem); +void msm_fb_wait_for_fence(struct msm_fb_data_type *mfd) +{ + int i, ret = 0; + /* buf sync */ + for (i = 0; i < mfd->acq_fen_cnt; i++) { + ret = sync_fence_wait(mfd->acq_fen[i], + WAIT_FENCE_FIRST_TIMEOUT); + if (ret == -ETIME) { + pr_warn("%s: sync_fence_wait timed out!" + "Waiting %ld more seconds\n", + __func__,WAIT_FENCE_FINAL_TIMEOUT/MSEC_PER_SEC); + ret = sync_fence_wait(mfd->acq_fen[i], + WAIT_FENCE_FINAL_TIMEOUT); + } + if (ret < 0) { + pr_err("%s: sync_fence_wait failed! ret = %x\n", + __func__, ret); + break; + } + sync_fence_put(mfd->acq_fen[i]); + } + if (ret < 0) { + while (i < mfd->acq_fen_cnt) { + sync_fence_put(mfd->acq_fen[i]); + i++; + } + } + mfd->acq_fen_cnt = 0; +} +int msm_fb_signal_timeline(struct msm_fb_data_type *mfd) +{ + mutex_lock(&mfd->sync_mutex); + if (mfd->timeline) { + sw_sync_timeline_inc(mfd->timeline, 1); + mfd->timeline_value++; + } + mutex_unlock(&mfd->sync_mutex); + return 0; +} static void bl_workqueue_handler(struct work_struct *work) { struct msm_fb_data_type *mfd = container_of(to_delayed_work(work), struct msm_fb_data_type, backlight_worker); struct msm_fb_panel_data *pdata = mfd->pdev->dev.platform_data; - - down(&mfd->sem); - if ((pdata) && (pdata->set_backlight) && (!bl_updated) - && mfd->panel_power_on) { + + if ((pdata) && (pdata->set_backlight) && (!bl_updated)) { + down(&mfd->sem); mfd->bl_level = unset_bl_level; - printk("%s,backlight=%d\n",__func__,unset_bl_level); pdata->set_backlight(mfd); bl_level_old = unset_bl_level; bl_updated = 1; + up(&mfd->sem); } - up(&mfd->sem); +} + +void msm_fb_release_timeline(struct msm_fb_data_type *mfd) +{ + mutex_lock(&mfd->sync_mutex); + if (mfd->timeline) { + sw_sync_timeline_inc(mfd->timeline, 2); + mfd->timeline_value += 2; + } + mutex_unlock(&mfd->sync_mutex); +} + +DEFINE_SEMAPHORE(msm_fb_pan_sem); +static int msm_fb_pan_idle(struct msm_fb_data_type *mfd) +{ + int ret = 0; + + mutex_lock(&mfd->sync_mutex); + if (mfd->is_committing) { + mutex_unlock(&mfd->sync_mutex); + ret = wait_for_completion_interruptible_timeout( + &mfd->commit_comp, + msecs_to_jiffies(WAIT_DISP_OP_TIMEOUT)); + if (ret <= 0) + ret = -ERESTARTSYS; + else if (!ret) + pr_err("%s wait for commit_comp timeout %d %d", + __func__, ret, mfd->is_committing); + if (ret <= 0) { + mutex_lock(&mfd->sync_mutex); + mfd->is_committing = 0; + complete_all(&mfd->commit_comp); + mutex_unlock(&mfd->sync_mutex); + } + } else { + mutex_unlock(&mfd->sync_mutex); + } + return ret; +} +static int msm_fb_pan_display_ex(struct fb_info *info, + struct mdp_display_commit *disp_commit) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct msm_fb_backup_type *fb_backup; + struct fb_var_screeninfo *var = &disp_commit->var; + u32 wait_for_finish = disp_commit->wait_for_finish; + int ret = 0; + + if (disp_commit->flags & + MDP_DISPLAY_COMMIT_OVERLAY) { + if (!mfd->panel_power_on) /* suspended */ + return -EPERM; + } else { + /* + * If framebuffer is 2, io pan display is not allowed. + */ + if (bf_supported && info->node == 2) { + pr_err("%s: no pan display for fb%d!", + __func__, info->node); + return -EPERM; + } + + if (info->node != 0 || mfd->cont_splash_done) /* primary */ + if ((!mfd->op_enable) || (!mfd->panel_power_on)) + return -EPERM; + + if (var->xoffset > (info->var.xres_virtual - info->var.xres)) + return -EINVAL; + + if (var->yoffset > (info->var.yres_virtual - info->var.yres)) + return -EINVAL; + } + msm_fb_pan_idle(mfd); + + mutex_lock(&mfd->sync_mutex); + + if (!(disp_commit->flags & + MDP_DISPLAY_COMMIT_OVERLAY)) { + if (info->fix.xpanstep) + info->var.xoffset = + (var->xoffset / info->fix.xpanstep) * + info->fix.xpanstep; + + if (info->fix.ypanstep) + info->var.yoffset = + (var->yoffset / info->fix.ypanstep) * + info->fix.ypanstep; + } + fb_backup = (struct msm_fb_backup_type *)mfd->msm_fb_backup; + memcpy(&fb_backup->info, info, sizeof(struct fb_info)); + memcpy(&fb_backup->disp_commit, disp_commit, + sizeof(struct mdp_display_commit)); + mfd->is_committing = 1; + INIT_COMPLETION(mfd->commit_comp); + schedule_work(&mfd->commit_work); + mutex_unlock(&mfd->sync_mutex); + if (wait_for_finish) + msm_fb_pan_idle(mfd); + return ret; } static int msm_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct mdp_display_commit disp_commit; + memset(&disp_commit, 0, sizeof(disp_commit)); + disp_commit.var = *var; + disp_commit.wait_for_finish = TRUE; + memcpy(&disp_commit.var, var, sizeof(struct fb_var_screeninfo)); + return msm_fb_pan_display_ex(info, &disp_commit); +} + +static bool is_first_frame = TRUE; + +static int msm_fb_pan_display_sub(struct fb_var_screeninfo *var, + struct fb_info *info) { struct mdp_dirty_region dirty; struct mdp_dirty_region *dirtyPtr = NULL; struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; - static int notify_cnt = 0; - int state = 0; - if (!suspend_set_state()) - { - printk("%s,suspend_status is true\n",__func__); - state= -EPERM; - return state; - - } - + /* * If framebuffer is 2, io pen display is not allowed. */ if (bf_supported && info->node == 2) { pr_err("%s: no pan display for fb%d!", __func__, info->node); - //return -EPERM; - state= -EPERM; - goto MSM_FB_PAN_ERROR; + return -EPERM; } if (info->node != 0 || mfd->cont_splash_done) /* primary */ if ((!mfd->op_enable) || (!mfd->panel_power_on)) - { - //return -EPERM; - state= -EPERM; - goto MSM_FB_PAN_ERROR; - } - + return -EPERM; + if (var->xoffset > (info->var.xres_virtual - info->var.xres)) - { - //return -EINVAL; - state= -EINVAL; - goto MSM_FB_PAN_ERROR; - } + return -EINVAL; if (var->yoffset > (info->var.yres_virtual - info->var.yres)) - { - //return -EINVAL; - state= -EINVAL; - goto MSM_FB_PAN_ERROR; - } + return -EINVAL; if (info->fix.xpanstep) info->var.xoffset = @@ -1928,17 +2140,9 @@ static int msm_fb_pan_display(struct fb_var_screeninfo *var, dirty.yoffset = (var->reserved[1] >> 16) & 0xffff; if ((var->reserved[2] & 0xffff) <= dirty.xoffset) - { - //return -EINVAL; - state= -EINVAL; - goto MSM_FB_PAN_ERROR; - } + return -EINVAL; if (((var->reserved[2] >> 16) & 0xffff) <= dirty.yoffset) - { - //return -EINVAL; - state= -EINVAL; - goto MSM_FB_PAN_ERROR; - } + return -EINVAL; dirty.width = (var->reserved[2] & 0xffff) - dirty.xoffset; dirty.height = @@ -1946,47 +2150,23 @@ static int msm_fb_pan_display(struct fb_var_screeninfo *var, info->var.yoffset = var->yoffset; if (dirty.xoffset < 0) - { - //return -EINVAL; - state= -EINVAL; - goto MSM_FB_PAN_ERROR; - } + return -EINVAL; if (dirty.yoffset < 0) - { - //return -EINVAL; - state= -EINVAL; - goto MSM_FB_PAN_ERROR; - } + return -EINVAL; if ((dirty.xoffset + dirty.width) > info->var.xres) - { - //return -EINVAL; - state= -EINVAL; - goto MSM_FB_PAN_ERROR; - } + return -EINVAL; if ((dirty.yoffset + dirty.height) > info->var.yres) - { - //return -EINVAL; - state= -EINVAL; - goto MSM_FB_PAN_ERROR; - } + return -EINVAL; if ((dirty.width <= 0) || (dirty.height <= 0)) - { - //return -EINVAL; - state= -EINVAL; - goto MSM_FB_PAN_ERROR; - } + return -EINVAL; + dirtyPtr = &dirty; } - /* FIXME: reduce fb change notification time to ease mess backlight configuration */ - if (notify_cnt >= 6) { - notify_cnt = 0; - complete(&mfd->msmfb_update_notify); - } else - notify_cnt++; + complete(&mfd->msmfb_update_notify); mutex_lock(&msm_fb_notify_update_sem); if (mfd->msmfb_no_update_notify_timer.function) del_timer(&mfd->msmfb_no_update_notify_timer); @@ -1996,47 +2176,68 @@ static int msm_fb_pan_display(struct fb_var_screeninfo *var, mutex_unlock(&msm_fb_notify_update_sem); down(&msm_fb_pan_sem); - + msm_fb_wait_for_fence(mfd); if (info->node == 0 && !(mfd->cont_splash_done)) { /* primary */ mdp_set_dma_pan_info(info, NULL, TRUE); if (msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable)) { pr_err("%s: can't turn on display!\n", __func__); - up(&msm_fb_pan_sem); - //return -EINVAL; - state= -EINVAL; - goto MSM_FB_PAN_ERROR; + msm_fb_release_timeline(mfd); + return -EINVAL; } - msleep(50); } - //mipi_dsi_set_column_addr(mfd); + mdp_set_dma_pan_info(info, dirtyPtr, - (var->activate == FB_ACTIVATE_VBL)); + (var->activate & FB_ACTIVATE_VBL)); +/* Do not refresh the first frame, because the frame's data is 0x00 */ + if (hack_lcd && is_first_frame) { + is_first_frame = FALSE; + printk("%s:Don't refresh the first frame to LCD\n",__func__); + } else { + /* async call */ mdp_dma_pan_update(info); + } + msm_fb_signal_timeline(mfd); up(&msm_fb_pan_sem); - -//fix BL can't turn on untill next frame come by wang.xiaojun3@byd.com 131219 - pan_display_flag = 1; - if (unset_bl_level && !bl_updated){ - MSM_DRM_DEBUG("call BL set schedule work\n"); + +/* Remove qcom backlight mechanism,user our own */ + if (!hack_lcd && unset_bl_level && !bl_updated) schedule_delayed_work(&mfd->backlight_worker, backlight_duration); - } - if (info->node == 0 && (mfd->cont_splash_done)) /* primary */ - mdp_free_splash_buffer(mfd); ++mfd->panel_info.frame_count; - - -MSM_FB_PAN_ERROR: - suspend_clen_state(); - - return state; + return 0; +} + +static void msm_fb_commit_wq_handler(struct work_struct *work) +{ + struct msm_fb_data_type *mfd; + struct fb_var_screeninfo *var; + struct fb_info *info; + struct msm_fb_backup_type *fb_backup; + + mfd = container_of(work, struct msm_fb_data_type, commit_work); + fb_backup = (struct msm_fb_backup_type *)mfd->msm_fb_backup; + info = &fb_backup->info; + if (fb_backup->disp_commit.flags & + MDP_DISPLAY_COMMIT_OVERLAY) { + var = &fb_backup->disp_commit.var; + msm_fb_pan_display_sub(var, info); + } else { + var = &fb_backup->disp_commit.var; + msm_fb_pan_display_sub(var, info); + } + mutex_lock(&mfd->sync_mutex); + mfd->is_committing = 0; + complete_all(&mfd->commit_comp); + mutex_unlock(&mfd->sync_mutex); + } static int msm_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + msm_fb_pan_idle(mfd); if (var->rotate != FB_ROTATE_UR) return -EINVAL; if (var->grayscale != info->var.grayscale) @@ -2125,12 +2326,6 @@ static int msm_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) if ((var->xres == 0) || (var->yres == 0)) return -EINVAL; - if ((var->xres > MAX(mfd->panel_info.xres, - mfd->panel_info.mode2_xres)) || - (var->yres > MAX(mfd->panel_info.yres, - mfd->panel_info.mode2_yres))) - return -EINVAL; - if (var->xoffset > (var->xres_virtual - var->xres)) return -EINVAL; @@ -2167,7 +2362,7 @@ static int msm_fb_set_par(struct fb_info *info) struct fb_var_screeninfo *var = &info->var; int old_imgType; int blank = 0; - + msm_fb_pan_idle(mfd); old_imgType = mfd->fb_imgType; switch (var->bits_per_pixel) { case 16: @@ -2188,7 +2383,9 @@ static int msm_fb_set_par(struct fb_info *info) break; case 32: - if (var->transp.offset == 24) + if ((var->transp.offset == 24) && (var->blue.offset == 0)) + mfd->fb_imgType = MDP_BGRA_8888; + else if (var->transp.offset == 24) mfd->fb_imgType = MDP_ARGB_8888; else mfd->fb_imgType = MDP_RGBA_8888; @@ -2214,6 +2411,10 @@ static int msm_fb_set_par(struct fb_info *info) if (blank) { msm_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable); + + if (mfd->update_panel_info) + mfd->update_panel_info(mfd); + msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable); } @@ -3163,11 +3364,17 @@ static int msmfb_overlay_play_wait(struct fb_info *info, unsigned long *argp) return ret; } +static int msmfb_overlay_commit(struct fb_info *info) +{ + return mdp4_overlay_commit(info); +} + static int msmfb_overlay_play(struct fb_info *info, unsigned long *argp) { int ret; struct msmfb_overlay_data req; struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct msm_fb_panel_data *pdata; if (mfd->overlay_play_enable == 0) /* nothing to do */ return 0; @@ -3198,10 +3405,14 @@ static int msmfb_overlay_play(struct fb_info *info, unsigned long *argp) ret = mdp4_overlay_play(info, &req); - if (unset_bl_level && !bl_updated) +/* Remove qcom backlight mechanism,user our own */ + if (!hack_lcd && unset_bl_level && !bl_updated) schedule_delayed_work(&mfd->backlight_worker, backlight_duration); + if (info->node == 0 && (mfd->cont_splash_done)) /* primary */ + mdp_free_splash_buffer(mfd); + return ret; } @@ -3322,6 +3533,29 @@ static int msmfb_overlay_ioctl_writeback_terminate(struct fb_info *info) return mdp4_writeback_terminate(info); } +static int msmfb_overlay_ioctl_writeback_set_mirr_hint(struct fb_info * + info, void *argp) +{ + int ret = 0, hint; + + if (!info) { + ret = -EINVAL; + goto error; + } + + ret = copy_from_user(&hint, argp, sizeof(hint)); + if (ret) + goto error; + + ret = mdp4_writeback_set_mirroring_hint(info, hint); + if (ret) + goto error; +error: + if (ret) + pr_err("%s: ioctl failed\n", __func__); + return ret; +} + #else static int msmfb_overlay_ioctl_writeback_init(struct fb_info *info) { @@ -3354,6 +3588,12 @@ static int msmfb_overlay_ioctl_writeback_terminate(struct fb_info *info) { return -ENOTSUPP; } + +static int msmfb_overlay_ioctl_writeback_set_mirr_hint(struct fb_info * + info, void *argp) +{ + return -ENOTSUPP; +} #endif static int msmfb_overlay_3d_sbys(struct fb_info *info, unsigned long *argp) @@ -3397,6 +3637,7 @@ static int msmfb_mixer_info(struct fb_info *info, unsigned long *argp) #endif DEFINE_SEMAPHORE(msm_fb_ioctl_ppp_sem); +DEFINE_SEMAPHORE(msm_fb_ioctl_vsync_sem); DEFINE_MUTEX(msm_fb_ioctl_lut_sem); /* Set color conversion matrix from user space */ @@ -3442,13 +3683,12 @@ static void msmfb_set_color_conv(struct mdp_csc *p) } #endif -static int msmfb_notify_update(struct fb_info *info, void __user *argp) +static int msmfb_notify_update(struct fb_info *info, unsigned long *argp) { - int ret; - unsigned int notify; + int ret, notify; struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; - ret = copy_from_user(¬ify, argp, sizeof(unsigned int)); + ret = copy_from_user(¬ify, argp, sizeof(int)); if (ret) { pr_err("%s:ioctl failed\n", __func__); return ret; @@ -3466,14 +3706,18 @@ static int msmfb_notify_update(struct fb_info *info, void __user *argp) ret = wait_for_completion_interruptible_timeout( &mfd->msmfb_no_update_notify, 4*HZ); } - return (ret > 0) ? 0 : -1; + if (ret == 0) + ret = -ETIMEDOUT; + return (ret > 0) ? 0 : ret; } static int msmfb_handle_pp_ioctl(struct msm_fb_data_type *mfd, struct msmfb_mdp_pp *pp_ptr) { int ret = -1; - +#ifdef CONFIG_FB_MSM_MDP40 + int i = 0; +#endif if (!pp_ptr) return ret; @@ -3481,6 +3725,15 @@ static int msmfb_handle_pp_ioctl(struct msm_fb_data_type *mfd, #ifdef CONFIG_FB_MSM_MDP40 case mdp_op_csc_cfg: ret = mdp4_csc_config(&(pp_ptr->data.csc_cfg_data)); + for (i = 0; i < CSC_MAX_BLOCKS; i++) { + if (pp_ptr->data.csc_cfg_data.block == + csc_cfg_matrix[i].block) { + memcpy(&csc_cfg_matrix[i].csc_data, + &(pp_ptr->data.csc_cfg_data.csc_data), + sizeof(struct mdp_csc_cfg)); + break; + } + } break; case mdp_op_pcc_cfg: @@ -3514,6 +3767,10 @@ static int msmfb_handle_pp_ioctl(struct msm_fb_data_type *mfd, ret = mdp4_qseed_cfg((struct mdp_qseed_cfg_data *) &pp_ptr->data.qseed_cfg_data); break; + case mdp_op_calib_cfg: + ret = mdp4_calib_config((struct mdp_calib_config_data *) + &pp_ptr->data.calib_cfg); + break; #endif case mdp_bl_scale_cfg: ret = mdp_bl_scale_config(mfd, (struct mdp_bl_scale_data *) @@ -3538,6 +3795,10 @@ static int msmfb_handle_metadata_ioctl(struct msm_fb_data_type *mfd, ret = mdp4_update_base_blend(mfd, &metadata_ptr->data.blend_cfg); break; + case metadata_op_wb_format: + ret = mdp4_update_writeback_format(mfd, + &metadata_ptr->data.mixer_cfg); + break; #endif default: pr_warn("Unsupported request to MDP META IOCTL.\n"); @@ -3546,10 +3807,151 @@ static int msmfb_handle_metadata_ioctl(struct msm_fb_data_type *mfd, } return ret; } + +static int msmfb_get_metadata(struct msm_fb_data_type *mfd, + struct msmfb_metadata *metadata_ptr) +{ + int ret = 0; + switch (metadata_ptr->op) { + case metadata_op_frame_rate: + metadata_ptr->data.panel_frame_rate = + mdp_get_panel_framerate(mfd); + break; + default: + pr_warn("Unsupported request to MDP META IOCTL.\n"); + ret = -EINVAL; + break; + } + return ret; +} + +static int msmfb_handle_buf_sync_ioctl(struct msm_fb_data_type *mfd, + struct mdp_buf_sync *buf_sync) +{ + int i, ret = 0; + u32 threshold; + int acq_fen_fd[MDP_MAX_FENCE_FD]; + struct sync_fence *fence; + struct sync_pt *release_sync_pt; + struct sync_pt *retire_sync_pt; + struct sync_fence *release_fence; + struct sync_fence *retire_fence; + int release_fen_fd; + int retire_fen_fd; + + if ((buf_sync->acq_fen_fd_cnt > MDP_MAX_FENCE_FD) || + (mfd->timeline == NULL)) + return -EINVAL; + + if ((!mfd->op_enable) || (!mfd->panel_power_on)) + return -EPERM; + + if (buf_sync->acq_fen_fd_cnt) + ret = copy_from_user(acq_fen_fd, buf_sync->acq_fen_fd, + buf_sync->acq_fen_fd_cnt * sizeof(int)); + if (ret) { + pr_err("%s:copy_from_user failed", __func__); + return ret; + } + mutex_lock(&mfd->sync_mutex); + for (i = 0; i < buf_sync->acq_fen_fd_cnt; i++) { + fence = sync_fence_fdget(acq_fen_fd[i]); + if (fence == NULL) { + pr_info("%s: null fence! i=%d fd=%d\n", __func__, i, + acq_fen_fd[i]); + ret = -EINVAL; + break; + } + mfd->acq_fen[i] = fence; + } + mfd->acq_fen_cnt = i; + if (ret) + goto buf_sync_err_1; + if (buf_sync->flags & MDP_BUF_SYNC_FLAG_WAIT) { + msm_fb_wait_for_fence(mfd); + } + if ((mfd->panel.type == MIPI_CMD_PANEL) || + (mfd->panel.type == WRITEBACK_PANEL)) + threshold = 1; + else + threshold = 2; + + release_fen_fd = get_unused_fd_flags(0); + if (release_fen_fd < 0) { + pr_err("%s: get_unused_fd_flags failed", __func__); + ret = -EIO; + goto buf_sync_err_1; + } + + retire_fen_fd = get_unused_fd_flags(0); + if (retire_fen_fd < 0) { + pr_err("%s: get_unused_fd_flags failed", __func__); + ret = -EIO; + goto buf_sync_err_2; + } + + release_sync_pt = sw_sync_pt_create(mfd->timeline, + mfd->timeline_value + threshold); + release_fence = sync_fence_create("mdp-fence", + release_sync_pt); + sync_fence_install(release_fence, release_fen_fd); + retire_sync_pt = sw_sync_pt_create(mfd->timeline, + mfd->timeline_value + threshold); + retire_fence = sync_fence_create("mdp-retire-fence", + retire_sync_pt); + sync_fence_install(retire_fence, retire_fen_fd); + + ret = copy_to_user(buf_sync->rel_fen_fd, + &release_fen_fd, sizeof(int)); + if (ret) { + pr_err("%s:copy_to_user failed", __func__); + goto buf_sync_err_3; + } + + ret = copy_to_user(buf_sync->retire_fen_fd, + &retire_fen_fd, sizeof(int)); + if (ret) { + pr_err("%s:copy_to_user failed", __func__); + goto buf_sync_err_3; + } + + mutex_unlock(&mfd->sync_mutex); + return ret; +buf_sync_err_3: + sync_fence_put(release_fence); + sync_fence_put(retire_fence); + put_unused_fd(retire_fen_fd); +buf_sync_err_2: + put_unused_fd(release_fen_fd); +buf_sync_err_1: + for (i = 0; i < mfd->acq_fen_cnt; i++) + sync_fence_put(mfd->acq_fen[i]); + mfd->acq_fen_cnt = 0; + mutex_unlock(&mfd->sync_mutex); + return ret; +} + +static int msmfb_display_commit(struct fb_info *info, + unsigned long *argp) +{ + int ret; + struct mdp_display_commit disp_commit; + ret = copy_from_user(&disp_commit, argp, + sizeof(disp_commit)); + if (ret) { + pr_err("%s:copy_from_user failed", __func__); + return ret; + } + + ret = msm_fb_pan_display_ex(info, &disp_commit); + + return ret; +} + static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { - struct msm_fb_data_type *mfd; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; void __user *argp = (void __user *)arg; struct fb_cursor cursor; struct fb_cmap cmap; @@ -3564,11 +3966,9 @@ static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd, struct mdp_page_protection fb_page_protection; struct msmfb_mdp_pp mdp_pp; struct msmfb_metadata mdp_metadata; + struct mdp_buf_sync buf_sync; int ret = 0; - - if (!info || !info->par) - return -EINVAL; - mfd = (struct msm_fb_data_type *)info->par; + msm_fb_pan_idle(mfd); switch (cmd) { #ifdef CONFIG_FB_MSM_OVERLAY @@ -3621,15 +4021,19 @@ static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd, case MSMFB_WRITEBACK_TERMINATE: ret = msmfb_overlay_ioctl_writeback_terminate(info); break; + case MSMFB_WRITEBACK_SET_MIRRORING_HINT: + ret = msmfb_overlay_ioctl_writeback_set_mirr_hint( + info, argp); + break; #endif case MSMFB_VSYNC_CTRL: case MSMFB_OVERLAY_VSYNC_CTRL: - down(&msm_fb_ioctl_ppp_sem); + down(&msm_fb_ioctl_vsync_sem); if (mdp_rev >= MDP_REV_40) ret = msmfb_overlay_vsync_ctrl(info, argp); else ret = msmfb_vsync_ctrl(info, argp); - up(&msm_fb_ioctl_ppp_sem); + up(&msm_fb_ioctl_vsync_sem); break; case MSMFB_BLIT: down(&msm_fb_ioctl_ppp_sem); @@ -3847,6 +4251,18 @@ static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd, return ret; ret = msmfb_handle_pp_ioctl(mfd, &mdp_pp); + if (ret == 1) + ret = copy_to_user(argp, &mdp_pp, sizeof(mdp_pp)); + break; + case MSMFB_BUFFER_SYNC: + ret = copy_from_user(&buf_sync, argp, sizeof(buf_sync)); + if (ret) + return ret; + + ret = msmfb_handle_buf_sync_ioctl(mfd, &buf_sync); + + if (!ret) + ret = copy_to_user(argp, &buf_sync, sizeof(buf_sync)); break; case MSMFB_METADATA_SET: @@ -3854,6 +4270,18 @@ static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd, if (ret) return ret; ret = msmfb_handle_metadata_ioctl(mfd, &mdp_metadata); + case MSMFB_DISPLAY_COMMIT: + ret = msmfb_display_commit(info, argp); + break; + + case MSMFB_METADATA_GET: + ret = copy_from_user(&mdp_metadata, argp, sizeof(mdp_metadata)); + if (ret) + return ret; + ret = msmfb_get_metadata(mfd, &mdp_metadata); + if (!ret) + ret = copy_to_user(argp, &mdp_metadata, + sizeof(mdp_metadata)); break; default: @@ -4117,7 +4545,7 @@ int msm_fb_v4l2_enable(struct mdp_overlay *req, bool enable, void **par) EXPORT_SYMBOL(msm_fb_v4l2_enable); /* Called by v4l2 driver to provide a frame for display */ -int msm_fb_v4l2_update(void *par, +int msm_fb_v4l2_update(void *par, bool bUserPtr, unsigned long srcp0_addr, unsigned long srcp0_size, unsigned long srcp1_addr, unsigned long srcp1_size, unsigned long srcp2_addr, unsigned long srcp2_size) @@ -4129,9 +4557,14 @@ int msm_fb_v4l2_update(void *par, srcp2_addr); #else #ifdef CONFIG_FB_MSM_MDP30 - return mdp_ppp_v4l2_overlay_play(fbi_list[0], - srcp0_addr, srcp0_size, - srcp1_addr, srcp1_size); + if (bUserPtr) + return mdp_ppp_v4l2_overlay_play(fbi_list[0], true, + srcp0_addr, srcp0_size, + srcp1_addr, srcp1_size); + else + return mdp_ppp_v4l2_overlay_play(fbi_list[0], false, + srcp0_addr, srcp0_size, + srcp1_addr, srcp1_size); #else return -EINVAL; #endif @@ -4139,4 +4572,8 @@ int msm_fb_v4l2_update(void *par, } EXPORT_SYMBOL(msm_fb_v4l2_update); +#ifdef CONFIG_FB_MSM_ALIGN_BUFFER +module_param(align_buffer, bool, 0644); +#endif + module_init(msm_fb_init); diff --git a/drivers/video/msm/msm_fb.c- b/drivers/video/msm/msm_fb.c- new file mode 100644 index 00000000..eb5a8cee --- /dev/null +++ b/drivers/video/msm/msm_fb.c- @@ -0,0 +1,4643 @@ +/* drivers/video/msm/msm_fb.c + * + * Core MSM framebuffer driver. + * + * Copyright (C) 2007 Google Incorporated + * Copyright (c) 2013 The Linux Foundation. All Rights Reserved. + * + * 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 +#include +#include +#include +#include + +#define MSM_FB_C +#include "msm_fb.h" +#include "mddihosti.h" +#include "tvenc.h" +#include "mdp.h" +#include "mdp4.h" + +#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER +#define MSM_FB_NUM 3 +#endif +#define LCM_1LED_CTRL_GPIO_PIN121 121 +#define LCD_GLANCEVIEW_SCREEN +#ifdef LCD_GLANCEVIEW_SCREEN +#include +#endif + +static unsigned char *fbram; +static unsigned char *fbram_phys; +static int fbram_size; +static boolean bf_supported; + +/* Set backlight on resume after 50 ms after first + * pan display on the panel. This is to avoid panel specific + * transients during resume. + */ +unsigned long backlight_duration = (HZ/20); + +#ifdef CONFIG_FB_MSM_ALIGN_BUFFER +static bool align_buffer = false; +#endif + +/* Set backlight on resume after 50 ms after first + * pan display on the panel. This is to avoid panel specific + * transients during resume. + */ +unsigned long backlight_duration = (HZ/20); + +/* Set backlight on resume after 50 ms after first + * pan display on the panel. This is to avoid panel specific + * transients during resume. + */ +unsigned long backlight_duration = (HZ/20); + +#ifdef CONFIG_FB_MSM_ALIGN_BUFFER +static bool align_buffer = false; +#endif + +static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; +static int pdev_list_cnt; + +int vsync_mode = 1; + +#define MAX_BLIT_REQ 256 + +#define MAX_FBI_LIST 32 +static struct fb_info *fbi_list[MAX_FBI_LIST]; +static int fbi_list_index; + +static struct msm_fb_data_type *mfd_list[MAX_FBI_LIST]; +static int mfd_list_index; + +static u32 msm_fb_pseudo_palette[16] = { + 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff +}; + +static struct ion_client *iclient; + +u32 msm_fb_debug_enabled; +/* Setting msm_fb_msg_level to 8 prints out ALL messages */ +u32 msm_fb_msg_level = 7; + +/* Setting mddi_msg_level to 8 prints out ALL messages */ +u32 mddi_msg_level = 5; + +extern int32 mdp_block_power_cnt[MDP_MAX_BLOCK]; +extern unsigned long mdp_timer_duration; + +static int msm_fb_register(struct msm_fb_data_type *mfd); +static int msm_fb_open(struct fb_info *info, int user); +static int msm_fb_release(struct fb_info *info, int user); +static int msm_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info); +static int msm_fb_stop_sw_refresher(struct msm_fb_data_type *mfd); +int msm_fb_resume_sw_refresher(struct msm_fb_data_type *mfd); +static int msm_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info); +static int msm_fb_set_par(struct fb_info *info); +static int msm_fb_blank_sub(int blank_mode, struct fb_info *info, + boolean op_enable); +static int msm_fb_suspend_sub(struct msm_fb_data_type *mfd); +static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg); +static int msm_fb_mmap(struct fb_info *info, struct vm_area_struct * vma); +static int mdp_bl_scale_config(struct msm_fb_data_type *mfd, + struct mdp_bl_scale_data *data); +static void msm_fb_scale_bl(__u32 *bl_lvl); +static void msm_fb_commit_wq_handler(struct work_struct *work); +static int msm_fb_pan_idle(struct msm_fb_data_type *mfd); +static int hack_lcd = 1; +boolean lcd_have_resume = FALSE; +boolean last_backlight_setting = FALSE; +int last_backlight_level = 0; + +static void msm_fb_commit_wq_handler(struct work_struct *work); +static int msm_fb_pan_idle(struct msm_fb_data_type *mfd); +static int hack_lcd = 1; +boolean lcd_have_resume = FALSE; +boolean last_backlight_setting = FALSE; +int last_backlight_level = 0; + + + +#ifdef MSM_FB_ENABLE_DBGFS + +#define MSM_FB_MAX_DBGFS 1024 +#define MAX_BACKLIGHT_BRIGHTNESS 255 + +#define WAIT_FENCE_FIRST_TIMEOUT (3 * MSEC_PER_SEC) +#define WAIT_FENCE_FINAL_TIMEOUT (10 * MSEC_PER_SEC) +/* Display op timeout should be greater than total timeout */ +#define WAIT_DISP_OP_TIMEOUT (WAIT_FENCE_FIRST_TIMEOUT +\ + WAIT_FENCE_FINAL_TIMEOUT) * MDP_MAX_FENCE_FD +#define MAX_TIMELINE_NAME_LEN 16 + +#define WAIT_FENCE_FIRST_TIMEOUT (3 * MSEC_PER_SEC) +#define WAIT_FENCE_FINAL_TIMEOUT (10 * MSEC_PER_SEC) +/* Display op timeout should be greater than total timeout */ +#define WAIT_DISP_OP_TIMEOUT (WAIT_FENCE_FIRST_TIMEOUT +\ + WAIT_FENCE_FINAL_TIMEOUT) * MDP_MAX_FENCE_FD +#define MAX_TIMELINE_NAME_LEN 16 + +int msm_fb_debugfs_file_index; +struct dentry *msm_fb_debugfs_root; +struct dentry *msm_fb_debugfs_file[MSM_FB_MAX_DBGFS]; +static int bl_scale, bl_min_lvl; + +DEFINE_MUTEX(msm_fb_notify_update_sem); +void msmfb_no_update_notify_timer_cb(unsigned long data) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data; + if (!mfd) + pr_err("%s mfd NULL\n", __func__); + complete(&mfd->msmfb_no_update_notify); +} + +struct dentry *msm_fb_get_debugfs_root(void) +{ + if (msm_fb_debugfs_root == NULL) + msm_fb_debugfs_root = debugfs_create_dir("msm_fb", NULL); + + return msm_fb_debugfs_root; +} + +void msm_fb_debugfs_file_create(struct dentry *root, const char *name, + u32 *var) +{ + if (msm_fb_debugfs_file_index >= MSM_FB_MAX_DBGFS) + return; + + msm_fb_debugfs_file[msm_fb_debugfs_file_index++] = + debugfs_create_u32(name, S_IRUGO | S_IWUSR, root, var); +} +#endif + +int msm_fb_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (!mfd->cursor_update) + return -ENODEV; + + return mfd->cursor_update(info, cursor); +} + +static int msm_fb_resource_initialized; + +#ifndef CONFIG_FB_BACKLIGHT +static int lcd_backlight_registered; + +static void msm_fb_set_bl_brightness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent); + int bl_lvl; + + if (value > MAX_BACKLIGHT_BRIGHTNESS) + value = MAX_BACKLIGHT_BRIGHTNESS; + + /* This maps android backlight level 0 to 255 into + driver backlight level 0 to bl_max with rounding */ + bl_lvl = value; + + if (!bl_lvl && value) + bl_lvl = 1; + + down(&mfd->sem); +/* Remove qcom backlight mechanism,user our own */ +/* If LCD don't resume ,don't turn on LCD backlight */ + if (hack_lcd) { + if(FALSE == lcd_have_resume) { + last_backlight_level = bl_lvl; + last_backlight_setting = TRUE; + } else { + msm_fb_set_backlight(mfd, bl_lvl); + last_backlight_setting = FALSE; + } + } else { + down(&mfd->sem); + msm_fb_set_backlight(mfd, bl_lvl); + up(&mfd->sem); + } +} + +static struct led_classdev backlight_led = { + .name = "lcd-backlight", + .brightness = MAX_BACKLIGHT_BRIGHTNESS, + .brightness_set = msm_fb_set_bl_brightness, +}; +#endif + +static struct msm_fb_platform_data *msm_fb_pdata; +unsigned char hdmi_prim_display; +unsigned char hdmi_prim_resolution; + +int msm_fb_detect_client(const char *name) +{ + int ret = 0; + u32 len; +#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT + u32 id; +#endif + if (!msm_fb_pdata) + return -EPERM; + + len = strnlen(name, PANEL_NAME_MAX_LEN); + if (strnlen(msm_fb_pdata->prim_panel_name, PANEL_NAME_MAX_LEN)) { + pr_err("\n name = %s, prim_display = %s", + name, msm_fb_pdata->prim_panel_name); + if (!strncmp((char *)msm_fb_pdata->prim_panel_name, + name, len)) { + if (!strncmp((char *)msm_fb_pdata->prim_panel_name, + "hdmi_msm", len)) + hdmi_prim_display = 1; + hdmi_prim_resolution = + msm_fb_pdata->ext_resolution; + return 0; + } else { + ret = -EPERM; + } + } + + if (strnlen(msm_fb_pdata->ext_panel_name, PANEL_NAME_MAX_LEN)) { + pr_err("\n name = %s, ext_display = %s", + name, msm_fb_pdata->ext_panel_name); + if (!strncmp((char *)msm_fb_pdata->ext_panel_name, name, len)) + return 0; + else + ret = -EPERM; + } + + if (ret) + return ret; + + ret = -EPERM; + if (msm_fb_pdata && msm_fb_pdata->detect_client) { + ret = msm_fb_pdata->detect_client(name); + + /* if it's non mddi panel, we need to pre-scan + mddi client to see if we can disable mddi host */ + +#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT + if (!ret && msm_fb_pdata->mddi_prescan) + id = mddi_get_client_id(); +#endif + } + + return ret; +} + +static ssize_t msm_fb_msm_fb_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct msm_fb_panel_data *pdata = + (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; + + switch (pdata->panel_info.type) { + case NO_PANEL: + ret = snprintf(buf, PAGE_SIZE, "no panel\n"); + break; + case MDDI_PANEL: + ret = snprintf(buf, PAGE_SIZE, "mddi panel\n"); + break; + case EBI2_PANEL: + ret = snprintf(buf, PAGE_SIZE, "ebi2 panel\n"); + break; + case LCDC_PANEL: + ret = snprintf(buf, PAGE_SIZE, "lcdc panel\n"); + break; + case EXT_MDDI_PANEL: + ret = snprintf(buf, PAGE_SIZE, "ext mddi panel\n"); + break; + case TV_PANEL: + ret = snprintf(buf, PAGE_SIZE, "tv panel\n"); + break; + case HDMI_PANEL: + ret = snprintf(buf, PAGE_SIZE, "hdmi panel\n"); + break; + case LVDS_PANEL: + ret = snprintf(buf, PAGE_SIZE, "lvds panel\n"); + break; + case DTV_PANEL: + ret = snprintf(buf, PAGE_SIZE, "dtv panel\n"); + break; + case MIPI_VIDEO_PANEL: + ret = snprintf(buf, PAGE_SIZE, "mipi dsi video panel\n"); + break; + case MIPI_CMD_PANEL: + ret = snprintf(buf, PAGE_SIZE, "mipi dsi cmd panel\n"); + break; + case WRITEBACK_PANEL: + ret = snprintf(buf, PAGE_SIZE, "writeback panel\n"); + break; + default: + ret = snprintf(buf, PAGE_SIZE, "unknown panel\n"); + break; + } + + return ret; +} + +static DEVICE_ATTR(msm_fb_type, S_IRUGO, msm_fb_msm_fb_type, NULL); +static struct attribute *msm_fb_attrs[] = { + &dev_attr_msm_fb_type.attr, + NULL, +}; +static struct attribute_group msm_fb_attr_group = { + .attrs = msm_fb_attrs, +}; + +static int msm_fb_create_sysfs(struct platform_device *pdev) +{ + int rc; + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + + rc = sysfs_create_group(&mfd->fbi->dev->kobj, &msm_fb_attr_group); + if (rc) + MSM_FB_ERR("%s: sysfs group creation failed, rc=%d\n", __func__, + rc); + return rc; +} +static void msm_fb_remove_sysfs(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + sysfs_remove_group(&mfd->fbi->dev->kobj, &msm_fb_attr_group); +} +static void bl_workqueue_handler(struct work_struct *work); + +static ssize_t msm_fb_xres(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = strnlen(buf, PAGE_SIZE); + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + + ret = snprintf(buf, PAGE_SIZE, "%d\n", mfd->panel_info.xres); + buf[strnlen(buf, PAGE_SIZE) + 1] = '\0'; + return ret; +} +static DEVICE_ATTR(xres, S_IRUGO, msm_fb_xres, NULL); +static struct attribute *xres_fs_attrs[] = { + &dev_attr_xres.attr, + NULL, +}; +static struct attribute_group xres_fs_attr_group = { + .attrs = xres_fs_attrs, +}; + +static ssize_t msm_fb_yres(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = strnlen(buf, PAGE_SIZE); + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + + ret = snprintf(buf, PAGE_SIZE, "%d\n", mfd->panel_info.yres); + buf[strnlen(buf, PAGE_SIZE) + 1] = '\0'; + return ret; +} +static DEVICE_ATTR(yres, S_IRUGO, msm_fb_yres, NULL); +static struct attribute *yres_fs_attrs[] = { + &dev_attr_yres.attr, + NULL, +}; +static struct attribute_group yres_fs_attr_group = { + .attrs = yres_fs_attrs, +}; + +static int msm_fb_resolution_sysfs(struct platform_device *pdev) +{ + int rc; + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + + if (!mfd) { + pr_err("%s: mfd not found\n", __func__); + return -ENODEV; + } + if (!mfd->fbi) { + pr_err("%s: mfd->fbi not found\n", __func__); + return -ENODEV; + } + if (!mfd->fbi->dev) { + pr_err("%s: mfd->fbi->dev not found\n", __func__); + return -ENODEV; + } + rc = sysfs_create_group(&mfd->fbi->dev->kobj, + &xres_fs_attr_group); + if (rc) { + pr_err("%s: sysfs group creation failed, rc=%d\n", + __func__, rc); + return rc; + } + + rc = sysfs_create_group(&mfd->fbi->dev->kobj, + &yres_fs_attr_group); + if (rc) { + pr_err("%s: sysfs group creation failed, rc=%d\n", + __func__, rc); + return rc; + } + return 0; +} + +static void bl_workqueue_handler(struct work_struct *work); +static ssize_t msm_fb_xres(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = strnlen(buf, PAGE_SIZE); + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + + ret = snprintf(buf, PAGE_SIZE, "%d\n", mfd->panel_info.xres); + buf[strnlen(buf, PAGE_SIZE) + 1] = '\0'; + return ret; +} +static DEVICE_ATTR(xres, S_IRUGO, msm_fb_xres, NULL); +static struct attribute *xres_fs_attrs[] = { + &dev_attr_xres.attr, + NULL, +}; +static struct attribute_group xres_fs_attr_group = { + .attrs = xres_fs_attrs, +}; + +static ssize_t msm_fb_yres(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = strnlen(buf, PAGE_SIZE); + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + + ret = snprintf(buf, PAGE_SIZE, "%d\n", mfd->panel_info.yres); + buf[strnlen(buf, PAGE_SIZE) + 1] = '\0'; + return ret; +} +static DEVICE_ATTR(yres, S_IRUGO, msm_fb_yres, NULL); +static struct attribute *yres_fs_attrs[] = { + &dev_attr_yres.attr, + NULL, +}; +static struct attribute_group yres_fs_attr_group = { + .attrs = yres_fs_attrs, +}; + +static int msm_fb_resolution_sysfs(struct platform_device *pdev) +{ + int rc; + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + + if (!mfd) { + pr_err("%s: mfd not found\n", __func__); + return -ENODEV; + } + if (!mfd->fbi) { + pr_err("%s: mfd->fbi not found\n", __func__); + return -ENODEV; + } + if (!mfd->fbi->dev) { + pr_err("%s: mfd->fbi->dev not found\n", __func__); + return -ENODEV; + } + rc = sysfs_create_group(&mfd->fbi->dev->kobj, + &xres_fs_attr_group); + if (rc) { + pr_err("%s: sysfs group creation failed, rc=%d\n", + __func__, rc); + return rc; + } + + rc = sysfs_create_group(&mfd->fbi->dev->kobj, + &yres_fs_attr_group); + if (rc) { + pr_err("%s: sysfs group creation failed, rc=%d\n", + __func__, rc); + return rc; + } + return 0; +} + +static void bl_workqueue_handler(struct work_struct *work); + +static ssize_t msm_fb_xres(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = strnlen(buf, PAGE_SIZE); + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + + ret = snprintf(buf, PAGE_SIZE, "%d\n", mfd->panel_info.xres); + buf[strnlen(buf, PAGE_SIZE) + 1] = '\0'; + return ret; +} +static DEVICE_ATTR(xres, S_IRUGO, msm_fb_xres, NULL); +static struct attribute *xres_fs_attrs[] = { + &dev_attr_xres.attr, + NULL, +}; +static struct attribute_group xres_fs_attr_group = { + .attrs = xres_fs_attrs, +}; + +static ssize_t msm_fb_yres(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = strnlen(buf, PAGE_SIZE); + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + + ret = snprintf(buf, PAGE_SIZE, "%d\n", mfd->panel_info.yres); + buf[strnlen(buf, PAGE_SIZE) + 1] = '\0'; + return ret; +} +static DEVICE_ATTR(yres, S_IRUGO, msm_fb_yres, NULL); +static struct attribute *yres_fs_attrs[] = { + &dev_attr_yres.attr, + NULL, +}; +static struct attribute_group yres_fs_attr_group = { + .attrs = yres_fs_attrs, +}; + +static int msm_fb_resolution_sysfs(struct platform_device *pdev) +{ + int rc; + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + + if (!mfd) { + pr_err("%s: mfd not found\n", __func__); + return -ENODEV; + } + if (!mfd->fbi) { + pr_err("%s: mfd->fbi not found\n", __func__); + return -ENODEV; + } + if (!mfd->fbi->dev) { + pr_err("%s: mfd->fbi->dev not found\n", __func__); + return -ENODEV; + } + rc = sysfs_create_group(&mfd->fbi->dev->kobj, + &xres_fs_attr_group); + if (rc) { + pr_err("%s: sysfs group creation failed, rc=%d\n", + __func__, rc); + return rc; + } + + rc = sysfs_create_group(&mfd->fbi->dev->kobj, + &yres_fs_attr_group); + if (rc) { + pr_err("%s: sysfs group creation failed, rc=%d\n", + __func__, rc); + return rc; + } + return 0; +} + +static int msm_fb_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + int rc; + int err = 0; + + MSM_FB_DEBUG("msm_fb_probe\n"); + + if ((pdev->id == 0) && (pdev->num_resources > 0)) { + msm_fb_pdata = pdev->dev.platform_data; + fbram_size = + pdev->resource[0].end - pdev->resource[0].start + 1; + fbram_phys = (char *)pdev->resource[0].start; + fbram = __va(fbram_phys); + + if (!fbram) { + printk(KERN_ERR "fbram ioremap failed!\n"); + return -ENOMEM; + } + MSM_FB_DEBUG("msm_fb_probe: phy_Addr = 0x%x virt = 0x%x\n", + (int)fbram_phys, (int)fbram); + + iclient = msm_ion_client_create(-1, pdev->name); + if (IS_ERR_OR_NULL(iclient)) { + pr_err("msm_ion_client_create() return" + " error, val %p\n", iclient); + iclient = NULL; + } + + msm_fb_resource_initialized = 1; + return 0; + } + + if (!msm_fb_resource_initialized) + return -EPERM; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + INIT_DELAYED_WORK(&mfd->backlight_worker, bl_workqueue_handler); + + INIT_DELAYED_WORK(&mfd->backlight_worker, bl_workqueue_handler); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) + return -ENOMEM; + + vsync_cntrl.dev = mfd->fbi->dev; + mfd->panel_info.frame_count = 0; + mfd->bl_level = 0; + bl_scale = 1024; + bl_min_lvl = 255; +#ifdef CONFIG_FB_MSM_OVERLAY + mfd->overlay_play_enable = 1; +#endif + + bf_supported = mdp4_overlay_borderfill_supported(); + + rc = msm_fb_register(mfd); + if (rc) + return rc; + err = pm_runtime_set_active(mfd->fbi->dev); + if (err < 0) + printk(KERN_ERR "pm_runtime: fail to set active.\n"); + pm_runtime_enable(mfd->fbi->dev); +#ifdef CONFIG_FB_BACKLIGHT + msm_fb_config_backlight(mfd); +#else + /* android supports only one lcd-backlight/lcd for now */ + if (!lcd_backlight_registered) { + if (led_classdev_register(&pdev->dev, &backlight_led)) + printk(KERN_ERR "led_classdev_register failed\n"); + else + lcd_backlight_registered = 1; + } +#endif + + pdev_list[pdev_list_cnt++] = pdev; + msm_fb_create_sysfs(pdev); + msm_fb_resolution_sysfs(pdev); + + if (mfd->timeline == NULL) { + char timeline_name[MAX_TIMELINE_NAME_LEN]; + snprintf(timeline_name, sizeof(timeline_name), + "mdp_fb_%d", mfd->index); + mfd->timeline = sw_sync_timeline_create(timeline_name); + if (mfd->timeline == NULL) { + pr_err("%s: cannot create time line", __func__); + return -ENOMEM; + } else { + mfd->timeline_value = 0; + } + } + + return 0; +} + +static int msm_fb_remove(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + MSM_FB_DEBUG("msm_fb_remove\n"); + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + msm_fb_pan_idle(mfd); + + msm_fb_remove_sysfs(pdev); + + pm_runtime_disable(mfd->fbi->dev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (msm_fb_suspend_sub(mfd)) + printk(KERN_ERR "msm_fb_remove: can't stop the device %d\n", mfd->index); + + if (mfd->channel_irq != 0) + free_irq(mfd->channel_irq, (void *)mfd); + + if (mfd->vsync_width_boundary) + vfree(mfd->vsync_width_boundary); + + if (mfd->vsync_resync_timer.function) + del_timer(&mfd->vsync_resync_timer); + + if (mfd->refresh_timer.function) + del_timer(&mfd->refresh_timer); + + if (mfd->dma_hrtimer.function) + hrtimer_cancel(&mfd->dma_hrtimer); + + if (mfd->msmfb_no_update_notify_timer.function) + del_timer(&mfd->msmfb_no_update_notify_timer); + complete(&mfd->msmfb_no_update_notify); + complete(&mfd->msmfb_update_notify); + + /* remove /dev/fb* */ + unregister_framebuffer(mfd->fbi); + +#ifdef CONFIG_FB_BACKLIGHT + /* remove /sys/class/backlight */ + backlight_device_unregister(mfd->fbi->bl_dev); +#else + if (lcd_backlight_registered) { + lcd_backlight_registered = 0; + led_classdev_unregister(&backlight_led); + } +#endif + +#ifdef MSM_FB_ENABLE_DBGFS + if (mfd->sub_dir) + debugfs_remove(mfd->sub_dir); +#endif + + return 0; +} + +#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND) +static int msm_fb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct msm_fb_data_type *mfd; + int ret = 0; + + MSM_FB_DEBUG("Enter\n"); + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + msm_fb_pan_idle(mfd); + + console_lock(); + fb_set_suspend(mfd->fbi, FBINFO_STATE_SUSPENDED); + ret = msm_fb_suspend_sub(mfd); + if (ret != 0) { + printk(KERN_ERR "msm_fb: failed to suspend! %d\n", ret); + fb_set_suspend(mfd->fbi, FBINFO_STATE_RUNNING); + } else { + pdev->dev.power.power_state = state; + } + + console_unlock(); + return ret; +} +#else +#define msm_fb_suspend NULL +#endif + +static int msm_fb_suspend_sub(struct msm_fb_data_type *mfd) +{ + int ret = 0; + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + if (mfd->msmfb_no_update_notify_timer.function) + del_timer(&mfd->msmfb_no_update_notify_timer); + complete(&mfd->msmfb_no_update_notify); + + /* + * suspend this channel + */ + mfd->suspend.sw_refreshing_enable = mfd->sw_refreshing_enable; + mfd->suspend.op_enable = mfd->op_enable; + mfd->suspend.panel_power_on = mfd->panel_power_on; + mfd->suspend.op_suspend = true; + MSM_DRM_DEBUG(" op_enable:%d,panel_power_on:%d\n", mfd->suspend.op_enable, mfd->suspend.panel_power_on); + + if (mfd->op_enable) { + ret = + msm_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi, + mfd->suspend.op_enable); + if (ret) { + MSM_FB_INFO + ("msm_fb_suspend: can't turn off display!\n"); + return ret; + } + mfd->op_enable = FALSE; + } + /* + * try to power down + */ + mdp_pipe_ctrl(MDP_MASTER_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + + /* + * detach display channel irq if there's any + * or wait until vsync-resync completes + */ + if ((mfd->dest == DISPLAY_LCD)) { + if (mfd->panel_info.lcd.vsync_enable) { + if (mfd->panel_info.lcd.hw_vsync_mode) { + if (mfd->channel_irq != 0) + disable_irq(mfd->channel_irq); + } else { + volatile boolean vh_pending; + do { + vh_pending = mfd->vsync_handler_pending; + } while (vh_pending); + } + } + } + + return 0; +} + +#ifdef CONFIG_PM +static int msm_fb_resume_sub(struct msm_fb_data_type *mfd) +{ + int ret = 0; + struct msm_fb_panel_data *pdata = NULL; + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; + + /* attach display channel irq if there's any */ + if (mfd->channel_irq != 0) + enable_irq(mfd->channel_irq); + + /* resume state var recover */ + mfd->sw_refreshing_enable = mfd->suspend.sw_refreshing_enable; + mfd->op_enable = mfd->suspend.op_enable; + MSM_DRM_DEBUG("op_enable:%d,panel_power_on:%d\n", + mfd->suspend.op_enable,mfd->suspend.panel_power_on); + + if (mfd->suspend.panel_power_on) { + if (mfd->panel_driver_on == FALSE) + msm_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi, + mfd->op_enable); + ret = + msm_fb_blank_sub(FB_BLANK_UNBLANK, mfd->fbi, + mfd->op_enable); + if (ret) + MSM_FB_INFO("msm_fb_resume: can't turn on display!\n"); + } + + mfd->suspend.op_suspend = false; + + return ret; +} +#endif + +#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND) +static int msm_fb_resume(struct platform_device *pdev) +{ + /* This resume function is called when interrupt is enabled. + */ + int ret = 0; + struct msm_fb_data_type *mfd; + + MSM_FB_DEBUG("Enter\n"); + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + msm_fb_pan_idle(mfd); + + console_lock(); + ret = msm_fb_resume_sub(mfd); + pdev->dev.power.power_state = PMSG_ON; + fb_set_suspend(mfd->fbi, FBINFO_STATE_RUNNING); + console_unlock(); + + return ret; +} +#else +#define msm_fb_resume NULL +#endif + +static int msm_fb_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: suspending...\n"); + return 0; +} + +static int msm_fb_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: resuming...\n"); + return 0; +} + +static int msm_fb_runtime_idle(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: idling...\n"); + return 0; +} + +#if (defined(CONFIG_SUSPEND) && defined(CONFIG_FB_MSM_HDMI_MSM_PANEL)) +static int msm_fb_ext_suspend(struct device *dev) +{ + struct msm_fb_data_type *mfd = dev_get_drvdata(dev); + struct msm_fb_panel_data *pdata = NULL; + int ret = 0; + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + msm_fb_pan_idle(mfd); + + pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; + if (mfd->panel_info.type == HDMI_PANEL || + mfd->panel_info.type == DTV_PANEL) { + ret = msm_fb_suspend_sub(mfd); + + /* Turn off the HPD circuitry */ + if (pdata->power_ctrl) { + MSM_FB_INFO("%s: Turning off HPD circuitry\n", + __func__); + pdata->power_ctrl(FALSE); + } + } + + return ret; +} + +static int msm_fb_ext_resume(struct device *dev) +{ + struct msm_fb_data_type *mfd = dev_get_drvdata(dev); + struct msm_fb_panel_data *pdata = NULL; + int ret = 0; + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + msm_fb_pan_idle(mfd); + pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; + if (mfd->panel_info.type == HDMI_PANEL || + mfd->panel_info.type == DTV_PANEL) { + /* Turn on the HPD circuitry */ + if (pdata->power_ctrl) { + pdata->power_ctrl(TRUE); + MSM_FB_INFO("%s: Turning on HPD circuitry\n", + __func__); + } + + ret = msm_fb_resume_sub(mfd); + } + + return ret; +} +#endif + +#ifdef LCD_GLANCEVIEW_SCREEN +int glancescreen_suspend_flag = 0; +void lcd_gv_mode_awake(struct device * dev) +{ + struct platform_device *pdev = container_of(dev, struct platform_device, dev); + struct msm_fb_data_type *mfd; + struct msm_fb_panel_data *pdata = NULL; + int ret = 0; + MSM_DRM_DEBUG("start\n"); + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + pdata = (struct msm_fb_panel_data *)dev->platform_data; + if ((!pdata) ||(!pdata->glance_screen)){ + printk(KERN_ERR "msm_fb_blank_sub: no panel operation detected!\n"); + } + + glancescreen_suspend_flag = 0; + + //pjn add to open mipi + msm_fb_resume_sub(mfd); + + ret = pdata->glance_screen(pdev); + if (ret) + printk("%s, lcd glance screen resume fail\n",__func__); + + return; +} + +void lcd_gv_mode_asleep(struct device * dev) +{ + struct platform_device *pdev = container_of(dev, struct platform_device, dev); + struct msm_fb_data_type *mfd; + struct msm_fb_panel_data *pdata = NULL; + int ret = 0; + MSM_DRM_DEBUG("start\n"); + //TODO: + /*Add low power mode under glance view mode here*/ + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + pdata = (struct msm_fb_panel_data *)dev->platform_data; + if ((!pdata) ||(!pdata->glance_screen)){ + printk(KERN_ERR "msm_fb_blank_sub: no panel operation detected!\n"); + } + + glancescreen_suspend_flag = 1; + ret = pdata->glance_screen(pdev); + if (ret) + printk("%s, lcd glance screen sleep fail\n",__func__); + + //pjn add to close mipi + msm_fb_suspend_sub(mfd); +} + +int lcd_gv_mode(struct device * dev,enum glanceview_op op) +{ + if(dev==NULL) + { + return -1; + } + switch(op){ + case gv_enable: + lcd_gv_mode_awake(dev); + break; + case gv_disable: + lcd_gv_mode_asleep(dev); + break; + default: + break; + } + + return 0; +} + +static struct glanceview_ops gv_dev= +{ + .gv_glance_mode=lcd_gv_mode, +}; +#endif +//pjn end +static struct dev_pm_ops msm_fb_dev_pm_ops = { + .runtime_suspend = msm_fb_runtime_suspend, + .runtime_resume = msm_fb_runtime_resume, + .runtime_idle = msm_fb_runtime_idle, +#if (defined(CONFIG_SUSPEND) && defined(CONFIG_FB_MSM_HDMI_MSM_PANEL)) + .suspend = msm_fb_ext_suspend, + .resume = msm_fb_ext_resume, +#endif +}; + +static struct platform_driver msm_fb_driver = { + .probe = msm_fb_probe, + .remove = msm_fb_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = msm_fb_suspend, + .resume = msm_fb_resume, +#endif + .shutdown = NULL, + .driver = { + /* Driver name must match the device name added in platform.c. */ + .name = "msm_fb", + .pm = &msm_fb_dev_pm_ops, + }, +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void msmfb_early_suspend(struct early_suspend *h) +{ + struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, + early_suspend); + + msm_fb_pan_idle(mfd); + + msm_fb_suspend_sub(mfd); +} + +static void msmfb_early_resume(struct early_suspend *h) +{ + struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, + early_suspend); + msm_fb_resume_sub(mfd); +} +#endif + +static int unset_bl_level, bl_updated; +static int bl_level_old, pan_display_flag; +static int mdp_bl_scale_config(struct msm_fb_data_type *mfd, + struct mdp_bl_scale_data *data) +{ + int ret = 0; + int curr_bl; + down(&mfd->sem); + curr_bl = mfd->bl_level; + bl_scale = data->scale; + bl_min_lvl = data->min_lvl; + pr_debug("%s: update scale = %d, min_lvl = %d\n", __func__, bl_scale, + bl_min_lvl); + + /* update current backlight to use new scaling*/ + if (mfd->panel_power_on && bl_updated) + msm_fb_set_backlight(mfd, curr_bl); + up(&mfd->sem); + return ret; +} + +static void msm_fb_scale_bl(__u32 *bl_lvl) +{ + __u32 temp = *bl_lvl; + pr_debug("%s: input = %d, scale = %d", __func__, temp, bl_scale); + if (temp >= bl_min_lvl) { + /* bl_scale is the numerator of scaling fraction (x/1024)*/ + temp = ((*bl_lvl) * bl_scale) / 1024; + + /*if less than minimum level, use min level*/ + if (temp < bl_min_lvl) + temp = bl_min_lvl; + } + pr_debug("%s: output = %d", __func__, temp); + + (*bl_lvl) = temp; +} + +void msm_fb_set_backlight(struct msm_fb_data_type *mfd, __u32 bkl_lvl) +{ + struct msm_fb_panel_data *pdata; + __u32 temp = bkl_lvl; + + if(gv_dev.state==1){ + unset_bl_level = 0; + MSM_DRM_DEBUG(":unset_bl_level = %d\n", unset_bl_level); + return; + } + if (!mfd->panel_power_on || !bl_updated) { + unset_bl_level = bkl_lvl; +//Normandy_P010154 remove to fix flash screen in charger mode by wang.xiaojun3@byd.com 131219 + if((unset_bl_level > 10) && !bl_updated && pan_display_flag) + schedule_delayed_work(&mfd->backlight_worker, backlight_duration); + MSM_DRM_DEBUG("bl_updated = %d,power_on=%d,level=%d\n", bl_updated,mfd->panel_power_on, bkl_lvl); + return; + } else { + unset_bl_level = 0; + } + + MSM_DRM_DEBUG(" %d\n", unset_bl_level); + pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; + + if ((pdata) && (pdata->set_backlight)) { + msm_fb_scale_bl(&temp); + mfd->bl_level = temp; + pdata->set_backlight(mfd); + mfd->bl_level = bkl_lvl; + bl_level_old = temp; + } +} + +static int msm_fb_blank_sub(int blank_mode, struct fb_info *info, + boolean op_enable) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct msm_fb_panel_data *pdata = NULL; + int ret = 0; + + if (!op_enable) + return -EPERM; + + pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; + if ((!pdata) || (!pdata->on) || (!pdata->off)) { + printk(KERN_ERR "msm_fb_blank_sub: no panel operation detected!\n"); + return -ENODEV; + } + + switch (blank_mode) { + case FB_BLANK_UNBLANK: + if (!mfd->panel_power_on) { + msleep(16); + ret = pdata->on(mfd->pdev); + if (ret == 0) { + down(&mfd->sem); + mfd->panel_power_on = TRUE; + up(&mfd->sem); + +/* ToDo: possible conflict with android which doesn't expect sw refresher */ +/* + if (!mfd->hw_refresh) + { + if ((ret = msm_fb_resume_sw_refresher(mfd)) != 0) + { + MSM_FB_INFO("msm_fb_blank_sub: msm_fb_resume_sw_refresher failed = %d!\n",ret); + } + } +*/ + } + } + break; + + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_NORMAL: + case FB_BLANK_POWERDOWN: + default: + if (mfd->panel_power_on) { + int curr_pwr_state; + + mfd->op_enable = FALSE; + curr_pwr_state = mfd->panel_power_on; + + down(&mfd->sem); + msm_fb_set_backlight(mfd, 0); + mfd->panel_power_on = FALSE; + bl_updated = 0; + pan_display_flag = 0; + up(&mfd->sem); + if (delayed_work_pending(&mfd->backlight_worker)) + cancel_delayed_work_sync(&mfd->backlight_worker); + + ret = pdata->off(mfd->pdev); + if (ret) + mfd->panel_power_on = curr_pwr_state; + + mfd->op_enable = TRUE; + } + break; + } + + return ret; +} + +int calc_fb_offset(struct msm_fb_data_type *mfd, struct fb_info *fbi, int bpp) +{ + struct msm_panel_info *panel_info = &mfd->panel_info; + int remainder, yres, offset; + +#ifdef CONFIG_FB_MSM_ALIGN_BUFFER + if (!align_buffer) + { + return fbi->var.xoffset * bpp + fbi->var.yoffset * fbi->fix.line_length; + } +#endif + + if (panel_info->mode2_yres != 0) { + yres = panel_info->mode2_yres; + remainder = (fbi->fix.line_length*yres) & (PAGE_SIZE - 1); + } else { + yres = panel_info->yres; + remainder = (fbi->fix.line_length*yres) & (PAGE_SIZE - 1); + } + + if (!remainder) + remainder = PAGE_SIZE; + + if (fbi->var.yoffset < yres) { + offset = (fbi->var.xoffset * bpp); + /* iBuf->buf += fbi->var.xoffset * bpp + 0 * + yres * fbi->fix.line_length; */ + } else if (fbi->var.yoffset >= yres && fbi->var.yoffset < 2 * yres) { + offset = (fbi->var.xoffset * bpp + yres * + fbi->fix.line_length + PAGE_SIZE - remainder); + } else { + offset = (fbi->var.xoffset * bpp + 2 * yres * + fbi->fix.line_length + 2 * (PAGE_SIZE - remainder)); + } + return offset; +} + +static void msm_fb_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + msm_fb_pan_idle(mfd); + cfb_fillrect(info, rect); + if (!mfd->hw_refresh && (info->var.yoffset == 0) && + !mfd->sw_currently_refreshing) { + struct fb_var_screeninfo var; + + var = info->var; + var.reserved[0] = 0x54445055; + var.reserved[1] = (rect->dy << 16) | (rect->dx); + var.reserved[2] = ((rect->dy + rect->height) << 16) | + (rect->dx + rect->width); + + msm_fb_pan_display(&var, info); + } +} + +static void msm_fb_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + msm_fb_pan_idle(mfd); + cfb_copyarea(info, area); + if (!mfd->hw_refresh && (info->var.yoffset == 0) && + !mfd->sw_currently_refreshing) { + struct fb_var_screeninfo var; + + var = info->var; + var.reserved[0] = 0x54445055; + var.reserved[1] = (area->dy << 16) | (area->dx); + var.reserved[2] = ((area->dy + area->height) << 16) | + (area->dx + area->width); + + msm_fb_pan_display(&var, info); + } +} + +static void msm_fb_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + msm_fb_pan_idle(mfd); + cfb_imageblit(info, image); + if (!mfd->hw_refresh && (info->var.yoffset == 0) && + !mfd->sw_currently_refreshing) { + struct fb_var_screeninfo var; + + var = info->var; + var.reserved[0] = 0x54445055; + var.reserved[1] = (image->dy << 16) | (image->dx); + var.reserved[2] = ((image->dy + image->height) << 16) | + (image->dx + image->width); + + msm_fb_pan_display(&var, info); + } +} + +static int msm_fb_blank(int blank_mode, struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + msm_fb_pan_idle(mfd); + if (mfd->op_enable == 0) { + if (blank_mode == FB_BLANK_UNBLANK) + mfd->suspend.panel_power_on = TRUE; + else + mfd->suspend.panel_power_on = FALSE; + } + return msm_fb_blank_sub(blank_mode, info, mfd->op_enable); +} + +static int msm_fb_set_lut(struct fb_cmap *cmap, struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (!mfd->lut_update) + return -ENODEV; + + mfd->lut_update(info, cmap); + return 0; +} + +/* + * Custom Framebuffer mmap() function for MSM driver. + * Differs from standard mmap() function by allowing for customized + * page-protection. + */ +static int msm_fb_mmap(struct fb_info *info, struct vm_area_struct * vma) +{ + /* Get frame buffer memory range. */ + unsigned long start = info->fix.smem_start; + u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); + unsigned long off = vma->vm_pgoff << PAGE_SHIFT; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + msm_fb_pan_idle(mfd); + if (off >= len) { + /* memory mapped io */ + off -= len; + if (info->var.accel_flags) { + mutex_unlock(&info->lock); + return -EINVAL; + } + start = info->fix.mmio_start; + len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len); + } + + /* Set VM flags. */ + start &= PAGE_MASK; + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + off += start; + vma->vm_pgoff = off >> PAGE_SHIFT; + /* This is an IO map - tell maydump to skip this VMA */ + vma->vm_flags |= VM_IO | VM_RESERVED; + + /* Set VM page protection */ + if (mfd->mdp_fb_page_protection == MDP_FB_PAGE_PROTECTION_WRITECOMBINE) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + else if (mfd->mdp_fb_page_protection == + MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE) + vma->vm_page_prot = pgprot_writethroughcache(vma->vm_page_prot); + else if (mfd->mdp_fb_page_protection == + MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE) + vma->vm_page_prot = pgprot_writebackcache(vma->vm_page_prot); + else if (mfd->mdp_fb_page_protection == + MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE) + vma->vm_page_prot = pgprot_writebackwacache(vma->vm_page_prot); + else + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + /* Remap the frame buffer I/O range */ + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} + +static struct fb_ops msm_fb_ops = { + .owner = THIS_MODULE, + .fb_open = msm_fb_open, + .fb_release = msm_fb_release, + .fb_read = NULL, + .fb_write = NULL, + .fb_cursor = NULL, + .fb_check_var = msm_fb_check_var, /* vinfo check */ + .fb_set_par = msm_fb_set_par, /* set the video mode according to info->var */ + .fb_setcolreg = NULL, /* set color register */ + .fb_blank = msm_fb_blank, /* blank display */ + .fb_pan_display = msm_fb_pan_display, /* pan display */ + .fb_fillrect = msm_fb_fillrect, /* Draws a rectangle */ + .fb_copyarea = msm_fb_copyarea, /* Copy data from area to another */ + .fb_imageblit = msm_fb_imageblit, /* Draws a image to the display */ + .fb_rotate = NULL, + .fb_sync = NULL, /* wait for blit idle, optional */ + .fb_ioctl = msm_fb_ioctl, /* perform fb specific ioctl (optional) */ + .fb_mmap = msm_fb_mmap, +}; + +static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp) +{ + /* The adreno GPU hardware requires that the pitch be aligned to + 32 pixels for color buffers, so for the cases where the GPU + is writing directly to fb0, the framebuffer pitch + also needs to be 32 pixel aligned */ + + if (fb_index == 0) + return ALIGN(xres, 32) * bpp; + else + return xres * bpp; +} + +static int msm_fb_register(struct msm_fb_data_type *mfd) +{ + int ret = -ENODEV; + int bpp; + struct msm_panel_info *panel_info = &mfd->panel_info; + struct fb_info *fbi = mfd->fbi; + struct fb_fix_screeninfo *fix; + struct fb_var_screeninfo *var; + int *id; + int fbram_offset; + int remainder, remainder_mode2; + + /* + * fb info initialization + */ + fix = &fbi->fix; + var = &fbi->var; + + fix->type_aux = 0; /* if type == FB_TYPE_INTERLEAVED_PLANES */ + fix->visual = FB_VISUAL_TRUECOLOR; /* True Color */ + fix->ywrapstep = 0; /* No support */ + fix->mmio_start = 0; /* No MMIO Address */ + fix->mmio_len = 0; /* No MMIO Address */ + fix->accel = FB_ACCEL_NONE;/* FB_ACCEL_MSM needes to be added in fb.h */ + + var->xoffset = 0, /* Offset from virtual to visible */ + var->yoffset = 0, /* resolution */ + var->grayscale = 0, /* No graylevels */ + var->nonstd = 0, /* standard pixel format */ + var->activate = FB_ACTIVATE_VBL, /* activate it at vsync */ + var->height = -1, /* height of picture in mm */ + var->width = -1, /* width of picture in mm */ + var->accel_flags = 0, /* acceleration flags */ + var->sync = 0, /* see FB_SYNC_* */ + var->rotate = 0, /* angle we rotate counter clockwise */ + mfd->op_enable = FALSE; + + switch (mfd->fb_imgType) { + case MDP_RGB_565: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 0; + var->green.offset = 5; + var->red.offset = 11; + var->blue.length = 5; + var->green.length = 6; + var->red.length = 5; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 0; + bpp = 2; + break; + + case MDP_RGB_888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 0; + var->green.offset = 8; + var->red.offset = 16; + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 0; + bpp = 3; + break; + + case MDP_ARGB_8888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 0; + var->green.offset = 8; + var->red.offset = 16; + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 24; + var->transp.length = 8; + bpp = 4; + break; + + case MDP_RGBA_8888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 8; + var->green.offset = 16; + var->red.offset = 24; + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 8; + bpp = 4; + break; + + case MDP_BGRA_8888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 0; + var->green.offset = 8; + var->red.offset = 16; + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 24; + var->transp.length = 8; + bpp = 4; + break; + + + case MDP_YCRYCB_H2V1: + /* ToDo: need to check TV-Out YUV422i framebuffer format */ + /* we might need to create new type define */ + fix->type = FB_TYPE_INTERLEAVED_PLANES; + fix->xpanstep = 2; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + + /* how about R/G/B offset? */ + var->blue.offset = 0; + var->green.offset = 5; + var->red.offset = 11; + var->blue.length = 5; + var->green.length = 6; + var->red.length = 5; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 0; + bpp = 2; + break; + + default: + MSM_FB_ERR("msm_fb_init: fb %d unkown image type!\n", + mfd->index); + return ret; + } + + fix->type = panel_info->is_3d_panel; + + fix->line_length = msm_fb_line_length(mfd->index, panel_info->xres, + bpp); + + /* Make sure all buffers can be addressed on a page boundary by an x + * and y offset */ + + remainder = (fix->line_length * panel_info->yres) & (PAGE_SIZE - 1); + /* PAGE_SIZE is a power of 2 */ + if (!remainder) + remainder = PAGE_SIZE; + remainder_mode2 = (fix->line_length * + panel_info->mode2_yres) & (PAGE_SIZE - 1); + if (!remainder_mode2) + remainder_mode2 = PAGE_SIZE; + + /* + * calculate smem_len based on max size of two supplied modes. + * Only fb0 has mem. fb1 and fb2 don't have mem. + */ + if (!bf_supported || mfd->index == 0) + fix->smem_len = MAX((msm_fb_line_length(mfd->index, + panel_info->xres, + bpp) * + panel_info->yres + PAGE_SIZE - + remainder) * mfd->fb_page, + (msm_fb_line_length(mfd->index, + panel_info->mode2_xres, + bpp) * + panel_info->mode2_yres + PAGE_SIZE - + remainder_mode2) * mfd->fb_page); + else if (mfd->index == 1 || mfd->index == 2) { + pr_debug("%s:%d no memory is allocated for fb%d!\n", + __func__, __LINE__, mfd->index); + fix->smem_len = 0; + } + + mfd->var_xres = panel_info->xres; + mfd->var_yres = panel_info->yres; + mfd->var_frame_rate = panel_info->frame_rate; + + var->pixclock = mfd->panel_info.clk_rate; + mfd->var_pixclock = var->pixclock; + + var->xres = panel_info->xres; + var->yres = panel_info->yres; + var->xres_virtual = panel_info->xres; + var->yres_virtual = panel_info->yres * mfd->fb_page + + ((PAGE_SIZE - remainder)/fix->line_length) * mfd->fb_page; + var->bits_per_pixel = bpp * 8; /* FrameBuffer color depth */ + if (mfd->dest == DISPLAY_LCD) { + if (panel_info->type == MDDI_PANEL && panel_info->mddi.is_type1) + var->reserved[4] = panel_info->lcd.refx100 / (100 * 2); + else + var->reserved[4] = 60;//panel_info->lcd.refx100 / 100; + } else { + if (panel_info->type == MIPI_VIDEO_PANEL) { + var->reserved[4] = panel_info->mipi.frame_rate; + } else { + var->reserved[4] = panel_info->clk_rate / + ((panel_info->lcdc.h_back_porch + + panel_info->lcdc.h_front_porch + + panel_info->lcdc.h_pulse_width + + panel_info->xres) * + (panel_info->lcdc.v_back_porch + + panel_info->lcdc.v_front_porch + + panel_info->lcdc.v_pulse_width + + panel_info->yres)); + } + } + pr_debug("reserved[4] %u\n", var->reserved[4]); + + /* + * id field for fb app + */ + id = (int *)&mfd->panel; + + switch (mdp_rev) { + case MDP_REV_20: + snprintf(fix->id, sizeof(fix->id), "msmfb20_%x", (__u32) *id); + break; + case MDP_REV_22: + snprintf(fix->id, sizeof(fix->id), "msmfb22_%x", (__u32) *id); + break; + case MDP_REV_30: + snprintf(fix->id, sizeof(fix->id), "msmfb30_%x", (__u32) *id); + break; + case MDP_REV_303: + snprintf(fix->id, sizeof(fix->id), "msmfb303_%x", (__u32) *id); + break; + case MDP_REV_31: + snprintf(fix->id, sizeof(fix->id), "msmfb31_%x", (__u32) *id); + break; + case MDP_REV_40: + snprintf(fix->id, sizeof(fix->id), "msmfb40_%x", (__u32) *id); + break; + case MDP_REV_41: + snprintf(fix->id, sizeof(fix->id), "msmfb41_%x", (__u32) *id); + break; + case MDP_REV_42: + snprintf(fix->id, sizeof(fix->id), "msmfb42_%x", (__u32) *id); + break; + case MDP_REV_43: + snprintf(fix->id, sizeof(fix->id), "msmfb43_%x", (__u32) *id); + break; + case MDP_REV_44: + snprintf(fix->id, sizeof(fix->id), "msmfb44_%x", (__u32) *id); + break; + default: + snprintf(fix->id, sizeof(fix->id), "msmfb0_%x", (__u32) *id); + break; + } + + fbi->fbops = &msm_fb_ops; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->pseudo_palette = msm_fb_pseudo_palette; + + mfd->ref_cnt = 0; + mfd->sw_currently_refreshing = FALSE; + mfd->sw_refreshing_enable = TRUE; + mfd->panel_power_on = FALSE; + + mfd->pan_waiting = FALSE; + init_completion(&mfd->pan_comp); + init_completion(&mfd->refresher_comp); + sema_init(&mfd->sem, 1); + + init_timer(&mfd->msmfb_no_update_notify_timer); + mfd->msmfb_no_update_notify_timer.function = + msmfb_no_update_notify_timer_cb; + mfd->msmfb_no_update_notify_timer.data = (unsigned long)mfd; + init_completion(&mfd->msmfb_update_notify); + init_completion(&mfd->msmfb_no_update_notify); + init_completion(&mfd->commit_comp); + mutex_init(&mfd->sync_mutex); + INIT_WORK(&mfd->commit_work, msm_fb_commit_wq_handler); + mfd->msm_fb_backup = kzalloc(sizeof(struct msm_fb_backup_type), + GFP_KERNEL); + if (mfd->msm_fb_backup == 0) { + pr_err("error: not enough memory!\n"); + return -ENOMEM; + } + fbram_offset = PAGE_ALIGN((int)fbram)-(int)fbram; + fbram += fbram_offset; + fbram_phys += fbram_offset; + fbram_size -= fbram_offset; + + if (!bf_supported || mfd->index == 0) + if (fbram_size < fix->smem_len) { + pr_err("error: no more framebuffer memory!\n"); + return -ENOMEM; + } + + fbi->screen_base = fbram; + fbi->fix.smem_start = (unsigned long)fbram_phys; + + memset(fbi->screen_base, 0x0, fix->smem_len); + + msm_iommu_map_contig_buffer(fbi->fix.smem_start, + DISPLAY_WRITE_DOMAIN, + GEN_POOL, + fbi->fix.smem_len, + SZ_4K, + 0, + &(mfd->display_iova)); + + msm_iommu_map_contig_buffer(fbi->fix.smem_start, + DISPLAY_READ_DOMAIN, + GEN_POOL, + fbi->fix.smem_len, + SZ_4K, + 0, + &(mfd->display_iova)); + + msm_iommu_map_contig_buffer(fbi->fix.smem_start, + ROTATOR_SRC_DOMAIN, + GEN_POOL, + fbi->fix.smem_len, + SZ_4K, + 0, + &(mfd->rotator_iova)); + + if (!bf_supported || mfd->index == 0) + memset(fbi->screen_base, 0x0, fix->smem_len); + + mfd->op_enable = TRUE; + mfd->panel_power_on = FALSE; + + /* cursor memory allocation */ + if (mfd->cursor_update) { + unsigned long cursor_buf_iommu = 0; + mfd->cursor_buf = dma_alloc_coherent(NULL, + MDP_CURSOR_SIZE, + (dma_addr_t *) &mfd->cursor_buf_phys, + GFP_KERNEL); + + msm_iommu_map_contig_buffer((unsigned long)mfd->cursor_buf_phys, + DISPLAY_READ_DOMAIN, + GEN_POOL, + MDP_CURSOR_SIZE, + SZ_4K, + 0, + &cursor_buf_iommu); + if (cursor_buf_iommu) + mfd->cursor_buf_phys = (void *)cursor_buf_iommu; + + if (!mfd->cursor_buf) + mfd->cursor_update = 0; + } + + if (mfd->lut_update) { + ret = fb_alloc_cmap(&fbi->cmap, 256, 0); + if (ret) + printk(KERN_ERR "%s: fb_alloc_cmap() failed!\n", + __func__); + } + + if (register_framebuffer(fbi) < 0) { + if (mfd->lut_update) + fb_dealloc_cmap(&fbi->cmap); + + if (mfd->cursor_buf) + dma_free_coherent(NULL, + MDP_CURSOR_SIZE, + mfd->cursor_buf, + (dma_addr_t) mfd->cursor_buf_phys); + + mfd->op_enable = FALSE; + return -EPERM; + } + + fbram += fix->smem_len; + fbram_phys += fix->smem_len; + fbram_size -= fix->smem_len; + + MSM_FB_INFO + ("FrameBuffer[%d] %dx%d size=%d bytes is registered successfully!\n", + mfd->index, fbi->var.xres, fbi->var.yres, fbi->fix.smem_len); + +#ifdef CONFIG_FB_MSM_LOGO + /* Flip buffer */ + if (!load_565rle_image(INIT_IMAGE_FILE, bf_supported)) + ; +#endif + ret = 0; + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (mfd->panel_info.type != DTV_PANEL) { + mfd->early_suspend.suspend = msmfb_early_suspend; + mfd->early_suspend.resume = msmfb_early_resume; + mfd->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 2; + register_early_suspend(&mfd->early_suspend); + } +#endif + +//pjn add to register glanceview +#ifdef LCD_GLANCEVIEW_SCREEN +glanceview_register(gv_device_id_lcd, &gv_dev, &mfd->pdev->dev); +#endif +#ifdef MSM_FB_ENABLE_DBGFS + { + struct dentry *root; + struct dentry *sub_dir; + char sub_name[2]; + + root = msm_fb_get_debugfs_root(); + if (root != NULL) { + sub_name[0] = (char)(mfd->index + 0x30); + sub_name[1] = '\0'; + sub_dir = debugfs_create_dir(sub_name, root); + } else { + sub_dir = NULL; + } + + mfd->sub_dir = sub_dir; + + if (sub_dir) { + msm_fb_debugfs_file_create(sub_dir, "op_enable", + (u32 *) &mfd->op_enable); + msm_fb_debugfs_file_create(sub_dir, "panel_power_on", + (u32 *) &mfd-> + panel_power_on); + msm_fb_debugfs_file_create(sub_dir, "ref_cnt", + (u32 *) &mfd->ref_cnt); + msm_fb_debugfs_file_create(sub_dir, "fb_imgType", + (u32 *) &mfd->fb_imgType); + msm_fb_debugfs_file_create(sub_dir, + "sw_currently_refreshing", + (u32 *) &mfd-> + sw_currently_refreshing); + msm_fb_debugfs_file_create(sub_dir, + "sw_refreshing_enable", + (u32 *) &mfd-> + sw_refreshing_enable); + + msm_fb_debugfs_file_create(sub_dir, "xres", + (u32 *) &mfd->panel_info. + xres); + msm_fb_debugfs_file_create(sub_dir, "yres", + (u32 *) &mfd->panel_info. + yres); + msm_fb_debugfs_file_create(sub_dir, "bpp", + (u32 *) &mfd->panel_info. + bpp); + msm_fb_debugfs_file_create(sub_dir, "type", + (u32 *) &mfd->panel_info. + type); + msm_fb_debugfs_file_create(sub_dir, "wait_cycle", + (u32 *) &mfd->panel_info. + wait_cycle); + msm_fb_debugfs_file_create(sub_dir, "pdest", + (u32 *) &mfd->panel_info. + pdest); + msm_fb_debugfs_file_create(sub_dir, "backbuff", + (u32 *) &mfd->panel_info. + fb_num); + msm_fb_debugfs_file_create(sub_dir, "clk_rate", + (u32 *) &mfd->panel_info. + clk_rate); + msm_fb_debugfs_file_create(sub_dir, "frame_count", + (u32 *) &mfd->panel_info. + frame_count); + + + switch (mfd->dest) { + case DISPLAY_LCD: + msm_fb_debugfs_file_create(sub_dir, + "vsync_enable", + (u32 *)&mfd->panel_info.lcd.vsync_enable); + msm_fb_debugfs_file_create(sub_dir, + "refx100", + (u32 *) &mfd->panel_info.lcd. refx100); + msm_fb_debugfs_file_create(sub_dir, + "v_back_porch", + (u32 *) &mfd->panel_info.lcd.v_back_porch); + msm_fb_debugfs_file_create(sub_dir, + "v_front_porch", + (u32 *) &mfd->panel_info.lcd.v_front_porch); + msm_fb_debugfs_file_create(sub_dir, + "v_pulse_width", + (u32 *) &mfd->panel_info.lcd.v_pulse_width); + msm_fb_debugfs_file_create(sub_dir, + "hw_vsync_mode", + (u32 *) &mfd->panel_info.lcd.hw_vsync_mode); + msm_fb_debugfs_file_create(sub_dir, + "vsync_notifier_period", (u32 *) + &mfd->panel_info.lcd.vsync_notifier_period); + break; + + case DISPLAY_LCDC: + msm_fb_debugfs_file_create(sub_dir, + "h_back_porch", + (u32 *) &mfd->panel_info.lcdc.h_back_porch); + msm_fb_debugfs_file_create(sub_dir, + "h_front_porch", + (u32 *) &mfd->panel_info.lcdc.h_front_porch); + msm_fb_debugfs_file_create(sub_dir, + "h_pulse_width", + (u32 *) &mfd->panel_info.lcdc.h_pulse_width); + msm_fb_debugfs_file_create(sub_dir, + "v_back_porch", + (u32 *) &mfd->panel_info.lcdc.v_back_porch); + msm_fb_debugfs_file_create(sub_dir, + "v_front_porch", + (u32 *) &mfd->panel_info.lcdc.v_front_porch); + msm_fb_debugfs_file_create(sub_dir, + "v_pulse_width", + (u32 *) &mfd->panel_info.lcdc.v_pulse_width); + msm_fb_debugfs_file_create(sub_dir, + "border_clr", + (u32 *) &mfd->panel_info.lcdc.border_clr); + msm_fb_debugfs_file_create(sub_dir, + "underflow_clr", + (u32 *) &mfd->panel_info.lcdc.underflow_clr); + msm_fb_debugfs_file_create(sub_dir, + "hsync_skew", + (u32 *) &mfd->panel_info.lcdc.hsync_skew); + break; + + default: + break; + } + } + } +#endif /* MSM_FB_ENABLE_DBGFS */ + + return ret; +} + +static int msm_fb_open(struct fb_info *info, int user) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + bool unblank = true; + int result; + + result = pm_runtime_get_sync(info->dev); + + if (result < 0) { + printk(KERN_ERR "pm_runtime: fail to wake up\n"); + } + + if (info->node == 0 && !(mfd->cont_splash_done)) { /* primary */ + mfd->ref_cnt++; + return 0; + } + + if (!mfd->ref_cnt) { + if (!bf_supported || + (info->node != 1 && info->node != 2)) + mdp_set_dma_pan_info(info, NULL, TRUE); + else + pr_debug("%s:%d no mdp_set_dma_pan_info %d\n", + __func__, __LINE__, info->node); + + if (mfd->is_panel_ready && !mfd->is_panel_ready()) + unblank = false; + + if (unblank) { + if (msm_fb_blank_sub(FB_BLANK_UNBLANK, + info, TRUE)) { + MSM_FB_ERR("%s: can't turn on display!\n", + __func__); + return -EPERM; + } + } + } + + mfd->ref_cnt++; + return 0; +} + +static int msm_fb_release(struct fb_info *info, int user) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int ret = 0; + + if (!mfd->ref_cnt) { + MSM_FB_INFO("msm_fb_release: try to close unopened fb %d!\n", + mfd->index); + return -EINVAL; + } + msm_fb_pan_idle(mfd); + mfd->ref_cnt--; + + if ((!mfd->ref_cnt) && (mfd->op_enable)) { + if ((ret = + msm_fb_blank_sub(FB_BLANK_POWERDOWN, info, + mfd->op_enable)) != 0) { + printk(KERN_ERR "msm_fb_release: can't turn off display!\n"); + return ret; + } + } + + pm_runtime_put(info->dev); + return ret; +} + +DEFINE_SEMAPHORE(msm_fb_pan_sem); + +static void bl_workqueue_handler(struct work_struct *work) +{ + struct msm_fb_data_type *mfd = container_of(to_delayed_work(work), + struct msm_fb_data_type, backlight_worker); + struct msm_fb_panel_data *pdata = mfd->pdev->dev.platform_data; + + down(&mfd->sem); + if ((pdata) && (pdata->set_backlight) && (!bl_updated) + && mfd->panel_power_on) { + mfd->bl_level = unset_bl_level; + printk("%s,backlight=%d\n",__func__,unset_bl_level); + pdata->set_backlight(mfd); + bl_level_old = unset_bl_level; + bl_updated = 1; + } + up(&mfd->sem); +} + +static int msm_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct mdp_dirty_region dirty; + struct mdp_dirty_region *dirtyPtr = NULL; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + static int notify_cnt = 0; + int state = 0; + if (!suspend_set_state()) + { + printk("%s,suspend_status is true\n",__func__); + state= -EPERM; + return state; + + } + + /* + * If framebuffer is 2, io pen display is not allowed. + */ + if (bf_supported && info->node == 2) { + pr_err("%s: no pan display for fb%d!", + __func__, info->node); + //return -EPERM; + state= -EPERM; + goto MSM_FB_PAN_ERROR; + } + + if (info->node != 0 || mfd->cont_splash_done) /* primary */ + if ((!mfd->op_enable) || (!mfd->panel_power_on)) + { + //return -EPERM; + state= -EPERM; + goto MSM_FB_PAN_ERROR; + } + + if (var->xoffset > (info->var.xres_virtual - info->var.xres)) + { + //return -EINVAL; + state= -EINVAL; + goto MSM_FB_PAN_ERROR; + } + + if (var->yoffset > (info->var.yres_virtual - info->var.yres)) + { + //return -EINVAL; + state= -EINVAL; + goto MSM_FB_PAN_ERROR; + } + + if (info->fix.xpanstep) + info->var.xoffset = + (var->xoffset / info->fix.xpanstep) * info->fix.xpanstep; + + if (info->fix.ypanstep) + info->var.yoffset = + (var->yoffset / info->fix.ypanstep) * info->fix.ypanstep; + + /* "UPDT" */ + if (var->reserved[0] == 0x54445055) { + + dirty.xoffset = var->reserved[1] & 0xffff; + dirty.yoffset = (var->reserved[1] >> 16) & 0xffff; + + if ((var->reserved[2] & 0xffff) <= dirty.xoffset) + { + //return -EINVAL; + state= -EINVAL; + goto MSM_FB_PAN_ERROR; + } + if (((var->reserved[2] >> 16) & 0xffff) <= dirty.yoffset) + { + //return -EINVAL; + state= -EINVAL; + goto MSM_FB_PAN_ERROR; + } + + dirty.width = (var->reserved[2] & 0xffff) - dirty.xoffset; + dirty.height = + ((var->reserved[2] >> 16) & 0xffff) - dirty.yoffset; + info->var.yoffset = var->yoffset; + + if (dirty.xoffset < 0) + { + //return -EINVAL; + state= -EINVAL; + goto MSM_FB_PAN_ERROR; + } + + if (dirty.yoffset < 0) + { + //return -EINVAL; + state= -EINVAL; + goto MSM_FB_PAN_ERROR; + } + + if ((dirty.xoffset + dirty.width) > info->var.xres) + { + //return -EINVAL; + state= -EINVAL; + goto MSM_FB_PAN_ERROR; + } + + if ((dirty.yoffset + dirty.height) > info->var.yres) + { + //return -EINVAL; + state= -EINVAL; + goto MSM_FB_PAN_ERROR; + } + + if ((dirty.width <= 0) || (dirty.height <= 0)) + { + //return -EINVAL; + state= -EINVAL; + goto MSM_FB_PAN_ERROR; + } + dirtyPtr = &dirty; + } + /* FIXME: reduce fb change notification time to ease mess backlight configuration */ + if (notify_cnt >= 6) { + notify_cnt = 0; + complete(&mfd->msmfb_update_notify); + } else + notify_cnt++; + mutex_lock(&msm_fb_notify_update_sem); + if (mfd->msmfb_no_update_notify_timer.function) + del_timer(&mfd->msmfb_no_update_notify_timer); + + mfd->msmfb_no_update_notify_timer.expires = jiffies + (2 * HZ); + add_timer(&mfd->msmfb_no_update_notify_timer); + mutex_unlock(&msm_fb_notify_update_sem); + + down(&msm_fb_pan_sem); + + if (info->node == 0 && !(mfd->cont_splash_done)) { /* primary */ + mdp_set_dma_pan_info(info, NULL, TRUE); + if (msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable)) { + pr_err("%s: can't turn on display!\n", __func__); + up(&msm_fb_pan_sem); + //return -EINVAL; + state= -EINVAL; + goto MSM_FB_PAN_ERROR; + } + msleep(50); + } + //mipi_dsi_set_column_addr(mfd); + mdp_set_dma_pan_info(info, dirtyPtr, + (var->activate == FB_ACTIVATE_VBL)); + mdp_dma_pan_update(info); + up(&msm_fb_pan_sem); + +//fix BL can't turn on untill next frame come by wang.xiaojun3@byd.com 131219 + pan_display_flag = 1; + if (unset_bl_level && !bl_updated){ + MSM_DRM_DEBUG("call BL set schedule work\n"); + schedule_delayed_work(&mfd->backlight_worker, + backlight_duration); + } + if (info->node == 0 && (mfd->cont_splash_done)) /* primary */ + mdp_free_splash_buffer(mfd); + + ++mfd->panel_info.frame_count; + + +MSM_FB_PAN_ERROR: + suspend_clen_state(); + + return state; +} + +static int msm_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (var->rotate != FB_ROTATE_UR) + return -EINVAL; + if (var->grayscale != info->var.grayscale) + return -EINVAL; + + switch (var->bits_per_pixel) { + case 16: + if ((var->green.offset != 5) || + !((var->blue.offset == 11) + || (var->blue.offset == 0)) || + !((var->red.offset == 11) + || (var->red.offset == 0)) || + (var->blue.length != 5) || + (var->green.length != 6) || + (var->red.length != 5) || + (var->blue.msb_right != 0) || + (var->green.msb_right != 0) || + (var->red.msb_right != 0) || + (var->transp.offset != 0) || + (var->transp.length != 0)) + return -EINVAL; + break; + + case 24: + if ((var->blue.offset != 0) || + (var->green.offset != 8) || + (var->red.offset != 16) || + (var->blue.length != 8) || + (var->green.length != 8) || + (var->red.length != 8) || + (var->blue.msb_right != 0) || + (var->green.msb_right != 0) || + (var->red.msb_right != 0) || + !(((var->transp.offset == 0) && + (var->transp.length == 0)) || + ((var->transp.offset == 24) && + (var->transp.length == 8)))) + return -EINVAL; + break; + + case 32: + /* Figure out if the user meant RGBA or ARGB + and verify the position of the RGB components */ + + if (var->transp.offset == 24) { + if ((var->blue.offset != 0) || + (var->green.offset != 8) || + (var->red.offset != 16)) + return -EINVAL; + } else if (var->transp.offset == 0) { + if ((var->blue.offset != 8) || + (var->green.offset != 16) || + (var->red.offset != 24)) + return -EINVAL; + } else + return -EINVAL; + + /* Check the common values for both RGBA and ARGB */ + + if ((var->blue.length != 8) || + (var->green.length != 8) || + (var->red.length != 8) || + (var->transp.length != 8) || + (var->blue.msb_right != 0) || + (var->green.msb_right != 0) || + (var->red.msb_right != 0)) + return -EINVAL; + + break; + + default: + return -EINVAL; + } + + if ((var->xres_virtual <= 0) || (var->yres_virtual <= 0)) + return -EINVAL; + + if (!bf_supported || + (info->node != 1 && info->node != 2)) + if (info->fix.smem_len < + (var->xres_virtual* + var->yres_virtual* + (var->bits_per_pixel/8))) + return -EINVAL; + + if ((var->xres == 0) || (var->yres == 0)) + return -EINVAL; + + if (var->xoffset > (var->xres_virtual - var->xres)) + return -EINVAL; + + if (var->yoffset > (var->yres_virtual - var->yres)) + return -EINVAL; + + return 0; +} + +int msm_fb_check_frame_rate(struct msm_fb_data_type *mfd + , struct fb_info *info) +{ + int panel_height, panel_width, var_frame_rate, fps_mod; + struct fb_var_screeninfo *var = &info->var; + fps_mod = 0; + if ((mfd->panel_info.type == DTV_PANEL) || + (mfd->panel_info.type == HDMI_PANEL)) { + panel_height = var->yres + var->upper_margin + + var->vsync_len + var->lower_margin; + panel_width = var->xres + var->right_margin + + var->hsync_len + var->left_margin; + var_frame_rate = ((var->pixclock)/(panel_height * panel_width)); + if (mfd->var_frame_rate != var_frame_rate) { + fps_mod = 1; + mfd->var_frame_rate = var_frame_rate; + } + } + return fps_mod; +} + +static int msm_fb_set_par(struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct fb_var_screeninfo *var = &info->var; + int old_imgType; + int blank = 0; + msm_fb_pan_idle(mfd); + old_imgType = mfd->fb_imgType; + switch (var->bits_per_pixel) { + case 16: + if (var->red.offset == 0) + mfd->fb_imgType = MDP_BGR_565; + else + mfd->fb_imgType = MDP_RGB_565; + break; + + case 24: + if ((var->transp.offset == 0) && (var->transp.length == 0)) + mfd->fb_imgType = MDP_RGB_888; + else if ((var->transp.offset == 24) && + (var->transp.length == 8)) { + mfd->fb_imgType = MDP_ARGB_8888; + info->var.bits_per_pixel = 32; + } + break; + + case 32: + if ((var->transp.offset == 24) && (var->blue.offset == 0)) + mfd->fb_imgType = MDP_BGRA_8888; + else if (var->transp.offset == 24) + mfd->fb_imgType = MDP_ARGB_8888; + else + mfd->fb_imgType = MDP_RGBA_8888; + break; + + default: + return -EINVAL; + } + + if ((mfd->var_pixclock != var->pixclock) || + (mfd->hw_refresh && ((mfd->fb_imgType != old_imgType) || + (mfd->var_pixclock != var->pixclock) || + (mfd->var_xres != var->xres) || + (mfd->var_yres != var->yres) || + (msm_fb_check_frame_rate(mfd, info))))) { + mfd->var_xres = var->xres; + mfd->var_yres = var->yres; + mfd->var_pixclock = var->pixclock; + blank = 1; + } + mfd->fbi->fix.line_length = msm_fb_line_length(mfd->index, var->xres, + var->bits_per_pixel/8); + + if (blank) { + msm_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable); + + if (mfd->update_panel_info) + mfd->update_panel_info(mfd); + + msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable); + } + + return 0; +} + +static int msm_fb_stop_sw_refresher(struct msm_fb_data_type *mfd) +{ + if (mfd->hw_refresh) + return -EPERM; + + if (mfd->sw_currently_refreshing) { + down(&mfd->sem); + mfd->sw_currently_refreshing = FALSE; + up(&mfd->sem); + + /* wait until the refresher finishes the last job */ + wait_for_completion_killable(&mfd->refresher_comp); + } + + return 0; +} + +int msm_fb_resume_sw_refresher(struct msm_fb_data_type *mfd) +{ + boolean do_refresh; + + if (mfd->hw_refresh) + return -EPERM; + + down(&mfd->sem); + if ((!mfd->sw_currently_refreshing) && (mfd->sw_refreshing_enable)) { + do_refresh = TRUE; + mfd->sw_currently_refreshing = TRUE; + } else { + do_refresh = FALSE; + } + up(&mfd->sem); + + if (do_refresh) + mdp_refresh_screen((unsigned long)mfd); + + return 0; +} + +#if defined CONFIG_FB_MSM_MDP31 +static int mdp_blit_split_height(struct fb_info *info, + struct mdp_blit_req *req) +{ + int ret; + struct mdp_blit_req splitreq; + int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1; + int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1; + + splitreq = *req; + /* break dest roi at height*/ + d_x_0 = d_x_1 = req->dst_rect.x; + d_w_0 = d_w_1 = req->dst_rect.w; + d_y_0 = req->dst_rect.y; + if (req->dst_rect.h % 32 == 3) + d_h_1 = (req->dst_rect.h - 3) / 2 - 1; + else if (req->dst_rect.h % 32 == 2) + d_h_1 = (req->dst_rect.h - 2) / 2 - 6; + else + d_h_1 = (req->dst_rect.h - 1) / 2 - 1; + d_h_0 = req->dst_rect.h - d_h_1; + d_y_1 = d_y_0 + d_h_0; + if (req->dst_rect.h == 3) { + d_h_1 = 2; + d_h_0 = 2; + d_y_1 = d_y_0 + 1; + } + + /* blit first region */ + if (((splitreq.flags & 0x07) == 0x04) || + ((splitreq.flags & 0x07) == 0x0)) { + + if (splitreq.flags & MDP_ROT_90) { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_h_1) / req->dst_rect.h; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_h_1 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } else { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_h_1) / req->dst_rect.h; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_h_1 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } + + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } else { + + if (splitreq.flags & MDP_ROT_90) { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_h_0) / req->dst_rect.h; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_h_0 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } else { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_h_0) / req->dst_rect.h; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_h_0 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } + ret = mdp_ppp_blit(info, &splitreq); + if (ret) + return ret; + + /* blit second region */ + if (((splitreq.flags & 0x07) == 0x04) || + ((splitreq.flags & 0x07) == 0x0)) { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } else { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } + ret = mdp_ppp_blit(info, &splitreq); + return ret; +} +#endif + +int mdp_blit(struct fb_info *info, struct mdp_blit_req *req) +{ + int ret; +#if defined CONFIG_FB_MSM_MDP31 || defined CONFIG_FB_MSM_MDP30 + unsigned int remainder = 0, is_bpp_4 = 0; + struct mdp_blit_req splitreq; + int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1; + int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1; + + if (req->flags & MDP_ROT_90) { + if (((req->dst_rect.h == 1) && ((req->src_rect.w != 1) || + (req->dst_rect.w != req->src_rect.h))) || + ((req->dst_rect.w == 1) && ((req->src_rect.h != 1) || + (req->dst_rect.h != req->src_rect.w)))) { + printk(KERN_ERR "mpd_ppp: error scaling when size is 1!\n"); + return -EINVAL; + } + } else { + if (((req->dst_rect.w == 1) && ((req->src_rect.w != 1) || + (req->dst_rect.h != req->src_rect.h))) || + ((req->dst_rect.h == 1) && ((req->src_rect.h != 1) || + (req->dst_rect.w != req->src_rect.w)))) { + printk(KERN_ERR "mpd_ppp: error scaling when size is 1!\n"); + return -EINVAL; + } + } +#endif + if (unlikely(req->src_rect.h == 0 || req->src_rect.w == 0)) { + printk(KERN_ERR "mpd_ppp: src img of zero size!\n"); + return -EINVAL; + } + if (unlikely(req->dst_rect.h == 0 || req->dst_rect.w == 0)) + return 0; + +#if defined CONFIG_FB_MSM_MDP31 + /* MDP width split workaround */ + remainder = (req->dst_rect.w)%32; + ret = mdp_get_bytes_per_pixel(req->dst.format, + (struct msm_fb_data_type *)info->par); + if (ret <= 0) { + printk(KERN_ERR "mdp_ppp: incorrect bpp!\n"); + return -EINVAL; + } + is_bpp_4 = (ret == 4) ? 1 : 0; + + if ((is_bpp_4 && (remainder == 6 || remainder == 14 || + remainder == 22 || remainder == 30)) || remainder == 3 || + (remainder == 1 && req->dst_rect.w != 1) || + (remainder == 2 && req->dst_rect.w != 2)) { + /* make new request as provide by user */ + splitreq = *req; + + /* break dest roi at width*/ + d_y_0 = d_y_1 = req->dst_rect.y; + d_h_0 = d_h_1 = req->dst_rect.h; + d_x_0 = req->dst_rect.x; + + if (remainder == 14) + d_w_1 = (req->dst_rect.w - 14) / 2 + 4; + else if (remainder == 22) + d_w_1 = (req->dst_rect.w - 22) / 2 + 10; + else if (remainder == 30) + d_w_1 = (req->dst_rect.w - 30) / 2 + 10; + else if (remainder == 6) + d_w_1 = req->dst_rect.w / 2 - 1; + else if (remainder == 3) + d_w_1 = (req->dst_rect.w - 3) / 2 - 1; + else if (remainder == 2) + d_w_1 = (req->dst_rect.w - 2) / 2 - 6; + else + d_w_1 = (req->dst_rect.w - 1) / 2 - 1; + d_w_0 = req->dst_rect.w - d_w_1; + d_x_1 = d_x_0 + d_w_0; + if (req->dst_rect.w == 3) { + d_w_1 = 2; + d_w_0 = 2; + d_x_1 = d_x_0 + 1; + } + + /* blit first region */ + if (((splitreq.flags & 0x07) == 0x07) || + ((splitreq.flags & 0x07) == 0x0)) { + + if (splitreq.flags & MDP_ROT_90) { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_w_1) / + req->dst_rect.w; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_w_1 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } else { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_w_1) / + req->dst_rect.w; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_w_1 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } + + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } else { + if (splitreq.flags & MDP_ROT_90) { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_w_0) / + req->dst_rect.w; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_w_0 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } else { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_w_0) / + req->dst_rect.w; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_w_0 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } + + if ((splitreq.dst_rect.h % 32 == 3) || + ((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) || + ((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2)) + ret = mdp_blit_split_height(info, &splitreq); + else + ret = mdp_ppp_blit(info, &splitreq); + if (ret) + return ret; + /* blit second region */ + if (((splitreq.flags & 0x07) == 0x07) || + ((splitreq.flags & 0x07) == 0x0)) { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } else { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } + if (((splitreq.dst_rect.h % 32) == 3) || + ((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) || + ((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2)) + ret = mdp_blit_split_height(info, &splitreq); + else + ret = mdp_ppp_blit(info, &splitreq); + if (ret) + return ret; + } else if ((req->dst_rect.h % 32) == 3 || + ((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) || + ((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2)) + ret = mdp_blit_split_height(info, req); + else + ret = mdp_ppp_blit(info, req); + return ret; +#elif defined CONFIG_FB_MSM_MDP30 + /* MDP width split workaround */ + remainder = (req->dst_rect.w)%16; + ret = mdp_get_bytes_per_pixel(req->dst.format, + (struct msm_fb_data_type *)info->par); + if (ret <= 0) { + printk(KERN_ERR "mdp_ppp: incorrect bpp!\n"); + return -EINVAL; + } + is_bpp_4 = (ret == 4) ? 1 : 0; + + if ((is_bpp_4 && (remainder == 6 || remainder == 14))) { + + /* make new request as provide by user */ + splitreq = *req; + + /* break dest roi at width*/ + d_y_0 = d_y_1 = req->dst_rect.y; + d_h_0 = d_h_1 = req->dst_rect.h; + d_x_0 = req->dst_rect.x; + + if (remainder == 14 || remainder == 6) + d_w_1 = req->dst_rect.w / 2; + else + d_w_1 = (req->dst_rect.w - 1) / 2 - 1; + + d_w_0 = req->dst_rect.w - d_w_1; + d_x_1 = d_x_0 + d_w_0; + + /* blit first region */ + if (((splitreq.flags & 0x07) == 0x07) || + ((splitreq.flags & 0x07) == 0x05) || + ((splitreq.flags & 0x07) == 0x02) || + ((splitreq.flags & 0x07) == 0x0)) { + + if (splitreq.flags & MDP_ROT_90) { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_w_1) / + req->dst_rect.w; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_w_1 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } else { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_w_1) / + req->dst_rect.w; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_w_1 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } + + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } else { + if (splitreq.flags & MDP_ROT_90) { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_w_0) / + req->dst_rect.w; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_w_0 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } else { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_w_0) / + req->dst_rect.w; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_w_0 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } + + /* No need to split in height */ + ret = mdp_ppp_blit(info, &splitreq); + + if (ret) + return ret; + + /* blit second region */ + if (((splitreq.flags & 0x07) == 0x07) || + ((splitreq.flags & 0x07) == 0x05) || + ((splitreq.flags & 0x07) == 0x02) || + ((splitreq.flags & 0x07) == 0x0)) { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } else { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } + + /* No need to split in height ... just width */ + ret = mdp_ppp_blit(info, &splitreq); + + if (ret) + return ret; + + } else + ret = mdp_ppp_blit(info, req); + return ret; +#else + ret = mdp_ppp_blit(info, req); + return ret; +#endif +} + +typedef void (*msm_dma_barrier_function_pointer) (void *, size_t); + +static inline void msm_fb_dma_barrier_for_rect(struct fb_info *info, + struct mdp_img *img, struct mdp_rect *rect, + msm_dma_barrier_function_pointer dma_barrier_fp + ) +{ + /* + * Compute the start and end addresses of the rectangles. + * NOTE: As currently implemented, the data between + * the end of one row and the start of the next is + * included in the address range rather than + * doing multiple calls for each row. + */ + unsigned long start; + size_t size; + char * const pmem_start = info->screen_base; + int bytes_per_pixel = mdp_get_bytes_per_pixel(img->format, + (struct msm_fb_data_type *)info->par); + if (bytes_per_pixel <= 0) { + printk(KERN_ERR "%s incorrect bpp!\n", __func__); + return; + } + start = (unsigned long)pmem_start + img->offset + + (img->width * rect->y + rect->x) * bytes_per_pixel; + size = (rect->h * img->width + rect->w) * bytes_per_pixel; + (*dma_barrier_fp) ((void *) start, size); + +} + +static inline void msm_dma_nc_pre(void) +{ + dmb(); +} +static inline void msm_dma_wt_pre(void) +{ + dmb(); +} +static inline void msm_dma_todevice_wb_pre(void *start, size_t size) +{ + dma_cache_pre_ops(start, size, DMA_TO_DEVICE); +} + +static inline void msm_dma_fromdevice_wb_pre(void *start, size_t size) +{ + dma_cache_pre_ops(start, size, DMA_FROM_DEVICE); +} + +static inline void msm_dma_nc_post(void) +{ + dmb(); +} + +static inline void msm_dma_fromdevice_wt_post(void *start, size_t size) +{ + dma_cache_post_ops(start, size, DMA_FROM_DEVICE); +} + +static inline void msm_dma_todevice_wb_post(void *start, size_t size) +{ + dma_cache_post_ops(start, size, DMA_TO_DEVICE); +} + +static inline void msm_dma_fromdevice_wb_post(void *start, size_t size) +{ + dma_cache_post_ops(start, size, DMA_FROM_DEVICE); +} + +/* + * Do the write barriers required to guarantee data is committed to RAM + * (from CPU cache or internal buffers) before a DMA operation starts. + * NOTE: As currently implemented, the data between + * the end of one row and the start of the next is + * included in the address range rather than + * doing multiple calls for each row. +*/ +static void msm_fb_ensure_memory_coherency_before_dma(struct fb_info *info, + struct mdp_blit_req *req_list, + int req_list_count) +{ +#ifdef CONFIG_ARCH_QSD8X50 + int i; + + /* + * Normally, do the requested barriers for each address + * range that corresponds to a rectangle. + * + * But if at least one write barrier is requested for data + * going to or from the device but no address range is + * needed for that barrier, then do the barrier, but do it + * only once, no matter how many requests there are. + */ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + switch (mfd->mdp_fb_page_protection) { + default: + case MDP_FB_PAGE_PROTECTION_NONCACHED: + case MDP_FB_PAGE_PROTECTION_WRITECOMBINE: + /* + * The following barrier is only done at most once, + * since further calls would be redundant. + */ + for (i = 0; i < req_list_count; i++) { + if (!(req_list[i].flags + & MDP_NO_DMA_BARRIER_START)) { + msm_dma_nc_pre(); + break; + } + } + break; + + case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE: + /* + * The following barrier is only done at most once, + * since further calls would be redundant. + */ + for (i = 0; i < req_list_count; i++) { + if (!(req_list[i].flags + & MDP_NO_DMA_BARRIER_START)) { + msm_dma_wt_pre(); + break; + } + } + break; + + case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE: + case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE: + for (i = 0; i < req_list_count; i++) { + if (!(req_list[i].flags & + MDP_NO_DMA_BARRIER_START)) { + + msm_fb_dma_barrier_for_rect(info, + &(req_list[i].src), + &(req_list[i].src_rect), + msm_dma_todevice_wb_pre + ); + + msm_fb_dma_barrier_for_rect(info, + &(req_list[i].dst), + &(req_list[i].dst_rect), + msm_dma_todevice_wb_pre + ); + } + } + break; + } +#else + dmb(); +#endif +} + + +/* + * Do the write barriers required to guarantee data will be re-read from RAM by + * the CPU after a DMA operation ends. + * NOTE: As currently implemented, the data between + * the end of one row and the start of the next is + * included in the address range rather than + * doing multiple calls for each row. +*/ +static void msm_fb_ensure_memory_coherency_after_dma(struct fb_info *info, + struct mdp_blit_req *req_list, + int req_list_count) +{ +#ifdef CONFIG_ARCH_QSD8X50 + int i; + + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + switch (mfd->mdp_fb_page_protection) { + default: + case MDP_FB_PAGE_PROTECTION_NONCACHED: + case MDP_FB_PAGE_PROTECTION_WRITECOMBINE: + /* + * The following barrier is only done at most once, + * since further calls would be redundant. + */ + for (i = 0; i < req_list_count; i++) { + if (!(req_list[i].flags + & MDP_NO_DMA_BARRIER_END)) { + msm_dma_nc_post(); + break; + } + } + break; + + case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE: + for (i = 0; i < req_list_count; i++) { + if (!(req_list[i].flags & + MDP_NO_DMA_BARRIER_END)) { + + msm_fb_dma_barrier_for_rect(info, + &(req_list[i].dst), + &(req_list[i].dst_rect), + msm_dma_fromdevice_wt_post + ); + } + } + break; + case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE: + case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE: + for (i = 0; i < req_list_count; i++) { + if (!(req_list[i].flags & + MDP_NO_DMA_BARRIER_END)) { + + msm_fb_dma_barrier_for_rect(info, + &(req_list[i].dst), + &(req_list[i].dst_rect), + msm_dma_fromdevice_wb_post + ); + } + } + break; + } +#else + dmb(); +#endif +} + +/* + * NOTE: The userspace issues blit operations in a sequence, the sequence + * start with a operation marked START and ends in an operation marked + * END. It is guranteed by the userspace that all the blit operations + * between START and END are only within the regions of areas designated + * by the START and END operations and that the userspace doesnt modify + * those areas. Hence it would be enough to perform barrier/cache operations + * only on the START and END operations. + */ +static int msmfb_blit(struct fb_info *info, void __user *p) +{ + /* + * CAUTION: The names of the struct types intentionally *DON'T* match + * the names of the variables declared -- they appear to be swapped. + * Read the code carefully and you should see that the variable names + * make sense. + */ + const int MAX_LIST_WINDOW = 16; + struct mdp_blit_req req_list[MAX_LIST_WINDOW]; + struct mdp_blit_req_list req_list_header; + + int count, i, req_list_count; + if (bf_supported && + (info->node == 1 || info->node == 2)) { + pr_err("%s: no pan display for fb%d.", + __func__, info->node); + return -EPERM; + } + /* Get the count size for the total BLIT request. */ + if (copy_from_user(&req_list_header, p, sizeof(req_list_header))) + return -EFAULT; + p += sizeof(req_list_header); + count = req_list_header.count; + if (count < 0 || count >= MAX_BLIT_REQ) + return -EINVAL; + while (count > 0) { + /* + * Access the requests through a narrow window to decrease copy + * overhead and make larger requests accessible to the + * coherency management code. + * NOTE: The window size is intended to be larger than the + * typical request size, but not require more than 2 + * kbytes of stack storage. + */ + req_list_count = count; + if (req_list_count > MAX_LIST_WINDOW) + req_list_count = MAX_LIST_WINDOW; + if (copy_from_user(&req_list, p, + sizeof(struct mdp_blit_req)*req_list_count)) + return -EFAULT; + + /* + * Ensure that any data CPU may have previously written to + * internal state (but not yet committed to memory) is + * guaranteed to be committed to memory now. + */ + msm_fb_ensure_memory_coherency_before_dma(info, + req_list, req_list_count); + + /* + * Do the blit DMA, if required -- returning early only if + * there is a failure. + */ + for (i = 0; i < req_list_count; i++) { + if (!(req_list[i].flags & MDP_NO_BLIT)) { + /* Do the actual blit. */ + int ret = mdp_blit(info, &(req_list[i])); + + /* + * Note that early returns don't guarantee + * memory coherency. + */ + if (ret) + return ret; + } + } + + /* + * Ensure that CPU cache and other internal CPU state is + * updated to reflect any change in memory modified by MDP blit + * DMA. + */ + msm_fb_ensure_memory_coherency_after_dma(info, + req_list, + req_list_count); + + /* Go to next window of requests. */ + count -= req_list_count; + p += sizeof(struct mdp_blit_req)*req_list_count; + } + return 0; +} + +static int msmfb_vsync_ctrl(struct fb_info *info, void __user *argp) +{ + int enable, ret; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + ret = copy_from_user(&enable, argp, sizeof(enable)); + if (ret) { + pr_err("%s:msmfb_overlay_vsync ioctl failed", __func__); + return ret; + } + + if (mfd->vsync_ctrl) + mfd->vsync_ctrl(enable); + else { + pr_err("%s: Vsync IOCTL not supported", __func__); + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_FB_MSM_OVERLAY +static int msmfb_overlay_get(struct fb_info *info, void __user *p) +{ + struct mdp_overlay req; + int ret; + + if (copy_from_user(&req, p, sizeof(req))) + return -EFAULT; + + ret = mdp4_overlay_get(info, &req); + if (ret) { + printk(KERN_ERR "%s: ioctl failed \n", + __func__); + return ret; + } + if (copy_to_user(p, &req, sizeof(req))) { + printk(KERN_ERR "%s: copy2user failed \n", + __func__); + return -EFAULT; + } + + return 0; +} + +static int msmfb_overlay_set(struct fb_info *info, void __user *p) +{ + struct mdp_overlay req; + int ret; + + if (copy_from_user(&req, p, sizeof(req))) + return -EFAULT; + + ret = mdp4_overlay_set(info, &req); + if (ret) { + printk(KERN_ERR "%s: ioctl failed, rc=%d\n", + __func__, ret); + return ret; + } + + if (copy_to_user(p, &req, sizeof(req))) { + printk(KERN_ERR "%s: copy2user failed \n", + __func__); + return -EFAULT; + } + + return 0; +} + +static int msmfb_overlay_unset(struct fb_info *info, unsigned long *argp) +{ + int ret, ndx; + + ret = copy_from_user(&ndx, argp, sizeof(ndx)); + if (ret) { + printk(KERN_ERR "%s:msmfb_overlay_unset ioctl failed \n", + __func__); + return ret; + } + + return mdp4_overlay_unset(info, ndx); +} + +static int msmfb_overlay_vsync_ctrl(struct fb_info *info, void __user *argp) +{ + int ret; + int enable; + + ret = copy_from_user(&enable, argp, sizeof(enable)); + if (ret) { + pr_err("%s:msmfb_overlay_vsync ioctl failed", __func__); + return ret; + } + + ret = mdp4_overlay_vsync_ctrl(info, enable); + + return ret; +} + +static int msmfb_overlay_play_wait(struct fb_info *info, unsigned long *argp) +{ + int ret; + struct msmfb_overlay_data req; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (mfd->overlay_play_enable == 0) /* nothing to do */ + return 0; + + ret = copy_from_user(&req, argp, sizeof(req)); + if (ret) { + pr_err("%s:msmfb_overlay_wait ioctl failed", __func__); + return ret; + } + + ret = mdp4_overlay_play_wait(info, &req); + + return ret; +} + +static int msmfb_overlay_play(struct fb_info *info, unsigned long *argp) +{ + int ret; + struct msmfb_overlay_data req; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (mfd->overlay_play_enable == 0) /* nothing to do */ + return 0; + + ret = copy_from_user(&req, argp, sizeof(req)); + if (ret) { + printk(KERN_ERR "%s:msmfb_overlay_play ioctl failed \n", + __func__); + return ret; + } + + complete(&mfd->msmfb_update_notify); + mutex_lock(&msm_fb_notify_update_sem); + if (mfd->msmfb_no_update_notify_timer.function) + del_timer(&mfd->msmfb_no_update_notify_timer); + + mfd->msmfb_no_update_notify_timer.expires = jiffies + (2 * HZ); + add_timer(&mfd->msmfb_no_update_notify_timer); + mutex_unlock(&msm_fb_notify_update_sem); + + if (info->node == 0 && !(mfd->cont_splash_done)) { /* primary */ + mdp_set_dma_pan_info(info, NULL, TRUE); + if (msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable)) { + pr_err("%s: can't turn on display!\n", __func__); + return -EINVAL; + } + } + + ret = mdp4_overlay_play(info, &req); + + if (unset_bl_level && !bl_updated) + schedule_delayed_work(&mfd->backlight_worker, + backlight_duration); + + return ret; +} + +static int msmfb_overlay_play_enable(struct fb_info *info, unsigned long *argp) +{ + int ret, enable; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + ret = copy_from_user(&enable, argp, sizeof(enable)); + if (ret) { + printk(KERN_ERR "%s:msmfb_overlay_play_enable ioctl failed \n", + __func__); + return ret; + } + + mfd->overlay_play_enable = enable; + + return 0; +} + +static int msmfb_overlay_blt(struct fb_info *info, unsigned long *argp) +{ + int ret; + struct msmfb_overlay_blt req; + + ret = copy_from_user(&req, argp, sizeof(req)); + if (ret) { + pr_err("%s: failed\n", __func__); + return ret; + } + + ret = mdp4_overlay_blt(info, &req); + + return ret; +} + +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL +static int msmfb_overlay_ioctl_writeback_init(struct fb_info *info) +{ + return mdp4_writeback_init(info); +} +static int msmfb_overlay_ioctl_writeback_start( + struct fb_info *info) +{ + int ret = 0; + ret = mdp4_writeback_start(info); + if (ret) + goto error; +error: + if (ret) + pr_err("%s:msmfb_writeback_start " + " ioctl failed\n", __func__); + return ret; +} + +static int msmfb_overlay_ioctl_writeback_stop( + struct fb_info *info) +{ + int ret = 0; + ret = mdp4_writeback_stop(info); + if (ret) + goto error; + +error: + if (ret) + pr_err("%s:msmfb_writeback_stop ioctl failed\n", + __func__); + return ret; +} + +static int msmfb_overlay_ioctl_writeback_queue_buffer( + struct fb_info *info, unsigned long *argp) +{ + int ret = 0; + struct msmfb_data data; + + ret = copy_from_user(&data, argp, sizeof(data)); + if (ret) + goto error; + + ret = mdp4_writeback_queue_buffer(info, &data); + if (ret) + goto error; + +error: + if (ret) + pr_err("%s:msmfb_writeback_queue_buffer ioctl failed\n", + __func__); + return ret; +} + +static int msmfb_overlay_ioctl_writeback_dequeue_buffer( + struct fb_info *info, unsigned long *argp) +{ + int ret = 0; + struct msmfb_data data; + + ret = copy_from_user(&data, argp, sizeof(data)); + if (ret) + goto error; + + ret = mdp4_writeback_dequeue_buffer(info, &data); + if (ret) + goto error; + + ret = copy_to_user(argp, &data, sizeof(data)); + if (ret) + goto error; + +error: + if (ret) + pr_err("%s:msmfb_writeback_dequeue_buffer ioctl failed\n", + __func__); + return ret; +} +static int msmfb_overlay_ioctl_writeback_terminate(struct fb_info *info) +{ + return mdp4_writeback_terminate(info); +} + +static int msmfb_overlay_ioctl_writeback_set_mirr_hint(struct fb_info * + info, void *argp) +{ + int ret = 0, hint; + + if (!info) { + ret = -EINVAL; + goto error; + } + + ret = copy_from_user(&hint, argp, sizeof(hint)); + if (ret) + goto error; + + ret = mdp4_writeback_set_mirroring_hint(info, hint); + if (ret) + goto error; +error: + if (ret) + pr_err("%s: ioctl failed\n", __func__); + return ret; +} + +#else +static int msmfb_overlay_ioctl_writeback_init(struct fb_info *info) +{ + return -ENOTSUPP; +} +static int msmfb_overlay_ioctl_writeback_start( + struct fb_info *info) +{ + return -ENOTSUPP; +} + +static int msmfb_overlay_ioctl_writeback_stop( + struct fb_info *info) +{ + return -ENOTSUPP; +} + +static int msmfb_overlay_ioctl_writeback_queue_buffer( + struct fb_info *info, unsigned long *argp) +{ + return -ENOTSUPP; +} + +static int msmfb_overlay_ioctl_writeback_dequeue_buffer( + struct fb_info *info, unsigned long *argp) +{ + return -ENOTSUPP; +} +static int msmfb_overlay_ioctl_writeback_terminate(struct fb_info *info) +{ + return -ENOTSUPP; +} + +static int msmfb_overlay_ioctl_writeback_set_mirr_hint(struct fb_info * + info, void *argp) +{ + return -ENOTSUPP; +} +#endif + +static int msmfb_overlay_3d_sbys(struct fb_info *info, unsigned long *argp) +{ + int ret; + struct msmfb_overlay_3d req; + + ret = copy_from_user(&req, argp, sizeof(req)); + if (ret) { + pr_err("%s:msmfb_overlay_3d_ctrl ioctl failed\n", + __func__); + return ret; + } + + ret = mdp4_overlay_3d_sbys(info, &req); + + return ret; +} + +static int msmfb_mixer_info(struct fb_info *info, unsigned long *argp) +{ + int ret, cnt; + struct msmfb_mixer_info_req req; + + ret = copy_from_user(&req, argp, sizeof(req)); + if (ret) { + pr_err("%s: failed\n", __func__); + return ret; + } + + cnt = mdp4_mixer_info(req.mixer_num, req.info); + req.cnt = cnt; + ret = copy_to_user(argp, &req, sizeof(req)); + if (ret) + pr_err("%s:msmfb_overlay_blt_off ioctl failed\n", + __func__); + + return cnt; +} + +#endif + +DEFINE_SEMAPHORE(msm_fb_ioctl_ppp_sem); +DEFINE_SEMAPHORE(msm_fb_ioctl_vsync_sem); +DEFINE_MUTEX(msm_fb_ioctl_lut_sem); + +/* Set color conversion matrix from user space */ + +#ifndef CONFIG_FB_MSM_MDP40 +static void msmfb_set_color_conv(struct mdp_ccs *p) +{ + int i; + + if (p->direction == MDP_CCS_RGB2YUV) { + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* RGB->YUV primary forward matrix */ + for (i = 0; i < MDP_CCS_SIZE; i++) + writel(p->ccs[i], MDP_CSC_PFMVn(i)); + + #ifdef CONFIG_FB_MSM_MDP31 + for (i = 0; i < MDP_BV_SIZE; i++) + writel(p->bv[i], MDP_CSC_POST_BV2n(i)); + #endif + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + } else { + /* MDP cmd block enable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + + /* YUV->RGB primary reverse matrix */ + for (i = 0; i < MDP_CCS_SIZE; i++) + writel(p->ccs[i], MDP_CSC_PRMVn(i)); + for (i = 0; i < MDP_BV_SIZE; i++) + writel(p->bv[i], MDP_CSC_PRE_BV1n(i)); + + /* MDP cmd block disable */ + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); + } +} +#else +static void msmfb_set_color_conv(struct mdp_csc *p) +{ + mdp4_vg_csc_update(p); +} +#endif + +static int msmfb_notify_update(struct fb_info *info, void __user *argp) +{ + int ret; + unsigned int notify; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + ret = copy_from_user(¬ify, argp, sizeof(unsigned int)); + if (ret) { + pr_err("%s:ioctl failed\n", __func__); + return ret; + } + + if (notify > NOTIFY_UPDATE_STOP) + return -EINVAL; + + if (notify == NOTIFY_UPDATE_START) { + INIT_COMPLETION(mfd->msmfb_update_notify); + ret = wait_for_completion_interruptible_timeout( + &mfd->msmfb_update_notify, 4*HZ); + } else { + INIT_COMPLETION(mfd->msmfb_no_update_notify); + ret = wait_for_completion_interruptible_timeout( + &mfd->msmfb_no_update_notify, 4*HZ); + } + if (ret == 0) + ret = -ETIMEDOUT; + return (ret > 0) ? 0 : ret; +} + +static int msmfb_handle_pp_ioctl(struct msm_fb_data_type *mfd, + struct msmfb_mdp_pp *pp_ptr) +{ + int ret = -1; +#ifdef CONFIG_FB_MSM_MDP40 + int i = 0; +#endif + if (!pp_ptr) + return ret; + + switch (pp_ptr->op) { +#ifdef CONFIG_FB_MSM_MDP40 + case mdp_op_csc_cfg: + ret = mdp4_csc_config(&(pp_ptr->data.csc_cfg_data)); + for (i = 0; i < CSC_MAX_BLOCKS; i++) { + if (pp_ptr->data.csc_cfg_data.block == + csc_cfg_matrix[i].block) { + memcpy(&csc_cfg_matrix[i].csc_data, + &(pp_ptr->data.csc_cfg_data.csc_data), + sizeof(struct mdp_csc_cfg)); + break; + } + } + break; + + case mdp_op_pcc_cfg: + ret = mdp4_pcc_cfg(&(pp_ptr->data.pcc_cfg_data)); + break; + + case mdp_op_lut_cfg: + switch (pp_ptr->data.lut_cfg_data.lut_type) { + case mdp_lut_igc: + ret = mdp4_igc_lut_config( + (struct mdp_igc_lut_data *) + &pp_ptr->data.lut_cfg_data.data); + break; + + case mdp_lut_pgc: + ret = mdp4_argc_cfg( + &pp_ptr->data.lut_cfg_data.data.pgc_lut_data); + break; + + case mdp_lut_hist: + ret = mdp_hist_lut_config( + (struct mdp_hist_lut_data *) + &pp_ptr->data.lut_cfg_data.data); + break; + + default: + break; + } + break; + case mdp_op_qseed_cfg: + ret = mdp4_qseed_cfg((struct mdp_qseed_cfg_data *) + &pp_ptr->data.qseed_cfg_data); + break; + case mdp_op_calib_cfg: + ret = mdp4_calib_config((struct mdp_calib_config_data *) + &pp_ptr->data.calib_cfg); + break; +#endif + case mdp_bl_scale_cfg: + ret = mdp_bl_scale_config(mfd, (struct mdp_bl_scale_data *) + &pp_ptr->data.bl_scale_data); + break; + + default: + pr_warn("Unsupported request to MDP_PP IOCTL.\n"); + ret = -EINVAL; + break; + } + + return ret; +} +static int msmfb_handle_metadata_ioctl(struct msm_fb_data_type *mfd, + struct msmfb_metadata *metadata_ptr) +{ + int ret; + switch (metadata_ptr->op) { +#ifdef CONFIG_FB_MSM_MDP40 + case metadata_op_base_blend: + ret = mdp4_update_base_blend(mfd, + &metadata_ptr->data.blend_cfg); + break; +#endif + default: + pr_warn("Unsupported request to MDP META IOCTL.\n"); + ret = -EINVAL; + break; + } + return ret; +} +static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct msm_fb_data_type *mfd; + void __user *argp = (void __user *)arg; + struct fb_cursor cursor; + struct fb_cmap cmap; + struct mdp_histogram_data hist; + struct mdp_histogram_start_req hist_req; + uint32_t block; +#ifndef CONFIG_FB_MSM_MDP40 + struct mdp_ccs ccs_matrix; +#else + struct mdp_csc csc_matrix; +#endif + struct mdp_page_protection fb_page_protection; + struct msmfb_mdp_pp mdp_pp; + struct msmfb_metadata mdp_metadata; + int ret = 0; + + if (!info || !info->par) + return -EINVAL; + mfd = (struct msm_fb_data_type *)info->par; + + switch (cmd) { +#ifdef CONFIG_FB_MSM_OVERLAY + case MSMFB_OVERLAY_GET: + ret = msmfb_overlay_get(info, argp); + break; + case MSMFB_OVERLAY_SET: + ret = msmfb_overlay_set(info, argp); + break; + case MSMFB_OVERLAY_UNSET: + ret = msmfb_overlay_unset(info, argp); + break; + case MSMFB_OVERLAY_PLAY: + ret = msmfb_overlay_play(info, argp); + break; + case MSMFB_OVERLAY_PLAY_ENABLE: + ret = msmfb_overlay_play_enable(info, argp); + break; + case MSMFB_OVERLAY_PLAY_WAIT: + ret = msmfb_overlay_play_wait(info, argp); + break; + case MSMFB_OVERLAY_BLT: + ret = msmfb_overlay_blt(info, argp); + break; + case MSMFB_OVERLAY_3D: + ret = msmfb_overlay_3d_sbys(info, argp); + break; + case MSMFB_MIXER_INFO: + ret = msmfb_mixer_info(info, argp); + break; + case MSMFB_WRITEBACK_INIT: + ret = msmfb_overlay_ioctl_writeback_init(info); + break; + case MSMFB_WRITEBACK_START: + ret = msmfb_overlay_ioctl_writeback_start( + info); + break; + case MSMFB_WRITEBACK_STOP: + ret = msmfb_overlay_ioctl_writeback_stop( + info); + break; + case MSMFB_WRITEBACK_QUEUE_BUFFER: + ret = msmfb_overlay_ioctl_writeback_queue_buffer( + info, argp); + break; + case MSMFB_WRITEBACK_DEQUEUE_BUFFER: + ret = msmfb_overlay_ioctl_writeback_dequeue_buffer( + info, argp); + break; + case MSMFB_WRITEBACK_TERMINATE: + ret = msmfb_overlay_ioctl_writeback_terminate(info); + break; +#endif + case MSMFB_VSYNC_CTRL: + case MSMFB_OVERLAY_VSYNC_CTRL: + down(&msm_fb_ioctl_ppp_sem); + if (mdp_rev >= MDP_REV_40) + ret = msmfb_overlay_vsync_ctrl(info, argp); + else + ret = msmfb_vsync_ctrl(info, argp); + up(&msm_fb_ioctl_ppp_sem); + break; + case MSMFB_BLIT: + down(&msm_fb_ioctl_ppp_sem); + ret = msmfb_blit(info, argp); + up(&msm_fb_ioctl_ppp_sem); + + break; + + /* Ioctl for setting ccs matrix from user space */ + case MSMFB_SET_CCS_MATRIX: +#ifndef CONFIG_FB_MSM_MDP40 + ret = copy_from_user(&ccs_matrix, argp, sizeof(ccs_matrix)); + if (ret) { + printk(KERN_ERR + "%s:MSMFB_SET_CCS_MATRIX ioctl failed \n", + __func__); + return ret; + } + + down(&msm_fb_ioctl_ppp_sem); + if (ccs_matrix.direction == MDP_CCS_RGB2YUV) + mdp_ccs_rgb2yuv = ccs_matrix; + else + mdp_ccs_yuv2rgb = ccs_matrix; + + msmfb_set_color_conv(&ccs_matrix) ; + up(&msm_fb_ioctl_ppp_sem); +#else + ret = copy_from_user(&csc_matrix, argp, sizeof(csc_matrix)); + if (ret) { + pr_err("%s:MSMFB_SET_CSC_MATRIX ioctl failed\n", + __func__); + return ret; + } + down(&msm_fb_ioctl_ppp_sem); + msmfb_set_color_conv(&csc_matrix); + up(&msm_fb_ioctl_ppp_sem); + +#endif + + break; + + /* Ioctl for getting ccs matrix to user space */ + case MSMFB_GET_CCS_MATRIX: +#ifndef CONFIG_FB_MSM_MDP40 + ret = copy_from_user(&ccs_matrix, argp, sizeof(ccs_matrix)) ; + if (ret) { + printk(KERN_ERR + "%s:MSMFB_GET_CCS_MATRIX ioctl failed \n", + __func__); + return ret; + } + + down(&msm_fb_ioctl_ppp_sem); + if (ccs_matrix.direction == MDP_CCS_RGB2YUV) + ccs_matrix = mdp_ccs_rgb2yuv; + else + ccs_matrix = mdp_ccs_yuv2rgb; + + ret = copy_to_user(argp, &ccs_matrix, sizeof(ccs_matrix)); + + if (ret) { + printk(KERN_ERR + "%s:MSMFB_GET_CCS_MATRIX ioctl failed \n", + __func__); + return ret ; + } + up(&msm_fb_ioctl_ppp_sem); +#else + ret = -EINVAL; +#endif + + break; + + case MSMFB_GRP_DISP: +#ifdef CONFIG_FB_MSM_MDP22 + { + unsigned long grp_id; + + ret = copy_from_user(&grp_id, argp, sizeof(grp_id)); + if (ret) + return ret; + + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); + writel(grp_id, MDP_FULL_BYPASS_WORD43); + mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, + FALSE); + break; + } +#else + return -EFAULT; +#endif + case MSMFB_SUSPEND_SW_REFRESHER: + if (!mfd->panel_power_on) + return -EPERM; + + mfd->sw_refreshing_enable = FALSE; + ret = msm_fb_stop_sw_refresher(mfd); + break; + + case MSMFB_RESUME_SW_REFRESHER: + if (!mfd->panel_power_on) + return -EPERM; + + mfd->sw_refreshing_enable = TRUE; + ret = msm_fb_resume_sw_refresher(mfd); + break; + + case MSMFB_CURSOR: + ret = copy_from_user(&cursor, argp, sizeof(cursor)); + if (ret) + return ret; + + ret = msm_fb_cursor(info, &cursor); + break; + + case MSMFB_SET_LUT: + ret = copy_from_user(&cmap, argp, sizeof(cmap)); + if (ret) + return ret; + + mutex_lock(&msm_fb_ioctl_lut_sem); + ret = msm_fb_set_lut(&cmap, info); + mutex_unlock(&msm_fb_ioctl_lut_sem); + break; + + case MSMFB_HISTOGRAM: + if (!mfd->panel_power_on) + return -EPERM; + + if (!mfd->do_histogram) + return -ENODEV; + + ret = copy_from_user(&hist, argp, sizeof(hist)); + if (ret) + return ret; + + ret = mfd->do_histogram(info, &hist); + break; + + case MSMFB_HISTOGRAM_START: + if (!mfd->panel_power_on) + return -EPERM; + + if (!mfd->start_histogram) + return -ENODEV; + + ret = copy_from_user(&hist_req, argp, sizeof(hist_req)); + if (ret) + return ret; + + ret = mfd->start_histogram(&hist_req); + break; + + case MSMFB_HISTOGRAM_STOP: + if (!mfd->stop_histogram) + return -ENODEV; + + ret = copy_from_user(&block, argp, sizeof(int)); + if (ret) + return ret; + + ret = mfd->stop_histogram(info, block); + break; + + + case MSMFB_GET_PAGE_PROTECTION: + fb_page_protection.page_protection + = mfd->mdp_fb_page_protection; + ret = copy_to_user(argp, &fb_page_protection, + sizeof(fb_page_protection)); + if (ret) + return ret; + break; + + case MSMFB_NOTIFY_UPDATE: + ret = msmfb_notify_update(info, argp); + break; + + case MSMFB_SET_PAGE_PROTECTION: +#if defined CONFIG_ARCH_QSD8X50 || defined CONFIG_ARCH_MSM8X60 + ret = copy_from_user(&fb_page_protection, argp, + sizeof(fb_page_protection)); + if (ret) + return ret; + + /* Validate the proposed page protection settings. */ + switch (fb_page_protection.page_protection) { + case MDP_FB_PAGE_PROTECTION_NONCACHED: + case MDP_FB_PAGE_PROTECTION_WRITECOMBINE: + case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE: + /* Write-back cache (read allocate) */ + case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE: + /* Write-back cache (write allocate) */ + case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE: + mfd->mdp_fb_page_protection = + fb_page_protection.page_protection; + break; + default: + ret = -EINVAL; + break; + } +#else + /* + * Don't allow caching until 7k DMA cache operations are + * available. + */ + ret = -EINVAL; +#endif + break; + + case MSMFB_MDP_PP: + ret = copy_from_user(&mdp_pp, argp, sizeof(mdp_pp)); + if (ret) + return ret; + + ret = msmfb_handle_pp_ioctl(mfd, &mdp_pp); + break; + + case MSMFB_METADATA_SET: + ret = copy_from_user(&mdp_metadata, argp, sizeof(mdp_metadata)); + if (ret) + return ret; + ret = msmfb_handle_metadata_ioctl(mfd, &mdp_metadata); + break; + + default: + MSM_FB_INFO("MDP: unknown ioctl (cmd=%x) received!\n", cmd); + ret = -EINVAL; + break; + } + + return ret; +} +static int msmfb_handle_metadata_ioctl(struct msm_fb_data_type *mfd, + struct msmfb_metadata *metadata_ptr) +{ + int ret; + switch (metadata_ptr->op) { +#ifdef CONFIG_FB_MSM_MDP40 + case metadata_op_base_blend: + ret = mdp4_update_base_blend(mfd, + &metadata_ptr->data.blend_cfg); + break; + case metadata_op_wb_format: + ret = mdp4_update_writeback_format(mfd, + &metadata_ptr->data.mixer_cfg); + break; +#endif + default: + pr_warn("Unsupported request to MDP META IOCTL.\n"); + ret = -EINVAL; + break; + } + return ret; +} + +static int msmfb_get_metadata(struct msm_fb_data_type *mfd, + struct msmfb_metadata *metadata_ptr) +{ + int ret = 0; + switch (metadata_ptr->op) { + case metadata_op_frame_rate: + metadata_ptr->data.panel_frame_rate = + mdp_get_panel_framerate(mfd); + break; + default: + pr_warn("Unsupported request to MDP META IOCTL.\n"); + ret = -EINVAL; + break; + } + return ret; +} + +static int msmfb_handle_buf_sync_ioctl(struct msm_fb_data_type *mfd, + struct mdp_buf_sync *buf_sync) +{ + int i, ret = 0; + u32 threshold; + int acq_fen_fd[MDP_MAX_FENCE_FD]; + struct sync_fence *fence; + struct sync_pt *release_sync_pt; + struct sync_pt *retire_sync_pt; + struct sync_fence *release_fence; + struct sync_fence *retire_fence; + int release_fen_fd; + int retire_fen_fd; + + if ((buf_sync->acq_fen_fd_cnt > MDP_MAX_FENCE_FD) || + (mfd->timeline == NULL)) + return -EINVAL; + + if ((!mfd->op_enable) || (!mfd->panel_power_on)) + return -EPERM; + + if (buf_sync->acq_fen_fd_cnt) + ret = copy_from_user(acq_fen_fd, buf_sync->acq_fen_fd, + buf_sync->acq_fen_fd_cnt * sizeof(int)); + if (ret) { + pr_err("%s:copy_from_user failed", __func__); + return ret; + } + mutex_lock(&mfd->sync_mutex); + for (i = 0; i < buf_sync->acq_fen_fd_cnt; i++) { + fence = sync_fence_fdget(acq_fen_fd[i]); + if (fence == NULL) { + pr_info("%s: null fence! i=%d fd=%d\n", __func__, i, + acq_fen_fd[i]); + ret = -EINVAL; + break; + } + mfd->acq_fen[i] = fence; + } + mfd->acq_fen_cnt = i; + if (ret) + goto buf_sync_err_1; + if (buf_sync->flags & MDP_BUF_SYNC_FLAG_WAIT) { + msm_fb_wait_for_fence(mfd); + } + if ((mfd->panel.type == MIPI_CMD_PANEL) || + (mfd->panel.type == WRITEBACK_PANEL)) + threshold = 1; + else + threshold = 2; + + release_fen_fd = get_unused_fd_flags(0); + if (release_fen_fd < 0) { + pr_err("%s: get_unused_fd_flags failed", __func__); + ret = -EIO; + goto buf_sync_err_1; + } + + retire_fen_fd = get_unused_fd_flags(0); + if (retire_fen_fd < 0) { + pr_err("%s: get_unused_fd_flags failed", __func__); + ret = -EIO; + goto buf_sync_err_2; + } + + release_sync_pt = sw_sync_pt_create(mfd->timeline, + mfd->timeline_value + threshold); + release_fence = sync_fence_create("mdp-fence", + release_sync_pt); + sync_fence_install(release_fence, release_fen_fd); + retire_sync_pt = sw_sync_pt_create(mfd->timeline, + mfd->timeline_value + threshold); + retire_fence = sync_fence_create("mdp-retire-fence", + retire_sync_pt); + sync_fence_install(retire_fence, retire_fen_fd); + + ret = copy_to_user(buf_sync->rel_fen_fd, + &release_fen_fd, sizeof(int)); + if (ret) { + pr_err("%s:copy_to_user failed", __func__); + goto buf_sync_err_3; + } + + ret = copy_to_user(buf_sync->retire_fen_fd, + &retire_fen_fd, sizeof(int)); + if (ret) { + pr_err("%s:copy_to_user failed", __func__); + goto buf_sync_err_3; + } + + mutex_unlock(&mfd->sync_mutex); + return ret; +buf_sync_err_3: + sync_fence_put(release_fence); + sync_fence_put(retire_fence); + put_unused_fd(retire_fen_fd); +buf_sync_err_2: + put_unused_fd(release_fen_fd); +buf_sync_err_1: + for (i = 0; i < mfd->acq_fen_cnt; i++) + sync_fence_put(mfd->acq_fen[i]); + mfd->acq_fen_cnt = 0; + mutex_unlock(&mfd->sync_mutex); + return ret; +} + +static int msmfb_display_commit(struct fb_info *info, + unsigned long *argp) +{ + int ret; + struct mdp_display_commit disp_commit; + ret = copy_from_user(&disp_commit, argp, + sizeof(disp_commit)); + if (ret) { + pr_err("%s:copy_from_user failed", __func__); + return ret; + } + + ret = msm_fb_pan_display_ex(info, &disp_commit); + + return ret; +} + +static int msm_fb_register_driver(void) +{ + return platform_driver_register(&msm_fb_driver); +} + +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL +struct fb_info *msm_fb_get_writeback_fb(void) +{ + int c = 0; + for (c = 0; c < fbi_list_index; ++c) { + struct msm_fb_data_type *mfd; + mfd = (struct msm_fb_data_type *)fbi_list[c]->par; + if (mfd->panel.type == WRITEBACK_PANEL) + return fbi_list[c]; + } + + return NULL; +} +EXPORT_SYMBOL(msm_fb_get_writeback_fb); + +int msm_fb_writeback_start(struct fb_info *info) +{ + return mdp4_writeback_start(info); +} +EXPORT_SYMBOL(msm_fb_writeback_start); + +int msm_fb_writeback_queue_buffer(struct fb_info *info, + struct msmfb_data *data) +{ + return mdp4_writeback_queue_buffer(info, data); +} +EXPORT_SYMBOL(msm_fb_writeback_queue_buffer); + +int msm_fb_writeback_dequeue_buffer(struct fb_info *info, + struct msmfb_data *data) +{ + return mdp4_writeback_dequeue_buffer(info, data); +} +EXPORT_SYMBOL(msm_fb_writeback_dequeue_buffer); + +int msm_fb_writeback_stop(struct fb_info *info) +{ + return mdp4_writeback_stop(info); +} +EXPORT_SYMBOL(msm_fb_writeback_stop); +int msm_fb_writeback_init(struct fb_info *info) +{ + return mdp4_writeback_init(info); +} +EXPORT_SYMBOL(msm_fb_writeback_init); +int msm_fb_writeback_terminate(struct fb_info *info) +{ + return mdp4_writeback_terminate(info); +} +EXPORT_SYMBOL(msm_fb_writeback_terminate); +#endif + +struct platform_device *msm_fb_add_device(struct platform_device *pdev) +{ + struct msm_fb_panel_data *pdata; + struct platform_device *this_dev = NULL; + struct fb_info *fbi; + struct msm_fb_data_type *mfd = NULL; + u32 type, id, fb_num; + + if (!pdev) + return NULL; + id = pdev->id; + + pdata = pdev->dev.platform_data; + if (!pdata) + return NULL; + type = pdata->panel_info.type; + +#if defined MSM_FB_NUM + /* + * over written fb_num which defined + * at panel_info + * + */ + if (type == HDMI_PANEL || type == DTV_PANEL || + type == TV_PANEL || type == WRITEBACK_PANEL) { + if (hdmi_prim_display) + pdata->panel_info.fb_num = 2; + else + pdata->panel_info.fb_num = 1; + } + else + pdata->panel_info.fb_num = MSM_FB_NUM; + + MSM_FB_INFO("setting pdata->panel_info.fb_num to %d. type: %d\n", + pdata->panel_info.fb_num, type); +#endif + fb_num = pdata->panel_info.fb_num; + + if (fb_num <= 0) + return NULL; + + if (fbi_list_index >= MAX_FBI_LIST) { + printk(KERN_ERR "msm_fb: no more framebuffer info list!\n"); + return NULL; + } + /* + * alloc panel device data + */ + this_dev = msm_fb_device_alloc(pdata, type, id); + + if (!this_dev) { + printk(KERN_ERR + "%s: msm_fb_device_alloc failed!\n", __func__); + return NULL; + } + + /* + * alloc framebuffer info + par data + */ + fbi = framebuffer_alloc(sizeof(struct msm_fb_data_type), NULL); + if (fbi == NULL) { + platform_device_put(this_dev); + printk(KERN_ERR "msm_fb: can't alloca framebuffer info data!\n"); + return NULL; + } + + mfd = (struct msm_fb_data_type *)fbi->par; + mfd->key = MFD_KEY; + mfd->fbi = fbi; + mfd->panel.type = type; + mfd->panel.id = id; + mfd->fb_page = fb_num; + mfd->index = fbi_list_index; + mfd->mdp_fb_page_protection = MDP_FB_PAGE_PROTECTION_WRITECOMBINE; + mfd->iclient = iclient; + /* link to the latest pdev */ + mfd->pdev = this_dev; + + mfd_list[mfd_list_index++] = mfd; + fbi_list[fbi_list_index++] = fbi; + + /* + * set driver data + */ + platform_set_drvdata(this_dev, mfd); + + if (platform_device_add(this_dev)) { + printk(KERN_ERR "msm_fb: platform_device_add failed!\n"); + platform_device_put(this_dev); + framebuffer_release(fbi); + fbi_list_index--; + return NULL; + } + return this_dev; +} +EXPORT_SYMBOL(msm_fb_add_device); + +int get_fb_phys_info(unsigned long *start, unsigned long *len, int fb_num, + int subsys_id) +{ + struct fb_info *info; + struct msm_fb_data_type *mfd; + + if (fb_num > MAX_FBI_LIST || + (subsys_id != DISPLAY_SUBSYSTEM_ID && + subsys_id != ROTATOR_SUBSYSTEM_ID)) { + pr_err("%s(): Invalid parameters\n", __func__); + return -1; + } + + info = fbi_list[fb_num]; + if (!info) { + pr_err("%s(): info is NULL\n", __func__); + return -1; + } + + mfd = (struct msm_fb_data_type *)info->par; + + if (subsys_id == DISPLAY_SUBSYSTEM_ID) { + if (mfd->display_iova) + *start = mfd->display_iova; + else + *start = info->fix.smem_start; + } else { + if (mfd->rotator_iova) + *start = mfd->rotator_iova; + else + *start = info->fix.smem_start; + } + + *len = info->fix.smem_len; + + return 0; +} +EXPORT_SYMBOL(get_fb_phys_info); + +int __init msm_fb_init(void) +{ + int rc = -ENODEV; + + if (msm_fb_register_driver()) + return rc; + +#ifdef MSM_FB_ENABLE_DBGFS + { + struct dentry *root; + + if ((root = msm_fb_get_debugfs_root()) != NULL) { + msm_fb_debugfs_file_create(root, + "msm_fb_msg_printing_level", + (u32 *) &msm_fb_msg_level); + msm_fb_debugfs_file_create(root, + "mddi_msg_printing_level", + (u32 *) &mddi_msg_level); + msm_fb_debugfs_file_create(root, "msm_fb_debug_enabled", + (u32 *) &msm_fb_debug_enabled); + } + } +#endif + + return 0; +} + +/* Called by v4l2 driver to enable/disable overlay pipe */ +int msm_fb_v4l2_enable(struct mdp_overlay *req, bool enable, void **par) +{ + int err = 0; +#ifdef CONFIG_FB_MSM_MDP40 + struct mdp4_overlay_pipe *pipe; + if (enable) { + + err = mdp4_v4l2_overlay_set(fbi_list[0], req, &pipe); + + *(struct mdp4_overlay_pipe **)par = pipe; + + } else { + pipe = *(struct mdp4_overlay_pipe **)par; + mdp4_v4l2_overlay_clear(pipe); + } +#else +#ifdef CONFIG_FB_MSM_MDP30 + if (enable) + err = mdp_ppp_v4l2_overlay_set(fbi_list[0], req); + else + err = mdp_ppp_v4l2_overlay_clear(); +#else + err = -EINVAL; +#endif +#endif + + return err; +} +EXPORT_SYMBOL(msm_fb_v4l2_enable); + +/* Called by v4l2 driver to provide a frame for display */ +int msm_fb_v4l2_update(void *par, bool bUserPtr, + unsigned long srcp0_addr, unsigned long srcp0_size, + unsigned long srcp1_addr, unsigned long srcp1_size, + unsigned long srcp2_addr, unsigned long srcp2_size) +{ +#ifdef CONFIG_FB_MSM_MDP40 + struct mdp4_overlay_pipe *pipe = (struct mdp4_overlay_pipe *)par; + return mdp4_v4l2_overlay_play(fbi_list[0], pipe, + srcp0_addr, srcp1_addr, + srcp2_addr); +#else +#ifdef CONFIG_FB_MSM_MDP30 + if (bUserPtr) + return mdp_ppp_v4l2_overlay_play(fbi_list[0], true, + srcp0_addr, srcp0_size, + srcp1_addr, srcp1_size); + else + return mdp_ppp_v4l2_overlay_play(fbi_list[0], false, + srcp0_addr, srcp0_size, + srcp1_addr, srcp1_size); +#else + return -EINVAL; +#endif +#endif +} +EXPORT_SYMBOL(msm_fb_v4l2_update); + +#ifdef CONFIG_FB_MSM_ALIGN_BUFFER +module_param(align_buffer, bool, 0644); +#endif + +module_init(msm_fb_init); diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h index 88e99e10..63e1d6da 100644 --- a/drivers/video/msm/msm_fb.h +++ b/drivers/video/msm/msm_fb.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2013, The Linux Foundation. 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 and @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include #ifdef CONFIG_HAS_EARLYSUSPEND @@ -54,6 +56,7 @@ struct disp_info_type_suspend { boolean op_enable; boolean sw_refreshing_enable; boolean panel_power_on; + boolean op_suspend; }; struct msmfb_writeback_data_list { @@ -138,6 +141,8 @@ struct msm_fb_data_type { int (*stop_histogram) (struct fb_info *info, uint32_t block); void (*vsync_ctrl) (int enable); void (*vsync_init) (int cndx); + void (*update_panel_info)(struct msm_fb_data_type *mfd); + bool (*is_panel_ready)(void); void *vsync_show; void *cursor_buf; void *cursor_buf_phys; @@ -178,6 +183,7 @@ struct msm_fb_data_type { struct list_head writeback_busy_queue; struct list_head writeback_free_queue; struct list_head writeback_register_queue; + struct switch_dev writeback_sdev; wait_queue_head_t wait_q; struct ion_client *iclient; unsigned long display_iova; @@ -192,7 +198,24 @@ struct msm_fb_data_type { int cont_splash_done; void *copy_splash_buf; unsigned char *copy_splash_phys; + + void *cpu_pm_hdl; + u32 avtimer_phy; int vsync_sysfs_created; + u32 acq_fen_cnt; + struct sync_fence *acq_fen[MDP_MAX_FENCE_FD]; + struct sw_sync_timeline *timeline; + int timeline_value; + struct mutex sync_mutex; + struct completion commit_comp; + u32 is_committing; + struct work_struct commit_work; + void *msm_fb_backup; + boolean panel_driver_on; +}; +struct msm_fb_backup_type { + struct fb_info info; + struct mdp_display_commit disp_commit; }; extern int msm_drm_debug; @@ -207,7 +230,6 @@ extern int msm_drm_debug; "[DISPLAY]%s: " _fmt , \ __func__ , ##_arg); \ } while (0) - struct dentry *msm_fb_get_debugfs_root(void); void msm_fb_debugfs_file_create(struct dentry *root, const char *name, u32 *var); @@ -225,16 +247,14 @@ int msm_fb_writeback_stop(struct fb_info *info); int msm_fb_writeback_terminate(struct fb_info *info); int msm_fb_detect_client(const char *name); int calc_fb_offset(struct msm_fb_data_type *mfd, struct fb_info *fbi, int bpp); -void mipi_dsi_set_column_addr(struct msm_fb_data_type *mfd); -bool suspend_set_state(void); -void suspend_clen_state(void); - +void msm_fb_wait_for_fence(struct msm_fb_data_type *mfd); +int msm_fb_signal_timeline(struct msm_fb_data_type *mfd); +void msm_fb_release_timeline(struct msm_fb_data_type *mfd); #ifdef CONFIG_FB_BACKLIGHT void msm_fb_config_backlight(struct msm_fb_data_type *mfd); #endif -void fill_black_screen(void); -void unfill_black_screen(void); +void fill_black_screen(bool on, uint8 pipe_num, uint8 mixer_num); int msm_fb_check_frame_rate(struct msm_fb_data_type *mfd, struct fb_info *info); diff --git a/drivers/video/msm/msm_fb_bl.c b/drivers/video/msm/msm_fb_bl.c old mode 100644 new mode 100755 diff --git a/drivers/video/msm/msm_fb_panel.c b/drivers/video/msm/msm_fb_panel.c index 8e6a8226..28698c12 100644 --- a/drivers/video/msm/msm_fb_panel.c +++ b/drivers/video/msm/msm_fb_panel.c @@ -78,6 +78,28 @@ int panel_next_off(struct platform_device *pdev) return ret; } +int panel_next_late_init(struct platform_device *pdev) +{ + int ret = 0; + struct msm_fb_panel_data *pdata; + struct msm_fb_panel_data *next_pdata; + struct platform_device *next_pdev; + + pdata = (struct msm_fb_panel_data *)pdev->dev.platform_data; + + if (pdata) { + next_pdev = pdata->next; + if (next_pdev) { + next_pdata = (struct msm_fb_panel_data *) + next_pdev->dev.platform_data; + if ((next_pdata) && (next_pdata->late_init)) + ret = next_pdata->late_init(next_pdev); + } + } + + return ret; +} + struct platform_device *msm_fb_device_alloc(struct msm_fb_panel_data *pdata, u32 type, u32 id) { diff --git a/drivers/video/msm/msm_fb_panel.h b/drivers/video/msm/msm_fb_panel.h old mode 100644 new mode 100755 index 1d429a00..d8a7bbfe --- a/drivers/video/msm/msm_fb_panel.h +++ b/drivers/video/msm/msm_fb_panel.h @@ -58,6 +58,11 @@ typedef enum { MAX_PHYS_TARGET_NUM, } DISP_TARGET_PHYS; +enum { + BLT_SWITCH_TG_OFF, + BLT_SWITCH_TG_ON +}; + /* panel info type */ struct lcd_panel_info { __u32 vsync_enable; @@ -67,6 +72,8 @@ struct lcd_panel_info { __u32 v_pulse_width; __u32 hw_vsync_mode; __u32 vsync_notifier_period; + __u32 blt_ctrl; + __u32 blt_mode; __u32 rev; }; @@ -102,7 +109,7 @@ struct mipi_panel_info { char data_lane1; char data_lane2; char data_lane3; - char dlane_swap; + char dlane_swap; /* data lane swap */ char rgb_swap; char b_sel; char g_sel; @@ -170,7 +177,7 @@ struct msm_panel_info { __u32 frame_count; __u32 is_3d_panel; __u32 frame_rate; - + __u32 frame_interval; struct mddi_panel_info mddi; struct lcd_panel_info lcd; @@ -195,12 +202,25 @@ struct msm_fb_panel_data { /* function entry chain */ int (*on) (struct platform_device *pdev); int (*off) (struct platform_device *pdev); + int (*late_init) (struct platform_device *pdev); int (*glance_screen) (struct platform_device *pdev); int (*power_ctrl) (boolean enable); struct platform_device *next; int (*clk_func) (int enable); }; +enum { + MDP4_OVERLAY_BLT_SWITCH_TG_OFF, + MDP4_OVERLAY_BLT_SWITCH_TG_ON, + MDP4_OVERLAY_BLT_SWITCH_POLL +}; + +enum { + MDP4_OVERLAY_MODE_BLT_CTRL, + MDP4_OVERLAY_MODE_BLT_ALWAYS_ON, + MDP4_OVERLAY_MODE_BLT_ALWAYS_OFF +}; + /*=========================================================================== FUNCTIONS PROTOTYPES ============================================================================*/ @@ -208,6 +228,7 @@ struct platform_device *msm_fb_device_alloc(struct msm_fb_panel_data *pdata, u32 type, u32 id); int panel_next_on(struct platform_device *pdev); int panel_next_off(struct platform_device *pdev); +int panel_next_late_init(struct platform_device *pdev); int lcdc_device_register(struct msm_panel_info *pinfo); diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c index 4948e8bf..28eff62a 100644 --- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c +++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c @@ -10,7 +10,7 @@ * GNU General Public License for more details. * */ -#include +#include #include #include "vcd_ddl.h" #include "vcd_ddl_shared_mem.h" @@ -278,7 +278,7 @@ u32 ddl_decoder_dpb_init(struct ddl_client_context *ddl) memset(frame[i].vcd_frm.virtual + luma_size, 0x80808080, frame[i].vcd_frm.alloc_len - luma_size); - if (frame[i].vcd_frm.ion_flag == CACHED) { + if (frame[i].vcd_frm.ion_flag == ION_FLAG_CACHED) { msm_ion_do_cache_op( ddl_context->video_ion_client, frame[i].vcd_frm.buff_ion_handle, diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c index 32960f16..6129a06b 100644 --- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c +++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c @@ -84,7 +84,7 @@ void *ddl_pmem_alloc(struct ddl_buf_addr *addr, size_t sz, u32 alignment) ionflag = CACHED; kernel_vaddr = (unsigned long *) ion_map_kernel( ddl_context->video_ion_client, - addr->alloc_handle, ionflag); + addr->alloc_handle); if (IS_ERR_OR_NULL(kernel_vaddr)) { DDL_MSG_ERROR("%s() :DDL ION map failed\n", __func__); diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c index 9417d53d..1bfc6f80 100644 --- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c +++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c @@ -69,7 +69,7 @@ static void *res_trk_pmem_map if (res_trk_get_enable_ion() && addr->alloc_handle) { kernel_vaddr = (unsigned long *) ion_map_kernel( ddl_context->video_ion_client, - addr->alloc_handle, UNCACHED); + addr->alloc_handle); if (IS_ERR_OR_NULL(kernel_vaddr)) { DDL_MSG_ERROR("%s():DDL ION client map failed\n", __func__); diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h index 639cfcea..6cb9fe71 100644 --- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h +++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h @@ -14,7 +14,7 @@ #define _VIDEO_720P_RESOURCE_TRACKER_H_ #include -#include +#include #include "vcd_res_tracker_api.h" #ifdef CONFIG_MSM_BUS_SCALING #include diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_utils.c b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_utils.c index aa5e72d3..0d29b8d5 100644 --- a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_utils.c +++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_utils.c @@ -142,8 +142,7 @@ void ddl_pmem_alloc(struct ddl_buf_addr *buff_addr, size_t sz, u32 align) buff_addr->physical_base_addr = (u32 *)phyaddr; kernel_vaddr = (unsigned long *) ion_map_kernel( ddl_context->video_ion_client, - buff_addr->alloc_handle, - UNCACHED); + buff_addr->alloc_handle); if (IS_ERR_OR_NULL(kernel_vaddr)) { ERR("\n%s(): DDL ION map failed\n", __func__); goto unmap_ion_buffer; diff --git a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c index 5b398183..8e3a7359 100644 --- a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c +++ b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c @@ -746,8 +746,10 @@ struct ion_client *res_trk_get_ion_client(void) u32 res_trk_get_mem_type(void) { - u32 mem_type = ION_HEAP(resource_context.memtype); - return mem_type; + if (resource_context.vidc_platform_data->enable_ion) + return (u32) ION_HEAP(resource_context.memtype); + else + return resource_context.memtype; } void res_trk_set_mem_type(enum ddl_mem_area mem_type) diff --git a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.h b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.h index 5f18076d..e1781b68 100644 --- a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.h +++ b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.h @@ -13,7 +13,7 @@ #ifndef _VIDEO_720P_RESOURCE_TRACKER_H_ #define _VIDEO_720P_RESOURCE_TRACKER_H_ #include -#include +#include #include "vcd_res_tracker_api.h" #define VCD_RESTRK_MIN_PERF_LEVEL 37900 diff --git a/drivers/video/msm/vidc/common/dec/vdec.c b/drivers/video/msm/vidc/common/dec/vdec.c index 3aa4d40b..803de9fb 100644 --- a/drivers/video/msm/vidc/common/dec/vdec.c +++ b/drivers/video/msm/vidc/common/dec/vdec.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -844,10 +843,8 @@ static u32 vid_dec_set_h264_mv_buffers(struct video_client_ctx *client_ctx, { struct vcd_property_hdr vcd_property_hdr; struct vcd_property_h264_mv_buffer *vcd_h264_mv_buffer = NULL; - struct msm_mapped_buffer *mapped_buffer = NULL; u32 vcd_status = VCD_ERR_FAIL; - u32 len = 0, flags = 0; - struct file *file; + u32 len = 0; int rc = 0; unsigned long ionflag = 0; unsigned long buffer_size = 0; @@ -868,28 +865,8 @@ static u32 vid_dec_set_h264_mv_buffers(struct video_client_ctx *client_ctx, vcd_h264_mv_buffer->offset = mv_data->offset; if (!vcd_get_ion_status()) { - if (get_pmem_file(vcd_h264_mv_buffer->pmem_fd, - (unsigned long *) (&(vcd_h264_mv_buffer-> - physical_addr)), - (unsigned long *) (&vcd_h264_mv_buffer-> - kernel_virtual_addr), - (unsigned long *) (&len), &file)) { - ERR("%s(): get_pmem_file failed\n", __func__); - return false; - } - put_pmem_file(file); - flags = MSM_SUBSYSTEM_MAP_IOVA; - mapped_buffer = msm_subsystem_map_buffer( - (unsigned long)vcd_h264_mv_buffer->physical_addr, len, - flags, vidc_mmu_subsystem, - sizeof(vidc_mmu_subsystem)/ - sizeof(unsigned int)); - if (IS_ERR(mapped_buffer)) { - pr_err("buffer map failed"); - return false; - } - vcd_h264_mv_buffer->client_data = (void *) mapped_buffer; - vcd_h264_mv_buffer->dev_addr = (u8 *)mapped_buffer->iova[0]; + pr_err("PMEM not available\n"); + return false; } else { client_ctx->h264_mv_ion_handle = ion_import_dma_buf( client_ctx->user_ion_client, @@ -1471,7 +1448,6 @@ static long vid_dec_ioctl(struct file *file, u32 vcd_status; unsigned long kernel_vaddr, phy_addr, len; unsigned long ker_vaddr; - struct file *pmem_file; u32 result = true; void __user *arg = (void __user *)u_arg; int rc = 0; @@ -1786,12 +1762,8 @@ static long vid_dec_ioctl(struct file *file, } if (!vcd_get_ion_status()) { - if (get_pmem_file(seq_header.pmem_fd, - &phy_addr, &kernel_vaddr, &len, &pmem_file)) { - ERR("%s(): get_pmem_file failed\n", __func__); - return false; - } - put_pmem_file(pmem_file); + pr_err("PMEM Not available\n"); + return -EINVAL; } else { client_ctx->seq_hdr_ion_handle = ion_import_dma_buf( client_ctx->user_ion_client, @@ -1812,7 +1784,7 @@ static long vid_dec_ioctl(struct file *file, } ker_vaddr = (unsigned long) ion_map_kernel( client_ctx->user_ion_client, - client_ctx->seq_hdr_ion_handle, ionflag); + client_ctx->seq_hdr_ion_handle); if (!ker_vaddr) { ERR("%s():get_ION_kernel virtual addr fail\n", __func__); diff --git a/drivers/video/msm/vidc/common/enc/venc.c b/drivers/video/msm/vidc/common/enc/venc.c index 29b74d17..816e645f 100644 --- a/drivers/video/msm/vidc/common/enc/venc.c +++ b/drivers/video/msm/vidc/common/enc/venc.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/msm/vidc/common/enc/venc_internal.c b/drivers/video/msm/vidc/common/enc/venc_internal.c index ff82d4bb..dda85136 100644 --- a/drivers/video/msm/vidc/common/enc/venc_internal.c +++ b/drivers/video/msm/vidc/common/enc/venc_internal.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -41,8 +40,6 @@ #endif #define ERR(x...) printk(KERN_ERR x) -static unsigned int vidc_mmu_subsystem[] = { - MSM_SUBSYSTEM_VIDEO}; u32 vid_enc_set_get_base_cfg(struct video_client_ctx *client_ctx, @@ -1577,7 +1574,12 @@ u32 vid_enc_set_buffer(struct video_client_ctx *client_ctx, enum vcd_buffer_type vcd_buffer_t = VCD_BUFFER_INPUT; enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT; u32 vcd_status = VCD_ERR_FAIL; - unsigned long kernel_vaddr, length = 0; + unsigned long kernel_vaddr, length, user_vaddr, phy_addr = 0; + int pmem_fd = 0; + struct ion_handle *buff_handle = NULL; + u32 ion_flag = 0; + struct file *file = NULL; + s32 buffer_index = 0; if (!client_ctx || !buffer_info) return false; @@ -1599,6 +1601,37 @@ u32 vid_enc_set_buffer(struct video_client_ctx *client_ctx, return false; } + /* + * Flush output buffers explcitly once, during registration. This ensures + * any pending CPU writes (if cleared after allocation) are + * committed right away, or else this may get flushed _after_ + * the hardware has written bitstream. rare bug, but can happen ! + */ + if (buffer == VEN_BUFFER_TYPE_OUTPUT) { + user_vaddr = (unsigned long)buffer_info->pbuffer; + if (!vidc_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, + true, &user_vaddr, &kernel_vaddr, + &phy_addr, &pmem_fd, &file, + &buffer_index)) { + ERR("%s(): vidc_lookup_addr_table failed\n", + __func__); + return false; + } + + ion_flag = vidc_get_fd_info(client_ctx, BUFFER_TYPE_OUTPUT, + buffer_info->fd, kernel_vaddr, buffer_index, + &buff_handle); + + if (ion_flag == ION_FLAG_CACHED && buff_handle) { + msm_ion_do_cache_op( + client_ctx->user_ion_client, + buff_handle, + NULL, + (unsigned long) length, + ION_IOC_INV_CACHES); + } + } + vcd_status = vcd_set_buffer(client_ctx->vcd_handle, vcd_buffer_t, (u8 *) kernel_vaddr, buffer_info->sz); @@ -1765,11 +1798,9 @@ u32 vid_enc_set_recon_buffers(struct video_client_ctx *client_ctx, struct venc_recon_addr *venc_recon) { u32 vcd_status = VCD_ERR_FAIL; - u32 len, i, flags = 0; - struct file *file; + u32 len, i; struct vcd_property_hdr vcd_property_hdr; struct vcd_property_enc_recon_buffer *control = NULL; - struct msm_mapped_buffer *mapped_buffer = NULL; int rc = -1; unsigned long ionflag = 0; unsigned long iova = 0; @@ -1801,25 +1832,8 @@ u32 vid_enc_set_recon_buffers(struct video_client_ctx *client_ctx, control->user_virtual_addr = venc_recon->pbuffer; if (!vcd_get_ion_status()) { - if (get_pmem_file(control->pmem_fd, (unsigned long *) - (&(control->physical_addr)), (unsigned long *) - (&control->kernel_virtual_addr), - (unsigned long *) (&len), &file)) { - ERR("%s(): get_pmem_file failed\n", __func__); - return false; - } - put_pmem_file(file); - flags = MSM_SUBSYSTEM_MAP_IOVA; - mapped_buffer = msm_subsystem_map_buffer( - (unsigned long)control->physical_addr, len, - flags, vidc_mmu_subsystem, - sizeof(vidc_mmu_subsystem)/sizeof(unsigned int)); - if (IS_ERR(mapped_buffer)) { - pr_err("buffer map failed"); - return false; - } - control->client_data = (void *) mapped_buffer; - control->dev_addr = (u8 *)mapped_buffer->iova[0]; + pr_err("PMEM not available\n"); + return false; } else { client_ctx->recon_buffer_ion_handle[i] = ion_import_dma_buf( client_ctx->user_ion_client, control->pmem_fd); @@ -1837,8 +1851,7 @@ u32 vid_enc_set_recon_buffers(struct video_client_ctx *client_ctx, } control->kernel_virtual_addr = (u8 *) ion_map_kernel( client_ctx->user_ion_client, - client_ctx->recon_buffer_ion_handle[i], - ionflag); + client_ctx->recon_buffer_ion_handle[i]); if (!control->kernel_virtual_addr) { ERR("%s(): get_ION_kernel virtual addr fail\n", __func__); diff --git a/drivers/video/msm/vidc/common/init/vidc_init.c b/drivers/video/msm/vidc/common/init/vidc_init.c index 870b8925..41ecce16 100644 --- a/drivers/video/msm/vidc/common/init/vidc_init.c +++ b/drivers/video/msm/vidc/common/init/vidc_init.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -49,7 +48,6 @@ static struct vidc_dev *vidc_device_p; static dev_t vidc_dev_num; static struct class *vidc_class; -static unsigned int vidc_mmu_subsystem[] = {MSM_SUBSYSTEM_VIDEO}; static const struct file_operations vidc_fops = { .owner = THIS_MODULE, @@ -565,9 +563,8 @@ u32 vidc_insert_addr_table(struct video_client_ctx *client_ctx, unsigned long len, phys_addr; struct file *file = NULL; u32 *num_of_buffers = NULL; - u32 i, flags; + u32 i; struct buf_addr_table *buf_addr_table; - struct msm_mapped_buffer *mapped_buffer = NULL; struct ion_handle *buff_ion_handle = NULL; unsigned long ionflag = 0; unsigned long iova = 0; @@ -609,26 +606,8 @@ u32 vidc_insert_addr_table(struct video_client_ctx *client_ctx, goto bail_out_add; } else { if (!vcd_get_ion_status()) { - if (get_pmem_file(pmem_fd, &phys_addr, - kernel_vaddr, &len, &file)) { - ERR("%s(): get_pmem_file failed\n", __func__); - goto bail_out_add; - } - put_pmem_file(file); - flags = (buffer == BUFFER_TYPE_INPUT) - ? MSM_SUBSYSTEM_MAP_IOVA : - MSM_SUBSYSTEM_MAP_IOVA|MSM_SUBSYSTEM_ALIGN_IOVA_8K; - mapped_buffer = msm_subsystem_map_buffer(phys_addr, - length, flags, vidc_mmu_subsystem, - sizeof(vidc_mmu_subsystem)/sizeof(unsigned int)); - if (IS_ERR(mapped_buffer)) { - pr_err("buffer map failed"); - goto bail_out_add; - } - buf_addr_table[*num_of_buffers].client_data = (void *) - mapped_buffer; - buf_addr_table[*num_of_buffers].dev_addr = - mapped_buffer->iova[0]; + pr_err("PMEM not available\n"); + return false; } else { buff_ion_handle = ion_import_dma_buf( client_ctx->user_ion_client, pmem_fd); @@ -647,8 +626,7 @@ u32 vidc_insert_addr_table(struct video_client_ctx *client_ctx, *kernel_vaddr = (unsigned long) ion_map_kernel( client_ctx->user_ion_client, - buff_ion_handle, - ionflag); + buff_ion_handle); if (IS_ERR_OR_NULL((void *)*kernel_vaddr)) { ERR("%s():ION virtual addr fail\n", __func__); @@ -913,7 +891,9 @@ void vidc_timer_start(void *timer_handle, u32 time_out) struct vidc_timer *hw_timer = (struct vidc_timer *)timer_handle; DBG("%s(): start timer\n ", __func__); if (hw_timer) { - hw_timer->hw_timeout.expires = jiffies + 1*HZ; + //hw_timer->hw_timeout.expires = jiffies + 1*HZ; + hw_timer->hw_timeout.expires = jiffies + 10*HZ; + add_timer(&hw_timer->hw_timeout); } } diff --git a/drivers/video/msm/vidc/common/vcd/vcd_core.h b/drivers/video/msm/vidc/common/vcd/vcd_core.h index 2dd69d84..c341e934 100644 --- a/drivers/video/msm/vidc/common/vcd/vcd_core.h +++ b/drivers/video/msm/vidc/common/vcd/vcd_core.h @@ -13,7 +13,7 @@ #ifndef _VCD_CORE_H_ #define _VCD_CORE_H_ -#include +#include #include #include "vcd_ddl_api.h" diff --git a/drivers/video/msm/vidc/common/vcd/vcd_sub.c b/drivers/video/msm/vidc/common/vcd/vcd_sub.c index 0eb2e0ea..300ef85a 100644 --- a/drivers/video/msm/vidc/common/vcd/vcd_sub.c +++ b/drivers/video/msm/vidc/common/vcd/vcd_sub.c @@ -105,8 +105,7 @@ static int vcd_pmem_alloc(size_t sz, u8 **kernel_vaddr, u8 **phy_addr, } *kernel_vaddr = (u8 *) ion_map_kernel( cctxt->vcd_ion_client, - map_buffer->alloc_handle, - ionflag); + map_buffer->alloc_handle); if (!(*kernel_vaddr)) { pr_err("%s() ION map failed", __func__); goto ion_free_bailout; diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 8aeadf6b..6af2a3e7 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -645,6 +645,11 @@ *(.security_initcall.init) \ VMLINUX_SYMBOL(__security_initcall_end) = .; +#define COMPAT_EXPORTS \ + VMLINUX_SYMBOL(__compat_exports_start) = .; \ + *(.exportcompat.init) \ + VMLINUX_SYMBOL(__compat_exports_end) = .; + #ifdef CONFIG_BLK_DEV_INITRD #define INIT_RAM_FS \ . = ALIGN(4); \ diff --git a/include/compat/linux/ath9k_platform.h b/include/compat/linux/ath9k_platform.h new file mode 100644 index 00000000..6e3f54f3 --- /dev/null +++ b/include/compat/linux/ath9k_platform.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2009 Gabor Juhos + * Copyright (c) 2009 Imre Kaloz + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LINUX_ATH9K_PLATFORM_H +#define _LINUX_ATH9K_PLATFORM_H + +#define ATH9K_PLAT_EEP_MAX_WORDS 2048 + +struct ath9k_platform_data { + u16 eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS]; + u8 *macaddr; + + int led_pin; + u32 gpio_mask; + u32 gpio_val; + + bool is_clk_25mhz; + int (*get_mac_revision)(void); + int (*external_reset)(void); +}; + +#endif /* _LINUX_ATH9K_PLATFORM_H */ diff --git a/include/compat/linux/atomic.h b/include/compat/linux/atomic.h new file mode 100644 index 00000000..378b748f --- /dev/null +++ b/include/compat/linux/atomic.h @@ -0,0 +1,46 @@ +#ifndef _COMPAT_LINUX_ATOMIC_H +#define _COMPAT_LINUX_ATOMIC_H 1 + +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36)) +#include_next +#else + +#include + +/** + * atomic_inc_not_zero_hint - increment if not null + * @v: pointer of type atomic_t + * @hint: probable value of the atomic before the increment + * + * This version of atomic_inc_not_zero() gives a hint of probable + * value of the atomic. This helps processor to not read the memory + * before doing the atomic read/modify/write cycle, lowering + * number of bus transactions on some arches. + * + * Returns: 0 if increment was not done, 1 otherwise. + */ +#ifndef atomic_inc_not_zero_hint +static inline int atomic_inc_not_zero_hint(atomic_t *v, int hint) +{ + int val, c = hint; + + /* sanity test, should be removed by compiler if hint is a constant */ + if (!hint) + return atomic_inc_not_zero(v); + + do { + val = atomic_cmpxchg(v, c, c + 1); + if (val == c) + return 1; + c = val; + } while (c); + + return 0; +} +#endif + +#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36)) */ + +#endif /* _COMPAT_LINUX_ATOMIC_H */ diff --git a/include/compat/linux/average.h b/include/compat/linux/average.h new file mode 100644 index 00000000..ece86ca3 --- /dev/null +++ b/include/compat/linux/average.h @@ -0,0 +1,5 @@ +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37)) +#include_next +#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37)) */ diff --git a/include/compat/linux/bcma/bcma.h b/include/compat/linux/bcma/bcma.h new file mode 100644 index 00000000..4d4b59de --- /dev/null +++ b/include/compat/linux/bcma/bcma.h @@ -0,0 +1,277 @@ +#ifndef LINUX_BCMA_H_ +#define LINUX_BCMA_H_ + +#include +#include + +#include +#include +#include +#include /* SPROM sharing */ + +#include "bcma_regs.h" + +struct bcma_device; +struct bcma_bus; + +enum bcma_hosttype { + BCMA_HOSTTYPE_PCI, + BCMA_HOSTTYPE_SDIO, + BCMA_HOSTTYPE_SOC, +}; + +struct bcma_chipinfo { + u16 id; + u8 rev; + u8 pkg; +}; + +enum bcma_clkmode { + BCMA_CLKMODE_FAST, + BCMA_CLKMODE_DYNAMIC, +}; + +struct bcma_host_ops { + u8 (*read8)(struct bcma_device *core, u16 offset); + u16 (*read16)(struct bcma_device *core, u16 offset); + u32 (*read32)(struct bcma_device *core, u16 offset); + void (*write8)(struct bcma_device *core, u16 offset, u8 value); + void (*write16)(struct bcma_device *core, u16 offset, u16 value); + void (*write32)(struct bcma_device *core, u16 offset, u32 value); +#ifdef CONFIG_BCMA_BLOCKIO + void (*block_read)(struct bcma_device *core, void *buffer, + size_t count, u16 offset, u8 reg_width); + void (*block_write)(struct bcma_device *core, const void *buffer, + size_t count, u16 offset, u8 reg_width); +#endif + /* Agent ops */ + u32 (*aread32)(struct bcma_device *core, u16 offset); + void (*awrite32)(struct bcma_device *core, u16 offset, u32 value); +}; + +/* Core manufacturers */ +#define BCMA_MANUF_ARM 0x43B +#define BCMA_MANUF_MIPS 0x4A7 +#define BCMA_MANUF_BCM 0x4BF + +/* Core class values. */ +#define BCMA_CL_SIM 0x0 +#define BCMA_CL_EROM 0x1 +#define BCMA_CL_CORESIGHT 0x9 +#define BCMA_CL_VERIF 0xB +#define BCMA_CL_OPTIMO 0xD +#define BCMA_CL_GEN 0xE +#define BCMA_CL_PRIMECELL 0xF + +/* Core-ID values. */ +#define BCMA_CORE_OOB_ROUTER 0x367 /* Out of band */ +#define BCMA_CORE_INVALID 0x700 +#define BCMA_CORE_CHIPCOMMON 0x800 +#define BCMA_CORE_ILINE20 0x801 +#define BCMA_CORE_SRAM 0x802 +#define BCMA_CORE_SDRAM 0x803 +#define BCMA_CORE_PCI 0x804 +#define BCMA_CORE_MIPS 0x805 +#define BCMA_CORE_ETHERNET 0x806 +#define BCMA_CORE_V90 0x807 +#define BCMA_CORE_USB11_HOSTDEV 0x808 +#define BCMA_CORE_ADSL 0x809 +#define BCMA_CORE_ILINE100 0x80A +#define BCMA_CORE_IPSEC 0x80B +#define BCMA_CORE_UTOPIA 0x80C +#define BCMA_CORE_PCMCIA 0x80D +#define BCMA_CORE_INTERNAL_MEM 0x80E +#define BCMA_CORE_MEMC_SDRAM 0x80F +#define BCMA_CORE_OFDM 0x810 +#define BCMA_CORE_EXTIF 0x811 +#define BCMA_CORE_80211 0x812 +#define BCMA_CORE_PHY_A 0x813 +#define BCMA_CORE_PHY_B 0x814 +#define BCMA_CORE_PHY_G 0x815 +#define BCMA_CORE_MIPS_3302 0x816 +#define BCMA_CORE_USB11_HOST 0x817 +#define BCMA_CORE_USB11_DEV 0x818 +#define BCMA_CORE_USB20_HOST 0x819 +#define BCMA_CORE_USB20_DEV 0x81A +#define BCMA_CORE_SDIO_HOST 0x81B +#define BCMA_CORE_ROBOSWITCH 0x81C +#define BCMA_CORE_PARA_ATA 0x81D +#define BCMA_CORE_SATA_XORDMA 0x81E +#define BCMA_CORE_ETHERNET_GBIT 0x81F +#define BCMA_CORE_PCIE 0x820 +#define BCMA_CORE_PHY_N 0x821 +#define BCMA_CORE_SRAM_CTL 0x822 +#define BCMA_CORE_MINI_MACPHY 0x823 +#define BCMA_CORE_ARM_1176 0x824 +#define BCMA_CORE_ARM_7TDMI 0x825 +#define BCMA_CORE_PHY_LP 0x826 +#define BCMA_CORE_PMU 0x827 +#define BCMA_CORE_PHY_SSN 0x828 +#define BCMA_CORE_SDIO_DEV 0x829 +#define BCMA_CORE_ARM_CM3 0x82A +#define BCMA_CORE_PHY_HT 0x82B +#define BCMA_CORE_MIPS_74K 0x82C +#define BCMA_CORE_MAC_GBIT 0x82D +#define BCMA_CORE_DDR12_MEM_CTL 0x82E +#define BCMA_CORE_PCIE_RC 0x82F /* PCIe Root Complex */ +#define BCMA_CORE_OCP_OCP_BRIDGE 0x830 +#define BCMA_CORE_SHARED_COMMON 0x831 +#define BCMA_CORE_OCP_AHB_BRIDGE 0x832 +#define BCMA_CORE_SPI_HOST 0x833 +#define BCMA_CORE_I2S 0x834 +#define BCMA_CORE_SDR_DDR1_MEM_CTL 0x835 /* SDR/DDR1 memory controller core */ +#define BCMA_CORE_SHIM 0x837 /* SHIM component in ubus/6362 */ +#define BCMA_CORE_DEFAULT 0xFFF + +#define BCMA_MAX_NR_CORES 16 + +struct bcma_device { + struct bcma_bus *bus; + struct bcma_device_id id; + + struct device dev; + struct device *dma_dev; + + unsigned int irq; + bool dev_registered; + + u8 core_index; + + u32 addr; + u32 wrap; + + void __iomem *io_addr; + void __iomem *io_wrap; + + void *drvdata; + struct list_head list; +}; + +static inline void *bcma_get_drvdata(struct bcma_device *core) +{ + return core->drvdata; +} +static inline void bcma_set_drvdata(struct bcma_device *core, void *drvdata) +{ + core->drvdata = drvdata; +} + +struct bcma_driver { + const char *name; + const struct bcma_device_id *id_table; + + int (*probe)(struct bcma_device *dev); + void (*remove)(struct bcma_device *dev); + int (*suspend)(struct bcma_device *dev, pm_message_t state); + int (*resume)(struct bcma_device *dev); + void (*shutdown)(struct bcma_device *dev); + + struct device_driver drv; +}; +extern +int __bcma_driver_register(struct bcma_driver *drv, struct module *owner); +#define bcma_driver_register(drv) \ + __bcma_driver_register(drv, THIS_MODULE) + +extern void bcma_driver_unregister(struct bcma_driver *drv); + +struct bcma_bus { + /* The MMIO area. */ + void __iomem *mmio; + + const struct bcma_host_ops *ops; + + enum bcma_hosttype hosttype; + union { + /* Pointer to the PCI bus (only for BCMA_HOSTTYPE_PCI) */ + struct pci_dev *host_pci; + /* Pointer to the SDIO device (only for BCMA_HOSTTYPE_SDIO) */ + struct sdio_func *host_sdio; + }; + + struct bcma_chipinfo chipinfo; + + struct bcma_device *mapped_core; + struct list_head cores; + u8 nr_cores; + u8 init_done:1; + + struct bcma_drv_cc drv_cc; + struct bcma_drv_pci drv_pci; + struct bcma_drv_mips drv_mips; + + /* We decided to share SPROM struct with SSB as long as we do not need + * any hacks for BCMA. This simplifies drivers code. */ + struct ssb_sprom sprom; +}; + +extern inline u32 bcma_read8(struct bcma_device *core, u16 offset) +{ + return core->bus->ops->read8(core, offset); +} +extern inline u32 bcma_read16(struct bcma_device *core, u16 offset) +{ + return core->bus->ops->read16(core, offset); +} +extern inline u32 bcma_read32(struct bcma_device *core, u16 offset) +{ + return core->bus->ops->read32(core, offset); +} +extern inline +void bcma_write8(struct bcma_device *core, u16 offset, u32 value) +{ + core->bus->ops->write8(core, offset, value); +} +extern inline +void bcma_write16(struct bcma_device *core, u16 offset, u32 value) +{ + core->bus->ops->write16(core, offset, value); +} +extern inline +void bcma_write32(struct bcma_device *core, u16 offset, u32 value) +{ + core->bus->ops->write32(core, offset, value); +} +#ifdef CONFIG_BCMA_BLOCKIO +extern inline void bcma_block_read(struct bcma_device *core, void *buffer, + size_t count, u16 offset, u8 reg_width) +{ + core->bus->ops->block_read(core, buffer, count, offset, reg_width); +} +extern inline void bcma_block_write(struct bcma_device *core, const void *buffer, + size_t count, u16 offset, u8 reg_width) +{ + core->bus->ops->block_write(core, buffer, count, offset, reg_width); +} +#endif +extern inline u32 bcma_aread32(struct bcma_device *core, u16 offset) +{ + return core->bus->ops->aread32(core, offset); +} +extern inline +void bcma_awrite32(struct bcma_device *core, u16 offset, u32 value) +{ + core->bus->ops->awrite32(core, offset, value); +} + +#define bcma_mask32(cc, offset, mask) \ + bcma_write32(cc, offset, bcma_read32(cc, offset) & (mask)) +#define bcma_set32(cc, offset, set) \ + bcma_write32(cc, offset, bcma_read32(cc, offset) | (set)) +#define bcma_maskset32(cc, offset, mask, set) \ + bcma_write32(cc, offset, (bcma_read32(cc, offset) & (mask)) | (set)) + +extern bool bcma_core_is_enabled(struct bcma_device *core); +extern void bcma_core_disable(struct bcma_device *core, u32 flags); +extern int bcma_core_enable(struct bcma_device *core, u32 flags); +extern void bcma_core_set_clockmode(struct bcma_device *core, + enum bcma_clkmode clkmode); +extern void bcma_core_pll_ctl(struct bcma_device *core, u32 req, u32 status, + bool on); +#define BCMA_DMA_TRANSLATION_MASK 0xC0000000 +#define BCMA_DMA_TRANSLATION_NONE 0x00000000 +#define BCMA_DMA_TRANSLATION_DMA32_CMT 0x40000000 /* Client Mode Translation for 32-bit DMA */ +#define BCMA_DMA_TRANSLATION_DMA64_CMT 0x80000000 /* Client Mode Translation for 64-bit DMA */ +extern u32 bcma_core_dma_translation(struct bcma_device *core); + +#endif /* LINUX_BCMA_H_ */ diff --git a/include/compat/linux/bcma/bcma_driver_chipcommon.h b/include/compat/linux/bcma/bcma_driver_chipcommon.h new file mode 100644 index 00000000..1526d965 --- /dev/null +++ b/include/compat/linux/bcma/bcma_driver_chipcommon.h @@ -0,0 +1,390 @@ +#ifndef LINUX_BCMA_DRIVER_CC_H_ +#define LINUX_BCMA_DRIVER_CC_H_ + +/** ChipCommon core registers. **/ +#define BCMA_CC_ID 0x0000 +#define BCMA_CC_ID_ID 0x0000FFFF +#define BCMA_CC_ID_ID_SHIFT 0 +#define BCMA_CC_ID_REV 0x000F0000 +#define BCMA_CC_ID_REV_SHIFT 16 +#define BCMA_CC_ID_PKG 0x00F00000 +#define BCMA_CC_ID_PKG_SHIFT 20 +#define BCMA_CC_ID_NRCORES 0x0F000000 +#define BCMA_CC_ID_NRCORES_SHIFT 24 +#define BCMA_CC_ID_TYPE 0xF0000000 +#define BCMA_CC_ID_TYPE_SHIFT 28 +#define BCMA_CC_CAP 0x0004 /* Capabilities */ +#define BCMA_CC_CAP_NRUART 0x00000003 /* # of UARTs */ +#define BCMA_CC_CAP_MIPSEB 0x00000004 /* MIPS in BigEndian Mode */ +#define BCMA_CC_CAP_UARTCLK 0x00000018 /* UART clock select */ +#define BCMA_CC_CAP_UARTCLK_INT 0x00000008 /* UARTs are driven by internal divided clock */ +#define BCMA_CC_CAP_UARTGPIO 0x00000020 /* UARTs on GPIO 15-12 */ +#define BCMA_CC_CAP_EXTBUS 0x000000C0 /* External buses present */ +#define BCMA_CC_CAP_FLASHT 0x00000700 /* Flash Type */ +#define BCMA_CC_FLASHT_NONE 0x00000000 /* No flash */ +#define BCMA_CC_FLASHT_STSER 0x00000100 /* ST serial flash */ +#define BCMA_CC_FLASHT_ATSER 0x00000200 /* Atmel serial flash */ +#define BCMA_CC_FLASHT_NFLASH 0x00000200 +#define BCMA_CC_FLASHT_PARA 0x00000700 /* Parallel flash */ +#define BCMA_CC_CAP_PLLT 0x00038000 /* PLL Type */ +#define BCMA_PLLTYPE_NONE 0x00000000 +#define BCMA_PLLTYPE_1 0x00010000 /* 48Mhz base, 3 dividers */ +#define BCMA_PLLTYPE_2 0x00020000 /* 48Mhz, 4 dividers */ +#define BCMA_PLLTYPE_3 0x00030000 /* 25Mhz, 2 dividers */ +#define BCMA_PLLTYPE_4 0x00008000 /* 48Mhz, 4 dividers */ +#define BCMA_PLLTYPE_5 0x00018000 /* 25Mhz, 4 dividers */ +#define BCMA_PLLTYPE_6 0x00028000 /* 100/200 or 120/240 only */ +#define BCMA_PLLTYPE_7 0x00038000 /* 25Mhz, 4 dividers */ +#define BCMA_CC_CAP_PCTL 0x00040000 /* Power Control */ +#define BCMA_CC_CAP_OTPS 0x00380000 /* OTP size */ +#define BCMA_CC_CAP_OTPS_SHIFT 19 +#define BCMA_CC_CAP_OTPS_BASE 5 +#define BCMA_CC_CAP_JTAGM 0x00400000 /* JTAG master present */ +#define BCMA_CC_CAP_BROM 0x00800000 /* Internal boot ROM active */ +#define BCMA_CC_CAP_64BIT 0x08000000 /* 64-bit Backplane */ +#define BCMA_CC_CAP_PMU 0x10000000 /* PMU available (rev >= 20) */ +#define BCMA_CC_CAP_ECI 0x20000000 /* ECI available (rev >= 20) */ +#define BCMA_CC_CAP_SPROM 0x40000000 /* SPROM present */ +#define BCMA_CC_CORECTL 0x0008 +#define BCMA_CC_CORECTL_UARTCLK0 0x00000001 /* Drive UART with internal clock */ +#define BCMA_CC_CORECTL_SE 0x00000002 /* sync clk out enable (corerev >= 3) */ +#define BCMA_CC_CORECTL_UARTCLKEN 0x00000008 /* UART clock enable (rev >= 21) */ +#define BCMA_CC_BIST 0x000C +#define BCMA_CC_OTPS 0x0010 /* OTP status */ +#define BCMA_CC_OTPS_PROGFAIL 0x80000000 +#define BCMA_CC_OTPS_PROTECT 0x00000007 +#define BCMA_CC_OTPS_HW_PROTECT 0x00000001 +#define BCMA_CC_OTPS_SW_PROTECT 0x00000002 +#define BCMA_CC_OTPS_CID_PROTECT 0x00000004 +#define BCMA_CC_OTPC 0x0014 /* OTP control */ +#define BCMA_CC_OTPC_RECWAIT 0xFF000000 +#define BCMA_CC_OTPC_PROGWAIT 0x00FFFF00 +#define BCMA_CC_OTPC_PRW_SHIFT 8 +#define BCMA_CC_OTPC_MAXFAIL 0x00000038 +#define BCMA_CC_OTPC_VSEL 0x00000006 +#define BCMA_CC_OTPC_SELVL 0x00000001 +#define BCMA_CC_OTPP 0x0018 /* OTP prog */ +#define BCMA_CC_OTPP_COL 0x000000FF +#define BCMA_CC_OTPP_ROW 0x0000FF00 +#define BCMA_CC_OTPP_ROW_SHIFT 8 +#define BCMA_CC_OTPP_READERR 0x10000000 +#define BCMA_CC_OTPP_VALUE 0x20000000 +#define BCMA_CC_OTPP_READ 0x40000000 +#define BCMA_CC_OTPP_START 0x80000000 +#define BCMA_CC_OTPP_BUSY 0x80000000 +#define BCMA_CC_IRQSTAT 0x0020 +#define BCMA_CC_IRQMASK 0x0024 +#define BCMA_CC_IRQ_GPIO 0x00000001 /* gpio intr */ +#define BCMA_CC_IRQ_EXT 0x00000002 /* ro: ext intr pin (corerev >= 3) */ +#define BCMA_CC_IRQ_WDRESET 0x80000000 /* watchdog reset occurred */ +#define BCMA_CC_CHIPCTL 0x0028 /* Rev >= 11 only */ +#define BCMA_CC_CHIPSTAT 0x002C /* Rev >= 11 only */ +#define BCMA_CC_JCMD 0x0030 /* Rev >= 10 only */ +#define BCMA_CC_JCMD_START 0x80000000 +#define BCMA_CC_JCMD_BUSY 0x80000000 +#define BCMA_CC_JCMD_PAUSE 0x40000000 +#define BCMA_CC_JCMD0_ACC_MASK 0x0000F000 +#define BCMA_CC_JCMD0_ACC_IRDR 0x00000000 +#define BCMA_CC_JCMD0_ACC_DR 0x00001000 +#define BCMA_CC_JCMD0_ACC_IR 0x00002000 +#define BCMA_CC_JCMD0_ACC_RESET 0x00003000 +#define BCMA_CC_JCMD0_ACC_IRPDR 0x00004000 +#define BCMA_CC_JCMD0_ACC_PDR 0x00005000 +#define BCMA_CC_JCMD0_IRW_MASK 0x00000F00 +#define BCMA_CC_JCMD_ACC_MASK 0x000F0000 /* Changes for corerev 11 */ +#define BCMA_CC_JCMD_ACC_IRDR 0x00000000 +#define BCMA_CC_JCMD_ACC_DR 0x00010000 +#define BCMA_CC_JCMD_ACC_IR 0x00020000 +#define BCMA_CC_JCMD_ACC_RESET 0x00030000 +#define BCMA_CC_JCMD_ACC_IRPDR 0x00040000 +#define BCMA_CC_JCMD_ACC_PDR 0x00050000 +#define BCMA_CC_JCMD_IRW_MASK 0x00001F00 +#define BCMA_CC_JCMD_IRW_SHIFT 8 +#define BCMA_CC_JCMD_DRW_MASK 0x0000003F +#define BCMA_CC_JIR 0x0034 /* Rev >= 10 only */ +#define BCMA_CC_JDR 0x0038 /* Rev >= 10 only */ +#define BCMA_CC_JCTL 0x003C /* Rev >= 10 only */ +#define BCMA_CC_JCTL_FORCE_CLK 4 /* Force clock */ +#define BCMA_CC_JCTL_EXT_EN 2 /* Enable external targets */ +#define BCMA_CC_JCTL_EN 1 /* Enable Jtag master */ +#define BCMA_CC_FLASHCTL 0x0040 +#define BCMA_CC_FLASHCTL_START 0x80000000 +#define BCMA_CC_FLASHCTL_BUSY BCMA_CC_FLASHCTL_START +#define BCMA_CC_FLASHADDR 0x0044 +#define BCMA_CC_FLASHDATA 0x0048 +#define BCMA_CC_BCAST_ADDR 0x0050 +#define BCMA_CC_BCAST_DATA 0x0054 +#define BCMA_CC_GPIOPULLUP 0x0058 /* Rev >= 20 only */ +#define BCMA_CC_GPIOPULLDOWN 0x005C /* Rev >= 20 only */ +#define BCMA_CC_GPIOIN 0x0060 +#define BCMA_CC_GPIOOUT 0x0064 +#define BCMA_CC_GPIOOUTEN 0x0068 +#define BCMA_CC_GPIOCTL 0x006C +#define BCMA_CC_GPIOPOL 0x0070 +#define BCMA_CC_GPIOIRQ 0x0074 +#define BCMA_CC_WATCHDOG 0x0080 +#define BCMA_CC_GPIOTIMER 0x0088 /* LED powersave (corerev >= 16) */ +#define BCMA_CC_GPIOTIMER_OFFTIME 0x0000FFFF +#define BCMA_CC_GPIOTIMER_OFFTIME_SHIFT 0 +#define BCMA_CC_GPIOTIMER_ONTIME 0xFFFF0000 +#define BCMA_CC_GPIOTIMER_ONTIME_SHIFT 16 +#define BCMA_CC_GPIOTOUTM 0x008C /* LED powersave (corerev >= 16) */ +#define BCMA_CC_CLOCK_N 0x0090 +#define BCMA_CC_CLOCK_SB 0x0094 +#define BCMA_CC_CLOCK_PCI 0x0098 +#define BCMA_CC_CLOCK_M2 0x009C +#define BCMA_CC_CLOCK_MIPS 0x00A0 +#define BCMA_CC_CLKDIV 0x00A4 /* Rev >= 3 only */ +#define BCMA_CC_CLKDIV_SFLASH 0x0F000000 +#define BCMA_CC_CLKDIV_SFLASH_SHIFT 24 +#define BCMA_CC_CLKDIV_OTP 0x000F0000 +#define BCMA_CC_CLKDIV_OTP_SHIFT 16 +#define BCMA_CC_CLKDIV_JTAG 0x00000F00 +#define BCMA_CC_CLKDIV_JTAG_SHIFT 8 +#define BCMA_CC_CLKDIV_UART 0x000000FF +#define BCMA_CC_CAP_EXT 0x00AC /* Capabilities */ +#define BCMA_CC_PLLONDELAY 0x00B0 /* Rev >= 4 only */ +#define BCMA_CC_FREFSELDELAY 0x00B4 /* Rev >= 4 only */ +#define BCMA_CC_SLOWCLKCTL 0x00B8 /* 6 <= Rev <= 9 only */ +#define BCMA_CC_SLOWCLKCTL_SRC 0x00000007 /* slow clock source mask */ +#define BCMA_CC_SLOWCLKCTL_SRC_LPO 0x00000000 /* source of slow clock is LPO */ +#define BCMA_CC_SLOWCLKCTL_SRC_XTAL 0x00000001 /* source of slow clock is crystal */ +#define BCMA_CC_SLOECLKCTL_SRC_PCI 0x00000002 /* source of slow clock is PCI */ +#define BCMA_CC_SLOWCLKCTL_LPOFREQ 0x00000200 /* LPOFreqSel, 1: 160Khz, 0: 32KHz */ +#define BCMA_CC_SLOWCLKCTL_LPOPD 0x00000400 /* LPOPowerDown, 1: LPO is disabled, 0: LPO is enabled */ +#define BCMA_CC_SLOWCLKCTL_FSLOW 0x00000800 /* ForceSlowClk, 1: sb/cores running on slow clock, 0: power logic control */ +#define BCMA_CC_SLOWCLKCTL_IPLL 0x00001000 /* IgnorePllOffReq, 1/0: power logic ignores/honors PLL clock disable requests from core */ +#define BCMA_CC_SLOWCLKCTL_ENXTAL 0x00002000 /* XtalControlEn, 1/0: power logic does/doesn't disable crystal when appropriate */ +#define BCMA_CC_SLOWCLKCTL_XTALPU 0x00004000 /* XtalPU (RO), 1/0: crystal running/disabled */ +#define BCMA_CC_SLOWCLKCTL_CLKDIV 0xFFFF0000 /* ClockDivider (SlowClk = 1/(4+divisor)) */ +#define BCMA_CC_SLOWCLKCTL_CLKDIV_SHIFT 16 +#define BCMA_CC_SYSCLKCTL 0x00C0 /* Rev >= 3 only */ +#define BCMA_CC_SYSCLKCTL_IDLPEN 0x00000001 /* ILPen: Enable Idle Low Power */ +#define BCMA_CC_SYSCLKCTL_ALPEN 0x00000002 /* ALPen: Enable Active Low Power */ +#define BCMA_CC_SYSCLKCTL_PLLEN 0x00000004 /* ForcePLLOn */ +#define BCMA_CC_SYSCLKCTL_FORCEALP 0x00000008 /* Force ALP (or HT if ALPen is not set */ +#define BCMA_CC_SYSCLKCTL_FORCEHT 0x00000010 /* Force HT */ +#define BCMA_CC_SYSCLKCTL_CLKDIV 0xFFFF0000 /* ClkDiv (ILP = 1/(4+divisor)) */ +#define BCMA_CC_SYSCLKCTL_CLKDIV_SHIFT 16 +#define BCMA_CC_CLKSTSTR 0x00C4 /* Rev >= 3 only */ +#define BCMA_CC_EROM 0x00FC +#define BCMA_CC_PCMCIA_CFG 0x0100 +#define BCMA_CC_PCMCIA_MEMWAIT 0x0104 +#define BCMA_CC_PCMCIA_ATTRWAIT 0x0108 +#define BCMA_CC_PCMCIA_IOWAIT 0x010C +#define BCMA_CC_IDE_CFG 0x0110 +#define BCMA_CC_IDE_MEMWAIT 0x0114 +#define BCMA_CC_IDE_ATTRWAIT 0x0118 +#define BCMA_CC_IDE_IOWAIT 0x011C +#define BCMA_CC_PROG_CFG 0x0120 +#define BCMA_CC_PROG_WAITCNT 0x0124 +#define BCMA_CC_FLASH_CFG 0x0128 +#define BCMA_CC_FLASH_CFG_DS 0x0010 /* Data size, 0=8bit, 1=16bit */ +#define BCMA_CC_FLASH_WAITCNT 0x012C +/* 0x1E0 is defined as shared BCMA_CLKCTLST */ +#define BCMA_CC_HW_WORKAROUND 0x01E4 /* Hardware workaround (rev >= 20) */ +#define BCMA_CC_UART0_DATA 0x0300 +#define BCMA_CC_UART0_IMR 0x0304 +#define BCMA_CC_UART0_FCR 0x0308 +#define BCMA_CC_UART0_LCR 0x030C +#define BCMA_CC_UART0_MCR 0x0310 +#define BCMA_CC_UART0_LSR 0x0314 +#define BCMA_CC_UART0_MSR 0x0318 +#define BCMA_CC_UART0_SCRATCH 0x031C +#define BCMA_CC_UART1_DATA 0x0400 +#define BCMA_CC_UART1_IMR 0x0404 +#define BCMA_CC_UART1_FCR 0x0408 +#define BCMA_CC_UART1_LCR 0x040C +#define BCMA_CC_UART1_MCR 0x0410 +#define BCMA_CC_UART1_LSR 0x0414 +#define BCMA_CC_UART1_MSR 0x0418 +#define BCMA_CC_UART1_SCRATCH 0x041C +/* PMU registers (rev >= 20) */ +#define BCMA_CC_PMU_CTL 0x0600 /* PMU control */ +#define BCMA_CC_PMU_CTL_ILP_DIV 0xFFFF0000 /* ILP div mask */ +#define BCMA_CC_PMU_CTL_ILP_DIV_SHIFT 16 +#define BCMA_CC_PMU_CTL_NOILPONW 0x00000200 /* No ILP on wait */ +#define BCMA_CC_PMU_CTL_HTREQEN 0x00000100 /* HT req enable */ +#define BCMA_CC_PMU_CTL_ALPREQEN 0x00000080 /* ALP req enable */ +#define BCMA_CC_PMU_CTL_XTALFREQ 0x0000007C /* Crystal freq */ +#define BCMA_CC_PMU_CTL_XTALFREQ_SHIFT 2 +#define BCMA_CC_PMU_CTL_ILPDIVEN 0x00000002 /* ILP div enable */ +#define BCMA_CC_PMU_CTL_LPOSEL 0x00000001 /* LPO sel */ +#define BCMA_CC_PMU_CAP 0x0604 /* PMU capabilities */ +#define BCMA_CC_PMU_CAP_REVISION 0x000000FF /* Revision mask */ +#define BCMA_CC_PMU_STAT 0x0608 /* PMU status */ +#define BCMA_CC_PMU_STAT_INTPEND 0x00000040 /* Interrupt pending */ +#define BCMA_CC_PMU_STAT_SBCLKST 0x00000030 /* Backplane clock status? */ +#define BCMA_CC_PMU_STAT_HAVEALP 0x00000008 /* ALP available */ +#define BCMA_CC_PMU_STAT_HAVEHT 0x00000004 /* HT available */ +#define BCMA_CC_PMU_STAT_RESINIT 0x00000003 /* Res init */ +#define BCMA_CC_PMU_RES_STAT 0x060C /* PMU res status */ +#define BCMA_CC_PMU_RES_PEND 0x0610 /* PMU res pending */ +#define BCMA_CC_PMU_TIMER 0x0614 /* PMU timer */ +#define BCMA_CC_PMU_MINRES_MSK 0x0618 /* PMU min res mask */ +#define BCMA_CC_PMU_MAXRES_MSK 0x061C /* PMU max res mask */ +#define BCMA_CC_PMU_RES_TABSEL 0x0620 /* PMU res table sel */ +#define BCMA_CC_PMU_RES_DEPMSK 0x0624 /* PMU res dep mask */ +#define BCMA_CC_PMU_RES_UPDNTM 0x0628 /* PMU res updown timer */ +#define BCMA_CC_PMU_RES_TIMER 0x062C /* PMU res timer */ +#define BCMA_CC_PMU_CLKSTRETCH 0x0630 /* PMU clockstretch */ +#define BCMA_CC_PMU_WATCHDOG 0x0634 /* PMU watchdog */ +#define BCMA_CC_PMU_RES_REQTS 0x0640 /* PMU res req timer sel */ +#define BCMA_CC_PMU_RES_REQT 0x0644 /* PMU res req timer */ +#define BCMA_CC_PMU_RES_REQM 0x0648 /* PMU res req mask */ +#define BCMA_CC_CHIPCTL_ADDR 0x0650 +#define BCMA_CC_CHIPCTL_DATA 0x0654 +#define BCMA_CC_REGCTL_ADDR 0x0658 +#define BCMA_CC_REGCTL_DATA 0x065C +#define BCMA_CC_PLLCTL_ADDR 0x0660 +#define BCMA_CC_PLLCTL_DATA 0x0664 +#define BCMA_CC_SPROM 0x0800 /* SPROM beginning */ +#define BCMA_CC_SPROM_PCIE6 0x0830 /* SPROM beginning on PCIe rev >= 6 */ + +/* Divider allocation in 4716/47162/5356 */ +#define BCMA_CC_PMU5_MAINPLL_CPU 1 +#define BCMA_CC_PMU5_MAINPLL_MEM 2 +#define BCMA_CC_PMU5_MAINPLL_SSB 3 + +/* PLL usage in 4716/47162 */ +#define BCMA_CC_PMU4716_MAINPLL_PLL0 12 + +/* PLL usage in 5356/5357 */ +#define BCMA_CC_PMU5356_MAINPLL_PLL0 0 +#define BCMA_CC_PMU5357_MAINPLL_PLL0 0 + +/* 4706 PMU */ +#define BCMA_CC_PMU4706_MAINPLL_PLL0 0 + +/* ALP clock on pre-PMU chips */ +#define BCMA_CC_PMU_ALP_CLOCK 20000000 +/* HT clock for systems with PMU-enabled chipcommon */ +#define BCMA_CC_PMU_HT_CLOCK 80000000 + +/* PMU rev 5 (& 6) */ +#define BCMA_CC_PPL_P1P2_OFF 0 +#define BCMA_CC_PPL_P1_MASK 0x0f000000 +#define BCMA_CC_PPL_P1_SHIFT 24 +#define BCMA_CC_PPL_P2_MASK 0x00f00000 +#define BCMA_CC_PPL_P2_SHIFT 20 +#define BCMA_CC_PPL_M14_OFF 1 +#define BCMA_CC_PPL_MDIV_MASK 0x000000ff +#define BCMA_CC_PPL_MDIV_WIDTH 8 +#define BCMA_CC_PPL_NM5_OFF 2 +#define BCMA_CC_PPL_NDIV_MASK 0xfff00000 +#define BCMA_CC_PPL_NDIV_SHIFT 20 +#define BCMA_CC_PPL_FMAB_OFF 3 +#define BCMA_CC_PPL_MRAT_MASK 0xf0000000 +#define BCMA_CC_PPL_MRAT_SHIFT 28 +#define BCMA_CC_PPL_ABRAT_MASK 0x08000000 +#define BCMA_CC_PPL_ABRAT_SHIFT 27 +#define BCMA_CC_PPL_FDIV_MASK 0x07ffffff +#define BCMA_CC_PPL_PLLCTL_OFF 4 +#define BCMA_CC_PPL_PCHI_OFF 5 +#define BCMA_CC_PPL_PCHI_MASK 0x0000003f + +/* BCM4331 ChipControl numbers. */ +#define BCMA_CHIPCTL_4331_BT_COEXIST BIT(0) /* 0 disable */ +#define BCMA_CHIPCTL_4331_SECI BIT(1) /* 0 SECI is disabled (JATG functional) */ +#define BCMA_CHIPCTL_4331_EXT_LNA BIT(2) /* 0 disable */ +#define BCMA_CHIPCTL_4331_SPROM_GPIO13_15 BIT(3) /* sprom/gpio13-15 mux */ +#define BCMA_CHIPCTL_4331_EXTPA_EN BIT(4) /* 0 ext pa disable, 1 ext pa enabled */ +#define BCMA_CHIPCTL_4331_GPIOCLK_ON_SPROMCS BIT(5) /* set drive out GPIO_CLK on sprom_cs pin */ +#define BCMA_CHIPCTL_4331_PCIE_MDIO_ON_SPROMCS BIT(6) /* use sprom_cs pin as PCIE mdio interface */ +#define BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5 BIT(7) /* aband extpa will be at gpio2/5 and sprom_dout */ +#define BCMA_CHIPCTL_4331_OVR_PIPEAUXCLKEN BIT(8) /* override core control on pipe_AuxClkEnable */ +#define BCMA_CHIPCTL_4331_OVR_PIPEAUXPWRDOWN BIT(9) /* override core control on pipe_AuxPowerDown */ +#define BCMA_CHIPCTL_4331_PCIE_AUXCLKEN BIT(10) /* pcie_auxclkenable */ +#define BCMA_CHIPCTL_4331_PCIE_PIPE_PLLDOWN BIT(11) /* pcie_pipe_pllpowerdown */ +#define BCMA_CHIPCTL_4331_BT_SHD0_ON_GPIO4 BIT(16) /* enable bt_shd0 at gpio4 */ +#define BCMA_CHIPCTL_4331_BT_SHD1_ON_GPIO5 BIT(17) /* enable bt_shd1 at gpio5 */ + +/* Data for the PMU, if available. + * Check availability with ((struct bcma_chipcommon)->capabilities & BCMA_CC_CAP_PMU) + */ +struct bcma_chipcommon_pmu { + u8 rev; /* PMU revision */ + u32 crystalfreq; /* The active crystal frequency (in kHz) */ +}; + +#ifdef CONFIG_BCMA_DRIVER_MIPS +struct bcma_pflash { + u8 buswidth; + u32 window; + u32 window_size; +}; + +struct bcma_serial_port { + void *regs; + unsigned long clockspeed; + unsigned int irq; + unsigned int baud_base; + unsigned int reg_shift; +}; +#endif /* CONFIG_BCMA_DRIVER_MIPS */ + +struct bcma_drv_cc { + struct bcma_device *core; + u32 status; + u32 capabilities; + u32 capabilities_ext; + u8 setup_done:1; + /* Fast Powerup Delay constant */ + u16 fast_pwrup_delay; + struct bcma_chipcommon_pmu pmu; +#ifdef CONFIG_BCMA_DRIVER_MIPS + struct bcma_pflash pflash; + + int nr_serial_ports; + struct bcma_serial_port serial_ports[4]; +#endif /* CONFIG_BCMA_DRIVER_MIPS */ +}; + +/* Register access */ +#define bcma_cc_read32(cc, offset) \ + bcma_read32((cc)->core, offset) +#define bcma_cc_write32(cc, offset, val) \ + bcma_write32((cc)->core, offset, val) + +#define bcma_cc_mask32(cc, offset, mask) \ + bcma_cc_write32(cc, offset, bcma_cc_read32(cc, offset) & (mask)) +#define bcma_cc_set32(cc, offset, set) \ + bcma_cc_write32(cc, offset, bcma_cc_read32(cc, offset) | (set)) +#define bcma_cc_maskset32(cc, offset, mask, set) \ + bcma_cc_write32(cc, offset, (bcma_cc_read32(cc, offset) & (mask)) | (set)) + +extern void bcma_core_chipcommon_init(struct bcma_drv_cc *cc); + +extern void bcma_chipco_suspend(struct bcma_drv_cc *cc); +extern void bcma_chipco_resume(struct bcma_drv_cc *cc); + +void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable); + +extern void bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, + u32 ticks); + +void bcma_chipco_irq_mask(struct bcma_drv_cc *cc, u32 mask, u32 value); + +u32 bcma_chipco_irq_status(struct bcma_drv_cc *cc, u32 mask); + +/* Chipcommon GPIO pin access. */ +u32 bcma_chipco_gpio_in(struct bcma_drv_cc *cc, u32 mask); +u32 bcma_chipco_gpio_out(struct bcma_drv_cc *cc, u32 mask, u32 value); +u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value); +u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value); +u32 bcma_chipco_gpio_intmask(struct bcma_drv_cc *cc, u32 mask, u32 value); +u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value); + +/* PMU support */ +extern void bcma_pmu_init(struct bcma_drv_cc *cc); + +extern void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, + u32 value); +extern void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, + u32 mask, u32 set); +extern void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc, + u32 offset, u32 mask, u32 set); +extern void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, + u32 offset, u32 mask, u32 set); + +#endif /* LINUX_BCMA_DRIVER_CC_H_ */ diff --git a/include/compat/linux/bcma/bcma_driver_mips.h b/include/compat/linux/bcma/bcma_driver_mips.h new file mode 100644 index 00000000..c0043645 --- /dev/null +++ b/include/compat/linux/bcma/bcma_driver_mips.h @@ -0,0 +1,51 @@ +#ifndef LINUX_BCMA_DRIVER_MIPS_H_ +#define LINUX_BCMA_DRIVER_MIPS_H_ + +#define BCMA_MIPS_IPSFLAG 0x0F08 +/* which sbflags get routed to mips interrupt 1 */ +#define BCMA_MIPS_IPSFLAG_IRQ1 0x0000003F +#define BCMA_MIPS_IPSFLAG_IRQ1_SHIFT 0 +/* which sbflags get routed to mips interrupt 2 */ +#define BCMA_MIPS_IPSFLAG_IRQ2 0x00003F00 +#define BCMA_MIPS_IPSFLAG_IRQ2_SHIFT 8 +/* which sbflags get routed to mips interrupt 3 */ +#define BCMA_MIPS_IPSFLAG_IRQ3 0x003F0000 +#define BCMA_MIPS_IPSFLAG_IRQ3_SHIFT 16 +/* which sbflags get routed to mips interrupt 4 */ +#define BCMA_MIPS_IPSFLAG_IRQ4 0x3F000000 +#define BCMA_MIPS_IPSFLAG_IRQ4_SHIFT 24 + +/* MIPS 74K core registers */ +#define BCMA_MIPS_MIPS74K_CORECTL 0x0000 +#define BCMA_MIPS_MIPS74K_EXCEPTBASE 0x0004 +#define BCMA_MIPS_MIPS74K_BIST 0x000C +#define BCMA_MIPS_MIPS74K_INTMASK_INT0 0x0014 +#define BCMA_MIPS_MIPS74K_INTMASK(int) \ + ((int) * 4 + BCMA_MIPS_MIPS74K_INTMASK_INT0) +#define BCMA_MIPS_MIPS74K_NMIMASK 0x002C +#define BCMA_MIPS_MIPS74K_GPIOSEL 0x0040 +#define BCMA_MIPS_MIPS74K_GPIOOUT 0x0044 +#define BCMA_MIPS_MIPS74K_GPIOEN 0x0048 +#define BCMA_MIPS_MIPS74K_CLKCTLST 0x01E0 + +#define BCMA_MIPS_OOBSELOUTA30 0x100 + +struct bcma_device; + +struct bcma_drv_mips { + struct bcma_device *core; + u8 setup_done:1; + unsigned int assigned_irqs; +}; + +#ifdef CONFIG_BCMA_DRIVER_MIPS +extern void bcma_core_mips_init(struct bcma_drv_mips *mcore); +#else +static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore) { } +#endif + +extern u32 bcma_cpu_clock(struct bcma_drv_mips *mcore); + +extern unsigned int bcma_core_mips_irq(struct bcma_device *dev); + +#endif /* LINUX_BCMA_DRIVER_MIPS_H_ */ diff --git a/include/compat/linux/bcma/bcma_driver_pci.h b/include/compat/linux/bcma/bcma_driver_pci.h new file mode 100644 index 00000000..3871b668 --- /dev/null +++ b/include/compat/linux/bcma/bcma_driver_pci.h @@ -0,0 +1,91 @@ +#ifndef LINUX_BCMA_DRIVER_PCI_H_ +#define LINUX_BCMA_DRIVER_PCI_H_ + +#include + +struct pci_dev; + +/** PCI core registers. **/ +#define BCMA_CORE_PCI_CTL 0x0000 /* PCI Control */ +#define BCMA_CORE_PCI_CTL_RST_OE 0x00000001 /* PCI_RESET Output Enable */ +#define BCMA_CORE_PCI_CTL_RST 0x00000002 /* PCI_RESET driven out to pin */ +#define BCMA_CORE_PCI_CTL_CLK_OE 0x00000004 /* Clock gate Output Enable */ +#define BCMA_CORE_PCI_CTL_CLK 0x00000008 /* Gate for clock driven out to pin */ +#define BCMA_CORE_PCI_ARBCTL 0x0010 /* PCI Arbiter Control */ +#define BCMA_CORE_PCI_ARBCTL_INTERN 0x00000001 /* Use internal arbiter */ +#define BCMA_CORE_PCI_ARBCTL_EXTERN 0x00000002 /* Use external arbiter */ +#define BCMA_CORE_PCI_ARBCTL_PARKID 0x00000006 /* Mask, selects which agent is parked on an idle bus */ +#define BCMA_CORE_PCI_ARBCTL_PARKID_LAST 0x00000000 /* Last requestor */ +#define BCMA_CORE_PCI_ARBCTL_PARKID_4710 0x00000002 /* 4710 */ +#define BCMA_CORE_PCI_ARBCTL_PARKID_EXT0 0x00000004 /* External requestor 0 */ +#define BCMA_CORE_PCI_ARBCTL_PARKID_EXT1 0x00000006 /* External requestor 1 */ +#define BCMA_CORE_PCI_ISTAT 0x0020 /* Interrupt status */ +#define BCMA_CORE_PCI_ISTAT_INTA 0x00000001 /* PCI INTA# */ +#define BCMA_CORE_PCI_ISTAT_INTB 0x00000002 /* PCI INTB# */ +#define BCMA_CORE_PCI_ISTAT_SERR 0x00000004 /* PCI SERR# (write to clear) */ +#define BCMA_CORE_PCI_ISTAT_PERR 0x00000008 /* PCI PERR# (write to clear) */ +#define BCMA_CORE_PCI_ISTAT_PME 0x00000010 /* PCI PME# */ +#define BCMA_CORE_PCI_IMASK 0x0024 /* Interrupt mask */ +#define BCMA_CORE_PCI_IMASK_INTA 0x00000001 /* PCI INTA# */ +#define BCMA_CORE_PCI_IMASK_INTB 0x00000002 /* PCI INTB# */ +#define BCMA_CORE_PCI_IMASK_SERR 0x00000004 /* PCI SERR# */ +#define BCMA_CORE_PCI_IMASK_PERR 0x00000008 /* PCI PERR# */ +#define BCMA_CORE_PCI_IMASK_PME 0x00000010 /* PCI PME# */ +#define BCMA_CORE_PCI_MBOX 0x0028 /* Backplane to PCI Mailbox */ +#define BCMA_CORE_PCI_MBOX_F0_0 0x00000100 /* PCI function 0, INT 0 */ +#define BCMA_CORE_PCI_MBOX_F0_1 0x00000200 /* PCI function 0, INT 1 */ +#define BCMA_CORE_PCI_MBOX_F1_0 0x00000400 /* PCI function 1, INT 0 */ +#define BCMA_CORE_PCI_MBOX_F1_1 0x00000800 /* PCI function 1, INT 1 */ +#define BCMA_CORE_PCI_MBOX_F2_0 0x00001000 /* PCI function 2, INT 0 */ +#define BCMA_CORE_PCI_MBOX_F2_1 0x00002000 /* PCI function 2, INT 1 */ +#define BCMA_CORE_PCI_MBOX_F3_0 0x00004000 /* PCI function 3, INT 0 */ +#define BCMA_CORE_PCI_MBOX_F3_1 0x00008000 /* PCI function 3, INT 1 */ +#define BCMA_CORE_PCI_BCAST_ADDR 0x0050 /* Backplane Broadcast Address */ +#define BCMA_CORE_PCI_BCAST_ADDR_MASK 0x000000FF +#define BCMA_CORE_PCI_BCAST_DATA 0x0054 /* Backplane Broadcast Data */ +#define BCMA_CORE_PCI_GPIO_IN 0x0060 /* rev >= 2 only */ +#define BCMA_CORE_PCI_GPIO_OUT 0x0064 /* rev >= 2 only */ +#define BCMA_CORE_PCI_GPIO_ENABLE 0x0068 /* rev >= 2 only */ +#define BCMA_CORE_PCI_GPIO_CTL 0x006C /* rev >= 2 only */ +#define BCMA_CORE_PCI_SBTOPCI0 0x0100 /* Backplane to PCI translation 0 (sbtopci0) */ +#define BCMA_CORE_PCI_SBTOPCI0_MASK 0xFC000000 +#define BCMA_CORE_PCI_SBTOPCI1 0x0104 /* Backplane to PCI translation 1 (sbtopci1) */ +#define BCMA_CORE_PCI_SBTOPCI1_MASK 0xFC000000 +#define BCMA_CORE_PCI_SBTOPCI2 0x0108 /* Backplane to PCI translation 2 (sbtopci2) */ +#define BCMA_CORE_PCI_SBTOPCI2_MASK 0xC0000000 +#define BCMA_CORE_PCI_PCICFG0 0x0400 /* PCI config space 0 (rev >= 8) */ +#define BCMA_CORE_PCI_PCICFG1 0x0500 /* PCI config space 1 (rev >= 8) */ +#define BCMA_CORE_PCI_PCICFG2 0x0600 /* PCI config space 2 (rev >= 8) */ +#define BCMA_CORE_PCI_PCICFG3 0x0700 /* PCI config space 3 (rev >= 8) */ +#define BCMA_CORE_PCI_SPROM(wordoffset) (0x0800 + ((wordoffset) * 2)) /* SPROM shadow area (72 bytes) */ + +/* SBtoPCIx */ +#define BCMA_CORE_PCI_SBTOPCI_MEM 0x00000000 +#define BCMA_CORE_PCI_SBTOPCI_IO 0x00000001 +#define BCMA_CORE_PCI_SBTOPCI_CFG0 0x00000002 +#define BCMA_CORE_PCI_SBTOPCI_CFG1 0x00000003 +#define BCMA_CORE_PCI_SBTOPCI_PREF 0x00000004 /* Prefetch enable */ +#define BCMA_CORE_PCI_SBTOPCI_BURST 0x00000008 /* Burst enable */ +#define BCMA_CORE_PCI_SBTOPCI_MRM 0x00000020 /* Memory Read Multiple */ +#define BCMA_CORE_PCI_SBTOPCI_RC 0x00000030 /* Read Command mask (rev >= 11) */ +#define BCMA_CORE_PCI_SBTOPCI_RC_READ 0x00000000 /* Memory read */ +#define BCMA_CORE_PCI_SBTOPCI_RC_READL 0x00000010 /* Memory read line */ +#define BCMA_CORE_PCI_SBTOPCI_RC_READM 0x00000020 /* Memory read multiple */ + +/* PCIcore specific boardflags */ +#define BCMA_CORE_PCI_BFL_NOPCI 0x00000400 /* Board leaves PCI floating */ + +struct bcma_drv_pci { + struct bcma_device *core; + u8 setup_done:1; +}; + +/* Register access */ +#define pcicore_read32(pc, offset) bcma_read32((pc)->core, offset) +#define pcicore_write32(pc, offset, val) bcma_write32((pc)->core, offset, val) + +extern void bcma_core_pci_init(struct bcma_drv_pci *pc); +extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, + struct bcma_device *core, bool enable); + +#endif /* LINUX_BCMA_DRIVER_PCI_H_ */ diff --git a/include/compat/linux/bcma/bcma_regs.h b/include/compat/linux/bcma/bcma_regs.h new file mode 100644 index 00000000..9faae2ae --- /dev/null +++ b/include/compat/linux/bcma/bcma_regs.h @@ -0,0 +1,59 @@ +#ifndef LINUX_BCMA_REGS_H_ +#define LINUX_BCMA_REGS_H_ + +/* Some single registers are shared between many cores */ +/* BCMA_CLKCTLST: ChipCommon (rev >= 20), PCIe, 80211 */ +#define BCMA_CLKCTLST 0x01E0 /* Clock control and status */ +#define BCMA_CLKCTLST_FORCEALP 0x00000001 /* Force ALP request */ +#define BCMA_CLKCTLST_FORCEHT 0x00000002 /* Force HT request */ +#define BCMA_CLKCTLST_FORCEILP 0x00000004 /* Force ILP request */ +#define BCMA_CLKCTLST_HAVEALPREQ 0x00000008 /* ALP available request */ +#define BCMA_CLKCTLST_HAVEHTREQ 0x00000010 /* HT available request */ +#define BCMA_CLKCTLST_HWCROFF 0x00000020 /* Force HW clock request off */ +#define BCMA_CLKCTLST_EXTRESREQ 0x00000700 /* Mask of external resource requests */ +#define BCMA_CLKCTLST_HAVEALP 0x00010000 /* ALP available */ +#define BCMA_CLKCTLST_HAVEHT 0x00020000 /* HT available */ +#define BCMA_CLKCTLST_BP_ON_ALP 0x00040000 /* RO: running on ALP clock */ +#define BCMA_CLKCTLST_BP_ON_HT 0x00080000 /* RO: running on HT clock */ +#define BCMA_CLKCTLST_EXTRESST 0x07000000 /* Mask of external resource status */ +/* Is there any BCM4328 on BCMA bus? */ +#define BCMA_CLKCTLST_4328A0_HAVEHT 0x00010000 /* 4328a0 has reversed bits */ +#define BCMA_CLKCTLST_4328A0_HAVEALP 0x00020000 /* 4328a0 has reversed bits */ + +/* Agent registers (common for every core) */ +#define BCMA_IOCTL 0x0408 /* IO control */ +#define BCMA_IOCTL_CLK 0x0001 +#define BCMA_IOCTL_FGC 0x0002 +#define BCMA_IOCTL_CORE_BITS 0x3FFC +#define BCMA_IOCTL_PME_EN 0x4000 +#define BCMA_IOCTL_BIST_EN 0x8000 +#define BCMA_IOST 0x0500 /* IO status */ +#define BCMA_IOST_CORE_BITS 0x0FFF +#define BCMA_IOST_DMA64 0x1000 +#define BCMA_IOST_GATED_CLK 0x2000 +#define BCMA_IOST_BIST_ERROR 0x4000 +#define BCMA_IOST_BIST_DONE 0x8000 +#define BCMA_RESET_CTL 0x0800 +#define BCMA_RESET_CTL_RESET 0x0001 + +/* BCMA PCI config space registers. */ +#define BCMA_PCI_PMCSR 0x44 +#define BCMA_PCI_PE 0x100 +#define BCMA_PCI_BAR0_WIN 0x80 /* Backplane address space 0 */ +#define BCMA_PCI_BAR1_WIN 0x84 /* Backplane address space 1 */ +#define BCMA_PCI_SPROMCTL 0x88 /* SPROM control */ +#define BCMA_PCI_SPROMCTL_WE 0x10 /* SPROM write enable */ +#define BCMA_PCI_BAR1_CONTROL 0x8c /* Address space 1 burst control */ +#define BCMA_PCI_IRQS 0x90 /* PCI interrupts */ +#define BCMA_PCI_IRQMASK 0x94 /* PCI IRQ control and mask (pcirev >= 6 only) */ +#define BCMA_PCI_BACKPLANE_IRQS 0x98 /* Backplane Interrupts */ +#define BCMA_PCI_BAR0_WIN2 0xAC +#define BCMA_PCI_GPIO_IN 0xB0 /* GPIO Input (pcirev >= 3 only) */ +#define BCMA_PCI_GPIO_OUT 0xB4 /* GPIO Output (pcirev >= 3 only) */ +#define BCMA_PCI_GPIO_OUT_ENABLE 0xB8 /* GPIO Output Enable/Disable (pcirev >= 3 only) */ +#define BCMA_PCI_GPIO_SCS 0x10 /* PCI config space bit 4 for 4306c0 slow clock source */ +#define BCMA_PCI_GPIO_HWRAD 0x20 /* PCI config space GPIO 13 for hw radio disable */ +#define BCMA_PCI_GPIO_XTAL 0x40 /* PCI config space GPIO 14 for Xtal powerup */ +#define BCMA_PCI_GPIO_PLL 0x80 /* PCI config space GPIO 15 for PLL powerdown */ + +#endif /* LINUX_BCMA_REGS_H_ */ diff --git a/include/compat/linux/bcma/bcma_soc.h b/include/compat/linux/bcma/bcma_soc.h new file mode 100644 index 00000000..4203c559 --- /dev/null +++ b/include/compat/linux/bcma/bcma_soc.h @@ -0,0 +1,16 @@ +#ifndef LINUX_BCMA_SOC_H_ +#define LINUX_BCMA_SOC_H_ + +#include + +struct bcma_soc { + struct bcma_bus bus; + struct bcma_device core_cc; + struct bcma_device core_mips; +}; + +int __init bcma_host_soc_register(struct bcma_soc *soc); + +int bcma_bus_register(struct bcma_bus *bus); + +#endif /* LINUX_BCMA_SOC_H_ */ diff --git a/include/compat/linux/bitops.h b/include/compat/linux/bitops.h new file mode 100644 index 00000000..aa76af18 --- /dev/null +++ b/include/compat/linux/bitops.h @@ -0,0 +1,211 @@ +#ifndef _LINUX_BITOPS_H +#define _LINUX_BITOPS_H +#include + +#ifdef __KERNEL__ +#define BIT(nr) (1UL << (nr)) +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) +#define BITS_PER_BYTE 8 +#ifndef BITS_TO_LONGS /* Older kernels define this already */ +#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) +#endif +#endif + +extern unsigned int __sw_hweight8(unsigned int w); +extern unsigned int __sw_hweight16(unsigned int w); +extern unsigned int __sw_hweight32(unsigned int w); +extern unsigned long __sw_hweight64(__u64 w); + +/* + * Include this here because some architectures need generic_ffs/fls in + * scope + */ +#include + +#define for_each_set_bit(bit, addr, size) \ + for ((bit) = find_first_bit((addr), (size)); \ + (bit) < (size); \ + (bit) = find_next_bit((addr), (size), (bit) + 1)) + +static __inline__ int get_bitmask_order(unsigned int count) +{ + int order; + + order = fls(count); + return order; /* We could be slightly more clever with -1 here... */ +} + +static __inline__ int get_count_order(unsigned int count) +{ + int order; + + order = fls(count) - 1; + if (count & (count - 1)) + order++; + return order; +} + +static inline unsigned long hweight_long(unsigned long w) +{ + return sizeof(w) == 4 ? hweight32(w) : hweight64(w); +} + +/** + * rol32 - rotate a 32-bit value left + * @word: value to rotate + * @shift: bits to roll + */ +static inline __u32 rol32(__u32 word, unsigned int shift) +{ + return (word << shift) | (word >> (32 - shift)); +} + +/** + * ror32 - rotate a 32-bit value right + * @word: value to rotate + * @shift: bits to roll + */ +static inline __u32 ror32(__u32 word, unsigned int shift) +{ + return (word >> shift) | (word << (32 - shift)); +} + +/** + * rol16 - rotate a 16-bit value left + * @word: value to rotate + * @shift: bits to roll + */ +static inline __u16 rol16(__u16 word, unsigned int shift) +{ + return (word << shift) | (word >> (16 - shift)); +} + +/** + * ror16 - rotate a 16-bit value right + * @word: value to rotate + * @shift: bits to roll + */ +static inline __u16 ror16(__u16 word, unsigned int shift) +{ + return (word >> shift) | (word << (16 - shift)); +} + +/** + * rol8 - rotate an 8-bit value left + * @word: value to rotate + * @shift: bits to roll + */ +static inline __u8 rol8(__u8 word, unsigned int shift) +{ + return (word << shift) | (word >> (8 - shift)); +} + +/** + * ror8 - rotate an 8-bit value right + * @word: value to rotate + * @shift: bits to roll + */ +static inline __u8 ror8(__u8 word, unsigned int shift) +{ + return (word >> shift) | (word << (8 - shift)); +} + +/** + * sign_extend32 - sign extend a 32-bit value using specified bit as sign-bit + * @value: value to sign extend + * @index: 0 based bit index (0<=index<32) to sign bit + */ +static inline __s32 sign_extend32(__u32 value, int index) +{ + __u8 shift = 31 - index; + return (__s32)(value << shift) >> shift; +} + +static inline unsigned fls_long(unsigned long l) +{ + if (sizeof(l) == 4) + return fls(l); + return fls64(l); +} + +/** + * __ffs64 - find first set bit in a 64 bit word + * @word: The 64 bit word + * + * On 64 bit arches this is a synomyn for __ffs + * The result is not defined if no bits are set, so check that @word + * is non-zero before calling this. + */ +static inline unsigned long __ffs64(u64 word) +{ +#if BITS_PER_LONG == 32 + if (((u32)word) == 0UL) + return __ffs((u32)(word >> 32)) + 32; +#elif BITS_PER_LONG != 64 +#error BITS_PER_LONG not 32 or 64 +#endif + return __ffs((unsigned long)word); +} + +#ifdef __KERNEL__ +#ifdef CONFIG_GENERIC_FIND_FIRST_BIT + +/** + * find_first_bit - find the first set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit number of the first set bit. + */ +extern unsigned long find_first_bit(const unsigned long *addr, + unsigned long size); + +/** + * find_first_zero_bit - find the first cleared bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit number of the first cleared bit. + */ +extern unsigned long find_first_zero_bit(const unsigned long *addr, + unsigned long size); +#endif /* CONFIG_GENERIC_FIND_FIRST_BIT */ + +#ifdef CONFIG_GENERIC_FIND_LAST_BIT +/** + * find_last_bit - find the last set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit number of the first set bit, or size. + */ +extern unsigned long find_last_bit(const unsigned long *addr, + unsigned long size); +#endif /* CONFIG_GENERIC_FIND_LAST_BIT */ + +#ifdef CONFIG_GENERIC_FIND_NEXT_BIT + +/** + * find_next_bit - find the next set bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The bitmap size in bits + */ +extern unsigned long find_next_bit(const unsigned long *addr, + unsigned long size, unsigned long offset); + +/** + * find_next_zero_bit - find the next cleared bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The bitmap size in bits + */ + +extern unsigned long find_next_zero_bit(const unsigned long *addr, + unsigned long size, + unsigned long offset); + +#endif /* CONFIG_GENERIC_FIND_NEXT_BIT */ +#endif /* __KERNEL__ */ +#endif diff --git a/include/compat/linux/compat-2.6.14.h b/include/compat/linux/compat-2.6.14.h new file mode 100644 index 00000000..1f19f7fe --- /dev/null +++ b/include/compat/linux/compat-2.6.14.h @@ -0,0 +1,13 @@ +#ifndef LINUX_26_14_COMPAT_H +#define LINUX_26_14_COMPAT_H + +#include + +/* Compat work for 2.6.14 */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)) + +typedef unsigned int gfp_t; + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)) */ + +#endif /* LINUX_26_14_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.18.h b/include/compat/linux/compat-2.6.18.h new file mode 100644 index 00000000..5e0182b1 --- /dev/null +++ b/include/compat/linux/compat-2.6.18.h @@ -0,0 +1,13 @@ +#ifndef LINUX_26_18_COMPAT_H +#define LINUX_26_18_COMPAT_H + +#include + +/* Compat work for 2.6.18 */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) + +#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) */ + +#endif /* LINUX_26_18_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.19.h b/include/compat/linux/compat-2.6.19.h new file mode 100644 index 00000000..1e648c0a --- /dev/null +++ b/include/compat/linux/compat-2.6.19.h @@ -0,0 +1,24 @@ +#ifndef LINUX_26_19_COMPAT_H +#define LINUX_26_19_COMPAT_H + +#include + +/* Compat work for 2.6.19 */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)) + +#include + +static inline int +compat_kmem_cache_destroy(struct kmem_cache *cachep) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) + return kmem_cache_destroy(cachep); +#else + kmem_cache_destroy(cachep); + return 0; +#endif +} + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) */ + +#endif /* LINUX_26_19_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.20.h b/include/compat/linux/compat-2.6.20.h new file mode 100644 index 00000000..14579e29 --- /dev/null +++ b/include/compat/linux/compat-2.6.20.h @@ -0,0 +1,21 @@ +#ifndef LINUX_26_20_COMPAT_H +#define LINUX_26_20_COMPAT_H + +#include + +/* Compat work for 2.6.20 */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) + +#include + +typedef void (*work_func_t)(struct work_struct *work); +typedef void (*compat_work_func_t)(void *work); +static inline void (INIT_WORK)(struct work_struct *work, work_func_t func) +{ + INIT_WORK(work, (compat_work_func_t)func, work); +} +#undef INIT_WORK + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) */ + +#endif /* LINUX_26_20_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.21.h b/include/compat/linux/compat-2.6.21.h new file mode 100644 index 00000000..89ed6d9a --- /dev/null +++ b/include/compat/linux/compat-2.6.21.h @@ -0,0 +1,18 @@ +#ifndef LINUX_26_21_COMPAT_H +#define LINUX_26_21_COMPAT_H + +#include + +/* Compat work for 2.6.21 */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + +#include + +#define register_sysctl_table(table) \ + ({ \ + register_sysctl_table((table), 0); \ + }) + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) */ + +#endif /* LINUX_26_21_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.22.h b/include/compat/linux/compat-2.6.22.h new file mode 100644 index 00000000..7ca1b18e --- /dev/null +++ b/include/compat/linux/compat-2.6.22.h @@ -0,0 +1,104 @@ +#ifndef LINUX_26_22_COMPAT_H +#define LINUX_26_22_COMPAT_H + +#include + +/* Compat work for 2.6.21 */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) + +#include +#include + +/* reuse ax25_ptr */ +#define ieee80211_ptr ax25_ptr + +#ifdef CONFIG_AX25 +#error Compat reuses the AX.25 pointer so that may not be enabled! +#endif + +static inline unsigned char *skb_mac_header(const struct sk_buff *skb) +{ + return skb->mac.raw; +} + +static inline void skb_set_mac_header(struct sk_buff *skb, int offset) +{ + skb->mac.raw = skb->data + offset; +} + +static inline void skb_reset_mac_header(struct sk_buff *skb) +{ + skb->mac.raw = skb->data; +} + +static inline void skb_reset_network_header(struct sk_buff *skb) +{ + skb->nh.raw = skb->data; +} + +static inline void skb_set_network_header(struct sk_buff *skb, int offset) +{ + skb->nh.raw = skb->data + offset; +} + +static inline void skb_set_transport_header(struct sk_buff *skb, int offset) +{ + skb->h.raw = skb->data + offset; +} + +static inline unsigned char *skb_transport_header(struct sk_buff *skb) +{ + return skb->h.raw; +} + +static inline unsigned char *skb_network_header(const struct sk_buff *skb) +{ + return skb->nh.raw; +} + +static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) +{ + return skb->tail; +} + +static inline struct iphdr *ip_hdr(const struct sk_buff *skb) +{ + return (struct iphdr *)skb_network_header(skb); +} + +static inline void skb_copy_from_linear_data(const struct sk_buff *skb, + void *to, + const unsigned int len) +{ + memcpy(to, skb->data, len); +} + +static inline void skb_copy_from_linear_data_offset(const struct sk_buff *skb, + const int offset, void *to, + const unsigned int len) +{ + memcpy(to, skb->data + offset, len); +} + +#define __maybe_unused __attribute__((unused)) + +#define uninitialized_var(x) x = x + +/* This will lead to very weird behaviour... */ +#define NLA_BINARY NLA_STRING + +static inline int pci_set_mwi(struct pci_dev *dev) +{ + return -ENOSYS; +} + +static inline void pci_clear_mwi(struct pci_dev *dev) +{ +} + +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) */ + +#endif /* LINUX_26_22_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.23.h b/include/compat/linux/compat-2.6.23.h new file mode 100644 index 00000000..fbfb470d --- /dev/null +++ b/include/compat/linux/compat-2.6.23.h @@ -0,0 +1,136 @@ +#ifndef LINUX_26_23_COMPAT_H +#define LINUX_26_23_COMPAT_H + +#include + +/* Compat work for < 2.6.23 */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)) + +#include +#include +#include +#include +#include + +/* + * Tell gcc if a function is cold. The compiler will assume any path + * directly leading to the call is unlikely. + */ + +#if !(__GNUC__ == 4 && __GNUC_MINOR__ < 3) +/* 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__)) +#endif /* gcc 4.3 check */ + +#ifndef __cold +#define __cold +#endif + +/* Added as of 2.6.23 in include/linux/netdevice.h */ +#define alloc_netdev_mq(sizeof_priv, name, setup, queue) \ + alloc_netdev(sizeof_priv, name, setup) +#define NETIF_F_MULTI_QUEUE 16384 + +/* Added as of 2.6.23 on include/linux/netdevice.h */ +static inline int netif_is_multiqueue(const struct net_device *dev) +{ + return (!!(NETIF_F_MULTI_QUEUE & dev->features)); +} + +/* 2.6.23 fixed a bug in tcf_destroy_chain and the parameter changed */ +static inline void tcf_destroy_chain_compat(struct tcf_proto **fl) +{ + struct tcf_proto *tp; + + while ((tp = *fl) != NULL) { + *fl = tp->next; + tp->ops->destroy(tp); + module_put(tp->ops->owner); + kfree(tp); + } +} + +/* dev_mc_list was replaced with dev_addr_list as of 2.6.23, + * only new member added is da_synced. */ +#define dev_addr_list dev_mc_list +#define da_addr dmi_addr +#define da_addrlen dmi_addrlen +#define da_users dmi_users +#define da_gusers dmi_gusers + +/* dev_set_promiscuity() was moved to __dev_set_promiscuity() on 2.6.23 and + * dev_set_promiscuity() became a wrapper. */ +#define __dev_set_promiscuity dev_set_promiscuity + +/* Our own 2.6.22 port on compat.c */ +extern void dev_mc_unsync(struct net_device *to, struct net_device *from); +extern int dev_mc_sync(struct net_device *to, struct net_device *from); + +/* Our own 2.6.22 port on compat.c */ +extern void __dev_set_rx_mode(struct net_device *dev); + +/* Simple to add this */ +extern int cancel_delayed_work_sync(struct delayed_work *work); + +#define cancel_delayed_work_sync cancel_rearming_delayed_work + +#define debugfs_rename(a, b, c, d) 1 + +/* nl80211 requires multicast group support which is new and added on + * 2.6.23. We can't add support for it for older kernels to support it + * genl_family structure was changed. Lets just let through the + * genl_register_mc_group call. This means no multicast group suppport */ + +#define genl_register_mc_group(a, b) 0 + +/** + * struct genl_multicast_group - generic netlink multicast group + * @name: name of the multicast group, names are per-family + * @id: multicast group ID, assigned by the core, to use with + * genlmsg_multicast(). + * @list: list entry for linking + * @family: pointer to family, need not be set before registering + */ +struct genl_multicast_group +{ + struct genl_family *family; /* private */ + struct list_head list; /* private */ + char name[GENL_NAMSIZ]; + u32 id; +}; + + +/* Added as of 2.6.23 */ +int pci_try_set_mwi(struct pci_dev *dev); + +/* Added as of 2.6.23 */ +#ifdef CONFIG_PM_SLEEP +/* + * Tell the freezer that the current task should be frozen by it + */ +static inline void set_freezable(void) +{ + current->flags &= ~PF_NOFREEZE; +} + +#else +static inline void set_freezable(void) {} +#endif /* CONFIG_PM_SLEEP */ + +#else +#define tcf_destroy_chain_compat tcf_destroy_chain +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)) */ + +#endif /* LINUX_26_23_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.24.h b/include/compat/linux/compat-2.6.24.h new file mode 100644 index 00000000..456dcb6c --- /dev/null +++ b/include/compat/linux/compat-2.6.24.h @@ -0,0 +1,247 @@ +#ifndef LINUX_26_24_COMPAT_H +#define LINUX_26_24_COMPAT_H + +#include + +/* Compat work for 2.6.21, 2.6.22 and 2.6.23 */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)) + +#include +#include +#include +#include +#include +#include +#include + +#define KEY_BLUETOOTH 237 +#define KEY_WLAN 238 +#define KEY_UWB 239 + +#define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL<<(n))-1)) + +struct proc_dir_entry; +struct net_device; +struct net { + atomic_t count; /* To decided when the network + * namespace should be freed. + */ + atomic_t use_count; /* To track references we + * destroy on demand + */ + struct list_head list; /* list of network namespaces */ + struct work_struct work; /* work struct for freeing */ + + struct proc_dir_entry *proc_net; + struct proc_dir_entry *proc_net_stat; + struct proc_dir_entry *proc_net_root; + + struct net_device *loopback_dev; /* The loopback */ + + struct list_head dev_base_head; + struct hlist_head *dev_name_head; + struct hlist_head *dev_index_head; +}; + +#ifdef CONFIG_NET +/* Init's network namespace */ +extern struct net init_net; +#define INIT_NET_NS(net_ns) .net_ns = &init_net, +#else +#define INIT_NET_NS(net_ns) +#endif + +/* Added on 2.6.24 in include/linux/types.h by Al viro on commit 142956af */ +typedef unsigned long uintptr_t; + +/* From include/linux/net.h */ +enum sock_shutdown_cmd { + SHUT_RD = 0, + SHUT_WR = 1, + SHUT_RDWR = 2, +}; + +#if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,23)) /* Local check */ +/* Added as of 2.6.24 in include/linux/skbuff.h. + * + * Although 2.6.23 does support for CONFIG_NETDEVICES_MULTIQUEUE + * this helper was not added until 2.6.24. This implementation + * is exactly as it is on newer kernels. + * + * For older kernels we use the an internal mac80211 hack. + * For details see changes to include/net/mac80211.h through + * compat.diff and compat/mq_compat.h */ +static inline u16 skb_get_queue_mapping(struct sk_buff *skb) +{ +#ifdef CONFIG_NETDEVICES_MULTIQUEUE + return skb->queue_mapping; +#else + return 0; +#endif +} +#endif /* Local 2.6.23 check */ + +/* On older kernels we handle this a bit differently, so we yield to that + * code for its implementation in mq_compat.h as we want to make + * use of the internal mac80211 __ieee80211_queue_stopped() which itself + * uses internal mac80211 data structure hacks. */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)) /* Local check */ +/** + * netif_subqueue_stopped - test status of subqueue + * @dev: network device + * @queue_index: sub queue index + * + * Check individual transmit queue of a device with multiple transmit queues. + */ +static inline int __netif_subqueue_stopped(const struct net_device *dev, + u16 queue_index) +{ +#ifdef CONFIG_NETDEVICES_MULTIQUEUE + return test_bit(__LINK_STATE_XOFF, + &dev->egress_subqueue[queue_index].state); +#else + return 0; +#endif +} + +/* Note: although the backport implementation for netif_subqueue_stopped + * on older kernels is identical to upstream __netif_subqueue_stopped() + * (except for a const qualifier) we implement netif_subqueue_stopped() + * as part of mac80211 as it relies on internal mac80211 structures we + * use for MQ support. We this implement it in mq_compat.h */ + +#endif /* Local 2.6.23 check */ + +/* + * Force link bug if constructor is used, can't be done compatibly + * because constructor arguments were swapped since then! + */ +extern void __incompatible_kmem_cache_create(void); + +/* 2.6.21 and 2.6.22 kmem_cache_create() takes 6 arguments */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)) +#define kmem_cache_create(name, objsize, align, flags, ctor) \ + ({ \ + if (ctor) __incompatible_kmem_cache_create(); \ + kmem_cache_create((name), (objsize), (align), \ + (flags), NULL, NULL); \ + }) +#endif + +/* 2.6.23 kmem_cache_create() takes 5 arguments */ +#if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,23)) +#define kmem_cache_create(name, objsize, align, flags, ctor) \ + ({ \ + if (ctor) __incompatible_kmem_cache_create(); \ + kmem_cache_create((name), (objsize), (align), \ + (flags), NULL); \ + }) +#endif + +/* From include/linux/mod_devicetable.h */ + +/* SSB core, see drivers/ssb/ */ +#ifndef SSB_DEVICE +struct ssb_device_id { + __u16 vendor; + __u16 coreid; + __u8 revision; +}; +#define SSB_DEVICE(_vendor, _coreid, _revision) \ + { .vendor = _vendor, .coreid = _coreid, .revision = _revision, } +#define SSB_DEVTABLE_END \ + { 0, }, + +#define SSB_ANY_VENDOR 0xFFFF +#define SSB_ANY_ID 0xFFFF +#define SSB_ANY_REV 0xFF +#endif + + +/* Namespace stuff, introduced on 2.6.24 */ +#define dev_get_by_index(a, b) dev_get_by_index(b) +#define __dev_get_by_index(a, b) __dev_get_by_index(b) + +extern int eth_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, void *daddr, + void *saddr, unsigned len); +extern int eth_rebuild_header(struct sk_buff *skb); +extern void eth_header_cache_update(struct hh_cache *hh, struct net_device *dev, + unsigned char * haddr); +extern int eth_header_cache(struct neighbour *neigh, + struct hh_cache *hh); + +/* This structure is simply not present on 2.6.22 and 2.6.23 */ +struct header_ops { + int (*create) (struct sk_buff *skb, struct net_device *dev, + unsigned short type, void *daddr, + void *saddr, unsigned len); + int (*parse)(const struct sk_buff *skb, unsigned char *haddr); + int (*rebuild)(struct sk_buff *skb); + #define HAVE_HEADER_CACHE + int (*cache)(struct neighbour *neigh, struct hh_cache *hh); + void (*cache_update)(struct hh_cache *hh, + struct net_device *dev, + unsigned char *haddr); +}; + +/* net/ieee80211/ieee80211_crypt_tkip uses sg_init_table. This was added on + * 2.6.24. CONFIG_DEBUG_SG was added in 2.6.24 as well, so lets just ignore + * the debug stuff. Note that adding this required changes to the struct + * scatterlist on include/asm/scatterlist*, so the right way to port this + * is to simply ignore the new structure changes and zero the scatterlist + * array. We lave the kdoc intact for reference. + */ + +/** + * sg_mark_end - Mark the end of the scatterlist + * @sg: SG entryScatterlist + * + * Description: + * Marks the passed in sg entry as the termination point for the sg + * table. A call to sg_next() on this entry will return NULL. + * + **/ +static inline void sg_mark_end(struct scatterlist *sg) +{ +#ifdef CONFIG_DEBUG_SG + BUG_ON(sg->sg_magic != SG_MAGIC); +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + /* + * Set termination bit, clear potential chain bit + */ + sg->page_link |= 0x02; + sg->page_link &= ~0x01; +#endif +} + +/** + * sg_init_table - Initialize SG table + * @sgl: The SG table + * @nents: Number of entries in table + * + * Notes: + * If this is part of a chained sg table, sg_mark_end() should be + * used only on the last table part. + * + **/ +static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents) +{ + memset(sgl, 0, sizeof(*sgl) * nents); +} + +/** + * usb_endpoint_num - get the endpoint's number + * @epd: endpoint to be checked + * + * Returns @epd's number: 0 to 15. + */ +static inline int usb_endpoint_num(const struct usb_endpoint_descriptor *epd) +{ + return epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; +} + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)) */ + +#endif /* LINUX_26_24_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.25.h b/include/compat/linux/compat-2.6.25.h new file mode 100644 index 00000000..c389ab2c --- /dev/null +++ b/include/compat/linux/compat-2.6.25.h @@ -0,0 +1,193 @@ +#ifndef LINUX_26_25_COMPAT_H +#define LINUX_26_25_COMPAT_H + +#include + +/* Compat work for 2.6.24 */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * phys_addr_t was added as a generic arch typedef on 2.6.28, + * that backport is dealt with in compat-2.6.28.h + */ +#if defined(CONFIG_X86) || defined(CONFIG_X86_64) + +#if defined(CONFIG_64BIT) || defined(CONFIG_X86_PAE) || defined(CONFIG_PHYS_64BIT) +typedef u64 phys_addr_t; +#else +typedef u32 phys_addr_t; +#endif + +#endif /* x86 */ + +/* The macro below uses a const upstream, this differs */ + +/** + * DEFINE_PCI_DEVICE_TABLE - macro used to describe a pci device table + * @_table: device table name + * + * This macro is used to create a struct pci_device_id array (a device table) + * in a generic manner. + */ +#define DEFINE_PCI_DEVICE_TABLE(_table) \ + const struct pci_device_id _table[] __devinitdata + +/* + * Backport work for QoS dependencies (kernel/pm_qos_params.c) + * pm-qos stuff written by mark gross mgross@linux.intel.com. + * + * ipw2100 now makes use of: + * + * pm_qos_add_requirement(), + * pm_qos_update_requirement() and + * pm_qos_remove_requirement() from it + * + * mac80211 uses the network latency to determine if to enable or not + * dynamic PS. mac80211 also and registers a notifier for when + * the latency changes. Since older kernels do no thave pm-qos stuff + * we just implement it completley here and register it upon cfg80211 + * init. I haven't tested ipw2100 on 2.6.24 though. + * + * This pm-qos implementation is copied verbatim from the kernel + * written by mark gross mgross@linux.intel.com. You don't have + * to do anythinig to use pm-qos except use the same exported + * routines as used in newer kernels. The compat_pm_qos_power_init() + * defned below is used by the compat module to initialize pm-qos. + */ +int compat_pm_qos_power_init(void); +int compat_pm_qos_power_deinit(void); + +/* + * 2.6.25 adds PM_EVENT_HIBERNATE as well here but + * we don't have this on <= 2.6.23) + */ +#ifndef PM_EVENT_SLEEP /* some distribution have mucked with their own headers to add this.. */ +#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND) +#endif + +/* Although we don't care about wimax this is needed for rfkill input stuff */ +#define KEY_WIMAX 246 + +/* Although pm_qos stuff is not implemented on <= 2.6.24 lets keep the define */ +#define PM_QOS_DEFAULT_VALUE -1 + +#define __WARN(foo) dump_stack() + +#define dev_emerg(dev, format, arg...) \ + dev_printk(KERN_EMERG , dev , format , ## arg) +#define dev_alert(dev, format, arg...) \ + dev_printk(KERN_ALERT , dev , format , ## arg) +#define dev_crit(dev, format, arg...) \ + dev_printk(KERN_CRIT , dev , format , ## arg) + +extern int __dev_addr_sync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); +extern void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); + +#define seq_file_net &init_net; + +enum nf_inet_hooks { + NF_INET_PRE_ROUTING = 0, + NF_INET_LOCAL_IN = 1, + NF_INET_FORWARD = 2, + NF_INET_LOCAL_OUT = 3, + NF_INET_POST_ROUTING = 4, + NF_INET_NUMHOOKS = 5 +}; + +/* The patch: + * commit 8b5f6883683c91ad7e1af32b7ceeb604d68e2865 + * Author: Marcin Slusarz + * Date: Fri Feb 8 04:20:12 2008 -0800 + * + * byteorder: move le32_add_cpu & friends from OCFS2 to core + * + * moves le*_add_cpu and be*_add_cpu functions from OCFS2 to core + * header (1st) and converted some existing code to it. We port + * it here as later kernels will most likely use it. + */ +static inline void le16_add_cpu(__le16 *var, u16 val) +{ + *var = cpu_to_le16(le16_to_cpu(*var) + val); +} + +static inline void le32_add_cpu(__le32 *var, u32 val) +{ + *var = cpu_to_le32(le32_to_cpu(*var) + val); +} + +static inline void le64_add_cpu(__le64 *var, u64 val) +{ + *var = cpu_to_le64(le64_to_cpu(*var) + val); +} + +static inline void be16_add_cpu(__be16 *var, u16 val) +{ + u16 v = be16_to_cpu(*var); + *var = cpu_to_be16(v + val); +} + +static inline void be32_add_cpu(__be32 *var, u32 val) +{ + u32 v = be32_to_cpu(*var); + *var = cpu_to_be32(v + val); +} + +static inline void be64_add_cpu(__be64 *var, u64 val) +{ + u64 v = be64_to_cpu(*var); + *var = cpu_to_be64(v + val); +} + +/* 2.6.25 changes hwrng_unregister()'s behaviour by supporting + * suspend of its parent device (the misc device, which is itself the + * hardware random number generator). It does this by passing a parameter to + * unregister_miscdev() which is not supported in older kernels. The suspend + * parameter allows us to enable access to the device's hardware + * number generator during suspend. As far as wireless is concerned this means + * if a driver goes to suspend it you won't have the HNR available in + * older kernels. */ +static inline void __hwrng_unregister(struct hwrng *rng, bool suspended) +{ + hwrng_unregister(rng); +} + +static inline void led_classdev_unregister_suspended(struct led_classdev *lcd) +{ + led_classdev_unregister(lcd); +} + +/** + * The following things are out of ./include/linux/kernel.h + * The new iwlwifi driver is using them. + */ +extern int strict_strtoul(const char *, unsigned int, unsigned long *); +extern int strict_strtol(const char *, unsigned int, long *); + +#else +/* + * Kernels >= 2.6.25 have pm-qos and its initialized as part of + * the bootup process + */ +static inline int compat_pm_qos_power_init(void) +{ + return 0; +} + +static inline int compat_pm_qos_power_deinit(void) +{ + return 0; +} +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)) */ + +#endif /* LINUX_26_25_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.26.h b/include/compat/linux/compat-2.6.26.h new file mode 100644 index 00000000..177792a3 --- /dev/null +++ b/include/compat/linux/compat-2.6.26.h @@ -0,0 +1,455 @@ +#ifndef LINUX_26_26_COMPAT_H +#define LINUX_26_26_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) + +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) +#include +#endif +#include +#include +#include + +/* These jiffie helpers added as of 2.6.26 */ + +/* + * These four macros compare jiffies and 'a' for convenience. + */ + +/* time_is_before_jiffies(a) return true if a is before jiffies */ +#define time_is_before_jiffies(a) time_after(jiffies, a) + +/* time_is_after_jiffies(a) return true if a is after jiffies */ +#define time_is_after_jiffies(a) time_before(jiffies, a) + +/* time_is_before_eq_jiffies(a) return true if a is before or equal to jiffies*/ +#define time_is_before_eq_jiffies(a) time_after_eq(jiffies, a) + +/* time_is_after_eq_jiffies(a) return true if a is after or equal to jiffies*/ +#define time_is_after_eq_jiffies(a) time_before_eq(jiffies, a) + +/* This comes from include/linux/input.h */ +#define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" + set = radio enabled */ + +/* From kernel.h */ +#define USHORT_MAX ((u16)(~0U)) +#define SHORT_MAX ((s16)(USHORT_MAX>>1)) +#define SHORT_MIN (-SHORT_MAX - 1) + +extern int dev_set_name(struct device *dev, const char *name, ...) + __attribute__((format(printf, 2, 3))); + +/** + * clamp - return a value clamped to a given range with strict typechecking + * @val: current value + * @min: minimum allowable value + * @max: maximum allowable value + * + * This macro does strict typechecking of min/max to make sure they are of the + * same type as val. See the unnecessary pointer comparisons. + */ +#define clamp(val, min, max) ({ \ + typeof(val) __val = (val); \ + typeof(min) __min = (min); \ + typeof(max) __max = (max); \ + (void) (&__val == &__min); \ + (void) (&__val == &__max); \ + __val = __val < __min ? __min: __val; \ + __val > __max ? __max: __val; }) + +/** + * clamp_t - return a value clamped to a given range using a given type + * @type: the type of variable to use + * @val: current value + * @min: minimum allowable value + * @max: maximum allowable value + * + * This macro does no typechecking and uses temporary variables of type + * 'type' to make all the comparisons. + */ +#define clamp_t(type, val, min, max) ({ \ + type __val = (val); \ + type __min = (min); \ + type __max = (max); \ + __val = __val < __min ? __min: __val; \ + __val > __max ? __max: __val; }) + + +/* from include/linux/device.h */ +/* device_create_drvdata() is new */ +extern struct device *device_create_drvdata(struct class *cls, + struct device *parent, + dev_t devt, + void *drvdata, + const char *fmt, ...) +__attribute__((format(printf, 5, 6))); + +/* This is from include/linux/list.h */ + +/** + * list_is_singular - tests whether a list has just one entry. + * @head: the list to test. + */ +static inline int list_is_singular(const struct list_head *head) +{ + return !list_empty(head) && (head->next == head->prev); +} + +/* This is from include/linux/device.h, which was added as of 2.6.26 */ +static inline const char *dev_name(struct device *dev) +{ + /* will be changed into kobject_name(&dev->kobj) in the near future */ + return dev->bus_id; +} + +/* This is from include/linux/kernel.h, which was added as of 2.6.26 */ + +/** + * clamp_val - return a value clamped to a given range using val's type + * @val: current value + * @min: minimum allowable value + * @max: maximum allowable value + * + * This macro does no typechecking and uses temporary variables of whatever + * type the input argument 'val' is. This is useful when val is an unsigned + * type and min and max are literals that will otherwise be assigned a signed + * integer type. + */ + +#define clamp_val(val, min, max) ({ \ + typeof(val) __val = (val); \ + typeof(val) __min = (min); \ + typeof(val) __max = (max); \ + __val = __val < __min ? __min: __val; \ + __val > __max ? __max: __val; }) + +/* This comes from include/net/net_namespace.h */ + +#ifdef CONFIG_NET_NS +static inline +int net_eq(const struct net *net1, const struct net *net2) +{ + return net1 == net2; +} +#else +static inline +int net_eq(const struct net *net1, const struct net *net2) +{ + return 1; +} +#endif + +static inline +void dev_net_set(struct net_device *dev, struct net *net) +{ +#ifdef CONFIG_NET_NS + release_net(dev->nd_net); + dev->nd_net = hold_net(net); +#endif +} + +static inline +struct net *sock_net(const struct sock *sk) +{ +#ifdef CONFIG_NET_NS + return sk->sk_net; +#else + return &init_net; +#endif +} + +/* This comes from include/linux/netdevice.h */ + +/* + * Net namespace inlines + */ +static inline +struct net *dev_net(const struct net_device *dev) +{ +#ifdef CONFIG_NET_NS + /* + * compat-wirelss backport note: + * For older kernels we may just need to always return init_net, + * not sure when we added dev->nd_net. + */ + return dev->nd_net; +#else + return &init_net; +#endif +} + + +/* + * 2.6.26 added its own unaligned API which the + * new drivers can use. Lets port it here by including it in older + * kernels and also deal with the architecture handling here. + */ + +#ifdef CONFIG_ALPHA + +#include +#include +#include + +#endif /* alpha */ +#ifdef CONFIG_ARM + +/* arm */ +#include +#include +#include + +#endif /* arm */ +#ifdef CONFIG_AVR32 + +/* + * AVR32 can handle some unaligned accesses, depending on the + * implementation. The AVR32 AP implementation can handle unaligned + * words, but halfwords must be halfword-aligned, and doublewords must + * be word-aligned. + * + * However, swapped word loads must be word-aligned so we can't + * optimize word loads in general. + */ + +#include +#include +#include + +#endif +#ifdef CONFIG_BLACKFIN + +#include +#include +#include + +#endif /* blackfin */ +#ifdef CONFIG_CRIS + +/* + * CRIS can do unaligned accesses itself. + */ +#include +#include + +#endif /* cris */ +#ifdef CONFIG_FRV + +#include +#include +#include + +#endif /* frv */ +#ifdef CONFIG_H8300 + +#include +#include +#include + +#endif /* h8300 */ +#ifdef CONFIG_IA64 + +#include +#include +#include + +#endif /* ia64 */ +#ifdef CONFIG_M32R + +#if defined(__LITTLE_ENDIAN__) +# include +# include +# include +#else +# include +# include +# include +#endif + +#endif /* m32r */ +#ifdef CONFIG_M68K /* this handles both m68k and m68knommu */ + +#ifdef CONFIG_COLDFIRE +#include +#include +#include +#else + +/* + * The m68k can do unaligned accesses itself. + */ +#include +#include +#endif + +#endif /* m68k and m68knommu */ +#ifdef CONFIG_MIPS + +#if defined(__MIPSEB__) +# include +# include +# include +# define get_unaligned __get_unaligned_be +# define put_unaligned __put_unaligned_be +#elif defined(__MIPSEL__) +# include +# include +# include +#endif + +#endif /* mips */ +#ifdef CONFIG_MN10300 + +#include +#include + +#endif /* mn10300 */ +#ifdef CONFIG_PARISC + +#include +#include +#include + +#endif /* parisc */ +#ifdef CONFIG_PPC +/* + * The PowerPC can do unaligned accesses itself in big endian mode. + */ +#include +#include + +#endif /* ppc */ +#ifdef CONFIG_S390 + +/* + * The S390 can do unaligned accesses itself. + */ +#include +#include + +#endif /* s390 */ +#ifdef CONFIG_SUPERH + +/* SH can't handle unaligned accesses. */ +#ifdef __LITTLE_ENDIAN__ +# include +# include +# include +#else +# include +# include +# include +#endif + +#endif /* sh - SUPERH */ +#ifdef CONFIG_SPARC + +/* sparc and sparc64 */ +#include +#include +#include + +#endif /* sparc */ +#ifdef CONFIG_UML + +#include "asm/arch/unaligned.h" + +#endif /* um - uml */ +#ifdef CONFIG_V850 + +#include +#include +#include + +#endif /* v850 */ +#ifdef CONFIG_X86 +/* + * The x86 can do unaligned accesses itself. + */ +#include +#include + +#endif /* x86 */ +#ifdef CONFIG_XTENSA + +#ifdef __XTENSA_EL__ +# include +# include +# include +#elif defined(__XTENSA_EB__) +# include +# include +# include +#else +# error processor byte order undefined! +#endif + +#endif /* xtensa */ + +#define PCIE_LINK_STATE_L0S 1 +#define PCIE_LINK_STATE_L1 2 +#define PCIE_LINK_STATE_CLKPM 4 + +static inline void pci_disable_link_state(struct pci_dev *pdev, int state) +{ +} +/* source: include/linux/pci-aspm.h */ + + +#if BITS_PER_LONG == 64 + +/** + * div_u64_rem - unsigned 64bit divide with 32bit divisor with remainder + * + * This is commonly provided by 32bit archs to provide an optimized 64bit + * divide. + */ +static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) +{ + *remainder = dividend % divisor; + return dividend / divisor; +} + +#elif BITS_PER_LONG == 32 + +#ifndef div_u64_rem +static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) +{ + *remainder = do_div(dividend, divisor); + return dividend; +} +#endif + +#endif /* BITS_PER_LONG */ + +/** + * div_u64 - unsigned 64bit divide with 32bit divisor + * + * This is the most common 64bit divide and should be used if possible, + * as many 32bit archs can optimize this variant better than a full 64bit + * divide. + */ +#ifndef div_u64 +static inline u64 div_u64(u64 dividend, u32 divisor) +{ + u32 remainder; + return div_u64_rem(dividend, divisor, &remainder); +} +#endif +/* source: include/math64.h */ + +#define hex_asc_lo(x) hex_asc((x) & 0x0f) +#define hex_asc_hi(x) hex_asc(((x) & 0xf0) >> 4) + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) */ + +#endif /* LINUX_26_26_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.27.h b/include/compat/linux/compat-2.6.27.h new file mode 100644 index 00000000..22fadcd2 --- /dev/null +++ b/include/compat/linux/compat-2.6.27.h @@ -0,0 +1,261 @@ +#ifndef LINUX_26_27_COMPAT_H +#define LINUX_26_27_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)) + +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) +#include +#include +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCI_PM_CAP_PME_SHIFT 11 + +/* I can't find a more suitable replacement... */ +#define flush_work(work) cancel_work_sync(work) + +struct builtin_fw { + char *name; + void *data; + unsigned long size; +}; + +/* + * On older kernels we do not have net_device Multi Queue support, but + * since we no longer use MQ on mac80211 we can simply use the 0 queue. + * Note that if other fullmac drivers make use of this they then need + * to be backported somehow or deal with just 1 queueue from MQ. + */ +static inline void netif_tx_wake_all_queues(struct net_device *dev) +{ + netif_wake_queue(dev); +} +static inline void netif_tx_start_all_queues(struct net_device *dev) +{ + netif_start_queue(dev); +} +static inline void netif_tx_stop_all_queues(struct net_device *dev) +{ + netif_stop_queue(dev); +} + +/* Are all TX queues of the device empty? */ +static inline bool qdisc_all_tx_empty(const struct net_device *dev) +{ + return skb_queue_empty(&dev->qdisc->q); +} + +bool pci_pme_capable(struct pci_dev *dev, pci_power_t state); + +/* + * The net_device has a spin_lock on newer kernels, on older kernels we're out of luck + */ +#define netif_addr_lock_bh(dev) +#define netif_addr_unlock_bh(dev) + +/* + * To port this properly we'd have to port warn_slowpath_null(), + * which I'm lazy to do so just do a regular print for now. If you + * want to port this read kernel/panic.c + */ +#define __WARN_printf(arg...) do { printk(arg); __WARN(); } while (0) + +/* This is ported directly as-is on newer kernels */ +#ifndef WARN +#define WARN(condition, format...) ({ \ + int __ret_warn_on = !!(condition); \ + if (unlikely(__ret_warn_on)) \ + __WARN_printf(format); \ + unlikely(__ret_warn_on); \ +}) +#endif + +/* On 2.6.27 a second argument was added, on older kernels we ignore it */ +#define dma_mapping_error(pdev, dma_addr) dma_mapping_error(dma_addr) +#define pci_dma_mapping_error(pdev, dma_addr) dma_mapping_error(pdev, dma_addr) + +/* This is from include/linux/ieee80211.h */ +#define IEEE80211_HT_CAP_DSSSCCK40 0x1000 + +/* New link list changes added as of 2.6.27, needed for ath9k */ + +static inline void __list_cut_position(struct list_head *list, + struct list_head *head, struct list_head *entry) +{ + struct list_head *new_first = entry->next; + list->next = head->next; + list->next->prev = list; + list->prev = entry; + entry->next = list; + head->next = new_first; + new_first->prev = head; +} + +/** + * list_cut_position - cut a list into two + * @list: a new list to add all removed entries + * @head: a list with entries + * @entry: an entry within head, could be the head itself + * and if so we won't cut the list + * + * This helper moves the initial part of @head, up to and + * including @entry, from @head to @list. You should + * pass on @entry an element you know is on @head. @list + * should be an empty list or a list you do not care about + * losing its data. + * + */ +static inline void list_cut_position(struct list_head *list, + struct list_head *head, struct list_head *entry) +{ + if (list_empty(head)) + return; + if (list_is_singular(head) && + (head->next != entry && head != entry)) + return; + if (entry == head) + INIT_LIST_HEAD(list); + else + __list_cut_position(list, head, entry); +} + + +/* __list_splice as re-implemented on 2.6.27, we backport it */ +static inline void __compat_list_splice_new_27(const struct list_head *list, + struct list_head *prev, + struct list_head *next) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + + first->prev = prev; + prev->next = first; + + last->next = next; + next->prev = last; +} + +/** + * list_splice_tail - join two lists, each list being a queue + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice_tail(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __compat_list_splice_new_27(list, head->prev, head); +} + +/** + * list_splice_tail_init - join two lists and reinitialise the emptied list + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * Each of the lists is a queue. + * The list at @list is reinitialised + */ +static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __compat_list_splice_new_27(list, head->prev, head); + INIT_LIST_HEAD(list); + } +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) +extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int); +extern unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz); +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) */ + +#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) +#define iwe_stream_add_event(info, stream, ends, iwe, event_len) iwe_stream_add_event(stream, ends, iwe, event_len) + +/* Flags available in struct iw_request_info */ +#define IW_REQUEST_FLAG_COMPAT 0x0001 /* Compat ioctl call */ + +static inline int iwe_stream_lcp_len(struct iw_request_info *info) +{ +#ifdef CONFIG_COMPAT + if (info->flags & IW_REQUEST_FLAG_COMPAT) + return IW_EV_COMPAT_LCP_LEN; +#endif + return IW_EV_LCP_LEN; +} + +#ifdef CONFIG_ARM + +/* + * The caller asks to handle a range between offset and offset + size, + * but we process a larger range from 0 to offset + size due to lack of + * offset support. + */ + +static inline void dma_sync_single_range_for_cpu(struct device *dev, + dma_addr_t handle, unsigned long offset, size_t size, + enum dma_data_direction dir) +{ + dma_sync_single_for_cpu(dev, handle, offset + size, dir); +} + +static inline void dma_sync_single_range_for_device(struct device *dev, + dma_addr_t handle, unsigned long offset, size_t size, + enum dma_data_direction dir) +{ + dma_sync_single_for_device(dev, handle, offset + size, dir); +} + +#endif /* arm */ + +#if defined(CONFIG_DEBUG_FS) +void debugfs_remove_recursive(struct dentry *dentry); +#else +static inline void debugfs_remove_recursive(struct dentry *dentry) +{ } +#endif + +#define device_create(cls, parent, devt, drvdata, fmt, ...) \ +({ \ + struct device *_dev; \ + _dev = (device_create)(cls, parent, devt, fmt, __VA_ARGS__); \ + dev_set_drvdata(_dev, drvdata); \ + _dev; \ +}) + +#define dev_name(dev) dev_name((struct device *)dev) + +static inline void ethtool_cmd_speed_set(struct ethtool_cmd *ep, + __u32 speed) +{ + ep->speed = (__u16)speed; +} + +static inline __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep) +{ + return ep->speed; +} + +/** + * lower_32_bits - return bits 0-31 of a number + * @n: the number we're accessing + */ +#define lower_32_bits(n) ((u32)(n)) + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)) */ + +#endif /* LINUX_26_27_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.28.h b/include/compat/linux/compat-2.6.28.h new file mode 100644 index 00000000..c0ca70b1 --- /dev/null +++ b/include/compat/linux/compat-2.6.28.h @@ -0,0 +1,264 @@ +#ifndef LINUX_26_28_COMPAT_H +#define LINUX_26_28_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)) + +#include +#include +#include +#include + +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif + +#include + +#if defined(CONFIG_X86) || defined(CONFIG_X86_64) || defined(CONFIG_PPC) +/* + * CONFIG_PHYS_ADDR_T_64BIT was added as new to all architectures + * as of 2.6.28 but x86 and ppc had it already. x86 only got phys_addr_t + * as of 2.6.25 but then is backported in compat-2.6.25.h + */ +#else +#if defined(CONFIG_64BIT) || defined(CONFIG_X86_PAE) || defned(CONFIG_PPC64) || defined(CONFIG_PHYS_64BIT) +#define CONFIG_PHYS_ADDR_T_64BIT 1 +typedef u64 phys_addr_t; +#else +typedef u32 phys_addr_t; +#endif + +#endif /* non x86 and ppc */ + +#ifndef WARN_ONCE +#define WARN_ONCE(condition, format...) ({ \ + static int __warned; \ + int __ret_warn_once = !!(condition); \ + \ + if (unlikely(__ret_warn_once)) \ + if (WARN(!__warned, format)) \ + __warned = 1; \ + unlikely(__ret_warn_once); \ +}) +#endif /* From include/asm-generic/bug.h */ + +#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE) + +#include +#include +#include +#ifdef pcmcia_parse_tuple +#undef pcmcia_parse_tuple +#define pcmcia_parse_tuple(tuple, parse) pccard_parse_tuple(tuple, parse) +#endif + +/* From : include/pcmcia/ds.h */ +/* loop CIS entries for valid configuration */ +int pcmcia_loop_config(struct pcmcia_device *p_dev, + int (*conf_check) (struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cfg, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data), + void *priv_data); + +#endif /* CONFIG_PCMCIA */ + +/* USB anchors were added as of 2.6.23 */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)) + +#if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE) +#if 0 +extern void usb_poison_urb(struct urb *urb); +#endif +extern void usb_unpoison_urb(struct urb *urb); + +#if 0 +extern void usb_poison_anchored_urbs(struct usb_anchor *anchor); +#endif + +extern int usb_anchor_empty(struct usb_anchor *anchor); +#endif /* CONFIG_USB */ +#endif + + +void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar); + +/** + * skb_queue_is_last - check if skb is the last entry in the queue + * @list: queue head + * @skb: buffer + * + * Returns true if @skb is the last buffer on the list. + */ +static inline bool skb_queue_is_last(const struct sk_buff_head *list, + const struct sk_buff *skb) +{ + return (skb->next == (struct sk_buff *) list); +} + +/** + * skb_queue_next - return the next packet in the queue + * @list: queue head + * @skb: current buffer + * + * Return the next packet in @list after @skb. It is only valid to + * call this if skb_queue_is_last() evaluates to false. + */ +static inline struct sk_buff *skb_queue_next(const struct sk_buff_head *list, + const struct sk_buff *skb) +{ + /* This BUG_ON may seem severe, but if we just return then we + * are going to dereference garbage. + */ + BUG_ON(skb_queue_is_last(list, skb)); + return skb->next; +} + +/** + * __skb_queue_head_init - initialize non-spinlock portions of sk_buff_head + * @list: queue to initialize + * + * This initializes only the list and queue length aspects of + * an sk_buff_head object. This allows to initialize the list + * aspects of an sk_buff_head without reinitializing things like + * the spinlock. It can also be used for on-stack sk_buff_head + * objects where the spinlock is known to not be used. + */ +static inline void __skb_queue_head_init(struct sk_buff_head *list) +{ + list->prev = list->next = (struct sk_buff *)list; + list->qlen = 0; +} + +static inline void __skb_queue_splice(const struct sk_buff_head *list, + struct sk_buff *prev, + struct sk_buff *next) +{ + struct sk_buff *first = list->next; + struct sk_buff *last = list->prev; + + first->prev = prev; + prev->next = first; + + last->next = next; + next->prev = last; +} + +/** + * skb_queue_splice - join two skb lists, this is designed for stacks + * @list: the new list to add + * @head: the place to add it in the first list + */ +static inline void skb_queue_splice(const struct sk_buff_head *list, + struct sk_buff_head *head) +{ + if (!skb_queue_empty(list)) { + __skb_queue_splice(list, (struct sk_buff *) head, head->next); + head->qlen += list->qlen; + } +} + +/** + * skb_queue_splice - join two skb lists and reinitialise the emptied list + * @list: the new list to add + * @head: the place to add it in the first list + * + * The list at @list is reinitialised + */ +static inline void skb_queue_splice_init(struct sk_buff_head *list, + struct sk_buff_head *head) +{ + if (!skb_queue_empty(list)) { + __skb_queue_splice(list, (struct sk_buff *) head, head->next); + head->qlen += list->qlen; + __skb_queue_head_init(list); + } +} + +/** + * skb_queue_splice_tail - join two skb lists and reinitialise the emptied list + * @list: the new list to add + * @head: the place to add it in the first list + * + * Each of the lists is a queue. + * The list at @list is reinitialised + */ +static inline void skb_queue_splice_tail_init(struct sk_buff_head *list, + struct sk_buff_head *head) +{ + if (!skb_queue_empty(list)) { + __skb_queue_splice(list, head->prev, (struct sk_buff *) head); + head->qlen += list->qlen; + __skb_queue_head_init(list); + } +} /* From include/linux/skbuff.h */ + +/** + * skb_queue_splice_tail - join two skb lists, each list being a queue + * @list: the new list to add + * @head: the place to add it in the first list + */ +static inline void skb_queue_splice_tail(const struct sk_buff_head *list, + struct sk_buff_head *head) +{ + if (!skb_queue_empty(list)) { + __skb_queue_splice(list, head->prev, (struct sk_buff *) head); + head->qlen += list->qlen; + } +} + +#ifndef DECLARE_TRACE + +#define TP_PROTO(args...) args +#define TP_ARGS(args...) args + +#define DECLARE_TRACE(name, proto, args) \ + static inline void _do_trace_##name(struct tracepoint *tp, proto) \ + { } \ + static inline void trace_##name(proto) \ + { } \ + static inline int register_trace_##name(void (*probe)(proto)) \ + { \ + return -ENOSYS; \ + } \ + static inline int unregister_trace_##name(void (*probe)(proto)) \ + { \ + return -ENOSYS; \ + } + +#define EXPORT_TRACEPOINT_SYMBOL_GPL(name) +#define EXPORT_TRACEPOINT_SYMBOL(name) + + +#endif + +/* openSuse includes round_jiffies_up in it's kernel 2.6.27. + * This is needed to prevent conflicts with the openSuse definition. + */ +#define round_jiffies_up backport_round_jiffies_up + +unsigned long round_jiffies_up(unsigned long j); + +extern void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, + int off, int size); + +#define wake_up_interruptible_poll(x, m) \ + __wake_up(x, TASK_INTERRUPTIBLE, 1, (void *) (m)) + +extern int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg); + +int pci_wake_from_d3(struct pci_dev *dev, bool enable); + +#define alloc_workqueue(name, flags, max_active) __create_workqueue(name, flags, max_active) + +#ifndef pr_fmt +#define pr_fmt(fmt) fmt +#endif + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)) */ + +#endif /* LINUX_26_28_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.29.h b/include/compat/linux/compat-2.6.29.h new file mode 100644 index 00000000..1e538bbd --- /dev/null +++ b/include/compat/linux/compat-2.6.29.h @@ -0,0 +1,350 @@ +#ifndef LINUX_26_29_COMPAT_H +#define LINUX_26_29_COMPAT_H + +#include +#include +#include + +/* + * I kow this looks odd.. but 2.6.32 added the netdev_tx_t + * and we backport that there so inlcude that header first + * as we need it for the netdev ops. + */ +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) + +#include +#include +#include + +#if \ + defined(CONFIG_ALPHA) || defined(CONFIG_AVR32) || \ + defined(CONFIG_BLACKFIN) || defined(CONFIG_CRIS) || \ + defined(CONFIG_H8300) || defined(CONFIG_IA64) || \ + defined(CONFIG_M68K) || defined(CONFIG_MIPS) || \ + defined(CONFIG_PARISC) || defined(CONFIG_S390) || \ + defined(CONFIG_PPC64) || defined(CONFIG_PPC32) || \ + defined(CONFIG_SUPERH) || defined(CONFIG_SPARC) || \ + defined(CONFIG_FRV) || defined(CONFIG_X86) || \ + defined(CONFIG_M32R) || defined(CONFIG_M68K) || \ + defined(CONFIG_MN10300) || defined(CONFIG_XTENSA) +#include +#else +typedef struct { + volatile int counter; +} atomic_t; + +#ifdef CONFIG_64BIT +typedef struct { + volatile long counter; +} atomic64_t; +#endif /* CONFIG_64BIT */ + +#endif + +#define PCI_EXP_LNKCTL_ES 0x0080 /* Extended Synch */ + +/* + * Older kernels do not have struct net_device_ops but what we can + * do is just define the data structure and use a caller to let us + * set the data structure's routines onto the old netdev, essentially + * doing it the old way. This avoids huge deltas on our backports. + */ + +/* + * This structure defines the management hooks for network devices. + * The following hooks can be defined; unless noted otherwise, they are + * optional and can be filled with a null pointer. + * + * int (*ndo_init)(struct net_device *dev); + * This function is called once when network device is registered. + * The network device can use this to any late stage initializaton + * or semantic validattion. It can fail with an error code which will + * be propogated back to register_netdev + * + * void (*ndo_uninit)(struct net_device *dev); + * This function is called when device is unregistered or when registration + * fails. It is not called if init fails. + * + * int (*ndo_open)(struct net_device *dev); + * This function is called when network device transistions to the up + * state. + * + * int (*ndo_stop)(struct net_device *dev); + * This function is called when network device transistions to the down + * state. + * + * netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb, + * struct net_device *dev); + * Called when a packet needs to be transmitted. + * Must return NETDEV_TX_OK , NETDEV_TX_BUSY. + * (can also return NETDEV_TX_LOCKED iff NETIF_F_LLTX) + * Required can not be NULL. + * + * u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb); + * Called to decide which queue to when device supports multiple + * transmit queues. + * + * void (*ndo_change_rx_flags)(struct net_device *dev, int flags); + * This function is called to allow device receiver to make + * changes to configuration when multicast or promiscious is enabled. + * + * void (*ndo_set_rx_mode)(struct net_device *dev); + * This function is called device changes address list filtering. + * + * void (*ndo_set_multicast_list)(struct net_device *dev); + * This function is called when the multicast address list changes. + * + * int (*ndo_set_mac_address)(struct net_device *dev, void *addr); + * This function is called when the Media Access Control address + * needs to be changed. If this interface is not defined, the + * mac address can not be changed. + * + * int (*ndo_validate_addr)(struct net_device *dev); + * Test if Media Access Control address is valid for the device. + * + * int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); + * Called when a user request an ioctl which can't be handled by + * the generic interface code. If not defined ioctl's return + * not supported error code. + * + * int (*ndo_set_config)(struct net_device *dev, struct ifmap *map); + * Used to set network devices bus interface parameters. This interface + * is retained for legacy reason, new devices should use the bus + * interface (PCI) for low level management. + * + * int (*ndo_change_mtu)(struct net_device *dev, int new_mtu); + * Called when a user wants to change the Maximum Transfer Unit + * of a device. If not defined, any request to change MTU will + * will return an error. + * + * void (*ndo_tx_timeout)(struct net_device *dev); + * Callback uses when the transmitter has not made any progress + * for dev->watchdog ticks. + * + * struct net_device_stats* (*ndo_get_stats)(struct net_device *dev); + * Called when a user wants to get the network device usage + * statistics. If not defined, the counters in dev->stats will + * be used. + * + * void (*ndo_vlan_rx_register)(struct net_device *dev, struct vlan_group *grp); + * If device support VLAN receive accleration + * (ie. dev->features & NETIF_F_HW_VLAN_RX), then this function is called + * when vlan groups for the device changes. Note: grp is NULL + * if no vlan's groups are being used. + * + * void (*ndo_vlan_rx_add_vid)(struct net_device *dev, unsigned short vid); + * If device support VLAN filtering (dev->features & NETIF_F_HW_VLAN_FILTER) + * this function is called when a VLAN id is registered. + * + * void (*ndo_vlan_rx_kill_vid)(struct net_device *dev, unsigned short vid); + * If device support VLAN filtering (dev->features & NETIF_F_HW_VLAN_FILTER) + * this function is called when a VLAN id is unregistered. + * + * void (*ndo_poll_controller)(struct net_device *dev); + * + * SR-IOV management functions. + * int (*ndo_set_vf_mac)(struct net_device *dev, int vf, u8* mac); + * int (*ndo_set_vf_vlan)(struct net_device *dev, int vf, u16 vlan, u8 qos); + * int (*ndo_set_vf_tx_rate)(struct net_device *dev, int vf, int rate); + * int (*ndo_get_vf_config)(struct net_device *dev, + * int vf, struct ifla_vf_info *ivf); + */ +#define HAVE_NET_DEVICE_OPS +struct net_device_ops { + int (*ndo_init)(struct net_device *dev); + void (*ndo_uninit)(struct net_device *dev); + int (*ndo_open)(struct net_device *dev); + int (*ndo_stop)(struct net_device *dev); + netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb, + struct net_device *dev); + u16 (*ndo_select_queue)(struct net_device *dev, + struct sk_buff *skb); + void (*ndo_change_rx_flags)(struct net_device *dev, + int flags); + void (*ndo_set_rx_mode)(struct net_device *dev); + void (*ndo_set_multicast_list)(struct net_device *dev); + int (*ndo_set_mac_address)(struct net_device *dev, + void *addr); + int (*ndo_validate_addr)(struct net_device *dev); + int (*ndo_do_ioctl)(struct net_device *dev, + struct ifreq *ifr, int cmd); + int (*ndo_set_config)(struct net_device *dev, + struct ifmap *map); + int (*ndo_change_mtu)(struct net_device *dev, + int new_mtu); + int (*ndo_neigh_setup)(struct net_device *dev, + struct neigh_parms *); + void (*ndo_tx_timeout) (struct net_device *dev); + + struct net_device_stats* (*ndo_get_stats)(struct net_device *dev); + + void (*ndo_vlan_rx_register)(struct net_device *dev, + struct vlan_group *grp); + void (*ndo_vlan_rx_add_vid)(struct net_device *dev, + unsigned short vid); + void (*ndo_vlan_rx_kill_vid)(struct net_device *dev, + unsigned short vid); +#ifdef CONFIG_NET_POLL_CONTROLLER + void (*ndo_poll_controller)(struct net_device *dev); +#endif + int (*ndo_set_vf_mac)(struct net_device *dev, + int queue, u8 *mac); + int (*ndo_set_vf_vlan)(struct net_device *dev, + int queue, u16 vlan, u8 qos); + int (*ndo_set_vf_tx_rate)(struct net_device *dev, + int vf, int rate); +/* + * The struct ifla_vf_info was added via b280da8d54b8d82b52f368a8703b7ada6c1744d5 + * on the v2.6.34-rc1~233^2~338 release + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) + int (*ndo_get_vf_config)(struct net_device *dev, + int vf, + struct ifla_vf_info *ivf); +#endif +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) + int (*ndo_fcoe_enable)(struct net_device *dev); + int (*ndo_fcoe_disable)(struct net_device *dev); + int (*ndo_fcoe_ddp_setup)(struct net_device *dev, + u16 xid, + struct scatterlist *sgl, + unsigned int sgc); + int (*ndo_fcoe_ddp_done)(struct net_device *dev, + u16 xid); +#define NETDEV_FCOE_WWNN 0 +#define NETDEV_FCOE_WWPN 1 + int (*ndo_fcoe_get_wwn)(struct net_device *dev, + u64 *wwn, int type); +#endif +}; + +void netdev_attach_ops(struct net_device *dev, + const struct net_device_ops *ops); + +/** + * skb_queue_is_first - check if skb is the first entry in the queue + * @list: queue head + * @skb: buffer + * + * Returns true if @skb is the first buffer on the list. + */ +static inline bool skb_queue_is_first(const struct sk_buff_head *list, + const struct sk_buff *skb) +{ + return (skb->prev == (struct sk_buff *) list); +} + +/** + * skb_queue_prev - return the prev packet in the queue + * @list: queue head + * @skb: current buffer + * + * Return the prev packet in @list before @skb. It is only valid to + * call this if skb_queue_is_first() evaluates to false. + */ +static inline struct sk_buff *skb_queue_prev(const struct sk_buff_head *list, + const struct sk_buff *skb) +{ + /* This BUG_ON may seem severe, but if we just return then we + * are going to dereference garbage. + */ + BUG_ON(skb_queue_is_first(list, skb)); + return skb->prev; +} + + +static inline struct net_device_stats *dev_get_stats(struct net_device *dev) +{ + return dev->get_stats(dev); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)) +#if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE) +extern void usb_unpoison_anchored_urbs(struct usb_anchor *anchor); +#endif /* CONFIG_USB */ +#endif + +#define DIV_ROUND_CLOSEST(x, divisor)( \ +{ \ + typeof(divisor) __divisor = divisor; \ + (((x) + ((__divisor) / 2)) / (__divisor)); \ +} \ +) + +extern int eth_mac_addr(struct net_device *dev, void *p); +extern int eth_change_mtu(struct net_device *dev, int new_mtu); +extern int eth_validate_addr(struct net_device *dev); + +#ifdef CONFIG_NET_NS + +static inline void write_pnet(struct net **pnet, struct net *net) +{ + *pnet = net; +} + +static inline struct net *read_pnet(struct net * const *pnet) +{ + return *pnet; +} + +#else + +#define write_pnet(pnet, net) do { (void)(net);} while (0) +#define read_pnet(pnet) (&init_net) + +/* + * swap - swap value of @a and @b + */ +#define swap(a, b) \ + do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) + +#endif + +extern int init_dummy_netdev(struct net_device *dev); + +#define compat_pci_suspend(fn) \ + int fn##_compat(struct pci_dev *pdev, pm_message_t state) \ + { \ + int r; \ + \ + r = fn(&pdev->dev); \ + if (r) \ + return r; \ + \ + pci_save_state(pdev); \ + pci_disable_device(pdev); \ + pci_set_power_state(pdev, PCI_D3hot); \ + \ + return 0; \ + } + +#define compat_pci_resume(fn) \ + int fn##_compat(struct pci_dev *pdev) \ + { \ + int r; \ + \ + pci_set_power_state(pdev, PCI_D0); \ + r = pci_enable_device(pdev); \ + if (r) \ + return r; \ + pci_restore_state(pdev); \ + \ + return fn(&pdev->dev); \ + } + +#else + +static inline void netdev_attach_ops(struct net_device *dev, + const struct net_device_ops *ops) +{ + dev->netdev_ops = ops; +} + +#define compat_pci_suspend(fn) +#define compat_pci_resume(fn) + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) */ + +#endif /* LINUX_26_29_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.30.h b/include/compat/linux/compat-2.6.30.h new file mode 100644 index 00000000..28f3d54a --- /dev/null +++ b/include/compat/linux/compat-2.6.30.h @@ -0,0 +1,47 @@ +#ifndef LINUX_26_30_COMPAT_H +#define LINUX_26_30_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) + +#include + +#ifndef TP_PROTO +#define TP_PROTO(args...) TPPROTO(args) +#endif +#ifndef TP_ARGS +#define TP_ARGS(args...) TPARGS(args) +#endif + +#define IRQ_WAKE_THREAD (2) + +/* From : include/linux/pm.h */ +/* How to reorder dpm_list after device_move() */ +enum dpm_order { + DPM_ORDER_NONE, + DPM_ORDER_DEV_AFTER_PARENT, + DPM_ORDER_PARENT_BEFORE_DEV, + DPM_ORDER_DEV_LAST, +}; + +static inline void dev_set_uevent_suppress(struct device *dev, int val) +{ + dev->uevent_suppress = val; +} + +/* + * Print a one-time message (analogous to WARN_ONCE() et al): + */ +#define printk_once(x...) ({ \ + static bool __print_once; \ + \ + if (!__print_once) { \ + __print_once = true; \ + printk(x); \ + } \ +}) + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) */ + +#endif /* LINUX_26_30_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.31.h b/include/compat/linux/compat-2.6.31.h new file mode 100644 index 00000000..25db973d --- /dev/null +++ b/include/compat/linux/compat-2.6.31.h @@ -0,0 +1,221 @@ +#ifndef LINUX_26_31_COMPAT_H +#define LINUX_26_31_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)) + +#include +#include +#include +#include +#include +#include + +/* + * These macros allow us to backport rfkill without any + * changes on cfg80211 through compat.diff. Note that this + * file will be included by rfkill_backport.h so we must + * not conflict with things there. + */ +#define rfkill_get_led_trigger_name backport_rfkill_get_led_trigger_name +#define rfkill_set_led_trigger_name backport_rfkill_set_led_trigger_name +#define rfkill_set_hw_state backport_rfkill_set_hw_state +#define rfkill_set_sw_state backport_rfkill_set_sw_state +#define rfkill_init_sw_state backport_rfkill_init_sw_state +#define rfkill_set_states backport_rfkill_set_states +#define rfkill_pause_polling backport_rfkill_pause_polling +#define rfkill_resume_polling backport_rfkill_resume_polling +#define rfkill_blocked backport_rfkill_blocked +#define rfkill_alloc backport_rfkill_alloc +#define rfkill_register backport_rfkill_register +#define rfkill_unregister backport_rfkill_unregister +#define rfkill_destroy backport_rfkill_destroy + +#ifndef ERFKILL +#if !defined(CONFIG_ALPHA) && !defined(CONFIG_MIPS) && !defined(CONFIG_PARISC) && !defined(CONFIG_SPARC) +#define ERFKILL 132 /* Operation not possible due to RF-kill */ +#endif +#ifdef CONFIG_ALPHA +#define ERFKILL 138 /* Operation not possible due to RF-kill */ +#endif +#ifdef CONFIG_MIPS +#define ERFKILL 167 /* Operation not possible due to RF-kill */ +#endif +#ifdef CONFIG_PARISC +#define ERFKILL 256 /* Operation not possible due to RF-kill */ +#endif +#ifdef CONFIG_SPARC +#define ERFKILL 134 /* Operation not possible due to RF-kill */ +#endif +#endif + +#ifndef NETDEV_PRE_UP +#define NETDEV_PRE_UP 0x000D +#endif + +#ifndef SDIO_DEVICE_ID_MARVELL_8688WLAN +#define SDIO_DEVICE_ID_MARVELL_8688WLAN 0x9104 +#endif + +struct compat_threaded_irq { + unsigned int irq; + irq_handler_t handler; + irq_handler_t thread_fn; + void *dev_id; + char wq_name[64]; + struct workqueue_struct *wq; + struct work_struct work; +}; + +/* + * kmemleak was introduced on 2.6.31, since older kernels do not have + * we simply ignore its tuning. + */ +static inline void kmemleak_ignore(const void *ptr) +{ + return; +} + +static inline void kmemleak_not_leak(const void *ptr) +{ + return; +} + +static inline void kmemleak_no_scan(const void *ptr) +{ + return; +} + +/* + * Added via adf30907d63893e4208dfe3f5c88ae12bc2f25d5 + * + * There is no _sk_dst on older kernels, so just set the + * old dst to NULL and release it directly. + */ +static inline void skb_dst_drop(struct sk_buff *skb) +{ + dst_release(skb->dst); + skb->dst = NULL; +} + +static inline struct dst_entry *skb_dst(const struct sk_buff *skb) +{ + return (struct dst_entry *)skb->dst; +} + +static inline void skb_dst_set(struct sk_buff *skb, struct dst_entry *dst) +{ + skb->dst = dst; +} + +static inline struct rtable *skb_rtable(const struct sk_buff *skb) +{ + return (struct rtable *)skb_dst(skb); +} + +/* Backport threaded IRQ support */ + +static inline +void compat_irq_work(struct work_struct *work) +{ + struct compat_threaded_irq *comp = container_of(work, struct compat_threaded_irq, work); + comp->thread_fn(comp->irq, comp->dev_id); +} + +static inline +irqreturn_t compat_irq_dispatcher(int irq, void *dev_id) +{ + struct compat_threaded_irq *comp = dev_id; + irqreturn_t res; + + res = comp->handler(irq, comp->dev_id); + if (res == IRQ_WAKE_THREAD) { + queue_work(comp->wq, &comp->work); + res = IRQ_HANDLED; + } + + return res; +} + +static inline +int compat_request_threaded_irq(struct compat_threaded_irq *comp, + unsigned int irq, + irq_handler_t handler, + irq_handler_t thread_fn, + unsigned long flags, + const char *name, + void *dev_id) +{ + comp->irq = irq; + comp->handler = handler; + comp->thread_fn = thread_fn; + comp->dev_id = dev_id; + INIT_WORK(&comp->work, compat_irq_work); + + if (!comp->wq) { + snprintf(comp->wq_name, sizeof(comp->wq_name), + "compirq/%u-%s", irq, name); + comp->wq = create_singlethread_workqueue(comp->wq_name); + if (!comp->wq) { + printk(KERN_ERR "Failed to create compat-threaded-IRQ workqueue %s\n", + comp->wq_name); + return -ENOMEM; + } + } + return request_irq(irq, compat_irq_dispatcher, flags, name, comp); +} + +static inline +void compat_free_threaded_irq(struct compat_threaded_irq *comp) +{ + free_irq(comp->irq, comp); +} + +static inline +void compat_destroy_threaded_irq(struct compat_threaded_irq *comp) +{ + if (comp->wq) + destroy_workqueue(comp->wq); + comp->wq = NULL; +} + +static inline +void compat_synchronize_threaded_irq(struct compat_threaded_irq *comp) +{ + synchronize_irq(comp->irq); + cancel_work_sync(&comp->work); +} + +/** + * list_entry_rcu - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * This primitive may safely run concurrently with the _rcu list-mutation + * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock(). + */ +#define list_entry_rcu(ptr, type, member) \ + container_of(rcu_dereference(ptr), type, member) + +#define skb_walk_frags(skb, iter) \ + for (iter = skb_shinfo(skb)->frag_list; iter; iter = iter->next) + +#ifndef CONFIG_64BIT + +typedef struct { + long long counter; +} atomic64_t; + +extern long long atomic64_read(const atomic64_t *v); +extern long long atomic64_add_return(long long a, atomic64_t *v); + +#define atomic64_inc_return(v) atomic64_add_return(1LL, (v)) + +#endif + + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)) */ + +#endif /* LINUX_26_31_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.32.h b/include/compat/linux/compat-2.6.32.h new file mode 100644 index 00000000..3506291d --- /dev/null +++ b/include/compat/linux/compat-2.6.32.h @@ -0,0 +1,129 @@ +#ifndef LINUX_26_32_COMPAT_H +#define LINUX_26_32_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)) + +#include +#include +#include +#include +#include + +#define SDIO_VENDOR_ID_INTEL 0x0089 +#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402 +#define SDIO_DEVICE_ID_INTEL_IWMC3200WIFI 0x1403 +#define SDIO_DEVICE_ID_INTEL_IWMC3200TOP 0x1404 +#define SDIO_DEVICE_ID_INTEL_IWMC3200GPS 0x1405 +#define SDIO_DEVICE_ID_INTEL_IWMC3200BT 0x1406 + +static inline void flush_delayed_work(struct delayed_work *dwork) +{ + if (del_timer_sync(&dwork->timer)) { + /* + * This is what would happen on 2.6.32 but since we don't have + * access to the singlethread_cpu we can't really backport this, + * so avoid really *flush*ing the work... Oh well. Any better ideas? + + struct cpu_workqueue_struct *cwq; + cwq = wq_per_cpu(keventd_wq, get_cpu()); + __queue_work(cwq, &dwork->work); + put_cpu(); + + */ + } + flush_work(&dwork->work); +} + +/* + * struct genl_multicast_group was made netns aware through + * patch "genetlink: make netns aware" by johannes, we just + * force this to always use the default init_net + */ +#define genl_info_net(x) &init_net +/* Just use init_net for older kernels */ +#define get_net_ns_by_pid(x) &init_net + +/* net namespace is lost */ +#define genlmsg_multicast_netns(a, b, c, d, e) genlmsg_multicast(b, c, d, e) +#define genlmsg_multicast_allns(a, b, c, d) genlmsg_multicast(a, b, c, d) +#define genlmsg_unicast(net, skb, pid) genlmsg_unicast(skb, pid) + +#define dev_change_net_namespace(a, b, c) (-EOPNOTSUPP) + +#define SET_NETDEV_DEVTYPE(netdev, type) + +#ifdef __KERNEL__ +/* Driver transmit return codes */ +enum netdev_tx { + BACKPORT_NETDEV_TX_OK = NETDEV_TX_OK, /* driver took care of packet */ + BACKPORT_NETDEV_TX_BUSY = NETDEV_TX_BUSY, /* driver tx path was busy*/ + BACKPORT_NETDEV_TX_LOCKED = NETDEV_TX_LOCKED, /* driver tx lock was already taken */ +}; +typedef enum netdev_tx netdev_tx_t; +#endif /* __KERNEL__ */ + +/* + * dev_pm_ops is only available on kernels >= 2.6.29, for + * older kernels we rely on reverting the work to old + * power management style stuff. + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) +/* + * Use this if you want to use the same suspend and resume callbacks for suspend + * to RAM and hibernation. + */ +#define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \ +struct dev_pm_ops name = { \ + .suspend = suspend_fn, \ + .resume = resume_fn, \ + .freeze = suspend_fn, \ + .thaw = resume_fn, \ + .poweroff = suspend_fn, \ + .restore = resume_fn, \ +} +#else +#define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) +#endif /* >= 2.6.29 */ + +#define wireless_send_event(a, b, c, d) wireless_send_event(a, b, c, (char * ) d) + +/* The export symbol in changed in compat/patches/15-symbol-export-conflicts.patch */ +#define ieee80211_rx(hw, skb) mac80211_ieee80211_rx(hw, skb) + +#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) + +#define lockdep_assert_held(l) do { } while (0) + +/* + * Similar to the struct tm in userspace , but it needs to be here so + * that the kernel source is self contained. + */ +struct tm { + /* + * the number of seconds after the minute, normally in the range + * 0 to 59, but can be up to 60 to allow for leap seconds + */ + int tm_sec; + /* the number of minutes after the hour, in the range 0 to 59*/ + int tm_min; + /* the number of hours past midnight, in the range 0 to 23 */ + int tm_hour; + /* the day of the month, in the range 1 to 31 */ + int tm_mday; + /* the number of months since January, in the range 0 to 11 */ + int tm_mon; + /* the number of years since 1900 */ + long tm_year; + /* the number of days since Sunday, in the range 0 to 6 */ + int tm_wday; + /* the number of days since January 1, in the range 0 to 365 */ + int tm_yday; +}; + +void time_to_tm(time_t totalsecs, int offset, struct tm *result); + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)) */ + +#endif /* LINUX_26_32_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.33.h b/include/compat/linux/compat-2.6.33.h new file mode 100644 index 00000000..c900a6fc --- /dev/null +++ b/include/compat/linux/compat-2.6.33.h @@ -0,0 +1,172 @@ +#ifndef LINUX_26_33_COMPAT_H +#define LINUX_26_33_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)) + +#include +#include +#if defined(CONFIG_PCCARD) || defined(CONFIG_PCCARD_MODULE) +#include +#include +#include +#endif +#include +#include + +#if defined(CONFIG_COMPAT_FIRMWARE_CLASS) +#define release_firmware compat_release_firmware +#define request_firmware compat_request_firmware +#define request_firmware_nowait compat_request_firmware_nowait +#endif + +#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) +int compat_request_firmware(const struct firmware **fw, const char *name, + struct device *device); +int compat_request_firmware_nowait( + struct module *module, int uevent, + const char *name, struct device *device, gfp_t gfp, void *context, + void (*cont)(const struct firmware *fw, void *context)); + +void compat_release_firmware(const struct firmware *fw); +#else +static inline int compat_request_firmware(const struct firmware **fw, + const char *name, + struct device *device) +{ + return -EINVAL; +} +static inline int request_firmware_nowait( + struct module *module, int uevent, + const char *name, struct device *device, gfp_t gfp, void *context, + void (*cont)(const struct firmware *fw, void *context)) +{ + return -EINVAL; +} + +static inline void compat_release_firmware(const struct firmware *fw) +{ +} +#endif + +/* mask KEY_RFKILL as RHEL6 backports this */ +#if !defined(KEY_RFKILL) +#define KEY_RFKILL 247 /* Key that controls all radios */ +#endif + +#define IFF_DONT_BRIDGE 0x800 /* disallow bridging this ether dev */ +/* source: include/linux/if.h */ + +/* this will never happen on older kernels */ +#define NETDEV_POST_INIT 0xffff + +/* mask netdev_alloc_skb_ip_align as debian squeeze also backports this */ +#define netdev_alloc_skb_ip_align(a, b) compat_netdev_alloc_skb_ip_align(a, b) + +static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev, + unsigned int length) +{ + struct sk_buff *skb = netdev_alloc_skb(dev, length + NET_IP_ALIGN); + + if (NET_IP_ALIGN && skb) + skb_reserve(skb, NET_IP_ALIGN); + return skb; +} + +#if defined(CONFIG_PCCARD) || defined(CONFIG_PCCARD_MODULE) + +#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE) + +#define pcmcia_request_window(a, b, c) pcmcia_request_window(&a, b, c) + +#define pcmcia_map_mem_page(a, b, c) pcmcia_map_mem_page(b, c) + +/* loop over CIS entries */ +int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code, + int (*loop_tuple) (struct pcmcia_device *p_dev, + tuple_t *tuple, + void *priv_data), + void *priv_data); + +#endif /* CONFIG_PCMCIA */ + +/* loop over CIS entries */ +int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function, + cisdata_t code, cisparse_t *parse, void *priv_data, + int (*loop_tuple) (tuple_t *tuple, + cisparse_t *parse, + void *priv_data)); + +#endif /* CONFIG_PCCARD */ + +/** + * list_for_each_entry_continue_rcu - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue_rcu(pos, head, member) \ + for (pos = list_entry_rcu(pos->member.next, typeof(*pos), member); \ + prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) + +#define sock_recv_ts_and_drops(msg, sk, skb) sock_recv_timestamp(msg, sk, skb) + +/* mask pci_pcie_cap as debian squeeze also backports this */ +#define pci_pcie_cap(a) compat_pci_pcie_cap(a) + +/** + * pci_pcie_cap - get the saved PCIe capability offset + * @dev: PCI device + * + * PCIe capability offset is calculated at PCI device initialization + * time and saved in the data structure. This function returns saved + * PCIe capability offset. Using this instead of pci_find_capability() + * reduces unnecessary search in the PCI configuration space. If you + * need to calculate PCIe capability offset from raw device for some + * reasons, please use pci_find_capability() instead. + */ +static inline int pci_pcie_cap(struct pci_dev *dev) +{ + return pci_find_capability(dev, PCI_CAP_ID_EXP); +} + +/* mask pci_is_pcie as RHEL6 backports this */ +#define pci_is_pcie(a) compat_pci_is_pcie(a) + +/** + * pci_is_pcie - check if the PCI device is PCI Express capable + * @dev: PCI device + * + * Retrun true if the PCI device is PCI Express capable, false otherwise. + */ +static inline bool pci_is_pcie(struct pci_dev *dev) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + return dev->is_pcie; +#else + return !!pci_pcie_cap(dev); +#endif +} + +#ifdef __GNUC__ +#define __always_unused __attribute__((unused)) +#else +#define __always_unused /* unimplemented */ +#endif + +/* mask IS_ERR_OR_NULL as debian squeeze also backports this */ +#define IS_ERR_OR_NULL(a) compat_IS_ERR_OR_NULL(a) + +static inline long __must_check IS_ERR_OR_NULL(const void *ptr) +{ + return !ptr || IS_ERR_VALUE((unsigned long)ptr); +} + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)) */ + +#endif /* LINUX_26_33_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.34.h b/include/compat/linux/compat-2.6.34.h new file mode 100644 index 00000000..da242ce0 --- /dev/null +++ b/include/compat/linux/compat-2.6.34.h @@ -0,0 +1,309 @@ +#ifndef LINUX_26_34_COMPAT_H +#define LINUX_26_34_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)) + +#include +#include + +#define netdev_mc_count(dev) ((dev)->mc_count) +#define netdev_mc_empty(dev) (netdev_mc_count(dev) == 0) + +#define netdev_for_each_mc_addr(mclist, dev) \ + for (mclist = dev->mc_list; mclist; mclist = mclist->next) +/* source: include/linux/netdevice.h */ + + +/* Logging, debugging and troubleshooting/diagnostic helpers. */ + +/* netdev_printk helpers, similar to dev_printk */ + +#ifndef netdev_name +#define netdev_name(__dev) \ + ((__dev->reg_state != NETREG_REGISTERED) ? \ + "(unregistered net_device)" : __dev->name) +#endif + +#define netdev_printk(level, netdev, format, args...) \ + dev_printk(level, (netdev)->dev.parent, \ + "%s: " format, \ + netdev_name(netdev), ##args) + +#define netdev_emerg(dev, format, args...) \ + netdev_printk(KERN_EMERG, dev, format, ##args) +#define netdev_alert(dev, format, args...) \ + netdev_printk(KERN_ALERT, dev, format, ##args) +#define netdev_crit(dev, format, args...) \ + netdev_printk(KERN_CRIT, dev, format, ##args) +#define netdev_err(dev, format, args...) \ + netdev_printk(KERN_ERR, dev, format, ##args) +#define netdev_warn(dev, format, args...) \ + netdev_printk(KERN_WARNING, dev, format, ##args) +#define netdev_notice(dev, format, args...) \ + netdev_printk(KERN_NOTICE, dev, format, ##args) +#define netdev_info(dev, format, args...) \ + netdev_printk(KERN_INFO, dev, format, ##args) + +/* mask netdev_dbg as RHEL6 backports this */ +#if !defined(netdev_dbg) + +#if defined(DEBUG) +#define netdev_dbg(__dev, format, args...) \ + netdev_printk(KERN_DEBUG, __dev, format, ##args) +#elif defined(CONFIG_DYNAMIC_DEBUG) +#define netdev_dbg(__dev, format, args...) \ +do { \ + dynamic_dev_dbg((__dev)->dev.parent, "%s: " format, \ + netdev_name(__dev), ##args); \ +} while (0) +#else +#define netdev_dbg(__dev, format, args...) \ +({ \ + if (0) \ + netdev_printk(KERN_DEBUG, __dev, format, ##args); \ + 0; \ +}) +#endif + +#endif + +/* mask netdev_vdbg as RHEL6 backports this */ +#if !defined(netdev_dbg) + +#if defined(VERBOSE_DEBUG) +#define netdev_vdbg netdev_dbg +#else + +#define netdev_vdbg(dev, format, args...) \ +({ \ + if (0) \ + netdev_printk(KERN_DEBUG, dev, format, ##args); \ + 0; \ +}) +#endif + +#endif + +/* + * netdev_WARN() acts like dev_printk(), but with the key difference + * of using a WARN/WARN_ON to get the message out, including the + * file/line information and a backtrace. + */ +#define netdev_WARN(dev, format, args...) \ + WARN(1, "netdevice: %s\n" format, netdev_name(dev), ##args); + +/* netif printk helpers, similar to netdev_printk */ + +#define netif_printk(priv, type, level, dev, fmt, args...) \ +do { \ + if (netif_msg_##type(priv)) \ + netdev_printk(level, (dev), fmt, ##args); \ +} while (0) + +#define netif_emerg(priv, type, dev, fmt, args...) \ + netif_printk(priv, type, KERN_EMERG, dev, fmt, ##args) +#define netif_alert(priv, type, dev, fmt, args...) \ + netif_printk(priv, type, KERN_ALERT, dev, fmt, ##args) +#define netif_crit(priv, type, dev, fmt, args...) \ + netif_printk(priv, type, KERN_CRIT, dev, fmt, ##args) +#define netif_err(priv, type, dev, fmt, args...) \ + netif_printk(priv, type, KERN_ERR, dev, fmt, ##args) +#define netif_warn(priv, type, dev, fmt, args...) \ + netif_printk(priv, type, KERN_WARNING, dev, fmt, ##args) +#define netif_notice(priv, type, dev, fmt, args...) \ + netif_printk(priv, type, KERN_NOTICE, dev, fmt, ##args) +#define netif_info(priv, type, dev, fmt, args...) \ + netif_printk(priv, type, KERN_INFO, (dev), fmt, ##args) + +/* mask netif_dbg as RHEL6 backports this */ +#if !defined(netif_dbg) + +#if defined(DEBUG) +#define netif_dbg(priv, type, dev, format, args...) \ + netif_printk(priv, type, KERN_DEBUG, dev, format, ##args) +#elif defined(CONFIG_DYNAMIC_DEBUG) +#define netif_dbg(priv, type, netdev, format, args...) \ +do { \ + if (netif_msg_##type(priv)) \ + dynamic_dev_dbg((netdev)->dev.parent, \ + "%s: " format, \ + netdev_name(netdev), ##args); \ +} while (0) +#else +#define netif_dbg(priv, type, dev, format, args...) \ +({ \ + if (0) \ + netif_printk(priv, type, KERN_DEBUG, dev, format, ##args); \ + 0; \ +}) +#endif + +#endif + +/* mask netif_vdbg as RHEL6 backports this */ +#if !defined(netif_vdbg) + +#if defined(VERBOSE_DEBUG) +#define netif_vdbg netdev_dbg +#else +#define netif_vdbg(priv, type, dev, format, args...) \ +({ \ + if (0) \ + netif_printk(KERN_DEBUG, dev, format, ##args); \ + 0; \ +}) +#endif +#endif +/* source: include/linux/netdevice.h */ + + +static inline void device_lock(struct device *dev) +{ +#if defined(CONFIG_PREEMPT_RT) || defined(CONFIG_PREEMPT_DESKTOP) + mutex_lock(&dev->mutex); +#else + down(&dev->sem); +#endif +} + +static inline int device_trylock(struct device *dev) +{ +#if defined(CONFIG_PREEMPT_RT) || defined(CONFIG_PREEMPT_DESKTOP) + return mutex_trylock(&dev->mutex); +#else + return down_trylock(&dev->sem); +#endif +} + +static inline void device_unlock(struct device *dev) +{ +#if defined(CONFIG_PREEMPT_RT) || defined(CONFIG_PREEMPT_DESKTOP) + mutex_unlock(&dev->mutex); +#else + up(&dev->sem); +#endif +} + +#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE) +#define PCMCIA_DEVICE_PROD_ID3(v3, vh3) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID3, \ + .prod_id = { NULL, NULL, (v3), NULL }, \ + .prod_id_hash = { 0, 0, (vh3), 0 }, } +#endif + +#define rcu_dereference_check(p, c) rcu_dereference(p) + +/** + * sysfs_attr_init - initialize a dynamically allocated sysfs attribute + * @attr: struct attribute to initialize + * + * Initialize a dynamically allocated struct attribute so we can + * make lockdep happy. This is a new requirement for attributes + * and initially this is only needed when lockdep is enabled. + * Lockdep gives a nice error when your attribute is added to + * sysfs if you don't have this. + */ +#ifdef CONFIG_DEBUG_LOCK_ALLOC +#define sysfs_attr_init(attr) \ +do { \ + static struct lock_class_key __key; \ + \ + (attr)->key = &__key; \ +} while(0) +#else +#define sysfs_attr_init(attr) do {} while(0) +#endif + +/** + * sysfs_bin_attr_init - initialize a dynamically allocated bin_attribute + * @attr: struct bin_attribute to initialize + * + * Initialize a dynamically allocated struct bin_attribute so we + * can make lockdep happy. This is a new requirement for + * attributes and initially this is only needed when lockdep is + * enabled. Lockdep gives a nice error when your attribute is + * added to sysfs if you don't have this. + */ +#define sysfs_bin_attr_init(bin_attr) sysfs_attr_init(&(bin_attr)->attr) + +#define usb_alloc_coherent(dev, size, mem_flags, dma) usb_buffer_alloc(dev, size, mem_flags, dma) +#define usb_free_coherent(dev, size, addr, dma) usb_buffer_free(dev, size, addr, dma) + +/* only include this if DEFINE_DMA_UNMAP_ADDR is not set as debian squeeze also backports this */ +#ifndef DEFINE_DMA_UNMAP_ADDR +#ifdef CONFIG_NEED_DMA_MAP_STATE +#define DEFINE_DMA_UNMAP_ADDR(ADDR_NAME) dma_addr_t ADDR_NAME +#define DEFINE_DMA_UNMAP_LEN(LEN_NAME) __u32 LEN_NAME +#define dma_unmap_addr(PTR, ADDR_NAME) ((PTR)->ADDR_NAME) +#define dma_unmap_addr_set(PTR, ADDR_NAME, VAL) (((PTR)->ADDR_NAME) = (VAL)) +#define dma_unmap_len(PTR, LEN_NAME) ((PTR)->LEN_NAME) +#define dma_unmap_len_set(PTR, LEN_NAME, VAL) (((PTR)->LEN_NAME) = (VAL)) +#else +#define DEFINE_DMA_UNMAP_ADDR(ADDR_NAME) +#define DEFINE_DMA_UNMAP_LEN(LEN_NAME) +#define dma_unmap_addr(PTR, ADDR_NAME) (0) +#define dma_unmap_addr_set(PTR, ADDR_NAME, VAL) do { } while (0) +#define dma_unmap_len(PTR, LEN_NAME) (0) +#define dma_unmap_len_set(PTR, LEN_NAME, VAL) do { } while (0) +#endif +#endif + +/* mask dma_set_coherent_mask as debian squeeze also backports this */ +#define dma_set_coherent_mask(a, b) compat_dma_set_coherent_mask(a, b) + +static inline int dma_set_coherent_mask(struct device *dev, u64 mask) +{ + if (!dma_supported(dev, mask)) + return -EIO; + dev->coherent_dma_mask = mask; + return 0; +} + +/* USB autosuspend and autoresume */ +static inline int usb_enable_autosuspend(struct usb_device *udev) +{ return 0; } +static inline int usb_disable_autosuspend(struct usb_device *udev) +{ return 0; } + +#define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */ +#define sdio_set_host_pm_flags(a, b) 0 + +#define rcu_dereference_protected(p, c) (p) +#define rcu_access_pointer(p) ACCESS_ONCE(p) + +#define rcu_dereference_raw(p) rcu_dereference(p) + +#define KEY_WPS_BUTTON 0x211 /* WiFi Protected Setup key */ + +/* + * This looks more complex than it should be. But we need to + * get the type for the ~ right in round_down (it needs to be + * as wide as the result!), and we want to evaluate the macro + * arguments just once each. + */ +#define __round_mask(x, y) ((__typeof__(x))((y)-1)) +#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) +#define round_down(x, y) ((x) & ~__round_mask(x, y)) + +static inline int rcu_read_lock_held(void) +{ + return 1; +} + +#ifdef CONFIG_PROVE_LOCKING +/* + * Obviously, this is wrong. But the base kernel will have rtnl_mutex + * declared static, with no way to access it. I think this is the best + * we can do... + */ +static inline int lockdep_rtnl_is_held(void) +{ + return 1; +} +#endif /* #ifdef CONFIG_PROVE_LOCKING */ + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)) */ + +#endif /* LINUX_26_34_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.35.h b/include/compat/linux/compat-2.6.35.h new file mode 100644 index 00000000..5e43002f --- /dev/null +++ b/include/compat/linux/compat-2.6.35.h @@ -0,0 +1,48 @@ +#ifndef LINUX_26_35_COMPAT_H +#define LINUX_26_35_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) +#include +#include +#include +#include + +/* added on linux/kernel.h */ +#define USHRT_MAX ((u16)(~0U)) +#define SHRT_MAX ((s16)(USHRT_MAX>>1)) +#define SHRT_MIN ((s16)(-SHRT_MAX - 1)) + +#define SDIO_BUS_ECSI 0x20 /* Enable continuous SPI interrupt */ +#define SDIO_BUS_SCSI 0x40 /* Support continuous SPI interrupt */ + +#define netdev_hw_addr dev_mc_list + +static inline wait_queue_head_t *sk_sleep(struct sock *sk) +{ + return sk->sk_sleep; +} + +#define sdio_writeb_readb(func, write_byte, addr, err_ret) sdio_readb(func, addr, err_ret) + +/* mask hex_to_bin as RHEL6 backports this */ +#define hex_to_bin(a) compat_hex_to_bin(a) + +int hex_to_bin(char ch); + +extern loff_t noop_llseek(struct file *file, loff_t offset, int origin); + +#define pm_qos_request(_qos) pm_qos_requirement(_qos) + +static inline struct usb_host_endpoint * +usb_pipe_endpoint(struct usb_device *dev, unsigned int pipe) +{ + struct usb_host_endpoint **eps; + eps = usb_pipein(pipe) ? dev->ep_in : dev->ep_out; + return eps[usb_pipeendpoint(pipe)]; +} + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) */ + +#endif /* LINUX_26_35_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.36.h b/include/compat/linux/compat-2.6.36.h new file mode 100644 index 00000000..314fb353 --- /dev/null +++ b/include/compat/linux/compat-2.6.36.h @@ -0,0 +1,172 @@ +#ifndef LINUX_26_36_COMPAT_H +#define LINUX_26_36_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) + +#include +#include +#include +#include +#include + +#define kparam_block_sysfs_write(a) +#define kparam_unblock_sysfs_write(a) + +/* mask va_format as RHEL6 backports this */ +#define va_format compat_va_format + +struct va_format { + const char *fmt; + va_list *va; +}; + +#define device_rename(dev, new_name) device_rename(dev, (char *)new_name) + +#ifdef CONFIG_COMPAT_USB_URB_THREAD_FIX +#define usb_scuttle_anchored_urbs(anchor) compat_usb_scuttle_anchored_urbs(anchor) +#define usb_get_from_anchor(anchor) compat_usb_get_from_anchor(anchor) +#define usb_unlink_anchored_urbs(anchor) compat_usb_unlink_anchored_urbs(anchor) + +extern void compat_usb_unlink_anchored_urbs(struct usb_anchor *anchor); +extern struct urb *compat_usb_get_from_anchor(struct usb_anchor *anchor); +extern void compat_usb_scuttle_anchored_urbs(struct usb_anchor *anchor); +#endif + +/** + * pcmcia_read_config_byte() - read a byte from a card configuration register + * + * pcmcia_read_config_byte() reads a byte from a configuration register in + * attribute memory. + */ +static inline int pcmcia_read_config_byte(struct pcmcia_device *p_dev, off_t where, u8 *val) +{ + int ret; + conf_reg_t reg = { 0, CS_READ, where, 0 }; + ret = pcmcia_access_configuration_register(p_dev, ®); + *val = reg.Value; + return ret; +} + +/** + * pcmcia_write_config_byte() - write a byte to a card configuration register + * + * pcmcia_write_config_byte() writes a byte to a configuration register in + * attribute memory. + */ +static inline int pcmcia_write_config_byte(struct pcmcia_device *p_dev, off_t where, u8 val) +{ + conf_reg_t reg = { 0, CS_WRITE, where, val }; + return pcmcia_access_configuration_register(p_dev, ®); +} + +struct pm_qos_request_list { + u32 qos; + void *request; +}; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) + +#define pm_qos_add_request(_req, _class, _value) do { \ + (_req)->request = #_req; \ + (_req)->qos = _class; \ + pm_qos_add_requirement((_class), (_req)->request, (_value)); \ + } while(0) + +#define pm_qos_update_request(_req, _value) \ + pm_qos_update_requirement((_req)->qos, (_req)->request, (_value)) + +#define pm_qos_remove_request(_req) \ + pm_qos_remove_requirement((_req)->qos, (_req)->request) + +#else + +#define pm_qos_add_request(_req, _class, _value) do { \ + (_req)->request = pm_qos_add_request((_class), (_value)); \ + } while (0) + +#define pm_qos_update_request(_req, _value) \ + pm_qos_update_request((_req)->request, (_value)) + +#define pm_qos_remove_request(_req) \ + pm_qos_remove_request((_req)->request) + +#endif + +/* + * Dummy printk for disabled debugging statements to use whilst maintaining + * gcc's format and side-effect checking. + */ +static inline __attribute__ ((format (printf, 1, 2))) +int no_printk(const char *s, ...) { return 0; } + +#ifndef alloc_workqueue +#define alloc_workqueue(name, flags, max_active) __create_workqueue(name, flags, max_active, 0) +#endif + +#define EXTPROC 0200000 +#define TIOCPKT_IOCTL 64 + +static inline void tty_lock(void) __acquires(kernel_lock) +{ +#ifdef CONFIG_LOCK_KERNEL + /* kernel_locked is 1 for !CONFIG_LOCK_KERNEL */ + WARN_ON(kernel_locked()); +#endif + lock_kernel(); +} +static inline void tty_unlock(void) __releases(kernel_lock) +{ + unlock_kernel(); +} +#define tty_locked() (kernel_locked()) + +#define usleep_range(_min, _max) msleep((_max) / 1000) + +#define __rcu + +static inline void pm_wakeup_event(struct device *dev, unsigned int msec) {} + +static inline bool skb_defer_rx_timestamp(struct sk_buff *skb) +{ + return false; +} + +static inline void skb_tx_timestamp(struct sk_buff *skb) +{ +} + +extern struct workqueue_struct *system_nrt_wq; + +void compat_system_workqueue_create(void); +void compat_system_workqueue_destroy(void); + +#define br_port_exists(dev) (dev->br_port) + +#else + +static inline void compat_system_workqueue_create(void) +{ +} + +static inline void compat_system_workqueue_destroy(void) +{ +} + +/* + * This is not part of The 2.6.37 kernel yet but we + * we use it to optimize the backport code we + * need to implement. Instead of using ifdefs + * to check what version of the check we use + * we just replace all checks on current code + * with this. I'll submit this upstream too, that + * way all we'd have to do is to implement this + * for older kernels, then we would not have to + * edit the upstrema code for backport efforts. + */ +#define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT) + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) */ + +#endif /* LINUX_26_36_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.37.h b/include/compat/linux/compat-2.6.37.h new file mode 100644 index 00000000..c19e7818 --- /dev/null +++ b/include/compat/linux/compat-2.6.37.h @@ -0,0 +1,140 @@ +#ifndef LINUX_26_37_COMPAT_H +#define LINUX_26_37_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) + +#include +#include + +#define SDIO_CLASS_BT_AMP 0x09 /* Type-A Bluetooth AMP interface */ + +extern struct kobj_ns_type_operations net_ns_type_operations; + +/* mask skb_checksum_none_assert as RHEL6 backports this */ +#define skb_checksum_none_assert(a) compat_skb_checksum_none_assert(a) + +/** + * skb_checksum_none_assert - make sure skb ip_summed is CHECKSUM_NONE + * @skb: skb to check + * + * fresh skbs have their ip_summed set to CHECKSUM_NONE. + * Instead of forcing ip_summed to CHECKSUM_NONE, we can + * use this helper, to document places where we make this assertion. + */ +static inline void skb_checksum_none_assert(struct sk_buff *skb) +{ +#ifdef DEBUG + BUG_ON(skb->ip_summed != CHECKSUM_NONE); +#endif +} + +#define pcmcia_enable_device(link) pcmcia_request_configuration(link, &link->conf) + +#include + +struct compat_genl_info { + struct genl_info *info; + + u32 snd_seq; + u32 snd_pid; + struct genlmsghdr *genlhdr; + struct nlattr **attrs; + void *user_ptr[2]; +}; +#define genl_info compat_genl_info + +struct compat_genl_ops { + struct genl_ops ops; + + u8 cmd; + u8 internal_flags; + unsigned int flags; + const struct nla_policy *policy; + + int (*doit)(struct sk_buff *skb, struct genl_info *info); + int (*dumpit)(struct sk_buff *skb, struct netlink_callback *cb); + int (*done)(struct netlink_callback *cb); +}; +#define genl_ops compat_genl_ops + +struct compat_genl_family { + struct genl_family family; + + struct list_head list; + + unsigned int id, hdrsize, version, maxattr; + const char *name; + bool netnsok; + + struct nlattr **attrbuf; + + int (*pre_doit)(struct genl_ops *ops, struct sk_buff *skb, + struct genl_info *info); + + void (*post_doit)(struct genl_ops *ops, struct sk_buff *skb, + struct genl_info *info); +}; + +#define genl_family compat_genl_family + +#define genl_register_family_with_ops compat_genl_register_family_with_ops + +int genl_register_family_with_ops(struct genl_family *family, + struct genl_ops *ops, size_t n_ops); + +#define genl_unregister_family compat_genl_unregister_family + +int genl_unregister_family(struct genl_family *family); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) +#define genl_info_net(_info) genl_info_net((_info)->info) +#endif + +#define genlmsg_reply(_msg, _info) genlmsg_reply(_msg, (_info)->info) +#define genlmsg_put(_skb, _pid, _seq, _fam, _flags, _cmd) genlmsg_put(_skb, _pid, _seq, &(_fam)->family, _flags, _cmd) +#define genl_register_mc_group(_fam, _grp) genl_register_mc_group(&(_fam)->family, _grp) +#define genl_unregister_mc_group(_fam, _grp) genl_unregister_mc_group(&(_fam)->family, _grp) + + +extern void led_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off); + +#define led_classdev_unregister compat_led_classdev_unregister +extern void compat_led_classdev_unregister(struct led_classdev *led_cdev); + +#define led_brightness_set compat_led_brightness_set +extern void compat_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness); + +#define alloc_ordered_workqueue(name, flags) create_singlethread_workqueue(name) + +#define netdev_refcnt_read(a) atomic_read(&a->refcnt) + +/* mask vzalloc as RHEL6 backports this */ +#define vzalloc(a) compat_vzalloc(a) + +extern void *vzalloc(unsigned long size); + +#define rtnl_dereference(p) \ + rcu_dereference_protected(p, lockdep_rtnl_is_held()) + +/** + * RCU_INIT_POINTER() - initialize an RCU protected pointer + * + * Initialize an RCU-protected pointer in such a way to avoid RCU-lockdep + * splats. + */ +#define RCU_INIT_POINTER(p, v) \ + p = (typeof(*v) __force __rcu *)(v) + +static inline bool skb_has_frag_list(const struct sk_buff *skb) +{ + return skb_shinfo(skb)->frag_list != NULL; +} + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) */ + +#endif /* LINUX_26_37_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.38.h b/include/compat/linux/compat-2.6.38.h new file mode 100644 index 00000000..63f9dd6a --- /dev/null +++ b/include/compat/linux/compat-2.6.38.h @@ -0,0 +1,95 @@ +#ifndef LINUX_26_38_COMPAT_H +#define LINUX_26_38_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) + +#include +#include +#include + +/* rename member in struct mmc_host in include/linux/mmc/host.h */ +#define max_segs max_hw_segs + + +/* Exponentially weighted moving average (EWMA) */ + +/* For more documentation see lib/average.c */ + +struct ewma { + unsigned long internal; + unsigned long factor; + unsigned long weight; +}; + +extern void ewma_init(struct ewma *avg, unsigned long factor, + unsigned long weight); + +extern struct ewma *ewma_add(struct ewma *avg, unsigned long val); + +/** + * ewma_read() - Get average value + * @avg: Average structure + * + * Returns the average value held in @avg. + */ +static inline unsigned long ewma_read(const struct ewma *avg) +{ + return DIV_ROUND_CLOSEST(avg->internal, avg->factor); +} + +#define pr_warn pr_warning +#define create_freezable_workqueue create_freezeable_workqueue + +static inline int skb_checksum_start_offset(const struct sk_buff *skb) +{ + return skb->csum_start - skb_headroom(skb); +} + +/* from include/linux/printk.h */ +#define pr_emerg_once(fmt, ...) \ + printk_once(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__) +#define pr_alert_once(fmt, ...) \ + printk_once(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__) +#define pr_crit_once(fmt, ...) \ + printk_once(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__) +#define pr_err_once(fmt, ...) \ + printk_once(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__) +#define pr_warn_once(fmt, ...) \ + printk_once(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__) +#define pr_notice_once(fmt, ...) \ + printk_once(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__) +#define pr_info_once(fmt, ...) \ + printk_once(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__) +#define pr_cont_once(fmt, ...) \ + printk_once(KERN_CONT pr_fmt(fmt), ##__VA_ARGS__) +#if defined(DEBUG) +#define pr_debug_once(fmt, ...) \ + printk_once(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) +#else +#define pr_debug_once(fmt, ...) \ + no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) +#endif + +/* include/linux/netdevice.h */ +#define alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs) \ + alloc_netdev_mq(sizeof_priv, name, setup, \ + max_t(unsigned int, txqs, rxqs)) + +#define ETH_P_LINK_CTL 0x886c /* HPNA, wlan link local tunnel */ + +/** + * is_unicast_ether_addr - Determine if the Ethernet address is unicast + * @addr: Pointer to a six-byte array containing the Ethernet address + * + * Return true if the address is a unicast address. + */ +static inline int is_unicast_ether_addr(const u8 *addr) +{ + return !is_multicast_ether_addr(addr); +} + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) */ + +#endif /* LINUX_26_38_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.39.h b/include/compat/linux/compat-2.6.39.h new file mode 100644 index 00000000..406f8ebf --- /dev/null +++ b/include/compat/linux/compat-2.6.39.h @@ -0,0 +1,169 @@ +#ifndef LINUX_26_39_COMPAT_H +#define LINUX_26_39_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) + +#include +#include +#include + +#define tiocmget(tty) tiocmget(tty, NULL) +#define tiocmset(tty, set, clear) tiocmset(tty, NULL, set, clear) + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) +extern int tty_set_termios(struct tty_struct *tty, struct ktermios *kt); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) */ + +static inline int irq_set_irq_wake(unsigned int irq, unsigned int on) +{ + return set_irq_wake(irq, on); +} +static inline int irq_set_chip(unsigned int irq, struct irq_chip *chip) +{ + return set_irq_chip(irq, chip); +} +static inline int irq_set_handler_data(unsigned int irq, void *data) +{ + return set_irq_data(irq, data); +} +static inline int irq_set_chip_data(unsigned int irq, void *data) +{ + return set_irq_chip_data(irq, data); +} +static inline int irq_set_irq_type(unsigned int irq, unsigned int type) +{ + return set_irq_type(irq, type); +} +static inline int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry) +{ + return set_irq_msi(irq, entry); +} +static inline struct irq_chip *irq_get_chip(unsigned int irq) +{ + return get_irq_chip(irq); +} +static inline void *irq_get_chip_data(unsigned int irq) +{ + return get_irq_chip_data(irq); +} +static inline void *irq_get_handler_data(unsigned int irq) +{ + return get_irq_data(irq); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) +static inline void *irq_data_get_irq_handler_data(struct irq_data *d) +{ + return irq_data_get_irq_data(d); +} +#endif + +static inline struct msi_desc *irq_get_msi_desc(unsigned int irq) +{ + return get_irq_msi(irq); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) +static inline void irq_set_noprobe(unsigned int irq) +{ + set_irq_noprobe(irq); +} +static inline void irq_set_probe(unsigned int irq) +{ + set_irq_probe(irq); +} +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) +static inline struct irq_chip *irq_desc_get_chip(struct irq_desc *desc) +{ + return get_irq_desc_chip(desc); +} +static inline void *irq_desc_get_handler_data(struct irq_desc *desc) +{ + return get_irq_desc_data(desc); +} + +static inline void *irq_desc_get_chip_data(struct irq_desc *desc) +{ + return get_irq_desc_chip_data(desc); +} + +static inline struct msi_desc *irq_desc_get_msi_desc(struct irq_desc *desc) +{ + return get_irq_desc_msi(desc); +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) */ + +/* + * kstrto* was included in kernel 2.6.38.4 and causes conflicts with the + * version included in compat-wireless. We use strict_strtol to check if + * kstrto* is already available. + */ +#ifndef strict_strtol +/* Internal, do not use. */ +int __must_check _kstrtoul(const char *s, unsigned int base, unsigned long *res); +int __must_check _kstrtol(const char *s, unsigned int base, long *res); + +int __must_check kstrtoull(const char *s, unsigned int base, unsigned long long *res); +int __must_check kstrtoll(const char *s, unsigned int base, long long *res); +static inline int __must_check kstrtoul(const char *s, unsigned int base, unsigned long *res) +{ + /* + * We want to shortcut function call, but + * __builtin_types_compatible_p(unsigned long, unsigned long long) = 0. + */ + if (sizeof(unsigned long) == sizeof(unsigned long long) && + __alignof__(unsigned long) == __alignof__(unsigned long long)) + return kstrtoull(s, base, (unsigned long long *)res); + else + return _kstrtoul(s, base, res); +} + +static inline int __must_check kstrtol(const char *s, unsigned int base, long *res) +{ + /* + * We want to shortcut function call, but + * __builtin_types_compatible_p(long, long long) = 0. + */ + if (sizeof(long) == sizeof(long long) && + __alignof__(long) == __alignof__(long long)) + return kstrtoll(s, base, (long long *)res); + else + return _kstrtol(s, base, res); +} + +int __must_check kstrtouint(const char *s, unsigned int base, unsigned int *res); +int __must_check kstrtoint(const char *s, unsigned int base, int *res); + +static inline int __must_check kstrtou64(const char *s, unsigned int base, u64 *res) +{ + return kstrtoull(s, base, res); +} + +static inline int __must_check kstrtos64(const char *s, unsigned int base, s64 *res) +{ + return kstrtoll(s, base, res); +} + +static inline int __must_check kstrtou32(const char *s, unsigned int base, u32 *res) +{ + return kstrtouint(s, base, res); +} + +static inline int __must_check kstrtos32(const char *s, unsigned int base, s32 *res) +{ + return kstrtoint(s, base, res); +} + +int __must_check kstrtou16(const char *s, unsigned int base, u16 *res); +int __must_check kstrtos16(const char *s, unsigned int base, s16 *res); +int __must_check kstrtou8(const char *s, unsigned int base, u8 *res); +int __must_check kstrtos8(const char *s, unsigned int base, s8 *res); +#endif /* ifndef strict_strtol */ + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ + +#endif /* LINUX_26_39_COMPAT_H */ diff --git a/include/compat/linux/compat-2.6.h b/include/compat/linux/compat-2.6.h new file mode 100644 index 00000000..e25aa51c --- /dev/null +++ b/include/compat/linux/compat-2.6.h @@ -0,0 +1,44 @@ +#ifndef LINUX_26_COMPAT_H +#define LINUX_26_COMPAT_H + +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)) +#include +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) +#include +#else +#include +#endif +#include + +/* + * Each compat file represents compatibility code for new kernel + * code introduced for *that* kernel revision. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* LINUX_26_COMPAT_H */ diff --git a/include/compat/linux/compat-3.0.h b/include/compat/linux/compat-3.0.h new file mode 100644 index 00000000..22ab5399 --- /dev/null +++ b/include/compat/linux/compat-3.0.h @@ -0,0 +1,130 @@ +#ifndef LINUX_3_0_COMPAT_H +#define LINUX_3_0_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)) + +#include + +/* + * since commit 1c5cae815d19ffe02bdfda1260949ef2b1806171 + * "net: call dev_alloc_name from register_netdevice" dev_alloc_name is + * called automatically. This is not implemented in older kernel + * versions so it will result in device wrong names. + */ +static inline int register_netdevice_name(struct net_device *dev) +{ + int err; + + if (strchr(dev->name, '%')) { + err = dev_alloc_name(dev, dev->name); + if (err < 0) + return err; + } + + return register_netdevice(dev); +} + +#define register_netdevice(dev) register_netdevice_name(dev) + +/* BCMA core, see drivers/bcma/ */ +#ifndef BCMA_CORE +/* Broadcom's specific AMBA core, see drivers/bcma/ */ +struct bcma_device_id { + __u16 manuf; + __u16 id; + __u8 rev; + __u8 class; +}; +#define BCMA_CORE(_manuf, _id, _rev, _class) \ + { .manuf = _manuf, .id = _id, .rev = _rev, .class = _class, } +#define BCMA_CORETABLE_END \ + { 0, }, + +#define BCMA_ANY_MANUF 0xFFFF +#define BCMA_ANY_ID 0xFFFF +#define BCMA_ANY_REV 0xFF +#define BCMA_ANY_CLASS 0xFF +#endif /* BCMA_CORE */ + +int mac_pton(const char *s, u8 *mac); + +int __must_check kstrtoull_from_user(const char __user *s, size_t count, unsigned int base, unsigned long long *res); +int __must_check kstrtoll_from_user(const char __user *s, size_t count, unsigned int base, long long *res); +int __must_check kstrtoul_from_user(const char __user *s, size_t count, unsigned int base, unsigned long *res); +int __must_check kstrtol_from_user(const char __user *s, size_t count, unsigned int base, long *res); +int __must_check kstrtouint_from_user(const char __user *s, size_t count, unsigned int base, unsigned int *res); +int __must_check kstrtoint_from_user(const char __user *s, size_t count, unsigned int base, int *res); +int __must_check kstrtou16_from_user(const char __user *s, size_t count, unsigned int base, u16 *res); +int __must_check kstrtos16_from_user(const char __user *s, size_t count, unsigned int base, s16 *res); +int __must_check kstrtou8_from_user(const char __user *s, size_t count, unsigned int base, u8 *res); +int __must_check kstrtos8_from_user(const char __user *s, size_t count, unsigned int base, s8 *res); + +static inline int __must_check kstrtou64_from_user(const char __user *s, size_t count, unsigned int base, u64 *res) +{ + return kstrtoull_from_user(s, count, base, res); +} + +static inline int __must_check kstrtos64_from_user(const char __user *s, size_t count, unsigned int base, s64 *res) +{ + return kstrtoll_from_user(s, count, base, res); +} + +static inline int __must_check kstrtou32_from_user(const char __user *s, size_t count, unsigned int base, u32 *res) +{ + return kstrtouint_from_user(s, count, base, res); +} + +static inline int __must_check kstrtos32_from_user(const char __user *s, size_t count, unsigned int base, s32 *res) +{ + return kstrtoint_from_user(s, count, base, res); +} + +/* + * This adds a nested function everywhere kfree_rcu() was called. This + * function frees the memory and is given as a function to call_rcu(). + * The rcu callback could happen every time also after the module was + * unloaded and this will cause problems. + */ +#define kfree_rcu(data, rcuhead) do { \ + void __kfree_rcu_fn(struct rcu_head *rcu_head) \ + { \ + void *___ptr; \ + ___ptr = container_of(rcu_head, typeof(*(data)), rcuhead);\ + kfree(___ptr); \ + } \ + call_rcu(&(data)->rcuhead, __kfree_rcu_fn); \ + } while (0) + +#ifdef MODULE + +/* + * The define overwriting module_exit is based on the original module_exit + * which looks like this: + * #define module_exit(exitfn) \ + * static inline exitcall_t __exittest(void) \ + * { return exitfn; } \ + * void cleanup_module(void) __attribute__((alias(#exitfn))); + * + * We replaced the call to the actual function exitfn() with a call to our + * function which calls the original exitfn() and then rcu_barrier() + * + * As a module will not be unloaded that ofter it should not have a big + * performance impact when rcu_barrier() is called on every module exit, + * also when no kfree_rcu() backport is used in that module. + */ +#undef module_exit +#define module_exit(exitfn) \ + static void __exit __exit_compat(void) \ + { \ + exitfn(); \ + rcu_barrier(); \ + } \ + void cleanup_module(void) __attribute__((alias("__exit_compat"))); + +#endif + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)) */ + +#endif /* LINUX_3_0_COMPAT_H */ diff --git a/include/compat/linux/compat-3.1.h b/include/compat/linux/compat-3.1.h new file mode 100644 index 00000000..5b00ac96 --- /dev/null +++ b/include/compat/linux/compat-3.1.h @@ -0,0 +1,49 @@ +#ifndef LINUX_3_1_COMPAT_H +#define LINUX_3_1_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0)) + +#include + +#define genl_dump_check_consistent(cb, user_hdr, family) + +#define IFF_TX_SKB_SHARING 0x10000 /* The interface supports sharing + * skbs on transmit */ + +#define PCMCIA_DEVICE_MANF_CARD_PROD_ID3(manf, card, v3, vh3) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \ + PCMCIA_DEV_ID_MATCH_CARD_ID| \ + PCMCIA_DEV_ID_MATCH_PROD_ID3, \ + .manf_id = (manf), \ + .card_id = (card), \ + .prod_id = { NULL, NULL, (v3), NULL }, \ + .prod_id_hash = { 0, 0, (vh3), 0 }, } + +/* + * This has been defined in include/linux/security.h for some time, but was + * only given an EXPORT_SYMBOL for 3.1. Add a compat_* definition to avoid + * breaking the compile. + */ +#define security_sk_clone(a, b) compat_security_sk_clone(a, b) + +static inline void security_sk_clone(const struct sock *sk, struct sock *newsk) +{ +} + +/* + * In many versions, several architectures do not seem to include an + * atomic64_t implementation, and do not include the software emulation from + * asm-generic/atomic64_t. + * Detect and handle this here. + */ +#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) && !defined(ATOMIC64_INIT) && !defined(CONFIG_X86) && !((LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) && defined(CONFIG_ARM) && !defined(CONFIG_GENERIC_ATOMIC64)) +#include +#endif + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0)) */ + +#endif /* LINUX_3_1_COMPAT_H */ diff --git a/include/compat/linux/compat-3.2.h b/include/compat/linux/compat-3.2.h new file mode 100644 index 00000000..cb14f0b1 --- /dev/null +++ b/include/compat/linux/compat-3.2.h @@ -0,0 +1,81 @@ +#ifndef LINUX_3_2_COMPAT_H +#define LINUX_3_2_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)) + +#include +#include + +#define PMSG_IS_AUTO(msg) (((msg).event & PM_EVENT_AUTO) != 0) + +/** + * skb_frag_page - retrieve the page refered to by a paged fragment + * @frag: the paged fragment + * + * Returns the &struct page associated with @frag. + */ +static inline struct page *skb_frag_page(const skb_frag_t *frag) +{ + return frag->page; +} + +/** + * skb_frag_dma_map - maps a paged fragment via the DMA API + * @device: the device to map the fragment to + * @frag: the paged fragment to map + * @offset: the offset within the fragment (starting at the + * fragment's own offset) + * @size: the number of bytes to map + * @direction: the direction of the mapping (%PCI_DMA_*) + * + * Maps the page associated with @frag to @device. + */ +static inline dma_addr_t skb_frag_dma_map(struct device *dev, + const skb_frag_t *frag, + size_t offset, size_t size, + enum dma_data_direction dir) +{ + return dma_map_page(dev, skb_frag_page(frag), + frag->page_offset + offset, size, dir); +} + +#define ETH_P_TDLS 0x890D /* TDLS */ + +static inline unsigned int skb_frag_size(const skb_frag_t *frag) +{ + return frag->size; +} + +static inline char *hex_byte_pack(char *buf, u8 byte) +{ + *buf++ = hex_asc_hi(byte); + *buf++ = hex_asc_lo(byte); + return buf; +} + +/* module_platform_driver() - Helper macro for drivers that don't do + * anything special in module init/exit. This eliminates a lot of + * boilerplate. Each module may only use this macro once, and + * calling it replaces module_init() and module_exit() + */ +#define module_platform_driver(__platform_driver) \ + module_driver(__platform_driver, platform_driver_register, \ + platform_driver_unregister) + +static inline void *dma_zalloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + void *ret = dma_alloc_coherent(dev, size, dma_handle, flag); + if (ret) + memset(ret, 0, size); + return ret; +} + +extern int __netdev_printk(const char *level, const struct net_device *dev, + struct va_format *vaf); + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)) */ + +#endif /* LINUX_3_2_COMPAT_H */ diff --git a/include/compat/linux/compat-3.3.h b/include/compat/linux/compat-3.3.h new file mode 100644 index 00000000..4965f234 --- /dev/null +++ b/include/compat/linux/compat-3.3.h @@ -0,0 +1,56 @@ +#ifndef LINUX_3_3_COMPAT_H +#define LINUX_3_3_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)) + +/* include to override NL80211_FEATURE_SK_TX_STATUS */ +#include + +static inline void skb_complete_wifi_ack(struct sk_buff *skb, bool acked) +{ + WARN_ON(1); +} +#define NL80211_FEATURE_SK_TX_STATUS 0 + +typedef u32 netdev_features_t; + +/* source include/linux/device.h */ +/** + * module_driver() - Helper macro for drivers that don't do anything + * special in module init/exit. This eliminates a lot of boilerplate. + * Each module may only use this macro once, and calling it replaces + * module_init() and module_exit(). + * + * Use this macro to construct bus specific macros for registering + * drivers, and do not use it on its own. + */ +#define module_driver(__driver, __register, __unregister) \ +static int __init __driver##_init(void) \ +{ \ + return __register(&(__driver)); \ +} \ +module_init(__driver##_init); \ +static void __exit __driver##_exit(void) \ +{ \ + __unregister(&(__driver)); \ +} \ +module_exit(__driver##_exit); + +/* source include/linux/usb.h */ +/** + * module_usb_driver() - Helper macro for registering a USB driver + * @__usb_driver: usb_driver struct + * + * Helper macro for USB drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_usb_driver(__usb_driver) \ + module_driver(__usb_driver, usb_register, \ + usb_deregister) + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)) */ + +#endif /* LINUX_3_3_COMPAT_H */ diff --git a/include/compat/linux/compat-3.4.h b/include/compat/linux/compat-3.4.h new file mode 100644 index 00000000..1b723742 --- /dev/null +++ b/include/compat/linux/compat-3.4.h @@ -0,0 +1,80 @@ +#ifndef LINUX_3_4_COMPAT_H +#define LINUX_3_4_COMPAT_H + +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)) + +#include +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)) +#define skb_add_rx_frag(skb, i, page, off, size, truesize) \ + v2_6_28_skb_add_rx_frag(skb, i, page, off, size) +#else +#define skb_add_rx_frag(skb, i, page, off, size, truesize) \ + skb_add_rx_frag(skb, i, page, off, size) +#endif + +#ifdef CONFIG_X86_X32_ABI +#define COMPAT_USE_64BIT_TIME \ + (!!(task_pt_regs(current)->orig_ax & __X32_SYSCALL_BIT)) +#else +#define COMPAT_USE_64BIT_TIME 0 +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)) +static inline void eth_hw_addr_random(struct net_device *dev) +{ +#error eth_hw_addr_random() needs to be implemented for < 2.6.12 +} +#else /* kernels >= 2.6.12 */ + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)) +static inline void eth_hw_addr_random(struct net_device *dev) +{ + get_random_bytes(dev->dev_addr, ETH_ALEN); + dev->dev_addr[0] &= 0xfe; /* clear multicast bit */ + dev->dev_addr[0] |= 0x02; /* set local assignment bit (IEEE802) */ +} +#else /* kernels >= 2.6.31 */ + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) +/* So this is 2.6.31..2.6.35 */ + +/* Just have the flags present, they won't really mean anything though */ +#define NET_ADDR_PERM 0 /* address is permanent (default) */ +#define NET_ADDR_RANDOM 1 /* address is generated randomly */ +#define NET_ADDR_STOLEN 2 /* address is stolen from other device */ + +static inline void eth_hw_addr_random(struct net_device *dev) +{ + random_ether_addr(dev->dev_addr); +} + +#else /* 2.6.36 and on */ +static inline void eth_hw_addr_random(struct net_device *dev) +{ + dev_hw_addr_random(dev, dev->dev_addr); +} +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)) */ + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)) */ +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)) */ + +/* source include/linux/pci.h */ +/** + * module_pci_driver() - Helper macro for registering a PCI driver + * @__pci_driver: pci_driver struct + * + * Helper macro for PCI drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_pci_driver(__pci_driver) \ + module_driver(__pci_driver, pci_register_driver, \ + pci_unregister_driver) + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)) */ + +#endif /* LINUX_5_4_COMPAT_H */ diff --git a/include/compat/linux/compat-3.5.h b/include/compat/linux/compat-3.5.h new file mode 100644 index 00000000..9c10a428 --- /dev/null +++ b/include/compat/linux/compat-3.5.h @@ -0,0 +1,13 @@ +#ifndef LINUX_3_5_COMPAT_H +#define LINUX_3_5_COMPAT_H + +#include +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)) + +extern int simple_open(struct inode *inode, struct file *file); + +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)) */ + +#endif /* LINUX_3_5_COMPAT_H */ diff --git a/include/compat/linux/compat_autoconf.h b/include/compat/linux/compat_autoconf.h new file mode 100644 index 00000000..813755c6 --- /dev/null +++ b/include/compat/linux/compat_autoconf.h @@ -0,0 +1,37 @@ +#ifndef COMPAT_AUTOCONF_INCLUDED +#define COMPAT_AUTOCONF_INCLUDED + +/* + * This is an example of a compat autconf header file that gets + * automatically generated by your projet. You will only need this + * if you are working with a lot of config options. + * + * Your project will likely just override this, or leave this as-is. + */ + +/* + * Example automatically generated C config: don't edit + * Thu Dec 10 10:34:11 PST 2009 + * compat-wireless-2.6: master-2009-11-19-5-gb4fd4dd + * linux-2.6: next-20091208 + */ +//#define COMPAT_PROJECT_FOO_RELEASE "master-2009-11-19-5-gb4fd4dd" +//#define COMPAT_UPSTREAM_FOO_KERNEL_RELEASE "next-20091208" + +/* Example kernel version minimum requirement */ +//#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)) +//#error Compat-wireless requirement: Linux >= 2,6,10 +//#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) */ + +/* Example hard requirement */ +//#error Compat-wireless requirement: CONFIG_WIRELESS_EXT must be enabled in your kernel +//#endif /* CONFIG_WIRELESS_EXT */ + +/* + * Example of how to match your external modules kconfig options into this. + * You'll need something to generate this for you. + */ +//#ifndef CONFIG_FOO_BAR +//#define CONFIG_FOO_BAR 1 + +#endif /* COMPAT_AUTOCONF_INCLUDED */ diff --git a/include/compat/linux/cordic.h b/include/compat/linux/cordic.h new file mode 100644 index 00000000..f932093e --- /dev/null +++ b/include/compat/linux/cordic.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __CORDIC_H_ +#define __CORDIC_H_ + +#include + +/** + * struct cordic_iq - i/q coordinate. + * + * @i: real part of coordinate (in phase). + * @q: imaginary part of coordinate (quadrature). + */ +struct cordic_iq { + s32 i; + s32 q; +}; + +/** + * cordic_calc_iq() - calculates the i/q coordinate for given angle. + * + * @theta: angle in degrees for which i/q coordinate is to be calculated. + * @coord: function output parameter holding the i/q coordinate. + * + * The function calculates the i/q coordinate for a given angle using + * cordic algorithm. The coordinate consists of a real (i) and an + * imaginary (q) part. The real part is essentially the cosine of the + * angle and the imaginary part is the sine of the angle. The returned + * values are scaled by 2^16 for precision. The range for theta is + * for -180 degrees to +180 degrees. Passed values outside this range are + * converted before doing the actual calculation. + */ +struct cordic_iq cordic_calc_iq(s32 theta); + +#endif /* __CORDIC_H_ */ diff --git a/include/compat/linux/crc8.h b/include/compat/linux/crc8.h new file mode 100644 index 00000000..13c8dabb --- /dev/null +++ b/include/compat/linux/crc8.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2011 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __CRC8_H_ +#define __CRC8_H_ + +#include + +/* see usage of this value in crc8() description */ +#define CRC8_INIT_VALUE 0xFF + +/* + * Return value of crc8() indicating valid message+crc. This is true + * if a CRC is inverted before transmission. The CRC computed over the + * whole received bitstream is _table[x], where x is the bit pattern + * of the modification (almost always 0xff). + */ +#define CRC8_GOOD_VALUE(_table) (_table[0xFF]) + +/* required table size for crc8 algorithm */ +#define CRC8_TABLE_SIZE 256 + +/* helper macro assuring right table size is used */ +#define DECLARE_CRC8_TABLE(_table) \ + static u8 _table[CRC8_TABLE_SIZE] + +/** + * crc8_populate_lsb - fill crc table for given polynomial in regular bit order. + * + * @table: table to be filled. + * @polynomial: polynomial for which table is to be filled. + * + * This function fills the provided table according the polynomial provided for + * regular bit order (lsb first). Polynomials in CRC algorithms are typically + * represented as shown below. + * + * poly = x^8 + x^7 + x^6 + x^4 + x^2 + 1 + * + * For lsb first direction x^7 maps to the lsb. So the polynomial is as below. + * + * - lsb first: poly = 10101011(1) = 0xAB + */ +void crc8_populate_lsb(u8 table[CRC8_TABLE_SIZE], u8 polynomial); + +/** + * crc8_populate_msb - fill crc table for given polynomial in reverse bit order. + * + * @table: table to be filled. + * @polynomial: polynomial for which table is to be filled. + * + * This function fills the provided table according the polynomial provided for + * reverse bit order (msb first). Polynomials in CRC algorithms are typically + * represented as shown below. + * + * poly = x^8 + x^7 + x^6 + x^4 + x^2 + 1 + * + * For msb first direction x^7 maps to the msb. So the polynomial is as below. + * + * - msb first: poly = (1)11010101 = 0xD5 + */ +void crc8_populate_msb(u8 table[CRC8_TABLE_SIZE], u8 polynomial); + +/** + * crc8() - calculate a crc8 over the given input data. + * + * @table: crc table used for calculation. + * @pdata: pointer to data buffer. + * @nbytes: number of bytes in data buffer. + * @crc: previous returned crc8 value. + * + * The CRC8 is calculated using the polynomial given in crc8_populate_msb() + * or crc8_populate_lsb(). + * + * 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 validating a byte + * stream (including CRC8), a final return value of %CRC8_GOOD_VALUE + * indicates the byte stream data can be considered valid. + * + * Reference: + * "A Painless Guide to CRC Error Detection Algorithms", ver 3, Aug 1993 + * Williams, Ross N., rossross.net + * (see URL http://www.ross.net/crc/download/crc_v3.txt). + */ +u8 crc8(const u8 table[CRC8_TABLE_SIZE], u8 *pdata, size_t nbytes, u8 crc); + +#endif /* __CRC8_H_ */ diff --git a/include/compat/linux/eeprom_93cx6.h b/include/compat/linux/eeprom_93cx6.h new file mode 100644 index 00000000..c4627cbd --- /dev/null +++ b/include/compat/linux/eeprom_93cx6.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2004 - 2006 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: eeprom_93cx6 + Abstract: EEPROM reader datastructures for 93cx6 chipsets. + Supported chipsets: 93c46, 93c56 and 93c66. + */ + +/* + * EEPROM operation defines. + */ +#define PCI_EEPROM_WIDTH_93C46 6 +#define PCI_EEPROM_WIDTH_93C56 8 +#define PCI_EEPROM_WIDTH_93C66 8 +#define PCI_EEPROM_WIDTH_93C86 8 +#define PCI_EEPROM_WIDTH_OPCODE 3 +#define PCI_EEPROM_WRITE_OPCODE 0x05 +#define PCI_EEPROM_READ_OPCODE 0x06 +#define PCI_EEPROM_EWDS_OPCODE 0x10 +#define PCI_EEPROM_EWEN_OPCODE 0x13 + +/** + * struct eeprom_93cx6 - control structure for setting the commands + * for reading the eeprom data. + * @data: private pointer for the driver. + * @register_read(struct eeprom_93cx6 *eeprom): handler to + * read the eeprom register, this function should set all reg_* fields. + * @register_write(struct eeprom_93cx6 *eeprom): handler to + * write to the eeprom register by using all reg_* fields. + * @width: eeprom width, should be one of the PCI_EEPROM_WIDTH_* defines + * @reg_data_in: register field to indicate data input + * @reg_data_out: register field to indicate data output + * @reg_data_clock: register field to set the data clock + * @reg_chip_select: register field to set the chip select + * + * This structure is used for the communication between the driver + * and the eeprom_93cx6 handlers for reading the eeprom. + */ +struct eeprom_93cx6 { + void *data; + + void (*register_read)(struct eeprom_93cx6 *eeprom); + void (*register_write)(struct eeprom_93cx6 *eeprom); + + int width; + + char reg_data_in; + char reg_data_out; + char reg_data_clock; + char reg_chip_select; +}; + +extern void eeprom_93cx6_read(struct eeprom_93cx6 *eeprom, + const u8 word, u16 *data); +extern void eeprom_93cx6_multiread(struct eeprom_93cx6 *eeprom, + const u8 word, __le16 *data, const u16 words); diff --git a/include/compat/linux/export.h b/include/compat/linux/export.h new file mode 100644 index 00000000..a0ced3ea --- /dev/null +++ b/include/compat/linux/export.h @@ -0,0 +1,12 @@ +#ifndef _COMPAT_LINUX_EXPORT_H +#define _COMPAT_LINUX_EXPORT_H 1 + +#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)) +#include_next +#else +#include +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)) */ + +#endif /* _COMPAT_LINUX_EXPORT_H */ diff --git a/include/compat/linux/ieee80211.h b/include/compat/linux/ieee80211.h new file mode 100644 index 00000000..66cedf6e --- /dev/null +++ b/include/compat/linux/ieee80211.h @@ -0,0 +1,1852 @@ +/* + * IEEE 802.11 defines + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen + * Copyright (c) 2005, Devicescape Software, Inc. + * Copyright (c) 2006, Michael Wu + * + * 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_IEEE80211_H +#define LINUX_IEEE80211_H + +#include +#include + +/* + * DS bit usage + * + * TA = transmitter address + * RA = receiver address + * DA = destination address + * SA = source address + * + * ToDS FromDS A1(RA) A2(TA) A3 A4 Use + * ----------------------------------------------------------------- + * 0 0 DA SA BSSID - IBSS/DLS + * 0 1 DA BSSID SA - AP -> STA + * 1 0 BSSID SA DA - AP <- STA + * 1 1 RA TA DA SA unspecified (WDS) + */ + +#define FCS_LEN 4 + +#define IEEE80211_FCTL_VERS 0x0003 +#define IEEE80211_FCTL_FTYPE 0x000c +#define IEEE80211_FCTL_STYPE 0x00f0 +#define IEEE80211_FCTL_TODS 0x0100 +#define IEEE80211_FCTL_FROMDS 0x0200 +#define IEEE80211_FCTL_MOREFRAGS 0x0400 +#define IEEE80211_FCTL_RETRY 0x0800 +#define IEEE80211_FCTL_PM 0x1000 +#define IEEE80211_FCTL_MOREDATA 0x2000 +#define IEEE80211_FCTL_PROTECTED 0x4000 +#define IEEE80211_FCTL_ORDER 0x8000 + +#define IEEE80211_SCTL_FRAG 0x000F +#define IEEE80211_SCTL_SEQ 0xFFF0 + +#define IEEE80211_FTYPE_MGMT 0x0000 +#define IEEE80211_FTYPE_CTL 0x0004 +#define IEEE80211_FTYPE_DATA 0x0008 + +/* management */ +#define IEEE80211_STYPE_ASSOC_REQ 0x0000 +#define IEEE80211_STYPE_ASSOC_RESP 0x0010 +#define IEEE80211_STYPE_REASSOC_REQ 0x0020 +#define IEEE80211_STYPE_REASSOC_RESP 0x0030 +#define IEEE80211_STYPE_PROBE_REQ 0x0040 +#define IEEE80211_STYPE_PROBE_RESP 0x0050 +#define IEEE80211_STYPE_BEACON 0x0080 +#define IEEE80211_STYPE_ATIM 0x0090 +#define IEEE80211_STYPE_DISASSOC 0x00A0 +#define IEEE80211_STYPE_AUTH 0x00B0 +#define IEEE80211_STYPE_DEAUTH 0x00C0 +#define IEEE80211_STYPE_ACTION 0x00D0 + +/* control */ +#define IEEE80211_STYPE_BACK_REQ 0x0080 +#define IEEE80211_STYPE_BACK 0x0090 +#define IEEE80211_STYPE_PSPOLL 0x00A0 +#define IEEE80211_STYPE_RTS 0x00B0 +#define IEEE80211_STYPE_CTS 0x00C0 +#define IEEE80211_STYPE_ACK 0x00D0 +#define IEEE80211_STYPE_CFEND 0x00E0 +#define IEEE80211_STYPE_CFENDACK 0x00F0 + +/* data */ +#define IEEE80211_STYPE_DATA 0x0000 +#define IEEE80211_STYPE_DATA_CFACK 0x0010 +#define IEEE80211_STYPE_DATA_CFPOLL 0x0020 +#define IEEE80211_STYPE_DATA_CFACKPOLL 0x0030 +#define IEEE80211_STYPE_NULLFUNC 0x0040 +#define IEEE80211_STYPE_CFACK 0x0050 +#define IEEE80211_STYPE_CFPOLL 0x0060 +#define IEEE80211_STYPE_CFACKPOLL 0x0070 +#define IEEE80211_STYPE_QOS_DATA 0x0080 +#define IEEE80211_STYPE_QOS_DATA_CFACK 0x0090 +#define IEEE80211_STYPE_QOS_DATA_CFPOLL 0x00A0 +#define IEEE80211_STYPE_QOS_DATA_CFACKPOLL 0x00B0 +#define IEEE80211_STYPE_QOS_NULLFUNC 0x00C0 +#define IEEE80211_STYPE_QOS_CFACK 0x00D0 +#define IEEE80211_STYPE_QOS_CFPOLL 0x00E0 +#define IEEE80211_STYPE_QOS_CFACKPOLL 0x00F0 + + +/* miscellaneous IEEE 802.11 constants */ +#define IEEE80211_MAX_FRAG_THRESHOLD 2352 +#define IEEE80211_MAX_RTS_THRESHOLD 2353 +#define IEEE80211_MAX_AID 2007 +#define IEEE80211_MAX_TIM_LEN 251 +/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section + 6.2.1.1.2. + + 802.11e clarifies the figure in section 7.1.2. The frame body is + up to 2304 octets long (maximum MSDU size) plus any crypt overhead. */ +#define IEEE80211_MAX_DATA_LEN 2304 +/* 30 byte 4 addr hdr, 2 byte QoS, 2304 byte MSDU, 12 byte crypt, 4 byte FCS */ +#define IEEE80211_MAX_FRAME_LEN 2352 + +#define IEEE80211_MAX_SSID_LEN 32 + +#define IEEE80211_MAX_MESH_ID_LEN 32 + +#define IEEE80211_QOS_CTL_LEN 2 +/* 1d tag mask */ +#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007 +/* TID mask */ +#define IEEE80211_QOS_CTL_TID_MASK 0x000f +/* EOSP */ +#define IEEE80211_QOS_CTL_EOSP 0x0010 +/* ACK policy */ +#define IEEE80211_QOS_CTL_ACK_POLICY_NORMAL 0x0000 +#define IEEE80211_QOS_CTL_ACK_POLICY_NOACK 0x0020 +#define IEEE80211_QOS_CTL_ACK_POLICY_NO_EXPL 0x0040 +#define IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK 0x0060 +#define IEEE80211_QOS_CTL_ACK_POLICY_MASK 0x0060 +/* A-MSDU 802.11n */ +#define IEEE80211_QOS_CTL_A_MSDU_PRESENT 0x0080 +/* Mesh Control 802.11s */ +#define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100 + +/* U-APSD queue for WMM IEs sent by AP */ +#define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7) +#define IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK 0x0f + +/* U-APSD queues for WMM IEs sent by STA */ +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VO (1<<0) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VI (1<<1) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BK (1<<2) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BE (1<<3) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK 0x0f + +/* U-APSD max SP length for WMM IEs sent by STA */ +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL 0x00 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_2 0x01 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_4 0x02 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_6 0x03 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK 0x03 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT 5 + +#define IEEE80211_HT_CTL_LEN 4 + +struct ieee80211_hdr { + __le16 frame_control; + __le16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + __le16 seq_ctrl; + u8 addr4[6]; +} __attribute__ ((packed)); + +struct ieee80211_hdr_3addr { + __le16 frame_control; + __le16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + __le16 seq_ctrl; +} __attribute__ ((packed)); + +struct ieee80211_qos_hdr { + __le16 frame_control; + __le16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + __le16 seq_ctrl; + __le16 qos_ctrl; +} __attribute__ ((packed)); + +/** + * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_tods(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_TODS)) != 0; +} + +/** + * ieee80211_has_fromds - check if IEEE80211_FCTL_FROMDS is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_fromds(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FROMDS)) != 0; +} + +/** + * ieee80211_has_a4 - check if IEEE80211_FCTL_TODS and IEEE80211_FCTL_FROMDS are set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_a4(__le16 fc) +{ + __le16 tmp = cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS); + return (fc & tmp) == tmp; +} + +/** + * ieee80211_has_morefrags - check if IEEE80211_FCTL_MOREFRAGS is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_morefrags(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) != 0; +} + +/** + * ieee80211_has_retry - check if IEEE80211_FCTL_RETRY is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_retry(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_RETRY)) != 0; +} + +/** + * ieee80211_has_pm - check if IEEE80211_FCTL_PM is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_pm(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_PM)) != 0; +} + +/** + * ieee80211_has_moredata - check if IEEE80211_FCTL_MOREDATA is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_moredata(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_MOREDATA)) != 0; +} + +/** + * ieee80211_has_protected - check if IEEE80211_FCTL_PROTECTED is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_protected(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_PROTECTED)) != 0; +} + +/** + * ieee80211_has_order - check if IEEE80211_FCTL_ORDER is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_order(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_ORDER)) != 0; +} + +/** + * ieee80211_is_mgmt - check if type is IEEE80211_FTYPE_MGMT + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_mgmt(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT); +} + +/** + * ieee80211_is_ctl - check if type is IEEE80211_FTYPE_CTL + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_ctl(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL); +} + +/** + * ieee80211_is_data - check if type is IEEE80211_FTYPE_DATA + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_data(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == + cpu_to_le16(IEEE80211_FTYPE_DATA); +} + +/** + * ieee80211_is_data_qos - check if type is IEEE80211_FTYPE_DATA and IEEE80211_STYPE_QOS_DATA is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_data_qos(__le16 fc) +{ + /* + * mask with QOS_DATA rather than IEEE80211_FCTL_STYPE as we just need + * to check the one bit + */ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_STYPE_QOS_DATA)) == + cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA); +} + +/** + * ieee80211_is_data_present - check if type is IEEE80211_FTYPE_DATA and has data + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_data_present(__le16 fc) +{ + /* + * mask with 0x40 and test that that bit is clear to only return true + * for the data-containing substypes. + */ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | 0x40)) == + cpu_to_le16(IEEE80211_FTYPE_DATA); +} + +/** + * ieee80211_is_assoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_assoc_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ); +} + +/** + * ieee80211_is_assoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_assoc_resp(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_RESP); +} + +/** + * ieee80211_is_reassoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_reassoc_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ); +} + +/** + * ieee80211_is_reassoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_reassoc_resp(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_RESP); +} + +/** + * ieee80211_is_probe_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_probe_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ); +} + +/** + * ieee80211_is_probe_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_probe_resp(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); +} + +/** + * ieee80211_is_beacon - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_BEACON + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_beacon(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); +} + +/** + * ieee80211_is_atim - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ATIM + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_atim(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ATIM); +} + +/** + * ieee80211_is_disassoc - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DISASSOC + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_disassoc(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC); +} + +/** + * ieee80211_is_auth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_AUTH + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_auth(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); +} + +/** + * ieee80211_is_deauth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DEAUTH + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_deauth(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); +} + +/** + * ieee80211_is_action - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ACTION + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_action(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); +} + +/** + * ieee80211_is_back_req - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_back_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ); +} + +/** + * ieee80211_is_back - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_back(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK); +} + +/** + * ieee80211_is_pspoll - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_PSPOLL + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_pspoll(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); +} + +/** + * ieee80211_is_rts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_RTS + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_rts(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); +} + +/** + * ieee80211_is_cts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CTS + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_cts(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS); +} + +/** + * ieee80211_is_ack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_ACK + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_ack(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK); +} + +/** + * ieee80211_is_cfend - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFEND + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_cfend(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFEND); +} + +/** + * ieee80211_is_cfendack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFENDACK + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_cfendack(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFENDACK); +} + +/** + * ieee80211_is_nullfunc - check if frame is a regular (non-QoS) nullfunc frame + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_nullfunc(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC); +} + +/** + * ieee80211_is_qos_nullfunc - check if frame is a QoS nullfunc frame + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_qos_nullfunc(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC); +} + +struct ieee80211s_hdr { + u8 flags; + u8 ttl; + __le32 seqnum; + u8 eaddr1[6]; + u8 eaddr2[6]; +} __attribute__ ((packed)); + +/* Mesh flags */ +#define MESH_FLAGS_AE_A4 0x1 +#define MESH_FLAGS_AE_A5_A6 0x2 +#define MESH_FLAGS_AE 0x3 +#define MESH_FLAGS_PS_DEEP 0x4 + +/** + * struct ieee80211_quiet_ie + * + * This structure refers to "Quiet information element" + */ +struct ieee80211_quiet_ie { + u8 count; + u8 period; + __le16 duration; + __le16 offset; +} __attribute__ ((packed)); + +/** + * struct ieee80211_msrment_ie + * + * This structure refers to "Measurement Request/Report information element" + */ +struct ieee80211_msrment_ie { + u8 token; + u8 mode; + u8 type; + u8 request[0]; +} __attribute__ ((packed)); + +/** + * struct ieee80211_channel_sw_ie + * + * This structure refers to "Channel Switch Announcement information element" + */ +struct ieee80211_channel_sw_ie { + u8 mode; + u8 new_ch_num; + u8 count; +} __attribute__ ((packed)); + +/** + * struct ieee80211_tim + * + * This structure refers to "Traffic Indication Map information element" + */ +struct ieee80211_tim_ie { + u8 dtim_count; + u8 dtim_period; + u8 bitmap_ctrl; + /* variable size: 1 - 251 bytes */ + u8 virtual_map[1]; +} __attribute__ ((packed)); + +/** + * struct ieee80211_meshconf_ie + * + * This structure refers to "Mesh Configuration information element" + */ +struct ieee80211_meshconf_ie { + u8 meshconf_psel; + u8 meshconf_pmetric; + u8 meshconf_congest; + u8 meshconf_synch; + u8 meshconf_auth; + u8 meshconf_form; + u8 meshconf_cap; +} __attribute__ ((packed)); + +/** + * struct ieee80211_rann_ie + * + * This structure refers to "Root Announcement information element" + */ +struct ieee80211_rann_ie { + u8 rann_flags; + u8 rann_hopcount; + u8 rann_ttl; + u8 rann_addr[6]; + u32 rann_seq; + u32 rann_interval; + u32 rann_metric; +} __attribute__ ((packed)); + +enum ieee80211_rann_flags { + RANN_FLAG_IS_GATE = 1 << 0, +}; + +#define WLAN_SA_QUERY_TR_ID_LEN 2 + +struct ieee80211_mgmt { + __le16 frame_control; + __le16 duration; + u8 da[6]; + u8 sa[6]; + u8 bssid[6]; + __le16 seq_ctrl; + union { + struct { + __le16 auth_alg; + __le16 auth_transaction; + __le16 status_code; + /* possibly followed by Challenge text */ + u8 variable[0]; + } __attribute__ ((packed)) auth; + struct { + __le16 reason_code; + } __attribute__ ((packed)) deauth; + struct { + __le16 capab_info; + __le16 listen_interval; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) assoc_req; + struct { + __le16 capab_info; + __le16 status_code; + __le16 aid; + /* followed by Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) assoc_resp, reassoc_resp; + struct { + __le16 capab_info; + __le16 listen_interval; + u8 current_ap[6]; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) reassoc_req; + struct { + __le16 reason_code; + } __attribute__ ((packed)) disassoc; + struct { + __le64 timestamp; + __le16 beacon_int; + __le16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params, TIM */ + u8 variable[0]; + } __attribute__ ((packed)) beacon; + struct { + /* only variable items: SSID, Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) probe_req; + struct { + __le64 timestamp; + __le16 beacon_int; + __le16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params */ + u8 variable[0]; + } __attribute__ ((packed)) probe_resp; + struct { + u8 category; + union { + struct { + u8 action_code; + u8 dialog_token; + u8 status_code; + u8 variable[0]; + } __attribute__ ((packed)) wme_action; + struct{ + u8 action_code; + u8 element_id; + u8 length; + struct ieee80211_channel_sw_ie sw_elem; + } __attribute__((packed)) chan_switch; + struct{ + u8 action_code; + u8 dialog_token; + u8 element_id; + u8 length; + struct ieee80211_msrment_ie msr_elem; + } __attribute__((packed)) measurement; + struct{ + u8 action_code; + u8 dialog_token; + __le16 capab; + __le16 timeout; + __le16 start_seq_num; + } __attribute__((packed)) addba_req; + struct{ + u8 action_code; + u8 dialog_token; + __le16 status; + __le16 capab; + __le16 timeout; + } __attribute__((packed)) addba_resp; + struct{ + u8 action_code; + __le16 params; + __le16 reason_code; + } __attribute__((packed)) delba; + struct { + u8 action_code; + u8 variable[0]; + } __attribute__((packed)) self_prot; + struct{ + u8 action_code; + u8 variable[0]; + } __attribute__((packed)) mesh_action; + struct { + u8 action; + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + } __attribute__ ((packed)) sa_query; + struct { + u8 action; + u8 smps_control; + } __attribute__ ((packed)) ht_smps; + struct { + u8 action_code; + u8 dialog_token; + __le16 capability; + u8 variable[0]; + } __packed tdls_discover_resp; + } u; + } __attribute__ ((packed)) action; + } u; +} __attribute__ ((packed)); + +/* Supported Rates value encodings in 802.11n-2009 7.3.2.2 */ +#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 + +/* mgmt header + 1 byte category code */ +#define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u) + + +/* Management MIC information element (IEEE 802.11w) */ +struct ieee80211_mmie { + u8 element_id; + u8 length; + __le16 key_id; + u8 sequence_number[6]; + u8 mic[8]; +} __attribute__ ((packed)); + +struct ieee80211_vendor_ie { + u8 element_id; + u8 len; + u8 oui[3]; + u8 oui_type; +} __packed; + +/* Control frames */ +struct ieee80211_rts { + __le16 frame_control; + __le16 duration; + u8 ra[6]; + u8 ta[6]; +} __attribute__ ((packed)); + +struct ieee80211_cts { + __le16 frame_control; + __le16 duration; + u8 ra[6]; +} __attribute__ ((packed)); + +struct ieee80211_pspoll { + __le16 frame_control; + __le16 aid; + u8 bssid[6]; + u8 ta[6]; +} __attribute__ ((packed)); + +/* TDLS */ + +/* Link-id information element */ +struct ieee80211_tdls_lnkie { + u8 ie_type; /* Link Identifier IE */ + u8 ie_len; + u8 bssid[6]; + u8 init_sta[6]; + u8 resp_sta[6]; +} __packed; + +struct ieee80211_tdls_data { + u8 da[6]; + u8 sa[6]; + __be16 ether_type; + u8 payload_type; + u8 category; + u8 action_code; + union { + struct { + u8 dialog_token; + __le16 capability; + u8 variable[0]; + } __packed setup_req; + struct { + __le16 status_code; + u8 dialog_token; + __le16 capability; + u8 variable[0]; + } __packed setup_resp; + struct { + __le16 status_code; + u8 dialog_token; + u8 variable[0]; + } __packed setup_cfm; + struct { + __le16 reason_code; + u8 variable[0]; + } __packed teardown; + struct { + u8 dialog_token; + u8 variable[0]; + } __packed discover_req; + } u; +} __packed; + +/** + * struct ieee80211_bar - HT Block Ack Request + * + * This structure refers to "HT BlockAckReq" as + * described in 802.11n draft section 7.2.1.7.1 + */ +struct ieee80211_bar { + __le16 frame_control; + __le16 duration; + __u8 ra[6]; + __u8 ta[6]; + __le16 control; + __le16 start_seq_num; +} __attribute__((packed)); + +/* 802.11 BAR control masks */ +#define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000 +#define IEEE80211_BAR_CTRL_MULTI_TID 0x0002 +#define IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA 0x0004 +#define IEEE80211_BAR_CTRL_TID_INFO_MASK 0xf000 +#define IEEE80211_BAR_CTRL_TID_INFO_SHIFT 12 + +#define IEEE80211_HT_MCS_MASK_LEN 10 + +/** + * struct ieee80211_mcs_info - MCS information + * @rx_mask: RX mask + * @rx_highest: highest supported RX rate. If set represents + * the highest supported RX data rate in units of 1 Mbps. + * If this field is 0 this value should not be used to + * consider the highest RX data rate supported. + * @tx_params: TX parameters + */ +struct ieee80211_mcs_info { + u8 rx_mask[IEEE80211_HT_MCS_MASK_LEN]; + __le16 rx_highest; + u8 tx_params; + u8 reserved[3]; +} __attribute__((packed)); + +/* 802.11n HT capability MSC set */ +#define IEEE80211_HT_MCS_RX_HIGHEST_MASK 0x3ff +#define IEEE80211_HT_MCS_TX_DEFINED 0x01 +#define IEEE80211_HT_MCS_TX_RX_DIFF 0x02 +/* value 0 == 1 stream etc */ +#define IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK 0x0C +#define IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT 2 +#define IEEE80211_HT_MCS_TX_MAX_STREAMS 4 +#define IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION 0x10 + +/* + * 802.11n D5.0 20.3.5 / 20.6 says: + * - indices 0 to 7 and 32 are single spatial stream + * - 8 to 31 are multiple spatial streams using equal modulation + * [8..15 for two streams, 16..23 for three and 24..31 for four] + * - remainder are multiple spatial streams using unequal modulation + */ +#define IEEE80211_HT_MCS_UNEQUAL_MODULATION_START 33 +#define IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE \ + (IEEE80211_HT_MCS_UNEQUAL_MODULATION_START / 8) + +/** + * struct ieee80211_ht_cap - HT capabilities + * + * This structure is the "HT capabilities element" as + * described in 802.11n D5.0 7.3.2.57 + */ +struct ieee80211_ht_cap { + __le16 cap_info; + u8 ampdu_params_info; + + /* 16 bytes MCS information */ + struct ieee80211_mcs_info mcs; + + __le16 extended_ht_cap_info; + __le32 tx_BF_cap_info; + u8 antenna_selection_info; +} __attribute__ ((packed)); + +/* 802.11n HT capabilities masks (for cap_info) */ +#define IEEE80211_HT_CAP_LDPC_CODING 0x0001 +#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002 +#define IEEE80211_HT_CAP_SM_PS 0x000C +#define IEEE80211_HT_CAP_SM_PS_SHIFT 2 +#define IEEE80211_HT_CAP_GRN_FLD 0x0010 +#define IEEE80211_HT_CAP_SGI_20 0x0020 +#define IEEE80211_HT_CAP_SGI_40 0x0040 +#define IEEE80211_HT_CAP_TX_STBC 0x0080 +#define IEEE80211_HT_CAP_RX_STBC 0x0300 +#define IEEE80211_HT_CAP_RX_STBC_SHIFT 8 +#define IEEE80211_HT_CAP_DELAY_BA 0x0400 +#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800 +#define IEEE80211_HT_CAP_DSSSCCK40 0x1000 +#define IEEE80211_HT_CAP_RESERVED 0x2000 +#define IEEE80211_HT_CAP_40MHZ_INTOLERANT 0x4000 +#define IEEE80211_HT_CAP_LSIG_TXOP_PROT 0x8000 + +/* 802.11n HT extended capabilities masks (for extended_ht_cap_info) */ +#define IEEE80211_HT_EXT_CAP_PCO 0x0001 +#define IEEE80211_HT_EXT_CAP_PCO_TIME 0x0006 +#define IEEE80211_HT_EXT_CAP_PCO_TIME_SHIFT 1 +#define IEEE80211_HT_EXT_CAP_MCS_FB 0x0300 +#define IEEE80211_HT_EXT_CAP_MCS_FB_SHIFT 8 +#define IEEE80211_HT_EXT_CAP_HTC_SUP 0x0400 +#define IEEE80211_HT_EXT_CAP_RD_RESPONDER 0x0800 + +/* 802.11n HT capability AMPDU settings (for ampdu_params_info) */ +#define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03 +#define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C +#define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2 + +/* + * Maximum length of AMPDU that the STA can receive. + * Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets) + */ +enum ieee80211_max_ampdu_length_exp { + IEEE80211_HT_MAX_AMPDU_8K = 0, + IEEE80211_HT_MAX_AMPDU_16K = 1, + IEEE80211_HT_MAX_AMPDU_32K = 2, + IEEE80211_HT_MAX_AMPDU_64K = 3 +}; + +#define IEEE80211_HT_MAX_AMPDU_FACTOR 13 + +/* Minimum MPDU start spacing */ +enum ieee80211_min_mpdu_spacing { + IEEE80211_HT_MPDU_DENSITY_NONE = 0, /* No restriction */ + IEEE80211_HT_MPDU_DENSITY_0_25 = 1, /* 1/4 usec */ + IEEE80211_HT_MPDU_DENSITY_0_5 = 2, /* 1/2 usec */ + IEEE80211_HT_MPDU_DENSITY_1 = 3, /* 1 usec */ + IEEE80211_HT_MPDU_DENSITY_2 = 4, /* 2 usec */ + IEEE80211_HT_MPDU_DENSITY_4 = 5, /* 4 usec */ + IEEE80211_HT_MPDU_DENSITY_8 = 6, /* 8 usec */ + IEEE80211_HT_MPDU_DENSITY_16 = 7 /* 16 usec */ +}; + +/** + * struct ieee80211_ht_info - HT information + * + * This structure is the "HT information element" as + * described in 802.11n D5.0 7.3.2.58 + */ +struct ieee80211_ht_info { + u8 control_chan; + u8 ht_param; + __le16 operation_mode; + __le16 stbc_param; + u8 basic_set[16]; +} __attribute__ ((packed)); + +/* for ht_param */ +#define IEEE80211_HT_PARAM_CHA_SEC_OFFSET 0x03 +#define IEEE80211_HT_PARAM_CHA_SEC_NONE 0x00 +#define IEEE80211_HT_PARAM_CHA_SEC_ABOVE 0x01 +#define IEEE80211_HT_PARAM_CHA_SEC_BELOW 0x03 +#define IEEE80211_HT_PARAM_CHAN_WIDTH_ANY 0x04 +#define IEEE80211_HT_PARAM_RIFS_MODE 0x08 +#define IEEE80211_HT_PARAM_SPSMP_SUPPORT 0x10 +#define IEEE80211_HT_PARAM_SERV_INTERVAL_GRAN 0xE0 + +/* for operation_mode */ +#define IEEE80211_HT_OP_MODE_PROTECTION 0x0003 +#define IEEE80211_HT_OP_MODE_PROTECTION_NONE 0 +#define IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER 1 +#define IEEE80211_HT_OP_MODE_PROTECTION_20MHZ 2 +#define IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED 3 +#define IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT 0x0004 +#define IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT 0x0010 + +/* for stbc_param */ +#define IEEE80211_HT_STBC_PARAM_DUAL_BEACON 0x0040 +#define IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT 0x0080 +#define IEEE80211_HT_STBC_PARAM_STBC_BEACON 0x0100 +#define IEEE80211_HT_STBC_PARAM_LSIG_TXOP_FULLPROT 0x0200 +#define IEEE80211_HT_STBC_PARAM_PCO_ACTIVE 0x0400 +#define IEEE80211_HT_STBC_PARAM_PCO_PHASE 0x0800 + + +/* block-ack parameters */ +#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002 +#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C +#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFC0 +#define IEEE80211_DELBA_PARAM_TID_MASK 0xF000 +#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800 + +/* + * A-PMDU buffer sizes + * According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2) + */ +#define IEEE80211_MIN_AMPDU_BUF 0x8 +#define IEEE80211_MAX_AMPDU_BUF 0x40 + + +/* Spatial Multiplexing Power Save Modes (for capability) */ +#define WLAN_HT_CAP_SM_PS_STATIC 0 +#define WLAN_HT_CAP_SM_PS_DYNAMIC 1 +#define WLAN_HT_CAP_SM_PS_INVALID 2 +#define WLAN_HT_CAP_SM_PS_DISABLED 3 + +/* for SM power control field lower two bits */ +#define WLAN_HT_SMPS_CONTROL_DISABLED 0 +#define WLAN_HT_SMPS_CONTROL_STATIC 1 +#define WLAN_HT_SMPS_CONTROL_DYNAMIC 3 + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 +#define WLAN_AUTH_FT 2 +#define WLAN_AUTH_SAE 3 +#define WLAN_AUTH_LEAP 128 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_ESS (1<<0) +#define WLAN_CAPABILITY_IBSS (1<<1) + +/* + * A mesh STA sets the ESS and IBSS capability bits to zero. + * however, this holds true for p2p probe responses (in the p2p_find + * phase) as well. + */ +#define WLAN_CAPABILITY_IS_STA_BSS(cap) \ + (!((cap) & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS))) + +#define WLAN_CAPABILITY_CF_POLLABLE (1<<2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3) +#define WLAN_CAPABILITY_PRIVACY (1<<4) +#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5) +#define WLAN_CAPABILITY_PBCC (1<<6) +#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7) + +/* 802.11h */ +#define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8) +#define WLAN_CAPABILITY_QOS (1<<9) +#define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10) +#define WLAN_CAPABILITY_DSSS_OFDM (1<<13) +/* measurement */ +#define IEEE80211_SPCT_MSR_RPRT_MODE_LATE (1<<0) +#define IEEE80211_SPCT_MSR_RPRT_MODE_INCAPABLE (1<<1) +#define IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED (1<<2) + +#define IEEE80211_SPCT_MSR_RPRT_TYPE_BASIC 0 +#define IEEE80211_SPCT_MSR_RPRT_TYPE_CCA 1 +#define IEEE80211_SPCT_MSR_RPRT_TYPE_RPI 2 + + +/* 802.11g ERP information element */ +#define WLAN_ERP_NON_ERP_PRESENT (1<<0) +#define WLAN_ERP_USE_PROTECTION (1<<1) +#define WLAN_ERP_BARKER_PREAMBLE (1<<2) + +/* WLAN_ERP_BARKER_PREAMBLE values */ +enum { + WLAN_ERP_PREAMBLE_SHORT = 0, + WLAN_ERP_PREAMBLE_LONG = 1, +}; + +/* Status codes */ +enum ieee80211_statuscode { + WLAN_STATUS_SUCCESS = 0, + WLAN_STATUS_UNSPECIFIED_FAILURE = 1, + WLAN_STATUS_CAPS_UNSUPPORTED = 10, + WLAN_STATUS_REASSOC_NO_ASSOC = 11, + WLAN_STATUS_ASSOC_DENIED_UNSPEC = 12, + WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG = 13, + WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION = 14, + WLAN_STATUS_CHALLENGE_FAIL = 15, + WLAN_STATUS_AUTH_TIMEOUT = 16, + WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17, + WLAN_STATUS_ASSOC_DENIED_RATES = 18, + /* 802.11b */ + WLAN_STATUS_ASSOC_DENIED_NOSHORTPREAMBLE = 19, + WLAN_STATUS_ASSOC_DENIED_NOPBCC = 20, + WLAN_STATUS_ASSOC_DENIED_NOAGILITY = 21, + /* 802.11h */ + WLAN_STATUS_ASSOC_DENIED_NOSPECTRUM = 22, + WLAN_STATUS_ASSOC_REJECTED_BAD_POWER = 23, + WLAN_STATUS_ASSOC_REJECTED_BAD_SUPP_CHAN = 24, + /* 802.11g */ + WLAN_STATUS_ASSOC_DENIED_NOSHORTTIME = 25, + WLAN_STATUS_ASSOC_DENIED_NODSSSOFDM = 26, + /* 802.11w */ + WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY = 30, + WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION = 31, + /* 802.11i */ + WLAN_STATUS_INVALID_IE = 40, + WLAN_STATUS_INVALID_GROUP_CIPHER = 41, + WLAN_STATUS_INVALID_PAIRWISE_CIPHER = 42, + WLAN_STATUS_INVALID_AKMP = 43, + WLAN_STATUS_UNSUPP_RSN_VERSION = 44, + WLAN_STATUS_INVALID_RSN_IE_CAP = 45, + WLAN_STATUS_CIPHER_SUITE_REJECTED = 46, + /* 802.11e */ + WLAN_STATUS_UNSPECIFIED_QOS = 32, + WLAN_STATUS_ASSOC_DENIED_NOBANDWIDTH = 33, + WLAN_STATUS_ASSOC_DENIED_LOWACK = 34, + WLAN_STATUS_ASSOC_DENIED_UNSUPP_QOS = 35, + WLAN_STATUS_REQUEST_DECLINED = 37, + WLAN_STATUS_INVALID_QOS_PARAM = 38, + WLAN_STATUS_CHANGE_TSPEC = 39, + WLAN_STATUS_WAIT_TS_DELAY = 47, + WLAN_STATUS_NO_DIRECT_LINK = 48, + WLAN_STATUS_STA_NOT_PRESENT = 49, + WLAN_STATUS_STA_NOT_QSTA = 50, + /* 802.11s */ + WLAN_STATUS_ANTI_CLOG_REQUIRED = 76, + WLAN_STATUS_FCG_NOT_SUPP = 78, + WLAN_STATUS_STA_NO_TBTT = 78, +}; + + +/* Reason codes */ +enum ieee80211_reasoncode { + WLAN_REASON_UNSPECIFIED = 1, + WLAN_REASON_PREV_AUTH_NOT_VALID = 2, + WLAN_REASON_DEAUTH_LEAVING = 3, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY = 4, + WLAN_REASON_DISASSOC_AP_BUSY = 5, + WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA = 6, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA = 7, + WLAN_REASON_DISASSOC_STA_HAS_LEFT = 8, + WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH = 9, + /* 802.11h */ + WLAN_REASON_DISASSOC_BAD_POWER = 10, + WLAN_REASON_DISASSOC_BAD_SUPP_CHAN = 11, + /* 802.11i */ + WLAN_REASON_INVALID_IE = 13, + WLAN_REASON_MIC_FAILURE = 14, + WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT = 15, + WLAN_REASON_GROUP_KEY_HANDSHAKE_TIMEOUT = 16, + WLAN_REASON_IE_DIFFERENT = 17, + WLAN_REASON_INVALID_GROUP_CIPHER = 18, + WLAN_REASON_INVALID_PAIRWISE_CIPHER = 19, + WLAN_REASON_INVALID_AKMP = 20, + WLAN_REASON_UNSUPP_RSN_VERSION = 21, + WLAN_REASON_INVALID_RSN_IE_CAP = 22, + WLAN_REASON_IEEE8021X_FAILED = 23, + WLAN_REASON_CIPHER_SUITE_REJECTED = 24, + /* 802.11e */ + WLAN_REASON_DISASSOC_UNSPECIFIED_QOS = 32, + WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH = 33, + WLAN_REASON_DISASSOC_LOW_ACK = 34, + WLAN_REASON_DISASSOC_QAP_EXCEED_TXOP = 35, + WLAN_REASON_QSTA_LEAVE_QBSS = 36, + WLAN_REASON_QSTA_NOT_USE = 37, + WLAN_REASON_QSTA_REQUIRE_SETUP = 38, + WLAN_REASON_QSTA_TIMEOUT = 39, + WLAN_REASON_QSTA_CIPHER_NOT_SUPP = 45, + /* 802.11s */ + WLAN_REASON_MESH_PEER_CANCELED = 52, + WLAN_REASON_MESH_MAX_PEERS = 53, + WLAN_REASON_MESH_CONFIG = 54, + WLAN_REASON_MESH_CLOSE = 55, + WLAN_REASON_MESH_MAX_RETRIES = 56, + WLAN_REASON_MESH_CONFIRM_TIMEOUT = 57, + WLAN_REASON_MESH_INVALID_GTK = 58, + WLAN_REASON_MESH_INCONSISTENT_PARAM = 59, + WLAN_REASON_MESH_INVALID_SECURITY = 60, + WLAN_REASON_MESH_PATH_ERROR = 61, + WLAN_REASON_MESH_PATH_NOFORWARD = 62, + WLAN_REASON_MESH_PATH_DEST_UNREACHABLE = 63, + WLAN_REASON_MAC_EXISTS_IN_MBSS = 64, + WLAN_REASON_MESH_CHAN_REGULATORY = 65, + WLAN_REASON_MESH_CHAN = 66, +}; + + +/* Information Element IDs */ +enum ieee80211_eid { + WLAN_EID_SSID = 0, + WLAN_EID_SUPP_RATES = 1, + WLAN_EID_FH_PARAMS = 2, + WLAN_EID_DS_PARAMS = 3, + WLAN_EID_CF_PARAMS = 4, + WLAN_EID_TIM = 5, + WLAN_EID_IBSS_PARAMS = 6, + WLAN_EID_CHALLENGE = 16, + + WLAN_EID_COUNTRY = 7, + WLAN_EID_HP_PARAMS = 8, + WLAN_EID_HP_TABLE = 9, + WLAN_EID_REQUEST = 10, + + WLAN_EID_QBSS_LOAD = 11, + WLAN_EID_EDCA_PARAM_SET = 12, + WLAN_EID_TSPEC = 13, + WLAN_EID_TCLAS = 14, + WLAN_EID_SCHEDULE = 15, + WLAN_EID_TS_DELAY = 43, + WLAN_EID_TCLAS_PROCESSING = 44, + WLAN_EID_QOS_CAPA = 46, + /* 802.11z */ + WLAN_EID_LINK_ID = 101, + /* 802.11s */ + WLAN_EID_MESH_CONFIG = 113, + WLAN_EID_MESH_ID = 114, + WLAN_EID_LINK_METRIC_REPORT = 115, + WLAN_EID_CONGESTION_NOTIFICATION = 116, + WLAN_EID_PEER_MGMT = 117, + WLAN_EID_CHAN_SWITCH_PARAM = 118, + WLAN_EID_MESH_AWAKE_WINDOW = 119, + WLAN_EID_BEACON_TIMING = 120, + WLAN_EID_MCCAOP_SETUP_REQ = 121, + WLAN_EID_MCCAOP_SETUP_RESP = 122, + WLAN_EID_MCCAOP_ADVERT = 123, + WLAN_EID_MCCAOP_TEARDOWN = 124, + WLAN_EID_GANN = 125, + WLAN_EID_RANN = 126, + WLAN_EID_PREQ = 130, + WLAN_EID_PREP = 131, + WLAN_EID_PERR = 132, + WLAN_EID_PXU = 137, + WLAN_EID_PXUC = 138, + WLAN_EID_AUTH_MESH_PEER_EXCH = 139, + WLAN_EID_MIC = 140, + + WLAN_EID_PWR_CONSTRAINT = 32, + WLAN_EID_PWR_CAPABILITY = 33, + WLAN_EID_TPC_REQUEST = 34, + WLAN_EID_TPC_REPORT = 35, + WLAN_EID_SUPPORTED_CHANNELS = 36, + WLAN_EID_CHANNEL_SWITCH = 37, + WLAN_EID_MEASURE_REQUEST = 38, + WLAN_EID_MEASURE_REPORT = 39, + WLAN_EID_QUIET = 40, + WLAN_EID_IBSS_DFS = 41, + + WLAN_EID_ERP_INFO = 42, + WLAN_EID_EXT_SUPP_RATES = 50, + + WLAN_EID_HT_CAPABILITY = 45, + WLAN_EID_HT_INFORMATION = 61, + + WLAN_EID_RSN = 48, + WLAN_EID_MMIE = 76, + WLAN_EID_WPA = 221, + WLAN_EID_GENERIC = 221, + WLAN_EID_VENDOR_SPECIFIC = 221, + WLAN_EID_QOS_PARAMETER = 222, + + WLAN_EID_AP_CHAN_REPORT = 51, + WLAN_EID_NEIGHBOR_REPORT = 52, + WLAN_EID_RCPI = 53, + WLAN_EID_BSS_AVG_ACCESS_DELAY = 63, + WLAN_EID_ANTENNA_INFO = 64, + WLAN_EID_RSNI = 65, + WLAN_EID_MEASUREMENT_PILOT_TX_INFO = 66, + WLAN_EID_BSS_AVAILABLE_CAPACITY = 67, + WLAN_EID_BSS_AC_ACCESS_DELAY = 68, + WLAN_EID_RRM_ENABLED_CAPABILITIES = 70, + WLAN_EID_MULTIPLE_BSSID = 71, + WLAN_EID_BSS_COEX_2040 = 72, + WLAN_EID_OVERLAP_BSS_SCAN_PARAM = 74, + WLAN_EID_EXT_CAPABILITY = 127, + + WLAN_EID_MOBILITY_DOMAIN = 54, + WLAN_EID_FAST_BSS_TRANSITION = 55, + WLAN_EID_TIMEOUT_INTERVAL = 56, + WLAN_EID_RIC_DATA = 57, + WLAN_EID_RIC_DESCRIPTOR = 75, + + WLAN_EID_DSE_REGISTERED_LOCATION = 58, + WLAN_EID_SUPPORTED_REGULATORY_CLASSES = 59, + WLAN_EID_EXT_CHANSWITCH_ANN = 60, +}; + +/* Action category code */ +enum ieee80211_category { + WLAN_CATEGORY_SPECTRUM_MGMT = 0, + WLAN_CATEGORY_QOS = 1, + WLAN_CATEGORY_DLS = 2, + WLAN_CATEGORY_BACK = 3, + WLAN_CATEGORY_PUBLIC = 4, + WLAN_CATEGORY_HT = 7, + WLAN_CATEGORY_SA_QUERY = 8, + WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION = 9, + WLAN_CATEGORY_TDLS = 12, + WLAN_CATEGORY_MESH_ACTION = 13, + WLAN_CATEGORY_MULTIHOP_ACTION = 14, + WLAN_CATEGORY_SELF_PROTECTED = 15, + WLAN_CATEGORY_WMM = 17, + WLAN_CATEGORY_VENDOR_SPECIFIC_PROTECTED = 126, + WLAN_CATEGORY_VENDOR_SPECIFIC = 127, +}; + +/* SPECTRUM_MGMT action code */ +enum ieee80211_spectrum_mgmt_actioncode { + WLAN_ACTION_SPCT_MSR_REQ = 0, + WLAN_ACTION_SPCT_MSR_RPRT = 1, + WLAN_ACTION_SPCT_TPC_REQ = 2, + WLAN_ACTION_SPCT_TPC_RPRT = 3, + WLAN_ACTION_SPCT_CHL_SWITCH = 4, +}; + +/* HT action codes */ +enum ieee80211_ht_actioncode { + WLAN_HT_ACTION_NOTIFY_CHANWIDTH = 0, + WLAN_HT_ACTION_SMPS = 1, + WLAN_HT_ACTION_PSMP = 2, + WLAN_HT_ACTION_PCO_PHASE = 3, + WLAN_HT_ACTION_CSI = 4, + WLAN_HT_ACTION_NONCOMPRESSED_BF = 5, + WLAN_HT_ACTION_COMPRESSED_BF = 6, + WLAN_HT_ACTION_ASEL_IDX_FEEDBACK = 7, +}; + +/* Self Protected Action codes */ +enum ieee80211_self_protected_actioncode { + WLAN_SP_RESERVED = 0, + WLAN_SP_MESH_PEERING_OPEN = 1, + WLAN_SP_MESH_PEERING_CONFIRM = 2, + WLAN_SP_MESH_PEERING_CLOSE = 3, + WLAN_SP_MGK_INFORM = 4, + WLAN_SP_MGK_ACK = 5, +}; + +/* Mesh action codes */ +enum ieee80211_mesh_actioncode { + WLAN_MESH_ACTION_LINK_METRIC_REPORT, + WLAN_MESH_ACTION_HWMP_PATH_SELECTION, + WLAN_MESH_ACTION_GATE_ANNOUNCEMENT, + WLAN_MESH_ACTION_CONGESTION_CONTROL_NOTIFICATION, + WLAN_MESH_ACTION_MCCA_SETUP_REQUEST, + WLAN_MESH_ACTION_MCCA_SETUP_REPLY, + WLAN_MESH_ACTION_MCCA_ADVERTISEMENT_REQUEST, + WLAN_MESH_ACTION_MCCA_ADVERTISEMENT, + WLAN_MESH_ACTION_MCCA_TEARDOWN, + WLAN_MESH_ACTION_TBTT_ADJUSTMENT_REQUEST, + WLAN_MESH_ACTION_TBTT_ADJUSTMENT_RESPONSE, +}; + +/* Security key length */ +enum ieee80211_key_len { + WLAN_KEY_LEN_WEP40 = 5, + WLAN_KEY_LEN_WEP104 = 13, + WLAN_KEY_LEN_CCMP = 16, + WLAN_KEY_LEN_TKIP = 32, + WLAN_KEY_LEN_AES_CMAC = 16, +}; + +/* Public action codes */ +enum ieee80211_pub_actioncode { + WLAN_PUB_ACTION_TDLS_DISCOVER_RES = 14, +}; + +/* TDLS action codes */ +enum ieee80211_tdls_actioncode { + WLAN_TDLS_SETUP_REQUEST = 0, + WLAN_TDLS_SETUP_RESPONSE = 1, + WLAN_TDLS_SETUP_CONFIRM = 2, + WLAN_TDLS_TEARDOWN = 3, + WLAN_TDLS_PEER_TRAFFIC_INDICATION = 4, + WLAN_TDLS_CHANNEL_SWITCH_REQUEST = 5, + WLAN_TDLS_CHANNEL_SWITCH_RESPONSE = 6, + WLAN_TDLS_PEER_PSM_REQUEST = 7, + WLAN_TDLS_PEER_PSM_RESPONSE = 8, + WLAN_TDLS_PEER_TRAFFIC_RESPONSE = 9, + WLAN_TDLS_DISCOVERY_REQUEST = 10, +}; + +/* + * TDLS capabililites to be enabled in the 5th byte of the + * @WLAN_EID_EXT_CAPABILITY information element + */ +#define WLAN_EXT_CAPA5_TDLS_ENABLED BIT(5) +#define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6) + +/* TDLS specific payload type in the LLC/SNAP header */ +#define WLAN_TDLS_SNAP_RFTYPE 0x2 + +/** + * enum - mesh path selection protocol identifier + * + * @IEEE80211_PATH_PROTOCOL_HWMP: the default path selection protocol + * @IEEE80211_PATH_PROTOCOL_VENDOR: a vendor specific protocol that will + * be specified in a vendor specific information element + */ +enum { + IEEE80211_PATH_PROTOCOL_HWMP = 0, + IEEE80211_PATH_PROTOCOL_VENDOR = 255, +}; + +/** + * enum - mesh path selection metric identifier + * + * @IEEE80211_PATH_METRIC_AIRTIME: the default path selection metric + * @IEEE80211_PATH_METRIC_VENDOR: a vendor specific metric that will be + * specified in a vendor specific information element + */ +enum { + IEEE80211_PATH_METRIC_AIRTIME = 0, + IEEE80211_PATH_METRIC_VENDOR = 255, +}; + + +/* + * IEEE 802.11-2007 7.3.2.9 Country information element + * + * Minimum length is 8 octets, ie len must be evenly + * divisible by 2 + */ + +/* Although the spec says 8 I'm seeing 6 in practice */ +#define IEEE80211_COUNTRY_IE_MIN_LEN 6 + +/* The Country String field of the element shall be 3 octets in length */ +#define IEEE80211_COUNTRY_STRING_LEN 3 + +/* + * For regulatory extension stuff see IEEE 802.11-2007 + * Annex I (page 1141) and Annex J (page 1147). Also + * review 7.3.2.9. + * + * When dot11RegulatoryClassesRequired is true and the + * first_channel/reg_extension_id is >= 201 then the IE + * compromises of the 'ext' struct represented below: + * + * - Regulatory extension ID - when generating IE this just needs + * to be monotonically increasing for each triplet passed in + * the IE + * - Regulatory class - index into set of rules + * - Coverage class - index into air propagation time (Table 7-27), + * in microseconds, you can compute the air propagation time from + * the index by multiplying by 3, so index 10 yields a propagation + * of 10 us. Valid values are 0-31, values 32-255 are not defined + * yet. A value of 0 inicates air propagation of <= 1 us. + * + * See also Table I.2 for Emission limit sets and table + * I.3 for Behavior limit sets. Table J.1 indicates how to map + * a reg_class to an emission limit set and behavior limit set. + */ +#define IEEE80211_COUNTRY_EXTENSION_ID 201 + +/* + * Channels numbers in the IE must be monotonically increasing + * if dot11RegulatoryClassesRequired is not true. + * + * If dot11RegulatoryClassesRequired is true consecutive + * subband triplets following a regulatory triplet shall + * have monotonically increasing first_channel number fields. + * + * Channel numbers shall not overlap. + * + * Note that max_power is signed. + */ +struct ieee80211_country_ie_triplet { + union { + struct { + u8 first_channel; + u8 num_channels; + s8 max_power; + } __attribute__ ((packed)) chans; + struct { + u8 reg_extension_id; + u8 reg_class; + u8 coverage_class; + } __attribute__ ((packed)) ext; + }; +} __attribute__ ((packed)); + +enum ieee80211_timeout_interval_type { + WLAN_TIMEOUT_REASSOC_DEADLINE = 1 /* 802.11r */, + WLAN_TIMEOUT_KEY_LIFETIME = 2 /* 802.11r */, + WLAN_TIMEOUT_ASSOC_COMEBACK = 3 /* 802.11w */, +}; + +/* BACK action code */ +enum ieee80211_back_actioncode { + WLAN_ACTION_ADDBA_REQ = 0, + WLAN_ACTION_ADDBA_RESP = 1, + WLAN_ACTION_DELBA = 2, +}; + +/* BACK (block-ack) parties */ +enum ieee80211_back_parties { + WLAN_BACK_RECIPIENT = 0, + WLAN_BACK_INITIATOR = 1, +}; + +/* SA Query action */ +enum ieee80211_sa_query_action { + WLAN_ACTION_SA_QUERY_REQUEST = 0, + WLAN_ACTION_SA_QUERY_RESPONSE = 1, +}; + + +/* cipher suite selectors */ +#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 +#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01 +#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02 +/* reserved: 0x000FAC03 */ +#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 +#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 +#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 + +#define WLAN_CIPHER_SUITE_SMS4 0x00147201 + +/* AKM suite selectors */ +#define WLAN_AKM_SUITE_8021X 0x000FAC01 +#define WLAN_AKM_SUITE_PSK 0x000FAC02 +#define WLAN_AKM_SUITE_SAE 0x000FAC08 +#define WLAN_AKM_SUITE_FT_OVER_SAE 0x000FAC09 + +#define WLAN_MAX_KEY_LEN 32 + +#define WLAN_PMKID_LEN 16 + +#define WLAN_OUI_WFA 0x506f9a +#define WLAN_OUI_TYPE_WFA_P2P 9 + +/* + * WMM/802.11e Tspec Element + */ +#define IEEE80211_WMM_IE_TSPEC_TID_MASK 0x0F +#define IEEE80211_WMM_IE_TSPEC_TID_SHIFT 1 + +enum ieee80211_tspec_status_code { + IEEE80211_TSPEC_STATUS_ADMISS_ACCEPTED = 0, + IEEE80211_TSPEC_STATUS_ADDTS_INVAL_PARAMS = 0x1, +}; + +struct ieee80211_tspec_ie { + u8 element_id; + u8 len; + u8 oui[3]; + u8 oui_type; + u8 oui_subtype; + u8 version; + __le16 tsinfo; + u8 tsinfo_resvd; + __le16 nominal_msdu; + __le16 max_msdu; + __le32 min_service_int; + __le32 max_service_int; + __le32 inactivity_int; + __le32 suspension_int; + __le32 service_start_time; + __le32 min_data_rate; + __le32 mean_data_rate; + __le32 peak_data_rate; + __le32 max_burst_size; + __le32 delay_bound; + __le32 min_phy_rate; + __le16 sba; + __le16 medium_time; +} __packed; + +/** + * ieee80211_get_qos_ctl - get pointer to qos control bytes + * @hdr: the frame + * + * The qos ctrl bytes come after the frame_control, duration, seq_num + * and 3 or 4 addresses of length ETH_ALEN. + * 3 addr: 2 + 2 + 2 + 3*6 = 24 + * 4 addr: 2 + 2 + 2 + 4*6 = 30 + */ +static inline u8 *ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr) +{ + if (ieee80211_has_a4(hdr->frame_control)) + return (u8 *)hdr + 30; + else + return (u8 *)hdr + 24; +} + +/** + * ieee80211_get_SA - get pointer to SA + * @hdr: the frame + * + * Given an 802.11 frame, this function returns the offset + * to the source address (SA). It does not verify that the + * header is long enough to contain the address, and the + * header must be long enough to contain the frame control + * field. + */ +static inline u8 *ieee80211_get_SA(struct ieee80211_hdr *hdr) +{ + if (ieee80211_has_a4(hdr->frame_control)) + return hdr->addr4; + if (ieee80211_has_fromds(hdr->frame_control)) + return hdr->addr3; + return hdr->addr2; +} + +/** + * ieee80211_get_DA - get pointer to DA + * @hdr: the frame + * + * Given an 802.11 frame, this function returns the offset + * to the destination address (DA). It does not verify that + * the header is long enough to contain the address, and the + * header must be long enough to contain the frame control + * field. + */ +static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) +{ + if (ieee80211_has_tods(hdr->frame_control)) + return hdr->addr3; + else + return hdr->addr1; +} + +/** + * ieee80211_is_robust_mgmt_frame - check if frame is a robust management frame + * @hdr: the frame (buffer must include at least the first octet of payload) + */ +static inline bool ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr) +{ + if (ieee80211_is_disassoc(hdr->frame_control) || + ieee80211_is_deauth(hdr->frame_control)) + return true; + + if (ieee80211_is_action(hdr->frame_control)) { + u8 *category; + + /* + * Action frames, excluding Public Action frames, are Robust + * Management Frames. However, if we are looking at a Protected + * frame, skip the check since the data may be encrypted and + * the frame has already been found to be a Robust Management + * Frame (by the other end). + */ + if (ieee80211_has_protected(hdr->frame_control)) + return true; + category = ((u8 *) hdr) + 24; + return *category != WLAN_CATEGORY_PUBLIC && + *category != WLAN_CATEGORY_HT && + *category != WLAN_CATEGORY_SELF_PROTECTED && + *category != WLAN_CATEGORY_VENDOR_SPECIFIC; + } + + return false; +} + +/** + * ieee80211_fhss_chan_to_freq - get channel frequency + * @channel: the FHSS channel + * + * Convert IEEE802.11 FHSS channel to frequency (MHz) + * Ref IEEE 802.11-2007 section 14.6 + */ +static inline int ieee80211_fhss_chan_to_freq(int channel) +{ + if ((channel > 1) && (channel < 96)) + return channel + 2400; + else + return -1; +} + +/** + * ieee80211_freq_to_fhss_chan - get channel + * @freq: the channels frequency + * + * Convert frequency (MHz) to IEEE802.11 FHSS channel + * Ref IEEE 802.11-2007 section 14.6 + */ +static inline int ieee80211_freq_to_fhss_chan(int freq) +{ + if ((freq > 2401) && (freq < 2496)) + return freq - 2400; + else + return -1; +} + +/** + * ieee80211_dsss_chan_to_freq - get channel center frequency + * @channel: the DSSS channel + * + * Convert IEEE802.11 DSSS channel to the center frequency (MHz). + * Ref IEEE 802.11-2007 section 15.6 + */ +static inline int ieee80211_dsss_chan_to_freq(int channel) +{ + if ((channel > 0) && (channel < 14)) + return 2407 + (channel * 5); + else if (channel == 14) + return 2484; + else + return -1; +} + +/** + * ieee80211_freq_to_dsss_chan - get channel + * @freq: the frequency + * + * Convert frequency (MHz) to IEEE802.11 DSSS channel + * Ref IEEE 802.11-2007 section 15.6 + * + * This routine selects the channel with the closest center frequency. + */ +static inline int ieee80211_freq_to_dsss_chan(int freq) +{ + if ((freq >= 2410) && (freq < 2475)) + return (freq - 2405) / 5; + else if ((freq >= 2482) && (freq < 2487)) + return 14; + else + return -1; +} + +/* Convert IEEE802.11 HR DSSS channel to frequency (MHz) and back + * Ref IEEE 802.11-2007 section 18.4.6.2 + * + * The channels and frequencies are the same as those defined for DSSS + */ +#define ieee80211_hr_chan_to_freq(chan) ieee80211_dsss_chan_to_freq(chan) +#define ieee80211_freq_to_hr_chan(freq) ieee80211_freq_to_dsss_chan(freq) + +/* Convert IEEE802.11 ERP channel to frequency (MHz) and back + * Ref IEEE 802.11-2007 section 19.4.2 + */ +#define ieee80211_erp_chan_to_freq(chan) ieee80211_hr_chan_to_freq(chan) +#define ieee80211_freq_to_erp_chan(freq) ieee80211_freq_to_hr_chan(freq) + +/** + * ieee80211_ofdm_chan_to_freq - get channel center frequency + * @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz + * @channel: the OFDM channel + * + * Convert IEEE802.11 OFDM channel to center frequency (MHz) + * Ref IEEE 802.11-2007 section 17.3.8.3.2 + */ +static inline int ieee80211_ofdm_chan_to_freq(int s_freq, int channel) +{ + if ((channel > 0) && (channel <= 200) && + (s_freq >= 4000)) + return s_freq + (channel * 5); + else + return -1; +} + +/** + * ieee80211_freq_to_ofdm_channel - get channel + * @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz + * @freq: the frequency + * + * Convert frequency (MHz) to IEEE802.11 OFDM channel + * Ref IEEE 802.11-2007 section 17.3.8.3.2 + * + * This routine selects the channel with the closest center frequency. + */ +static inline int ieee80211_freq_to_ofdm_chan(int s_freq, int freq) +{ + if ((freq > (s_freq + 2)) && (freq <= (s_freq + 1202)) && + (s_freq >= 4000)) + return (freq + 2 - s_freq) / 5; + else + return -1; +} + +/** + * ieee80211_tu_to_usec - convert time units (TU) to microseconds + * @tu: the TUs + */ +static inline unsigned long ieee80211_tu_to_usec(unsigned long tu) +{ + return 1024 * tu; +} + +/** + * ieee80211_check_tim - check if AID bit is set in TIM + * @tim: the TIM IE + * @tim_len: length of the TIM IE + * @aid: the AID to look for + */ +static inline bool ieee80211_check_tim(struct ieee80211_tim_ie *tim, + u8 tim_len, u16 aid) +{ + u8 mask; + u8 index, indexn1, indexn2; + + if (unlikely(!tim || tim_len < sizeof(*tim))) + return false; + + aid &= 0x3fff; + index = aid / 8; + mask = 1 << (aid & 7); + + indexn1 = tim->bitmap_ctrl & 0xfe; + indexn2 = tim_len + indexn1 - 4; + + if (index < indexn1 || index > indexn2) + return false; + + index -= indexn1; + + return !!(tim->virtual_map[index] & mask); +} + +#endif /* LINUX_IEEE80211_H */ diff --git a/include/compat/linux/kfifo.h b/include/compat/linux/kfifo.h new file mode 100644 index 00000000..e30bb546 --- /dev/null +++ b/include/compat/linux/kfifo.h @@ -0,0 +1,857 @@ +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) +#include_next +#else +/* + * A generic kernel FIFO implementation + * + * Copyright (C) 2009/2010 Stefani Seibold + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _LINUX_KFIFO_H +#define _LINUX_KFIFO_H + +/* + * How to porting drivers to the new generic FIFO API: + * + * - Modify the declaration of the "struct kfifo *" object into a + * in-place "struct kfifo" object + * - Init the in-place object with kfifo_alloc() or kfifo_init() + * Note: The address of the in-place "struct kfifo" object must be + * passed as the first argument to this functions + * - Replace the use of __kfifo_put into kfifo_in and __kfifo_get + * into kfifo_out + * - Replace the use of kfifo_put into kfifo_in_spinlocked and kfifo_get + * into kfifo_out_spinlocked + * Note: the spinlock pointer formerly passed to kfifo_init/kfifo_alloc + * must be passed now to the kfifo_in_spinlocked and kfifo_out_spinlocked + * as the last parameter + * - The formerly __kfifo_* functions are renamed into kfifo_* + */ + +/* + * Note about locking : There is no locking required until only * one reader + * and one writer is using the fifo and no kfifo_reset() will be * called + * kfifo_reset_out() can be safely used, until it will be only called + * in the reader thread. + * For multiple writer and one reader there is only a need to lock the writer. + * And vice versa for only one writer and multiple reader there is only a need + * to lock the reader. + */ + +#include +#include +#include +#include + +struct __kfifo { + unsigned int in; + unsigned int out; + unsigned int mask; + unsigned int esize; + void *data; +}; + +#define __STRUCT_KFIFO_COMMON(datatype, recsize, ptrtype) \ + union { \ + struct __kfifo kfifo; \ + datatype *type; \ + char (*rectype)[recsize]; \ + ptrtype *ptr; \ + const ptrtype *ptr_const; \ + } + +#define __STRUCT_KFIFO(type, size, recsize, ptrtype) \ +{ \ + __STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \ + type buf[((size < 2) || (size & (size - 1))) ? -1 : size]; \ +} + +#define STRUCT_KFIFO(type, size) \ + struct __STRUCT_KFIFO(type, size, 0, type) + +#define __STRUCT_KFIFO_PTR(type, recsize, ptrtype) \ +{ \ + __STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \ + type buf[0]; \ +} + +#define STRUCT_KFIFO_PTR(type) \ + struct __STRUCT_KFIFO_PTR(type, 0, type) + +/* + * define compatibility "struct kfifo" for dynamic allocated fifos + */ +struct kfifo __STRUCT_KFIFO_PTR(unsigned char, 0, void); + +#define STRUCT_KFIFO_REC_1(size) \ + struct __STRUCT_KFIFO(unsigned char, size, 1, void) + +#define STRUCT_KFIFO_REC_2(size) \ + struct __STRUCT_KFIFO(unsigned char, size, 2, void) + +/* + * define kfifo_rec types + */ +struct kfifo_rec_ptr_1 __STRUCT_KFIFO_PTR(unsigned char, 1, void); +struct kfifo_rec_ptr_2 __STRUCT_KFIFO_PTR(unsigned char, 2, void); + +/* + * helper macro to distinguish between real in place fifo where the fifo + * array is a part of the structure and the fifo type where the array is + * outside of the fifo structure. + */ +#define __is_kfifo_ptr(fifo) (sizeof(*fifo) == sizeof(struct __kfifo)) + +/** + * DECLARE_KFIFO_PTR - macro to declare a fifo pointer object + * @fifo: name of the declared fifo + * @type: type of the fifo elements + */ +#define DECLARE_KFIFO_PTR(fifo, type) STRUCT_KFIFO_PTR(type) fifo + +/** + * DECLARE_KFIFO - macro to declare a fifo object + * @fifo: name of the declared fifo + * @type: type of the fifo elements + * @size: the number of elements in the fifo, this must be a power of 2 + */ +#define DECLARE_KFIFO(fifo, type, size) STRUCT_KFIFO(type, size) fifo + +/** + * INIT_KFIFO - Initialize a fifo declared by DECLARE_KFIFO + * @fifo: name of the declared fifo datatype + */ +#define INIT_KFIFO(fifo) \ +(void)({ \ + typeof(&(fifo)) __tmp = &(fifo); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + __kfifo->in = 0; \ + __kfifo->out = 0; \ + __kfifo->mask = __is_kfifo_ptr(__tmp) ? 0 : ARRAY_SIZE(__tmp->buf) - 1;\ + __kfifo->esize = sizeof(*__tmp->buf); \ + __kfifo->data = __is_kfifo_ptr(__tmp) ? NULL : __tmp->buf; \ +}) + +/** + * DEFINE_KFIFO - macro to define and initialize a fifo + * @fifo: name of the declared fifo datatype + * @type: type of the fifo elements + * @size: the number of elements in the fifo, this must be a power of 2 + * + * Note: the macro can be used for global and local fifo data type variables. + */ +#define DEFINE_KFIFO(fifo, type, size) \ + DECLARE_KFIFO(fifo, type, size) = \ + (typeof(fifo)) { \ + { \ + { \ + .in = 0, \ + .out = 0, \ + .mask = __is_kfifo_ptr(&(fifo)) ? \ + 0 : \ + ARRAY_SIZE((fifo).buf) - 1, \ + .esize = sizeof(*(fifo).buf), \ + .data = __is_kfifo_ptr(&(fifo)) ? \ + NULL : \ + (fifo).buf, \ + } \ + } \ + } + + +static inline unsigned int __must_check +__kfifo_uint_must_check_helper(unsigned int val) +{ + return val; +} + +static inline int __must_check +__kfifo_int_must_check_helper(int val) +{ + return val; +} + +/** + * kfifo_initialized - Check if the fifo is initialized + * @fifo: address of the fifo to check + * + * Return %true if fifo is initialized, otherwise %false. + * Assumes the fifo was 0 before. + */ +#define kfifo_initialized(fifo) ((fifo)->kfifo.mask) + +/** + * kfifo_esize - returns the size of the element managed by the fifo + * @fifo: address of the fifo to be used + */ +#define kfifo_esize(fifo) ((fifo)->kfifo.esize) + +/** + * kfifo_recsize - returns the size of the record length field + * @fifo: address of the fifo to be used + */ +#define kfifo_recsize(fifo) (sizeof(*(fifo)->rectype)) + +/** + * kfifo_size - returns the size of the fifo in elements + * @fifo: address of the fifo to be used + */ +#define kfifo_size(fifo) ((fifo)->kfifo.mask + 1) + +/** + * kfifo_reset - removes the entire fifo content + * @fifo: address of the fifo to be used + * + * Note: usage of kfifo_reset() is dangerous. It should be only called when the + * fifo is exclusived locked or when it is secured that no other thread is + * accessing the fifo. + */ +#define kfifo_reset(fifo) \ +(void)({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + __tmp->kfifo.in = __tmp->kfifo.out = 0; \ +}) + +/** + * kfifo_reset_out - skip fifo content + * @fifo: address of the fifo to be used + * + * Note: The usage of kfifo_reset_out() is safe until it will be only called + * from the reader thread and there is only one concurrent reader. Otherwise + * it is dangerous and must be handled in the same way as kfifo_reset(). + */ +#define kfifo_reset_out(fifo) \ +(void)({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + __tmp->kfifo.out = __tmp->kfifo.in; \ +}) + +/** + * kfifo_len - returns the number of used elements in the fifo + * @fifo: address of the fifo to be used + */ +#define kfifo_len(fifo) \ +({ \ + typeof((fifo) + 1) __tmpl = (fifo); \ + __tmpl->kfifo.in - __tmpl->kfifo.out; \ +}) + +/** + * kfifo_is_empty - returns true if the fifo is empty + * @fifo: address of the fifo to be used + */ +#define kfifo_is_empty(fifo) \ +({ \ + typeof((fifo) + 1) __tmpq = (fifo); \ + __tmpq->kfifo.in == __tmpq->kfifo.out; \ +}) + +/** + * kfifo_is_full - returns true if the fifo is full + * @fifo: address of the fifo to be used + */ +#define kfifo_is_full(fifo) \ +({ \ + typeof((fifo) + 1) __tmpq = (fifo); \ + kfifo_len(__tmpq) > __tmpq->kfifo.mask; \ +}) + +/** + * kfifo_avail - returns the number of unused elements in the fifo + * @fifo: address of the fifo to be used + */ +#define kfifo_avail(fifo) \ +__kfifo_uint_must_check_helper( \ +({ \ + typeof((fifo) + 1) __tmpq = (fifo); \ + const size_t __recsize = sizeof(*__tmpq->rectype); \ + unsigned int __avail = kfifo_size(__tmpq) - kfifo_len(__tmpq); \ + (__recsize) ? ((__avail <= __recsize) ? 0 : \ + __kfifo_max_r(__avail - __recsize, __recsize)) : \ + __avail; \ +}) \ +) + +/** + * kfifo_skip - skip output data + * @fifo: address of the fifo to be used + */ +#define kfifo_skip(fifo) \ +(void)({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + if (__recsize) \ + __kfifo_skip_r(__kfifo, __recsize); \ + else \ + __kfifo->out++; \ +}) + +/** + * kfifo_peek_len - gets the size of the next fifo record + * @fifo: address of the fifo to be used + * + * This function returns the size of the next fifo record in number of bytes. + */ +#define kfifo_peek_len(fifo) \ +__kfifo_uint_must_check_helper( \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + (!__recsize) ? kfifo_len(__tmp) * sizeof(*__tmp->type) : \ + __kfifo_len_r(__kfifo, __recsize); \ +}) \ +) + +/** + * kfifo_alloc - dynamically allocates a new fifo buffer + * @fifo: pointer to the fifo + * @size: the number of elements in the fifo, this must be a power of 2 + * @gfp_mask: get_free_pages mask, passed to kmalloc() + * + * This macro dynamically allocates a new fifo buffer. + * + * The numer of elements will be rounded-up to a power of 2. + * The fifo will be release with kfifo_free(). + * Return 0 if no error, otherwise an error code. + */ +#define kfifo_alloc(fifo, size, gfp_mask) \ +__kfifo_int_must_check_helper( \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + __is_kfifo_ptr(__tmp) ? \ + __kfifo_alloc(__kfifo, size, sizeof(*__tmp->type), gfp_mask) : \ + -EINVAL; \ +}) \ +) + +/** + * kfifo_free - frees the fifo + * @fifo: the fifo to be freed + */ +#define kfifo_free(fifo) \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + if (__is_kfifo_ptr(__tmp)) \ + __kfifo_free(__kfifo); \ +}) + +/** + * kfifo_init - initialize a fifo using a preallocated buffer + * @fifo: the fifo to assign the buffer + * @buffer: the preallocated buffer to be used + * @size: the size of the internal buffer, this have to be a power of 2 + * + * This macro initialize a fifo using a preallocated buffer. + * + * The numer of elements will be rounded-up to a power of 2. + * Return 0 if no error, otherwise an error code. + */ +#define kfifo_init(fifo, buffer, size) \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + __is_kfifo_ptr(__tmp) ? \ + __kfifo_init(__kfifo, buffer, size, sizeof(*__tmp->type)) : \ + -EINVAL; \ +}) + +/** + * kfifo_put - put data into the fifo + * @fifo: address of the fifo to be used + * @val: the data to be added + * + * This macro copies the given value into the fifo. + * It returns 0 if the fifo was full. Otherwise it returns the number + * processed elements. + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macro. + */ +#define kfifo_put(fifo, val) \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + typeof((val) + 1) __val = (val); \ + unsigned int __ret; \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + if (0) { \ + typeof(__tmp->ptr_const) __dummy __attribute__ ((unused)); \ + __dummy = (typeof(__val))NULL; \ + } \ + if (__recsize) \ + __ret = __kfifo_in_r(__kfifo, __val, sizeof(*__val), \ + __recsize); \ + else { \ + __ret = !kfifo_is_full(__tmp); \ + if (__ret) { \ + (__is_kfifo_ptr(__tmp) ? \ + ((typeof(__tmp->type))__kfifo->data) : \ + (__tmp->buf) \ + )[__kfifo->in & __tmp->kfifo.mask] = \ + *(typeof(__tmp->type))__val; \ + smp_wmb(); \ + __kfifo->in++; \ + } \ + } \ + __ret; \ +}) + +/** + * kfifo_get - get data from the fifo + * @fifo: address of the fifo to be used + * @val: the var where to store the data to be added + * + * This macro reads the data from the fifo. + * It returns 0 if the fifo was empty. Otherwise it returns the number + * processed elements. + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macro. + */ +#define kfifo_get(fifo, val) \ +__kfifo_uint_must_check_helper( \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + typeof((val) + 1) __val = (val); \ + unsigned int __ret; \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + if (0) \ + __val = (typeof(__tmp->ptr))0; \ + if (__recsize) \ + __ret = __kfifo_out_r(__kfifo, __val, sizeof(*__val), \ + __recsize); \ + else { \ + __ret = !kfifo_is_empty(__tmp); \ + if (__ret) { \ + *(typeof(__tmp->type))__val = \ + (__is_kfifo_ptr(__tmp) ? \ + ((typeof(__tmp->type))__kfifo->data) : \ + (__tmp->buf) \ + )[__kfifo->out & __tmp->kfifo.mask]; \ + smp_wmb(); \ + __kfifo->out++; \ + } \ + } \ + __ret; \ +}) \ +) + +/** + * kfifo_peek - get data from the fifo without removing + * @fifo: address of the fifo to be used + * @val: the var where to store the data to be added + * + * This reads the data from the fifo without removing it from the fifo. + * It returns 0 if the fifo was empty. Otherwise it returns the number + * processed elements. + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macro. + */ +#define kfifo_peek(fifo, val) \ +__kfifo_uint_must_check_helper( \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + typeof((val) + 1) __val = (val); \ + unsigned int __ret; \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + if (0) \ + __val = (typeof(__tmp->ptr))NULL; \ + if (__recsize) \ + __ret = __kfifo_out_peek_r(__kfifo, __val, sizeof(*__val), \ + __recsize); \ + else { \ + __ret = !kfifo_is_empty(__tmp); \ + if (__ret) { \ + *(typeof(__tmp->type))__val = \ + (__is_kfifo_ptr(__tmp) ? \ + ((typeof(__tmp->type))__kfifo->data) : \ + (__tmp->buf) \ + )[__kfifo->out & __tmp->kfifo.mask]; \ + smp_wmb(); \ + } \ + } \ + __ret; \ +}) \ +) + +/** + * kfifo_in - put data into the fifo + * @fifo: address of the fifo to be used + * @buf: the data to be added + * @n: number of elements to be added + * + * This macro copies the given buffer into the fifo and returns the + * number of copied elements. + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macro. + */ +#define kfifo_in(fifo, buf, n) \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + typeof((buf) + 1) __buf = (buf); \ + unsigned long __n = (n); \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + if (0) { \ + typeof(__tmp->ptr_const) __dummy __attribute__ ((unused)); \ + __dummy = (typeof(__buf))NULL; \ + } \ + (__recsize) ?\ + __kfifo_in_r(__kfifo, __buf, __n, __recsize) : \ + __kfifo_in(__kfifo, __buf, __n); \ +}) + +/** + * kfifo_in_spinlocked - put data into the fifo using a spinlock for locking + * @fifo: address of the fifo to be used + * @buf: the data to be added + * @n: number of elements to be added + * @lock: pointer to the spinlock to use for locking + * + * This macro copies the given values buffer into the fifo and returns the + * number of copied elements. + */ +#define kfifo_in_spinlocked(fifo, buf, n, lock) \ +({ \ + unsigned long __flags; \ + unsigned int __ret; \ + spin_lock_irqsave(lock, __flags); \ + __ret = kfifo_in(fifo, buf, n); \ + spin_unlock_irqrestore(lock, __flags); \ + __ret; \ +}) + +/* alias for kfifo_in_spinlocked, will be removed in a future release */ +#define kfifo_in_locked(fifo, buf, n, lock) \ + kfifo_in_spinlocked(fifo, buf, n, lock) + +/** + * kfifo_out - get data from the fifo + * @fifo: address of the fifo to be used + * @buf: pointer to the storage buffer + * @n: max. number of elements to get + * + * This macro get some data from the fifo and return the numbers of elements + * copied. + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macro. + */ +#define kfifo_out(fifo, buf, n) \ +__kfifo_uint_must_check_helper( \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + typeof((buf) + 1) __buf = (buf); \ + unsigned long __n = (n); \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + if (0) { \ + typeof(__tmp->ptr) __dummy = NULL; \ + __buf = __dummy; \ + } \ + (__recsize) ?\ + __kfifo_out_r(__kfifo, __buf, __n, __recsize) : \ + __kfifo_out(__kfifo, __buf, __n); \ +}) \ +) + +/** + * kfifo_out_spinlocked - get data from the fifo using a spinlock for locking + * @fifo: address of the fifo to be used + * @buf: pointer to the storage buffer + * @n: max. number of elements to get + * @lock: pointer to the spinlock to use for locking + * + * This macro get the data from the fifo and return the numbers of elements + * copied. + */ +#define kfifo_out_spinlocked(fifo, buf, n, lock) \ +__kfifo_uint_must_check_helper( \ +({ \ + unsigned long __flags; \ + unsigned int __ret; \ + spin_lock_irqsave(lock, __flags); \ + __ret = kfifo_out(fifo, buf, n); \ + spin_unlock_irqrestore(lock, __flags); \ + __ret; \ +}) \ +) + +/* alias for kfifo_out_spinlocked, will be removed in a future release */ +#define kfifo_out_locked(fifo, buf, n, lock) \ + kfifo_out_spinlocked(fifo, buf, n, lock) + +/** + * kfifo_from_user - puts some data from user space into the fifo + * @fifo: address of the fifo to be used + * @from: pointer to the data to be added + * @len: the length of the data to be added + * @copied: pointer to output variable to store the number of copied bytes + * + * This macro copies at most @len bytes from the @from into the + * fifo, depending of the available space and returns -EFAULT/0. + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macro. + */ +#define kfifo_from_user(fifo, from, len, copied) \ +__kfifo_uint_must_check_helper( \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + const void __user *__from = (from); \ + unsigned int __len = (len); \ + unsigned int *__copied = (copied); \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + (__recsize) ? \ + __kfifo_from_user_r(__kfifo, __from, __len, __copied, __recsize) : \ + __kfifo_from_user(__kfifo, __from, __len, __copied); \ +}) \ +) + +/** + * kfifo_to_user - copies data from the fifo into user space + * @fifo: address of the fifo to be used + * @to: where the data must be copied + * @len: the size of the destination buffer + * @copied: pointer to output variable to store the number of copied bytes + * + * This macro copies at most @len bytes from the fifo into the + * @to buffer and returns -EFAULT/0. + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macro. + */ +#define kfifo_to_user(fifo, to, len, copied) \ +__kfifo_uint_must_check_helper( \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + void __user *__to = (to); \ + unsigned int __len = (len); \ + unsigned int *__copied = (copied); \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + (__recsize) ? \ + __kfifo_to_user_r(__kfifo, __to, __len, __copied, __recsize) : \ + __kfifo_to_user(__kfifo, __to, __len, __copied); \ +}) \ +) + +/** + * kfifo_dma_in_prepare - setup a scatterlist for DMA input + * @fifo: address of the fifo to be used + * @sgl: pointer to the scatterlist array + * @nents: number of entries in the scatterlist array + * @len: number of elements to transfer + * + * This macro fills a scatterlist for DMA input. + * It returns the number entries in the scatterlist array. + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macros. + */ +#define kfifo_dma_in_prepare(fifo, sgl, nents, len) \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + struct scatterlist *__sgl = (sgl); \ + int __nents = (nents); \ + unsigned int __len = (len); \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + (__recsize) ? \ + __kfifo_dma_in_prepare_r(__kfifo, __sgl, __nents, __len, __recsize) : \ + __kfifo_dma_in_prepare(__kfifo, __sgl, __nents, __len); \ +}) + +/** + * kfifo_dma_in_finish - finish a DMA IN operation + * @fifo: address of the fifo to be used + * @len: number of bytes to received + * + * This macro finish a DMA IN operation. The in counter will be updated by + * the len parameter. No error checking will be done. + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macros. + */ +#define kfifo_dma_in_finish(fifo, len) \ +(void)({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + unsigned int __len = (len); \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + if (__recsize) \ + __kfifo_dma_in_finish_r(__kfifo, __len, __recsize); \ + else \ + __kfifo->in += __len / sizeof(*__tmp->type); \ +}) + +/** + * kfifo_dma_out_prepare - setup a scatterlist for DMA output + * @fifo: address of the fifo to be used + * @sgl: pointer to the scatterlist array + * @nents: number of entries in the scatterlist array + * @len: number of elements to transfer + * + * This macro fills a scatterlist for DMA output which at most @len bytes + * to transfer. + * It returns the number entries in the scatterlist array. + * A zero means there is no space available and the scatterlist is not filled. + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macros. + */ +#define kfifo_dma_out_prepare(fifo, sgl, nents, len) \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + struct scatterlist *__sgl = (sgl); \ + int __nents = (nents); \ + unsigned int __len = (len); \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + (__recsize) ? \ + __kfifo_dma_out_prepare_r(__kfifo, __sgl, __nents, __len, __recsize) : \ + __kfifo_dma_out_prepare(__kfifo, __sgl, __nents, __len); \ +}) + +/** + * kfifo_dma_out_finish - finish a DMA OUT operation + * @fifo: address of the fifo to be used + * @len: number of bytes transferd + * + * This macro finish a DMA OUT operation. The out counter will be updated by + * the len parameter. No error checking will be done. + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macros. + */ +#define kfifo_dma_out_finish(fifo, len) \ +(void)({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + unsigned int __len = (len); \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + if (__recsize) \ + __kfifo_dma_out_finish_r(__kfifo, __recsize); \ + else \ + __kfifo->out += __len / sizeof(*__tmp->type); \ +}) + +/** + * kfifo_out_peek - gets some data from the fifo + * @fifo: address of the fifo to be used + * @buf: pointer to the storage buffer + * @n: max. number of elements to get + * + * This macro get the data from the fifo and return the numbers of elements + * copied. The data is not removed from the fifo. + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macro. + */ +#define kfifo_out_peek(fifo, buf, n) \ +__kfifo_uint_must_check_helper( \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + typeof((buf) + 1) __buf = (buf); \ + unsigned long __n = (n); \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + if (0) { \ + typeof(__tmp->ptr) __dummy __attribute__ ((unused)) = NULL; \ + __buf = __dummy; \ + } \ + (__recsize) ? \ + __kfifo_out_peek_r(__kfifo, __buf, __n, __recsize) : \ + __kfifo_out_peek(__kfifo, __buf, __n); \ +}) \ +) + +extern int __kfifo_alloc(struct __kfifo *fifo, unsigned int size, + size_t esize, gfp_t gfp_mask); + +extern void __kfifo_free(struct __kfifo *fifo); + +extern int __kfifo_init(struct __kfifo *fifo, void *buffer, + unsigned int size, size_t esize); + +extern unsigned int __kfifo_in(struct __kfifo *fifo, + const void *buf, unsigned int len); + +extern unsigned int __kfifo_out(struct __kfifo *fifo, + void *buf, unsigned int len); + +extern int __kfifo_from_user(struct __kfifo *fifo, + const void __user *from, unsigned long len, unsigned int *copied); + +extern int __kfifo_to_user(struct __kfifo *fifo, + void __user *to, unsigned long len, unsigned int *copied); + +extern unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo, + struct scatterlist *sgl, int nents, unsigned int len); + +extern unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo, + struct scatterlist *sgl, int nents, unsigned int len); + +extern unsigned int __kfifo_out_peek(struct __kfifo *fifo, + void *buf, unsigned int len); + +extern unsigned int __kfifo_in_r(struct __kfifo *fifo, + const void *buf, unsigned int len, size_t recsize); + +extern unsigned int __kfifo_out_r(struct __kfifo *fifo, + void *buf, unsigned int len, size_t recsize); + +extern int __kfifo_from_user_r(struct __kfifo *fifo, + const void __user *from, unsigned long len, unsigned int *copied, + size_t recsize); + +extern int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to, + unsigned long len, unsigned int *copied, size_t recsize); + +extern unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo, + struct scatterlist *sgl, int nents, unsigned int len, size_t recsize); + +extern void __kfifo_dma_in_finish_r(struct __kfifo *fifo, + unsigned int len, size_t recsize); + +extern unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo, + struct scatterlist *sgl, int nents, unsigned int len, size_t recsize); + +extern void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize); + +extern unsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize); + +extern void __kfifo_skip_r(struct __kfifo *fifo, size_t recsize); + +extern unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, + void *buf, unsigned int len, size_t recsize); + +extern unsigned int __kfifo_max_r(unsigned int len, size_t recsize); + +#endif +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) */ diff --git a/include/compat/linux/math64.h b/include/compat/linux/math64.h new file mode 100644 index 00000000..eb9e8e15 --- /dev/null +++ b/include/compat/linux/math64.h @@ -0,0 +1,10 @@ +#ifndef _COMPAT_LINUX_MATH64_H +#define _COMPAT_LINUX_MATH64_H 1 + +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) +#include_next +#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) */ + +#endif /* _COMPAT_LINUX_MATH64_H */ diff --git a/include/compat/linux/nl80211.h b/include/compat/linux/nl80211.h new file mode 100644 index 00000000..bcf8e8c2 --- /dev/null +++ b/include/compat/linux/nl80211.h @@ -0,0 +1,2963 @@ +#ifndef __LINUX_NL80211_H +#define __LINUX_NL80211_H +/* + * 802.11 netlink interface public header + * + * Copyright 2006-2010 Johannes Berg + * Copyright 2008 Michael Wu + * Copyright 2008 Luis Carlos Cobo + * Copyright 2008 Michael Buesch + * Copyright 2008, 2009 Luis R. Rodriguez + * Copyright 2008 Jouni Malinen + * Copyright 2008 Colin McCabe + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include + +/** + * DOC: Station handling + * + * Stations are added per interface, but a special case exists with VLAN + * interfaces. When a station is bound to an AP interface, it may be moved + * into a VLAN identified by a VLAN interface index (%NL80211_ATTR_STA_VLAN). + * The station is still assumed to belong to the AP interface it was added + * to. + * + * TODO: need more info? + */ + +/** + * DOC: Frame transmission/registration support + * + * Frame transmission and registration support exists to allow userspace + * management entities such as wpa_supplicant react to management frames + * that are not being handled by the kernel. This includes, for example, + * certain classes of action frames that cannot be handled in the kernel + * for various reasons. + * + * Frame registration is done on a per-interface basis and registrations + * cannot be removed other than by closing the socket. It is possible to + * specify a registration filter to register, for example, only for a + * certain type of action frame. In particular with action frames, those + * that userspace registers for will not be returned as unhandled by the + * driver, so that the registered application has to take responsibility + * for doing that. + * + * The type of frame that can be registered for is also dependent on the + * driver and interface type. The frame types are advertised in wiphy + * attributes so applications know what to expect. + * + * NOTE: When an interface changes type while registrations are active, + * these registrations are ignored until the interface type is + * changed again. This means that changing the interface type can + * lead to a situation that couldn't otherwise be produced, but + * any such registrations will be dormant in the sense that they + * will not be serviced, i.e. they will not receive any frames. + * + * Frame transmission allows userspace to send for example the required + * responses to action frames. It is subject to some sanity checking, + * but many frames can be transmitted. When a frame was transmitted, its + * status is indicated to the sending socket. + * + * For more technical details, see the corresponding command descriptions + * below. + */ + +/** + * DOC: Virtual interface / concurrency capabilities + * + * Some devices are able to operate with virtual MACs, they can have + * more than one virtual interface. The capability handling for this + * is a bit complex though, as there may be a number of restrictions + * on the types of concurrency that are supported. + * + * To start with, each device supports the interface types listed in + * the %NL80211_ATTR_SUPPORTED_IFTYPES attribute, but by listing the + * types there no concurrency is implied. + * + * Once concurrency is desired, more attributes must be observed: + * To start with, since some interface types are purely managed in + * software, like the AP-VLAN type in mac80211 for example, there's + * an additional list of these, they can be added at any time and + * are only restricted by some semantic restrictions (e.g. AP-VLAN + * cannot be added without a corresponding AP interface). This list + * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute. + * + * Further, the list of supported combinations is exported. This is + * in the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute. Basically, + * it exports a list of "groups", and at any point in time the + * interfaces that are currently active must fall into any one of + * the advertised groups. Within each group, there are restrictions + * on the number of interfaces of different types that are supported + * and also the number of different channels, along with potentially + * some other restrictions. See &enum nl80211_if_combination_attrs. + * + * All together, these attributes define the concurrency of virtual + * interfaces that a given device supports. + */ + +/** + * enum nl80211_commands - supported nl80211 commands + * + * @NL80211_CMD_UNSPEC: unspecified command to catch errors + * + * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request + * to get a list of all present wiphys. + * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or + * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, + * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, + * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. + * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL + * instead, the support here is for backward compatibility only. + * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request + * or rename notification. Has attributes %NL80211_ATTR_WIPHY and + * %NL80211_ATTR_WIPHY_NAME. + * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME. + * + * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration; + * either a dump request on a %NL80211_ATTR_WIPHY or a specific get + * on an %NL80211_ATTR_IFINDEX is supported. + * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE. + * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response + * to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX, + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also + * be sent from userspace to request creation of a new virtual interface, + * then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and + * %NL80211_ATTR_IFNAME. + * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from + * userspace to request deletion of a virtual interface, then requires + * attribute %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified + * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. + * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT, + * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD. + * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA, + * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER, + * and %NL80211_ATTR_KEY_SEQ attributes. + * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX + * or %NL80211_ATTR_MAC. + * + * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a + * %NL80222_CMD_NEW_BEACON message) + * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface + * using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD, + * %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes. + * Following attributes are provided for drivers that generate full Beacon + * and Probe Response frames internally: %NL80211_ATTR_SSID, + * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, + * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, + * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, + * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_IE, %NL80211_ATTR_IE_PROBE_RESP, + * %NL80211_ATTR_IE_ASSOC_RESP. + * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface, + * parameters are like for %NL80211_CMD_SET_BEACON. + * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it + * + * @NL80211_CMD_GET_STATION: Get station attributes for station identified by + * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_STATION: Set station attributes for station identified by + * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_STATION: Add a station with given attributes to the + * the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC + * or, if no MAC address given, all stations, on the interface identified + * by %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by + * %NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP. + * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by + * %NL80211_ATTR_MAC. + * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the + * the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC + * or, if no MAC address given, all mesh paths, on the interface identified + * by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by + * %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set + * regulatory domain. + * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command + * after being queried by the kernel. CRDA replies by sending a regulatory + * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our + * current alpha2 if it found a match. It also provides + * NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each + * regulatory rule is a nested set of attributes given by + * %NL80211_ATTR_REG_RULE_FREQ_[START|END] and + * %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by + * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and + * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP. + * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain + * to the specified ISO/IEC 3166-1 alpha2 country code. The core will + * store this as a valid request and then query userspace for it. + * + * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MESH_CONFIG: Set mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The + * interface is identified with %NL80211_ATTR_IFINDEX and the management + * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be + * added to the end of the specified management frame is specified with + * %NL80211_ATTR_IE. If the command succeeds, the requested data will be + * added to all specified management frames generated by + * kernel/firmware/driver. + * Note: This command has been removed and it is only reserved at this + * point to avoid re-using existing command number. The functionality this + * command was planned for has been provided with cleaner design with the + * option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN, + * NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, + * NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE. + * + * @NL80211_CMD_GET_SCAN: get scan results + * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters + * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the + * probe requests at CCK rate or not. + * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to + * NL80211_CMD_GET_SCAN and on the "scan" multicast group) + * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, + * partial scan results may be available + * + * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain + * intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL. + * Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS) + * are passed, they are used in the probe requests. For + * broadcast, a broadcast SSID must be passed (ie. an empty + * string). If no SSID is passed, no probe requests are sent and + * a passive scan is performed. %NL80211_ATTR_SCAN_FREQUENCIES, + * if passed, define which channels should be scanned; if not + * passed, all channels allowed for the current regulatory domain + * are used. Extra IEs can also be passed from the userspace by + * using the %NL80211_ATTR_IE attribute. + * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT + * if scheduled scan is not running. + * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan + * results available. + * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has + * stopped. The driver may issue this event at any time during a + * scheduled scan. One reason for stopping the scan is if the hardware + * does not support starting an association or a normal scan while running + * a scheduled scan. This event is also sent when the + * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface + * is brought down while a scheduled scan was running. + * + * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation + * or noise level + * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to + * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) + * + * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain + * has been changed and provides details of the request information + * that caused the change such as who initiated the regulatory request + * (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx + * (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if + * the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or + * %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain + * set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is + * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on + * to (%NL80211_ATTR_REG_ALPHA2). + * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon + * has been found while world roaming thus enabling active scan or + * any mode of operation that initiates TX (beacons) on a channel + * where we would not have been able to do either before. As an example + * if you are world roaming (regulatory domain set to world or if your + * driver is using a custom world roaming regulatory domain) and while + * doing a passive scan on the 5 GHz band you find an AP there (if not + * on a DFS channel) you will now be able to actively scan for that AP + * or use AP mode on your card on that same channel. Note that this will + * never be used for channels 1-11 on the 2 GHz band as they are always + * enabled world wide. This beacon hint is only sent if your device had + * either disabled active scanning or beaconing on a channel. We send to + * userspace the wiphy on which we removed a restriction from + * (%NL80211_ATTR_WIPHY) and the channel on which this occurred + * before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER) + * the beacon hint was processed. + * + * @NL80211_CMD_AUTHENTICATE: authentication request and notification. + * This command is used both as a command (request to authenticate) and + * as an event on the "mlme" multicast group indicating completion of the + * authentication process. + * When used as a command, %NL80211_ATTR_IFINDEX is used to identify the + * interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and + * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify + * the SSID (mainly for association, but is included in authentication + * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used + * to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE + * is used to specify the authentication type. %NL80211_ATTR_IE is used to + * define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs) + * to be added to the frame. + * When used as an event, this reports reception of an Authentication + * frame in station and IBSS modes when the local MLME processed the + * frame, i.e., it was for the local STA and was received in correct + * state. This is similar to MLME-AUTHENTICATE.confirm primitive in the + * MLME SAP interface (kernel providing MLME, userspace SME). The + * included %NL80211_ATTR_FRAME attribute contains the management frame + * (including both the header and frame body, but not FCS). This event is + * also used to indicate if the authentication attempt timed out. In that + * case the %NL80211_ATTR_FRAME attribute is replaced with a + * %NL80211_ATTR_TIMED_OUT flag (and %NL80211_ATTR_MAC to indicate which + * pending authentication timed out). + * @NL80211_CMD_ASSOCIATE: association request and notification; like + * NL80211_CMD_AUTHENTICATE but for Association and Reassociation + * (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request, + * MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). + * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like + * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to + * MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication + * primitives). + * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like + * NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to + * MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives). + * + * @NL80211_CMD_MICHAEL_MIC_FAILURE: notification of a locally detected Michael + * MIC (part of TKIP) failure; sent on the "mlme" multicast group; the + * event includes %NL80211_ATTR_MAC to describe the source MAC address of + * the frame with invalid MIC, %NL80211_ATTR_KEY_TYPE to show the key + * type, %NL80211_ATTR_KEY_IDX to indicate the key identifier, and + * %NL80211_ATTR_KEY_SEQ to indicate the TSC value of the frame; this + * event matches with MLME-MICHAELMICFAILURE.indication() primitive + * + * @NL80211_CMD_JOIN_IBSS: Join a new IBSS -- given at least an SSID and a + * FREQ attribute (for the initial frequency if no peer can be found) + * and optionally a MAC (as BSSID) and FREQ_FIXED attribute if those + * should be fixed rather than automatically determined. Can only be + * executed on a network interface that is UP, and fixed BSSID/FREQ + * may be rejected. Another optional parameter is the beacon interval, + * given in the %NL80211_ATTR_BEACON_INTERVAL attribute, which if not + * given defaults to 100 TU (102.4ms). + * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is + * determined by the network interface. + * + * @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute + * to identify the device, and the TESTDATA blob attribute to pass through + * to the driver. + * + * @NL80211_CMD_CONNECT: connection request and notification; this command + * requests to connect to a specified network but without separating + * auth and assoc steps. For this, you need to specify the SSID in a + * %NL80211_ATTR_SSID attribute, and can optionally specify the association + * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC, + * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, + * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and + * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT. + * Background scan period can optionally be + * specified in %NL80211_ATTR_BG_SCAN_PERIOD, + * if not specified default background scan configuration + * in driver is used and if period value is 0, bg scan will be disabled. + * This attribute is ignored if driver does not support roam scan. + * It is also sent as an event, with the BSSID and response IEs when the + * connection is established or failed to be established. This can be + * determined by the STATUS_CODE attribute. + * @NL80211_CMD_ROAM: request that the card roam (currently not implemented), + * sent as an event when the card/driver roamed by itself. + * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify + * userspace that a connection was dropped by the AP or due to other + * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and + * %NL80211_ATTR_REASON_CODE attributes are used. + * + * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices + * associated with this wiphy must be down and will follow. + * + * @NL80211_CMD_REMAIN_ON_CHANNEL: Request to remain awake on the specified + * channel for the specified amount of time. This can be used to do + * off-channel operations like transmit a Public Action frame and wait for + * a response while being associated to an AP on another channel. + * %NL80211_ATTR_IFINDEX is used to specify which interface (and thus + * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the + * frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be + * optionally used to specify additional channel parameters. + * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds + * to remain on the channel. This command is also used as an event to + * notify when the requested duration starts (it may take a while for the + * driver to schedule this time due to other concurrent needs for the + * radio). + * When called, this operation returns a cookie (%NL80211_ATTR_COOKIE) + * that will be included with any events pertaining to this request; + * the cookie is also used to cancel the request. + * @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a + * pending remain-on-channel duration if the desired operation has been + * completed prior to expiration of the originally requested duration. + * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the + * radio. The %NL80211_ATTR_COOKIE attribute must be given as well to + * uniquely identify the request. + * This command is also used as an event to notify when a requested + * remain-on-channel duration has expired. + * + * @NL80211_CMD_SET_TX_BITRATE_MASK: Set the mask of rates to be used in TX + * rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface + * and @NL80211_ATTR_TX_RATES the set of allowed rates. + * + * @NL80211_CMD_REGISTER_FRAME: Register for receiving certain mgmt frames + * (via @NL80211_CMD_FRAME) for processing in userspace. This command + * requires an interface index, a frame type attribute (optional for + * backward compatibility reasons, if not given assumes action frames) + * and a match attribute containing the first few bytes of the frame + * that should match, e.g. a single byte for only a category match or + * four bytes for vendor frames including the OUI. The registration + * cannot be dropped, but is removed automatically when the netlink + * socket is closed. Multiple registrations can be made. + * @NL80211_CMD_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for + * backward compatibility + * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This + * command is used both as a request to transmit a management frame and + * as an event indicating reception of a frame that was not processed in + * kernel code, but is for us (i.e., which may need to be processed in a + * user space application). %NL80211_ATTR_FRAME is used to specify the + * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and + * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on + * which channel the frame is to be transmitted or was received. If this + * channel is not the current channel (remain-on-channel or the + * operational channel) the device will switch to the given channel and + * transmit the frame, optionally waiting for a response for the time + * specified using %NL80211_ATTR_DURATION. When called, this operation + * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the + * TX status event pertaining to the TX request. + * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the + * management frames at CCK rate or not in 2GHz band. + * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this + * command may be used with the corresponding cookie to cancel the wait + * time if it is known that it is no longer necessary. + * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility. + * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame + * transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies + * the TX command and %NL80211_ATTR_FRAME includes the contents of the + * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged + * the frame. + * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for + * backward compatibility. + * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command + * is used to configure connection quality monitoring notification trigger + * levels. + * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This + * command is used as an event to indicate the that a trigger level was + * reached. + * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ + * and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed + * by %NL80211_ATTR_IFINDEX) shall operate on. + * In case multiple channels are supported by the device, the mechanism + * with which it switches channels is implementation-defined. + * When a monitor interface is given, it can only switch channel while + * no other interfaces are operating to avoid disturbing the operation + * of any other interfaces, and other interfaces will again take + * precedence when they are used. + * + * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface. + * + * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial + * mesh config parameters may be given. + * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the + * network is determined by the network interface. + * + * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame + * notification. This event is used to indicate that an unprotected + * deauthentication frame was dropped when MFP is in use. + * @NL80211_CMD_UNPROT_DISASSOCIATE: Unprotected disassociation frame + * notification. This event is used to indicate that an unprotected + * disassociation frame was dropped when MFP is in use. + * + * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a + * beacon or probe response from a compatible mesh peer. This is only + * sent while no station information (sta_info) exists for the new peer + * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH is set. On + * reception of this notification, userspace may decide to create a new + * station (@NL80211_CMD_NEW_STATION). To stop this notification from + * reoccurring, the userspace authentication daemon may want to create the + * new station with the AUTHENTICATED flag unset and maybe change it later + * depending on the authentication result. + * + * @NL80211_CMD_GET_WOWLAN: get Wake-on-Wireless-LAN (WoWLAN) settings. + * @NL80211_CMD_SET_WOWLAN: set Wake-on-Wireless-LAN (WoWLAN) settings. + * Since wireless is more complex than wired ethernet, it supports + * various triggers. These triggers can be configured through this + * command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For + * more background information, see + * http://wireless.kernel.org/en/users/Documentation/WoWLAN. + * + * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver + * the necessary information for supporting GTK rekey offload. This + * feature is typically used during WoWLAN. The configuration data + * is contained in %NL80211_ATTR_REKEY_DATA (which is nested and + * contains the data in sub-attributes). After rekeying happened, + * this command may also be sent by the driver as an MLME event to + * inform userspace of the new replay counter. + * + * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace + * of PMKSA caching dandidates. + * + * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). + * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. + * + * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP + * (or GO) interface (i.e. hostapd) to ask for unexpected frames to + * implement sending deauth to stations that send unexpected class 3 + * frames. Also used as the event sent by the kernel when such a frame + * is received. + * For the event, the %NL80211_ATTR_MAC attribute carries the TA and + * other attributes like the interface index are present. + * If used as the command it must have an interface index and you can + * only unsubscribe from the event by closing the socket. Subscription + * is also for %NL80211_CMD_UNEXPECTED_4ADDR_FRAME events. + * + * @NL80211_CMD_UNEXPECTED_4ADDR_FRAME: Sent as an event indicating that the + * associated station identified by %NL80211_ATTR_MAC sent a 4addr frame + * and wasn't already in a 4-addr VLAN. The event will be sent similarly + * to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener. + * + * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface + * by sending a null data frame to it and reporting when the frame is + * acknowleged. This is used to allow timing out inactive clients. Uses + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a + * direct reply with an %NL80211_ATTR_COOKIE that is later used to match + * up the event with the request. The event includes the same data and + * has %NL80211_ATTR_ACK set if the frame was ACKed. + * + * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from + * other BSSes when any interfaces are in AP mode. This helps implement + * OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME + * messages. Note that per PHY only one application may register. + * + * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels + * independently of the userspace SME, send this event indicating + * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ with + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE. + * + * @NL80211_CMD_CONN_FAILED: connection request to an AP failed; used to + * notify userspace that AP has rejected the connection request from a + * station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON + * is used for this. + * + * @NL80211_CMD_BTCOEX: Send BTCOEX command to firmware. This is + * used by the firmware to be aware of BT traffic and share radio + * between WiFi and BT. + * + * @NL80211_ATTR_STA_CAPA_REQ: Restrict only particular mode capable station + * to connect (applicable only in AP mode). For example, this can be used + * to allow only the remote station which supports 11G only mode to connect + * to the AP. Currently used with %NL80211_CMD_NEW_BEACON command, + * see &enum nl80211_sta_capab_req_options. + * + * @NL80211_CMD_SET_MAC_ACL: sets a list of mac addresses for access control. + * This is to be used with the drivers advertising the support of mac + * address based access control. The list of mac addresses defined by + * %NL80211_ATTR_MAC_ADDRS would replace any existing acl list in driver + * for a particular acl policy specified by %NL80211_ATTR_ACL_POLICY. + * When the passed list of mac address is empty for a particular acl + * policy, driver has to clear corresponding acl list. This command is + * used in AP/P2P GO mode. Driver has to make sure it's acl lists are + * cleared during %NL80211_CMD_START_AP and NL80211_CMD_STOP_AP. + * + * @NL80211_CMD_MAX: highest used command number + * @__NL80211_CMD_AFTER_LAST: internal use + */ +enum nl80211_commands { +/* don't change the order or add anything between, this is ABI! */ + NL80211_CMD_UNSPEC, + + NL80211_CMD_GET_WIPHY, /* can dump */ + NL80211_CMD_SET_WIPHY, + NL80211_CMD_NEW_WIPHY, + NL80211_CMD_DEL_WIPHY, + + NL80211_CMD_GET_INTERFACE, /* can dump */ + NL80211_CMD_SET_INTERFACE, + NL80211_CMD_NEW_INTERFACE, + NL80211_CMD_DEL_INTERFACE, + + NL80211_CMD_GET_KEY, + NL80211_CMD_SET_KEY, + NL80211_CMD_NEW_KEY, + NL80211_CMD_DEL_KEY, + + NL80211_CMD_GET_BEACON, + NL80211_CMD_SET_BEACON, + NL80211_CMD_NEW_BEACON, + NL80211_CMD_DEL_BEACON, + + NL80211_CMD_GET_STATION, + NL80211_CMD_SET_STATION, + NL80211_CMD_NEW_STATION, + NL80211_CMD_DEL_STATION, + + NL80211_CMD_GET_MPATH, + NL80211_CMD_SET_MPATH, + NL80211_CMD_NEW_MPATH, + NL80211_CMD_DEL_MPATH, + + NL80211_CMD_SET_BSS, + + NL80211_CMD_SET_REG, + NL80211_CMD_REQ_SET_REG, + + NL80211_CMD_GET_MESH_CONFIG, + NL80211_CMD_SET_MESH_CONFIG, + + NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */, + + NL80211_CMD_GET_REG, + + NL80211_CMD_GET_SCAN, + NL80211_CMD_TRIGGER_SCAN, + NL80211_CMD_NEW_SCAN_RESULTS, + NL80211_CMD_SCAN_ABORTED, + + NL80211_CMD_REG_CHANGE, + + NL80211_CMD_AUTHENTICATE, + NL80211_CMD_ASSOCIATE, + NL80211_CMD_DEAUTHENTICATE, + NL80211_CMD_DISASSOCIATE, + + NL80211_CMD_MICHAEL_MIC_FAILURE, + + NL80211_CMD_REG_BEACON_HINT, + + NL80211_CMD_JOIN_IBSS, + NL80211_CMD_LEAVE_IBSS, + + NL80211_CMD_TESTMODE, + + NL80211_CMD_CONNECT, + NL80211_CMD_ROAM, + NL80211_CMD_DISCONNECT, + + NL80211_CMD_SET_WIPHY_NETNS, + + NL80211_CMD_GET_SURVEY, + NL80211_CMD_NEW_SURVEY_RESULTS, + + NL80211_CMD_SET_PMKSA, + NL80211_CMD_DEL_PMKSA, + NL80211_CMD_FLUSH_PMKSA, + + NL80211_CMD_REMAIN_ON_CHANNEL, + NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, + + NL80211_CMD_SET_TX_BITRATE_MASK, + + NL80211_CMD_REGISTER_FRAME, + NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME, + NL80211_CMD_FRAME, + NL80211_CMD_ACTION = NL80211_CMD_FRAME, + NL80211_CMD_FRAME_TX_STATUS, + NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS, + + NL80211_CMD_SET_POWER_SAVE, + NL80211_CMD_GET_POWER_SAVE, + + NL80211_CMD_SET_CQM, + NL80211_CMD_NOTIFY_CQM, + + NL80211_CMD_SET_CHANNEL, + NL80211_CMD_SET_WDS_PEER, + + NL80211_CMD_FRAME_WAIT_CANCEL, + + NL80211_CMD_JOIN_MESH, + NL80211_CMD_LEAVE_MESH, + + NL80211_CMD_UNPROT_DEAUTHENTICATE, + NL80211_CMD_UNPROT_DISASSOCIATE, + + NL80211_CMD_NEW_PEER_CANDIDATE, + + NL80211_CMD_GET_WOWLAN, + NL80211_CMD_SET_WOWLAN, + + NL80211_CMD_START_SCHED_SCAN, + NL80211_CMD_STOP_SCHED_SCAN, + NL80211_CMD_SCHED_SCAN_RESULTS, + NL80211_CMD_SCHED_SCAN_STOPPED, + + NL80211_CMD_SET_REKEY_OFFLOAD, + + NL80211_CMD_PMKSA_CANDIDATE, + + NL80211_CMD_TDLS_OPER, + NL80211_CMD_TDLS_MGMT, + + NL80211_CMD_UNEXPECTED_FRAME, + + NL80211_CMD_PROBE_CLIENT, + + NL80211_CMD_REGISTER_BEACONS, + + NL80211_CMD_UNEXPECTED_4ADDR_FRAME, + + NL80211_CMD_SET_NOACK_MAP, /* just to maintain ABI with CH_SWITCH_NOTIFY */ + + NL80211_CMD_CH_SWITCH_NOTIFY, + + NL80211_CMD_CONN_FAILED, + + NL80211_CMD_SET_MAC_ACL, + + NL80211_CMD_BTCOEX, + + /* add new commands above here */ + + /* used to define NL80211_CMD_MAX below */ + __NL80211_CMD_AFTER_LAST, + NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 +}; + +/* + * Allow user space programs to use #ifdef on new commands by defining them + * here + */ +#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS +#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE +#define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE +#define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE +#define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE +#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE +#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE +#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT + +#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS + +/* source-level API compatibility */ +#define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG +#define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG +#define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE + +/** + * enum nl80211_attrs - nl80211 netlink attributes + * + * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors + * + * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf. + * /sys/class/ieee80211//index + * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) + * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters + * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz + * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ + * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included): + * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including + * this attribute) + * NL80211_CHAN_HT20 = HT20 only + * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel + * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel + * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is + * less than or equal to the RTS threshold; allowed range: 1..255; + * dot11ShortRetryLimit; u8 + * @NL80211_ATTR_WIPHY_RETRY_LONG: TX retry limit for frames whose length is + * greater than the RTS threshold; allowed range: 1..255; + * dot11ShortLongLimit; u8 + * @NL80211_ATTR_WIPHY_FRAG_THRESHOLD: fragmentation threshold, i.e., maximum + * length in octets for frames; allowed range: 256..8000, disable + * fragmentation with (u32)-1; dot11FragmentationThreshold; u32 + * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length + * larger than or equal to this use RTS/CTS handshake); allowed range: + * 0..65536, disable with (u32)-1; dot11RTSThreshold; u32 + * @NL80211_ATTR_WIPHY_COVERAGE_CLASS: Coverage Class as defined by IEEE 802.11 + * section 7.3.2.9; dot11CoverageClass; u8 + * + * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on + * @NL80211_ATTR_IFNAME: network interface name + * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype + * + * @NL80211_ATTR_MAC: MAC address (various uses) + * + * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of + * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC + * keys + * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3) + * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 + * section 7.3.2.25.1, e.g. 0x000FAC04) + * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and + * CCMP keys, each six bytes in little endian + * + * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU + * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing + * @NL80211_ATTR_BEACON_HEAD: portion of the beacon before the TIM IE + * @NL80211_ATTR_BEACON_TAIL: portion of the beacon after the TIM IE + * + * @NL80211_ATTR_STA_AID: Association ID for the station (u16) + * @NL80211_ATTR_STA_FLAGS: flags, nested element with NLA_FLAG attributes of + * &enum nl80211_sta_flags (deprecated, use %NL80211_ATTR_STA_FLAGS2) + * @NL80211_ATTR_STA_LISTEN_INTERVAL: listen interval as defined by + * IEEE 802.11 7.3.1.6 (u16). + * @NL80211_ATTR_STA_SUPPORTED_RATES: supported rates, array of supported + * rates as defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). + * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station + * to, or the AP interface the station was originally added to to. + * @NL80211_ATTR_STA_INFO: information about a station, part of station info + * given for %NL80211_CMD_GET_STATION, nested attribute containing + * info as possible, see &enum nl80211_sta_info. + * + * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands, + * consisting of a nested array. + * + * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes). + * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link. + * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. + * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path + * info given for %NL80211_CMD_GET_MPATH, nested attribute described at + * &enum nl80211_mpath_info. + * + * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of + * &enum nl80211_mntr_flags. + * + * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the + * current regulatory domain should be set to or is already set to. + * For example, 'CR', for Costa Rica. This attribute is used by the kernel + * to query the CRDA to retrieve one regulatory domain. This attribute can + * also be used by userspace to query the kernel for the currently set + * regulatory domain. We chose an alpha2 as that is also used by the + * IEEE-802.11d country information element to identify a country. + * Users can also simply ask the wireless core to set regulatory domain + * to a specific alpha2. + * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory + * rules. + * + * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic + * rates in format defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). + * + * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION) + * + * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all + * supported interface types, each a flag attribute with the number + * of the interface mode. + * + * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for + * %NL80211_CMD_SET_MGMT_EXTRA_IE. + * + * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with + * %NL80211_CMD_SET_MGMT_EXTRA_IE). + * + * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with + * a single scan request, a wiphy attribute. + * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: number of SSIDs you can + * scan with a single scheduled scan request, a wiphy attribute. + * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements + * that can be added to a scan request + * @NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: maximum length of information + * elements that can be added to a scheduled scan request + * @NL80211_ATTR_MAX_MATCH_SETS: maximum number of sets that can be + * used with @NL80211_ATTR_SCHED_SCAN_MATCH, a wiphy attribute. + * + * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) + * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive + * scanning and include a zero-length SSID (wildcard) for wildcard scan + * @NL80211_ATTR_BSS: scan result BSS + * + * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain + * currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_* + * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently + * set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*) + * + * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies + * an array of command numbers (i.e. a mapping index to command number) + * that the driver for the given wiphy supports. + * + * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header + * and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and + * NL80211_CMD_ASSOCIATE events + * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets) + * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type, + * represented as a u32 + * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and + * %NL80211_CMD_DISASSOCIATE, u16 + * + * @NL80211_ATTR_KEY_TYPE: Key Type, see &enum nl80211_key_type, represented as + * a u32 + * + * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change + * due to considerations from a beacon hint. This attribute reflects + * the state of the channel _before_ the beacon hint processing. This + * attributes consists of a nested attribute containing + * NL80211_FREQUENCY_ATTR_* + * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change + * due to considerations from a beacon hint. This attribute reflects + * the state of the channel _after_ the beacon hint processing. This + * attributes consists of a nested attribute containing + * NL80211_FREQUENCY_ATTR_* + * + * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported + * cipher suites + * + * @NL80211_ATTR_FREQ_FIXED: a flag indicating the IBSS should not try to look + * for other networks on different channels + * + * @NL80211_ATTR_TIMED_OUT: a flag indicating than an operation timed out; this + * is used, e.g., with %NL80211_CMD_AUTHENTICATE event + * + * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is + * used for the association (&enum nl80211_mfp, represented as a u32); + * this attribute can be used + * with %NL80211_CMD_ASSOCIATE request + * + * @NL80211_ATTR_STA_FLAGS2: Attribute containing a + * &struct nl80211_sta_flag_update. + * + * @NL80211_ATTR_CONTROL_PORT: A flag indicating whether user space controls + * IEEE 802.1X port, i.e., sets/clears %NL80211_STA_FLAG_AUTHORIZED, in + * station mode. If the flag is included in %NL80211_CMD_ASSOCIATE + * request, the driver will assume that the port is unauthorized until + * authorized by user space. Otherwise, port is marked authorized by + * default in station mode. + * @NL80211_ATTR_CONTROL_PORT_ETHERTYPE: A 16-bit value indicating the + * ethertype that will be used for key negotiation. It can be + * specified with the associate and connect commands. If it is not + * specified, the value defaults to 0x888E (PAE, 802.1X). This + * attribute is also used as a flag in the wiphy information to + * indicate that protocols other than PAE are supported. + * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with + * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom + * ethertype frames used for key negotiation must not be encrypted. + * + * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. + * We recommend using nested, driver-specific attributes within this. + * + * @NL80211_ATTR_DISCONNECTED_BY_AP: A flag indicating that the DISCONNECT + * event was due to the AP disconnecting the station, and not due to + * a local disconnect request. + * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT + * event (u16) + * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating + * that protected APs should be used. This is also used with NEW_BEACON to + * indicate that the BSS is to use protection. + * + * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON + * to indicate which unicast key ciphers will be used with the connection + * (an array of u32). + * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to + * indicate which group key cipher will be used with the connection (a + * u32). + * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to + * indicate which WPA version(s) the AP we want to associate with is using + * (a u32 with flags from &enum nl80211_wpa_versions). + * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to + * indicate which key management algorithm(s) to use (an array of u32). + * + * @NL80211_ATTR_REQ_IE: (Re)association request information elements as + * sent out by the card, for ROAM and successful CONNECT events. + * @NL80211_ATTR_RESP_IE: (Re)association response information elements as + * sent by peer, for ROAM and successful CONNECT events. + * + * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE + * commands to specify using a reassociate frame + * + * @NL80211_ATTR_KEY: key information in a nested attribute with + * %NL80211_KEY_* sub-attributes + * @NL80211_ATTR_KEYS: array of keys for static WEP keys for connect() + * and join_ibss(), key information is in a nested attribute each + * with %NL80211_KEY_* sub-attributes + * + * @NL80211_ATTR_PID: Process ID of a network namespace. + * + * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for + * dumps. This number increases whenever the object list being + * dumped changes, and as such userspace can verify that it has + * obtained a complete and consistent snapshot by verifying that + * all dump messages contain the same generation number. If it + * changed then the list changed and the dump should be repeated + * completely from scratch. + * + * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface + * + * @NL80211_ATTR_SURVEY_INFO: survey information about a channel, part of + * the survey response for %NL80211_CMD_GET_SURVEY, nested attribute + * containing info as possible, see &enum survey_info. + * + * @NL80211_ATTR_PMKID: PMK material for PMKSA caching. + * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can + * cache, a wiphy attribute. + * + * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32. + * @NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION: Device attribute that + * specifies the maximum duration that can be requested with the + * remain-on-channel operation, in milliseconds, u32. + * + * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects. + * + * @NL80211_ATTR_TX_RATES: Nested set of attributes + * (enum nl80211_tx_rate_attributes) describing TX rates per band. The + * enum nl80211_band value is used as the index (nla_type() of the nested + * data. If a band is not included, it will be configured to allow all + * rates based on negotiated supported rates information. This attribute + * is used with %NL80211_CMD_SET_TX_BITRATE_MASK. + * + * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain + * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME. + * @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the + * @NL80211_CMD_REGISTER_FRAME command. + * @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a + * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing + * information about which frame types can be transmitted with + * %NL80211_CMD_FRAME. + * @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a + * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing + * information about which frame types can be registered for RX. + * + * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was + * acknowledged by the recipient. + * + * @NL80211_ATTR_CQM: connection quality monitor configuration in a + * nested attribute with %NL80211_ATTR_CQM_* sub-attributes. + * + * @NL80211_ATTR_LOCAL_STATE_CHANGE: Flag attribute to indicate that a command + * is requesting a local authentication/association state change without + * invoking actual management frame exchange. This can be used with + * NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE, + * NL80211_CMD_DISASSOCIATE. + * + * @NL80211_ATTR_AP_ISOLATE: (AP mode) Do not forward traffic between stations + * connected to this BSS. + * + * @NL80211_ATTR_WIPHY_TX_POWER_SETTING: Transmit power setting type. See + * &enum nl80211_tx_power_setting for possible values. + * @NL80211_ATTR_WIPHY_TX_POWER_LEVEL: Transmit power level in signed mBm units. + * This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING + * for non-automatic settings. + * + * @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly + * means support for per-station GTKs. + * + * @NL80211_ATTR_WIPHY_ANTENNA_TX: Bitmap of allowed antennas for transmitting. + * This can be used to mask out antennas which are not attached or should + * not be used for transmitting. If an antenna is not selected in this + * bitmap the hardware is not allowed to transmit on this antenna. + * + * Each bit represents one antenna, starting with antenna 1 at the first + * bit. Depending on which antennas are selected in the bitmap, 802.11n + * drivers can derive which chainmasks to use (if all antennas belonging to + * a particular chain are disabled this chain should be disabled) and if + * a chain has diversity antennas wether diversity should be used or not. + * HT capabilities (STBC, TX Beamforming, Antenna selection) can be + * derived from the available chains after applying the antenna mask. + * Non-802.11n drivers can derive wether to use diversity or not. + * Drivers may reject configurations or RX/TX mask combinations they cannot + * support by returning -EINVAL. + * + * @NL80211_ATTR_WIPHY_ANTENNA_RX: Bitmap of allowed antennas for receiving. + * This can be used to mask out antennas which are not attached or should + * not be used for receiving. If an antenna is not selected in this bitmap + * the hardware should not be configured to receive on this antenna. + * For a more detailed description see @NL80211_ATTR_WIPHY_ANTENNA_TX. + * + * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: Bitmap of antennas which are available + * for configuration as TX antennas via the above parameters. + * + * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available + * for configuration as RX antennas via the above parameters. + * + * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS + * + * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be + * transmitted on another channel when the channel given doesn't match + * the current channel. If the current channel doesn't match and this + * flag isn't set, the frame will be rejected. This is also used as an + * nl80211 capability flag. + * + * @NL80211_ATTR_BSS_HTOPMODE: HT operation mode (u16) + * + * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags + * attributes, specifying what a key should be set as default as. + * See &enum nl80211_key_default_types. + * + * @NL80211_ATTR_MESH_SETUP: Optional mesh setup parameters. These cannot be + * changed once the mesh is active. + * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute + * containing attributes from &enum nl80211_meshconf_params. + * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver + * allows auth frames in a mesh to be passed to userspace for processing via + * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag. + * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as + * defined in &enum nl80211_plink_state. Used when userspace is + * driving the peer link management state machine. + * @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled. + * + * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy + * capabilities, the supported WoWLAN triggers + * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to + * indicate which WoW triggers should be enabled. This is also + * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN + * triggers. + + * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan + * cycles, in msecs. + + * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more + * sets of attributes to match during scheduled scans. Only BSSs + * that match any of the sets will be reported. These are + * pass-thru filter rules. + * For a match to succeed, the BSS must match all attributes of a + * set. Since not every hardware supports matching all types of + * attributes, there is no guarantee that the reported BSSs are + * fully complying with the match sets and userspace needs to be + * able to ignore them by itself. + * Thus, the implementation is somewhat hardware-dependent, but + * this is only an optimization and the userspace application + * needs to handle all the non-filtered results anyway. + * If the match attributes don't make sense when combined with + * the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID + * is included in the probe request, but the match attributes + * will never let it go through), -EINVAL may be returned. + * If ommited, no filtering is done. + * + * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported + * interface combinations. In each nested item, it contains attributes + * defined in &enum nl80211_if_combination_attrs. + * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like + * %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that + * are managed in software: interfaces of these types aren't subject to + * any restrictions in their number or combinations. + * + * @%NL80211_ATTR_REKEY_DATA: nested attribute containing the information + * necessary for GTK rekeying in the device, see &enum nl80211_rekey_data. + * + * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan, + * nested array attribute containing an entry for each band, with the entry + * being a list of supported rates as defined by IEEE 802.11 7.3.2.2 but + * without the length restriction (at most %NL80211_MAX_SUPP_RATES). + * + * @NL80211_ATTR_HIDDEN_SSID: indicates whether SSID is to be hidden from Beacon + * and Probe Response (when response to wildcard Probe Request); see + * &enum nl80211_hidden_ssid, represented as a u32 + * + * @NL80211_ATTR_IE_PROBE_RESP: Information element(s) for Probe Response frame. + * This is used with %NL80211_CMD_NEW_BEACON and %NL80211_CMD_SET_BEACON to + * provide extra IEs (e.g., WPS/P2P IE) into Probe Response frames when the + * driver (or firmware) replies to Probe Request frames. + * @NL80211_ATTR_IE_ASSOC_RESP: Information element(s) for (Re)Association + * Response frames. This is used with %NL80211_CMD_NEW_BEACON and + * %NL80211_CMD_SET_BEACON to provide extra IEs (e.g., WPS/P2P IE) into + * (Re)Association Response frames when the driver (or firmware) replies to + * (Re)Association Request frames. + * + * @NL80211_ATTR_STA_WME: Nested attribute containing the wme configuration + * of the station, see &enum nl80211_sta_wme_attr. + * @NL80211_ATTR_SUPPORT_AP_UAPSD: the device supports uapsd when working + * as AP. + * + * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of + * roaming to another AP in the same ESS if the signal lever is low. + * + * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching + * candidate information, see &enum nl80211_pmksa_candidate_attr. + * + * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not + * for management frames transmission. In order to avoid p2p probe/action + * frames are being transmitted at CCK rate in 2GHz band, the user space + * applications use this attribute. + * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and + * %NL80211_CMD_FRAME commands. + * + * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup + * request, link setup confirm, link teardown, etc.). Values are + * described in the TDLS (802.11z) specification. + * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a + * TDLS conversation between two devices. + * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see + * &enum nl80211_tdls_operation, represented as a u8. + * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate + * as a TDLS peer sta. + * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown + * procedures should be performed by sending TDLS packets via + * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be + * used for asking the driver to perform a TDLS operation. + * + * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices + * that have AP support to indicate that they have the AP SME integrated + * with support for the features listed in this attribute, see + * &enum nl80211_ap_sme_features. + * + * @NL80211_ATTR_DONT_WAIT_FOR_ACK: Used with %NL80211_CMD_FRAME, this tells + * the driver to not wait for an acknowledgement. Note that due to this, + * it will also not give a status callback nor return a cookie. This is + * mostly useful for probe responses to save airtime. + * + * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from + * &enum nl80211_feature_flags and is advertised in wiphy information. + * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe + * + * requests while operating in AP-mode. + * This attribute holds a bitmap of the supported protocols for + * offloading (see &enum nl80211_probe_resp_offload_support_attr). + * + * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire + * probe-response frame. The DA field in the 802.11 header is zero-ed out, + * to be filled by the FW. + * + * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds + * or 0 to disable background scan. + * + * @NL80211_ATTR_WDEV: wireless device identifier, used for pseudo-devices + * that don't have a netdev (u64) + * + * @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from + * userspace. If unset it is assumed the hint comes directly from + * a user. If set code could specify exactly what type of source + * was used to provide the hint. For the different types of + * allowed user regulatory hints see nl80211_user_reg_hint_type. + * + * @NL80211_ATTR_CONN_FAILED_REASON: The reason for which AP has rejected + * the connection request from a station. nl80211_connect_failed_reason + * enum has different reasons of connection failure. + * + * @NL80211_ATTR_BTCOEX_DATA: BT coex wmi command. + * + * @NL80211_ATTR_ACS: Enable automatic channel selection by the driver + * for AP/GO mode. + * + * @NL80211_ATTR_MAC_ACL: u8 attribute to enable or disable mac address + * based access control in driver, needs to be used with the drivers + * which advertise this support. + * + * @NL80211_ATTR_MAC_ADDRS: Nested attribute with mac addresses used for ACL. + * + * @NL80211_ATTR_ACL_POLICY: policy of access control, + * see &enum nl80211_acl_policy_attr. + * + * @NL80211_ATTR_MAX: highest attribute number currently defined + * @__NL80211_ATTR_AFTER_LAST: internal use + */ +enum nl80211_attrs { +/* don't change the order or add anything between, this is ABI! */ + NL80211_ATTR_UNSPEC, + + NL80211_ATTR_WIPHY, + NL80211_ATTR_WIPHY_NAME, + + NL80211_ATTR_IFINDEX, + NL80211_ATTR_IFNAME, + NL80211_ATTR_IFTYPE, + + NL80211_ATTR_MAC, + + NL80211_ATTR_KEY_DATA, + NL80211_ATTR_KEY_IDX, + NL80211_ATTR_KEY_CIPHER, + NL80211_ATTR_KEY_SEQ, + NL80211_ATTR_KEY_DEFAULT, + + NL80211_ATTR_BEACON_INTERVAL, + NL80211_ATTR_DTIM_PERIOD, + NL80211_ATTR_BEACON_HEAD, + NL80211_ATTR_BEACON_TAIL, + + NL80211_ATTR_STA_AID, + NL80211_ATTR_STA_FLAGS, + NL80211_ATTR_STA_LISTEN_INTERVAL, + NL80211_ATTR_STA_SUPPORTED_RATES, + NL80211_ATTR_STA_VLAN, + NL80211_ATTR_STA_INFO, + + NL80211_ATTR_WIPHY_BANDS, + + NL80211_ATTR_MNTR_FLAGS, + + NL80211_ATTR_MESH_ID, + NL80211_ATTR_STA_PLINK_ACTION, + NL80211_ATTR_MPATH_NEXT_HOP, + NL80211_ATTR_MPATH_INFO, + + NL80211_ATTR_BSS_CTS_PROT, + NL80211_ATTR_BSS_SHORT_PREAMBLE, + NL80211_ATTR_BSS_SHORT_SLOT_TIME, + + NL80211_ATTR_HT_CAPABILITY, + + NL80211_ATTR_SUPPORTED_IFTYPES, + + NL80211_ATTR_REG_ALPHA2, + NL80211_ATTR_REG_RULES, + + NL80211_ATTR_MESH_CONFIG, + + NL80211_ATTR_BSS_BASIC_RATES, + + NL80211_ATTR_WIPHY_TXQ_PARAMS, + NL80211_ATTR_WIPHY_FREQ, + NL80211_ATTR_WIPHY_CHANNEL_TYPE, + + NL80211_ATTR_KEY_DEFAULT_MGMT, + + NL80211_ATTR_MGMT_SUBTYPE, + NL80211_ATTR_IE, + + NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + + NL80211_ATTR_SCAN_FREQUENCIES, + NL80211_ATTR_SCAN_SSIDS, + NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */ + NL80211_ATTR_BSS, + + NL80211_ATTR_REG_INITIATOR, + NL80211_ATTR_REG_TYPE, + + NL80211_ATTR_SUPPORTED_COMMANDS, + + NL80211_ATTR_FRAME, + NL80211_ATTR_SSID, + NL80211_ATTR_AUTH_TYPE, + NL80211_ATTR_REASON_CODE, + + NL80211_ATTR_KEY_TYPE, + + NL80211_ATTR_MAX_SCAN_IE_LEN, + NL80211_ATTR_CIPHER_SUITES, + + NL80211_ATTR_FREQ_BEFORE, + NL80211_ATTR_FREQ_AFTER, + + NL80211_ATTR_FREQ_FIXED, + + + NL80211_ATTR_WIPHY_RETRY_SHORT, + NL80211_ATTR_WIPHY_RETRY_LONG, + NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + NL80211_ATTR_WIPHY_RTS_THRESHOLD, + + NL80211_ATTR_TIMED_OUT, + + NL80211_ATTR_USE_MFP, + + NL80211_ATTR_STA_FLAGS2, + + NL80211_ATTR_CONTROL_PORT, + + NL80211_ATTR_TESTDATA, + + NL80211_ATTR_PRIVACY, + + NL80211_ATTR_DISCONNECTED_BY_AP, + NL80211_ATTR_STATUS_CODE, + + NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + NL80211_ATTR_CIPHER_SUITE_GROUP, + NL80211_ATTR_WPA_VERSIONS, + NL80211_ATTR_AKM_SUITES, + + NL80211_ATTR_REQ_IE, + NL80211_ATTR_RESP_IE, + + NL80211_ATTR_PREV_BSSID, + + NL80211_ATTR_KEY, + NL80211_ATTR_KEYS, + + NL80211_ATTR_PID, + + NL80211_ATTR_4ADDR, + + NL80211_ATTR_SURVEY_INFO, + + NL80211_ATTR_PMKID, + NL80211_ATTR_MAX_NUM_PMKIDS, + + NL80211_ATTR_DURATION, + + NL80211_ATTR_COOKIE, + + NL80211_ATTR_WIPHY_COVERAGE_CLASS, + + NL80211_ATTR_TX_RATES, + + NL80211_ATTR_FRAME_MATCH, + + NL80211_ATTR_ACK, + + NL80211_ATTR_PS_STATE, + + NL80211_ATTR_CQM, + + NL80211_ATTR_LOCAL_STATE_CHANGE, + + NL80211_ATTR_AP_ISOLATE, + + NL80211_ATTR_WIPHY_TX_POWER_SETTING, + NL80211_ATTR_WIPHY_TX_POWER_LEVEL, + + NL80211_ATTR_TX_FRAME_TYPES, + NL80211_ATTR_RX_FRAME_TYPES, + NL80211_ATTR_FRAME_TYPE, + + NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, + + NL80211_ATTR_SUPPORT_IBSS_RSN, + + NL80211_ATTR_WIPHY_ANTENNA_TX, + NL80211_ATTR_WIPHY_ANTENNA_RX, + + NL80211_ATTR_MCAST_RATE, + + NL80211_ATTR_OFFCHANNEL_TX_OK, + + NL80211_ATTR_BSS_HT_OPMODE, + + NL80211_ATTR_KEY_DEFAULT_TYPES, + + NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, + + NL80211_ATTR_MESH_SETUP, + + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, + + NL80211_ATTR_SUPPORT_MESH_AUTH, + NL80211_ATTR_STA_PLINK_STATE, + + NL80211_ATTR_WOWLAN_TRIGGERS, + NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, + + NL80211_ATTR_SCHED_SCAN_INTERVAL, + + NL80211_ATTR_INTERFACE_COMBINATIONS, + NL80211_ATTR_SOFTWARE_IFTYPES, + + NL80211_ATTR_REKEY_DATA, + + NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, + NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, + + NL80211_ATTR_SCAN_SUPP_RATES, + + NL80211_ATTR_HIDDEN_SSID, + + NL80211_ATTR_IE_PROBE_RESP, + NL80211_ATTR_IE_ASSOC_RESP, + + NL80211_ATTR_STA_WME, + NL80211_ATTR_SUPPORT_AP_UAPSD, + + NL80211_ATTR_ROAM_SUPPORT, + + NL80211_ATTR_SCHED_SCAN_MATCH, + NL80211_ATTR_MAX_MATCH_SETS, + + NL80211_ATTR_PMKSA_CANDIDATE, + + NL80211_ATTR_TX_NO_CCK_RATE, + + NL80211_ATTR_TDLS_ACTION, + NL80211_ATTR_TDLS_DIALOG_TOKEN, + NL80211_ATTR_TDLS_OPERATION, + NL80211_ATTR_TDLS_SUPPORT, + NL80211_ATTR_TDLS_EXTERNAL_SETUP, + + NL80211_ATTR_DEVICE_AP_SME, + + NL80211_ATTR_DONT_WAIT_FOR_ACK, + + NL80211_ATTR_FEATURE_FLAGS, + + NL80211_ATTR_PROBE_RESP_OFFLOAD, + + NL80211_ATTR_PROBE_RESP, + + NL80211_ATTR_DFS_REGION, + + NL80211_ATTR_DISABLE_HT, + + NL80211_ATTR_HT_CAPABILITY_MASK, + + NL80211_ATTR_NOACK_MAP, + + NL80211_ATTR_INACTIVITY_TIMEOUT, + + NL80211_ATTR_RX_SIGNAL_DBM, + + NL80211_ATTR_BG_SCAN_PERIOD, + + NL80211_ATTR_WDEV, + + NL80211_ATTR_USER_REG_HINT_TYPE, + + NL80211_ATTR_CONN_FAILED_REASON, + + NL80211_ATTR_STA_CAP_REQ, + + NL80211_ATTR_ACS, + + NL80211_ATTR_MAC_ACL, + + NL80211_ATTR_MAC_ADDRS, + + NL80211_ATTR_ACL_POLICY, + + NL80211_ATTR_BTCOEX_DATA, + + /* add attributes here, update the policy in nl80211.c */ + + __NL80211_ATTR_AFTER_LAST, + NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 +}; + +/* source-level API compatibility */ +#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION +#define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG + +/* + * Allow user space programs to use #ifdef on new attributes by defining them + * here + */ +#define NL80211_CMD_CONNECT NL80211_CMD_CONNECT +#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY +#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES +#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS +#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ +#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE +#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE +#define NL80211_ATTR_IE NL80211_ATTR_IE +#define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR +#define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE +#define NL80211_ATTR_FRAME NL80211_ATTR_FRAME +#define NL80211_ATTR_SSID NL80211_ATTR_SSID +#define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE +#define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE +#define NL80211_ATTR_CIPHER_SUITES_PAIRWISE NL80211_ATTR_CIPHER_SUITES_PAIRWISE +#define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP +#define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS +#define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES +#define NL80211_ATTR_KEY NL80211_ATTR_KEY +#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS +#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS + +#define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_HT_RATES 77 +#define NL80211_MAX_SUPP_REG_RULES 32 +#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 +#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 +#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 +#define NL80211_HT_CAPABILITY_LEN 26 + +#define NL80211_MAX_NR_CIPHER_SUITES 5 +#define NL80211_MAX_NR_AKM_SUITES 2 + +#define NL80211_MIN_REMAIN_ON_CHANNEL_TIME 10 + +/* default RSSI threshold for scan results if none specified. */ +#define NL80211_SCAN_RSSI_THOLD_OFF -300 + +#define NL80211_CQM_TXE_MAX_INTVL 1800 + +/** + * enum nl80211_iftype - (virtual) interface types + * + * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides + * @NL80211_IFTYPE_ADHOC: independent BSS member + * @NL80211_IFTYPE_STATION: managed BSS member + * @NL80211_IFTYPE_AP: access point + * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points; VLAN interfaces + * are a bit special in that they must always be tied to a pre-existing + * AP type interface. + * @NL80211_IFTYPE_WDS: wireless distribution interface + * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames + * @NL80211_IFTYPE_MESH_POINT: mesh point + * @NL80211_IFTYPE_P2P_CLIENT: P2P client + * @NL80211_IFTYPE_P2P_GO: P2P group owner + * @NL80211_IFTYPE_MAX: highest interface type number currently defined + * @NUM_NL80211_IFTYPES: number of defined interface types + * + * These values are used with the %NL80211_ATTR_IFTYPE + * to set the type of an interface. + * + */ +enum nl80211_iftype { + NL80211_IFTYPE_UNSPECIFIED, + NL80211_IFTYPE_ADHOC, + NL80211_IFTYPE_STATION, + NL80211_IFTYPE_AP, + NL80211_IFTYPE_AP_VLAN, + NL80211_IFTYPE_WDS, + NL80211_IFTYPE_MONITOR, + NL80211_IFTYPE_MESH_POINT, + NL80211_IFTYPE_P2P_CLIENT, + NL80211_IFTYPE_P2P_GO, + + /* keep last */ + NUM_NL80211_IFTYPES, + NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1 +}; + +/** + * enum nl80211_sta_flags - station flags + * + * Station flags. When a station is added to an AP interface, it is + * assumed to be already associated (and hence authenticated.) + * + * @__NL80211_STA_FLAG_INVALID: attribute number 0 is reserved + * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X) + * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames + * with short barker preamble + * @NL80211_STA_FLAG_WME: station is WME/QoS capable + * @NL80211_STA_FLAG_MFP: station uses management frame protection + * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated + * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer + * @NL80211_STA_FLAG_MAX: highest station flag number currently defined + * @__NL80211_STA_FLAG_AFTER_LAST: internal use + */ +enum nl80211_sta_flags { + __NL80211_STA_FLAG_INVALID, + NL80211_STA_FLAG_AUTHORIZED, + NL80211_STA_FLAG_SHORT_PREAMBLE, + NL80211_STA_FLAG_WME, + NL80211_STA_FLAG_MFP, + NL80211_STA_FLAG_AUTHENTICATED, + NL80211_STA_FLAG_TDLS_PEER, + + /* keep last */ + __NL80211_STA_FLAG_AFTER_LAST, + NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 +}; + +/** + * struct nl80211_sta_flag_update - station flags mask/set + * @mask: mask of station flags to set + * @set: which values to set them to + * + * Both mask and set contain bits as per &enum nl80211_sta_flags. + */ +struct nl80211_sta_flag_update { + __u32 mask; + __u32 set; +} __attribute__((packed)); + +/** + * enum nl80211_rate_info - bitrate information + * + * These attribute types are used with %NL80211_STA_INFO_TXRATE + * when getting information about the bitrate of a station. + * + * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved + * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) + * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) + * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate + * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval + * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined + * @__NL80211_RATE_INFO_AFTER_LAST: internal use + */ +enum nl80211_rate_info { + __NL80211_RATE_INFO_INVALID, + NL80211_RATE_INFO_BITRATE, + NL80211_RATE_INFO_MCS, + NL80211_RATE_INFO_40_MHZ_WIDTH, + NL80211_RATE_INFO_SHORT_GI, + + /* keep last */ + __NL80211_RATE_INFO_AFTER_LAST, + NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_bss_param - BSS information collected by STA + * + * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM + * when getting information about the bitrate of a station. + * + * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved + * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag) + * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE: whether short preamble is enabled + * (flag) + * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME: whether short slot time is enabled + * (flag) + * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8) + * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16) + * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined + * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use + */ +enum nl80211_sta_bss_param { + __NL80211_STA_BSS_PARAM_INVALID, + NL80211_STA_BSS_PARAM_CTS_PROT, + NL80211_STA_BSS_PARAM_SHORT_PREAMBLE, + NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME, + NL80211_STA_BSS_PARAM_DTIM_PERIOD, + NL80211_STA_BSS_PARAM_BEACON_INTERVAL, + + /* keep last */ + __NL80211_STA_BSS_PARAM_AFTER_LAST, + NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_info - station information + * + * These attribute types are used with %NL80211_ATTR_STA_INFO + * when getting information about a station. + * + * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved + * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) + * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) + * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) + * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) + * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute + * containing info as possible, see &enum nl80211_rate_info + * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) + * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this + * station) + * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station) + * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station) + * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm) + * @NL80211_STA_INFO_LLID: the station's mesh LLID + * @NL80211_STA_INFO_PLID: the station's mesh PLID + * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station + * (see %enum nl80211_plink_state) + * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested + * attribute, like NL80211_STA_INFO_TX_BITRATE. + * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute + * containing info as possible, see &enum nl80211_sta_bss_param + * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected + * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update. + * @__NL80211_STA_INFO_AFTER_LAST: internal + * @NL80211_STA_INFO_MAX: highest possible station info attribute + */ +enum nl80211_sta_info { + __NL80211_STA_INFO_INVALID, + NL80211_STA_INFO_INACTIVE_TIME, + NL80211_STA_INFO_RX_BYTES, + NL80211_STA_INFO_TX_BYTES, + NL80211_STA_INFO_LLID, + NL80211_STA_INFO_PLID, + NL80211_STA_INFO_PLINK_STATE, + NL80211_STA_INFO_SIGNAL, + NL80211_STA_INFO_TX_BITRATE, + NL80211_STA_INFO_RX_PACKETS, + NL80211_STA_INFO_TX_PACKETS, + NL80211_STA_INFO_TX_RETRIES, + NL80211_STA_INFO_TX_FAILED, + NL80211_STA_INFO_SIGNAL_AVG, + NL80211_STA_INFO_RX_BITRATE, + NL80211_STA_INFO_BSS_PARAM, + NL80211_STA_INFO_CONNECTED_TIME, + NL80211_STA_INFO_STA_FLAGS, + + /* keep last */ + __NL80211_STA_INFO_AFTER_LAST, + NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mpath_flags - nl80211 mesh path flags + * + * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active + * @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running + * @NL80211_MPATH_FLAG_SN_VALID: the mesh path contains a valid SN + * @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set + * @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded + */ +enum nl80211_mpath_flags { + NL80211_MPATH_FLAG_ACTIVE = 1<<0, + NL80211_MPATH_FLAG_RESOLVING = 1<<1, + NL80211_MPATH_FLAG_SN_VALID = 1<<2, + NL80211_MPATH_FLAG_FIXED = 1<<3, + NL80211_MPATH_FLAG_RESOLVED = 1<<4, +}; + +/** + * enum nl80211_mpath_info - mesh path information + * + * These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting + * information about a mesh path. + * + * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved + * @NL80211_MPATH_INFO_FRAME_QLEN: number of queued frames for this destination + * @NL80211_MPATH_INFO_SN: destination sequence number + * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path + * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now + * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in + * &enum nl80211_mpath_flags; + * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec + * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries + * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number + * currently defind + * @__NL80211_MPATH_INFO_AFTER_LAST: internal use + */ +enum nl80211_mpath_info { + __NL80211_MPATH_INFO_INVALID, + NL80211_MPATH_INFO_FRAME_QLEN, + NL80211_MPATH_INFO_SN, + NL80211_MPATH_INFO_METRIC, + NL80211_MPATH_INFO_EXPTIME, + NL80211_MPATH_INFO_FLAGS, + NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, + NL80211_MPATH_INFO_DISCOVERY_RETRIES, + + /* keep last */ + __NL80211_MPATH_INFO_AFTER_LAST, + NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_band_attr - band attributes + * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band, + * an array of nested frequency attributes + * @NL80211_BAND_ATTR_RATES: supported bitrates in this band, + * an array of nested bitrate attributes + * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as + * defined in 802.11n + * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE + * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n + * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n + * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined + * @__NL80211_BAND_ATTR_AFTER_LAST: internal use + */ +enum nl80211_band_attr { + __NL80211_BAND_ATTR_INVALID, + NL80211_BAND_ATTR_FREQS, + NL80211_BAND_ATTR_RATES, + + NL80211_BAND_ATTR_HT_MCS_SET, + NL80211_BAND_ATTR_HT_CAPA, + NL80211_BAND_ATTR_HT_AMPDU_FACTOR, + NL80211_BAND_ATTR_HT_AMPDU_DENSITY, + + /* keep last */ + __NL80211_BAND_ATTR_AFTER_LAST, + NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 +}; + +#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA + +/** + * enum nl80211_frequency_attr - frequency attributes + * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz + * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current + * regulatory domain. + * @NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: Only passive scanning is + * permitted on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_IBSS: IBSS networks are not permitted + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm + * (100 * dBm). + * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number + * currently defined + * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use + */ +enum nl80211_frequency_attr { + __NL80211_FREQUENCY_ATTR_INVALID, + NL80211_FREQUENCY_ATTR_FREQ, + NL80211_FREQUENCY_ATTR_DISABLED, + NL80211_FREQUENCY_ATTR_PASSIVE_SCAN, + NL80211_FREQUENCY_ATTR_NO_IBSS, + NL80211_FREQUENCY_ATTR_RADAR, + NL80211_FREQUENCY_ATTR_MAX_TX_POWER, + + /* keep last */ + __NL80211_FREQUENCY_ATTR_AFTER_LAST, + NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1 +}; + +#define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER + +/** + * enum nl80211_bitrate_attr - bitrate attributes + * @__NL80211_BITRATE_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps + * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported + * in 2.4 GHz band. + * @NL80211_BITRATE_ATTR_MAX: highest bitrate attribute number + * currently defined + * @__NL80211_BITRATE_ATTR_AFTER_LAST: internal use + */ +enum nl80211_bitrate_attr { + __NL80211_BITRATE_ATTR_INVALID, + NL80211_BITRATE_ATTR_RATE, + NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE, + + /* keep last */ + __NL80211_BITRATE_ATTR_AFTER_LAST, + NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_initiator - Indicates the initiator of a reg domain request + * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world + * regulatory domain. + * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the + * regulatory domain. + * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the + * wireless core it thinks its knows the regulatory domain we should be in. + * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an + * 802.11 country information element with regulatory information it + * thinks we should consider. cfg80211 only processes the country + * code from the IE, and relies on the regulatory domain information + * structure passed by userspace (CRDA) from our wireless-regdb. + * If a channel is enabled but the country code indicates it should + * be disabled we disable the channel and re-enable it upon disassociation. + */ +enum nl80211_reg_initiator { + NL80211_REGDOM_SET_BY_CORE, + NL80211_REGDOM_SET_BY_USER, + NL80211_REGDOM_SET_BY_DRIVER, + NL80211_REGDOM_SET_BY_COUNTRY_IE, +}; + +/** + * enum nl80211_reg_type - specifies the type of regulatory domain + * @NL80211_REGDOM_TYPE_COUNTRY: the regulatory domain set is one that pertains + * to a specific country. When this is set you can count on the + * ISO / IEC 3166 alpha2 country code being valid. + * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory + * domain. + * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom + * driver specific world regulatory domain. These do not apply system-wide + * and are only applicable to the individual devices which have requested + * them to be applied. + * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product + * of an intersection between two regulatory domains -- the previously + * set regulatory domain on the system and the last accepted regulatory + * domain request to be processed. + */ +enum nl80211_reg_type { + NL80211_REGDOM_TYPE_COUNTRY, + NL80211_REGDOM_TYPE_WORLD, + NL80211_REGDOM_TYPE_CUSTOM_WORLD, + NL80211_REGDOM_TYPE_INTERSECTION, +}; + +/** + * enum nl80211_reg_rule_attr - regulatory rule attributes + * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional + * considerations for a given frequency range. These are the + * &enum nl80211_reg_rule_flags. + * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory + * rule in KHz. This is not a center of frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule + * in KHz. This is not a center a frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this + * frequency range, in KHz. + * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain + * for a given frequency range. The value is in mBi (100 * dBi). + * If you don't have one then don't send this. + * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for + * a given frequency range. The value is in mBm (100 * dBm). + * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number + * currently defined + * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use + */ +enum nl80211_reg_rule_attr { + __NL80211_REG_RULE_ATTR_INVALID, + NL80211_ATTR_REG_RULE_FLAGS, + + NL80211_ATTR_FREQ_RANGE_START, + NL80211_ATTR_FREQ_RANGE_END, + NL80211_ATTR_FREQ_RANGE_MAX_BW, + + NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, + NL80211_ATTR_POWER_RULE_MAX_EIRP, + + /* keep last */ + __NL80211_REG_RULE_ATTR_AFTER_LAST, + NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sched_scan_match_attr - scheduled scan match attributes + * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, + * only report BSS with matching SSID. + * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a + * BSS in scan results. Filtering is turned off if not specified. + * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter + * attribute number currently defined + * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use + */ +enum nl80211_sched_scan_match_attr { + __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID, + + NL80211_SCHED_SCAN_MATCH_ATTR_SSID, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + + /* keep last */ + __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, + NL80211_SCHED_SCAN_MATCH_ATTR_MAX = + __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1 +}; + +/* only for backward compatibility */ +#define NL80211_ATTR_SCHED_SCAN_MATCH_SSID NL80211_SCHED_SCAN_MATCH_ATTR_SSID + +/** + * enum nl80211_reg_rule_flags - regulatory rule flags + * + * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed + * @NL80211_RRF_NO_CCK: CCK modulation not allowed + * @NL80211_RRF_NO_INDOOR: indoor operation not allowed + * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed + * @NL80211_RRF_DFS: DFS support is required to be used + * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links + * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links + * @NL80211_RRF_PASSIVE_SCAN: passive scan is required + * @NL80211_RRF_NO_IBSS: no IBSS is allowed + */ +enum nl80211_reg_rule_flags { + NL80211_RRF_NO_OFDM = 1<<0, + NL80211_RRF_NO_CCK = 1<<1, + NL80211_RRF_NO_INDOOR = 1<<2, + NL80211_RRF_NO_OUTDOOR = 1<<3, + NL80211_RRF_DFS = 1<<4, + NL80211_RRF_PTP_ONLY = 1<<5, + NL80211_RRF_PTMP_ONLY = 1<<6, + NL80211_RRF_PASSIVE_SCAN = 1<<7, + NL80211_RRF_NO_IBSS = 1<<8, +}; + +/** + * enum nl80211_survey_info - survey information + * + * These attribute types are used with %NL80211_ATTR_SURVEY_INFO + * when getting information about a survey. + * + * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved + * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel + * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) + * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used + * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio + * spent on this channel + * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary + * channel was sensed busy (either due to activity or energy detect) + * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension + * channel was sensed busy + * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent + * receiving data + * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent + * transmitting data + * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number + * currently defined + * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use + */ +enum nl80211_survey_info { + __NL80211_SURVEY_INFO_INVALID, + NL80211_SURVEY_INFO_FREQUENCY, + NL80211_SURVEY_INFO_NOISE, + NL80211_SURVEY_INFO_IN_USE, + NL80211_SURVEY_INFO_CHANNEL_TIME, + NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, + NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, + NL80211_SURVEY_INFO_CHANNEL_TIME_RX, + NL80211_SURVEY_INFO_CHANNEL_TIME_TX, + + /* keep last */ + __NL80211_SURVEY_INFO_AFTER_LAST, + NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mntr_flags - monitor configuration flags + * + * Monitor configuration flags. + * + * @__NL80211_MNTR_FLAG_INVALID: reserved + * + * @NL80211_MNTR_FLAG_FCSFAIL: pass frames with bad FCS + * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP + * @NL80211_MNTR_FLAG_CONTROL: pass control frames + * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering + * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing. + * overrides all other flags. + * + * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use + * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag + */ +enum nl80211_mntr_flags { + __NL80211_MNTR_FLAG_INVALID, + NL80211_MNTR_FLAG_FCSFAIL, + NL80211_MNTR_FLAG_PLCPFAIL, + NL80211_MNTR_FLAG_CONTROL, + NL80211_MNTR_FLAG_OTHER_BSS, + NL80211_MNTR_FLAG_COOK_FRAMES, + + /* keep last */ + __NL80211_MNTR_FLAG_AFTER_LAST, + NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 +}; + +/** + * enum nl80211_meshconf_params - mesh configuration parameters + * + * Mesh configuration parameters. These can be changed while the mesh is + * active. + * + * @__NL80211_MESHCONF_INVALID: internal use + * + * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in + * millisecond units, used by the Peer Link Open message + * + * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in + * millisecond units, used by the peer link management to close a peer link + * + * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in + * millisecond units + * + * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed + * on this mesh interface + * + * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link + * open retries that can be sent to establish a new peer link instance in a + * mesh + * + * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh + * point. + * + * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically + * open peer links when we detect compatible mesh peers. + * + * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames + * containing a PREQ that an MP can send to a particular destination (path + * target) + * + * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths + * (in milliseconds) + * + * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait + * until giving up on a path discovery (in milliseconds) + * + * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh + * points receiving a PREQ shall consider the forwarding information from the + * root to be valid. (TU = time unit) + * + * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in + * TUs) during which an MP can send only one action frame containing a PREQ + * reference element + * + * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs) + * that it takes for an HWMP information element to propagate across the mesh + * + * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not + * + * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a + * source mesh point for path selection elements. + * + * @NL80211_MESHCONF_HWMP_RANN_INTERVAL: The interval of time (in TUs) between + * root announcements are transmitted. + * + * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has + * access to a broader network beyond the MBSS. This is done via Root + * Announcement frames. + * + * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute + * + * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use + */ +enum nl80211_meshconf_params { + __NL80211_MESHCONF_INVALID, + NL80211_MESHCONF_RETRY_TIMEOUT, + NL80211_MESHCONF_CONFIRM_TIMEOUT, + NL80211_MESHCONF_HOLDING_TIMEOUT, + NL80211_MESHCONF_MAX_PEER_LINKS, + NL80211_MESHCONF_MAX_RETRIES, + NL80211_MESHCONF_TTL, + NL80211_MESHCONF_AUTO_OPEN_PLINKS, + NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, + NL80211_MESHCONF_PATH_REFRESH_TIME, + NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, + NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, + NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, + NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, + NL80211_MESHCONF_HWMP_ROOTMODE, + NL80211_MESHCONF_ELEMENT_TTL, + NL80211_MESHCONF_HWMP_RANN_INTERVAL, + NL80211_MESHCONF_GATE_ANNOUNCEMENTS, + + /* keep last */ + __NL80211_MESHCONF_ATTR_AFTER_LAST, + NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mesh_setup_params - mesh setup parameters + * + * Mesh setup parameters. These are used to start/join a mesh and cannot be + * changed while the mesh is active. + * + * @__NL80211_MESH_SETUP_INVALID: Internal use + * + * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a + * vendor specific path selection algorithm or disable it to use the default + * HWMP. + * + * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a + * vendor specific path metric or disable it to use the default Airtime + * metric. + * + * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a + * robust security network ie, or a vendor specific information element that + * vendors will use to identify the path selection methods and metrics in use. + * + * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication + * daemon will be authenticating mesh candidates. + * + * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication + * daemon will be securing peer link frames. AMPE is a secured version of Mesh + * Peering Management (MPM) and is implemented with the assistance of a + * userspace daemon. When this flag is set, the kernel will send peer + * management frames to a userspace daemon that will implement AMPE + * functionality (security capabilities selection, key confirmation, and key + * management). When the flag is unset (default), the kernel can autonomously + * complete (unsecured) mesh peering without the need of a userspace daemon. + * + * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number + * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use + */ +enum nl80211_mesh_setup_params { + __NL80211_MESH_SETUP_INVALID, + NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL, + NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC, + NL80211_MESH_SETUP_IE, + NL80211_MESH_SETUP_USERSPACE_AUTH, + NL80211_MESH_SETUP_USERSPACE_AMPE, + + /* keep last */ + __NL80211_MESH_SETUP_ATTR_AFTER_LAST, + NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_txq_attr - TX queue parameter attributes + * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved + * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*) + * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning + * disabled + * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255] + * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal + * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number + */ +enum nl80211_txq_attr { + __NL80211_TXQ_ATTR_INVALID, + NL80211_TXQ_ATTR_QUEUE, + NL80211_TXQ_ATTR_TXOP, + NL80211_TXQ_ATTR_CWMIN, + NL80211_TXQ_ATTR_CWMAX, + NL80211_TXQ_ATTR_AIFS, + + /* keep last */ + __NL80211_TXQ_ATTR_AFTER_LAST, + NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1 +}; + +enum nl80211_txq_q { + NL80211_TXQ_Q_VO, + NL80211_TXQ_Q_VI, + NL80211_TXQ_Q_BE, + NL80211_TXQ_Q_BK +}; + +enum nl80211_channel_type { + NL80211_CHAN_NO_HT, + NL80211_CHAN_HT20, + NL80211_CHAN_HT40MINUS, + NL80211_CHAN_HT40PLUS +}; + +/** + * enum nl80211_bss - netlink attributes for a BSS + * + * @__NL80211_BSS_INVALID: invalid + * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets) + * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) + * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) + * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) + * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) + * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the + * raw information elements from the probe response/beacon (bin); + * if the %NL80211_BSS_BEACON_IES attribute is present, the IEs here are + * from a Probe Response frame; otherwise they are from a Beacon frame. + * However, if the driver does not indicate the source of the IEs, these + * IEs may be from either frame subtype. + * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon + * in mBm (100 * dBm) (s32) + * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon + * in unspecified units, scaled to 0..100 (u8) + * @NL80211_BSS_STATUS: status, if this BSS is "used" + * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms + * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information + * elements from a Beacon frame (bin); not present if no Beacon frame has + * yet been received + * @__NL80211_BSS_AFTER_LAST: internal + * @NL80211_BSS_MAX: highest BSS attribute + */ +enum nl80211_bss { + __NL80211_BSS_INVALID, + NL80211_BSS_BSSID, + NL80211_BSS_FREQUENCY, + NL80211_BSS_TSF, + NL80211_BSS_BEACON_INTERVAL, + NL80211_BSS_CAPABILITY, + NL80211_BSS_INFORMATION_ELEMENTS, + NL80211_BSS_SIGNAL_MBM, + NL80211_BSS_SIGNAL_UNSPEC, + NL80211_BSS_STATUS, + NL80211_BSS_SEEN_MS_AGO, + NL80211_BSS_BEACON_IES, + + /* keep last */ + __NL80211_BSS_AFTER_LAST, + NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 +}; + +/** + * enum nl80211_bss_status - BSS "status" + * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS. + * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS. + * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS. + * + * The BSS status is a BSS attribute in scan dumps, which + * indicates the status the interface has wrt. this BSS. + */ +enum nl80211_bss_status { + NL80211_BSS_STATUS_AUTHENTICATED, + NL80211_BSS_STATUS_ASSOCIATED, + NL80211_BSS_STATUS_IBSS_JOINED, +}; + +/** + * enum nl80211_auth_type - AuthenticationType + * + * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication + * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only) + * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) + * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) + * @__NL80211_AUTHTYPE_NUM: internal + * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm + * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by + * trying multiple times); this is invalid in netlink -- leave out + * the attribute for this on CONNECT commands. + */ +enum nl80211_auth_type { + NL80211_AUTHTYPE_OPEN_SYSTEM, + NL80211_AUTHTYPE_SHARED_KEY, + NL80211_AUTHTYPE_FT, + NL80211_AUTHTYPE_NETWORK_EAP, + + /* keep last */ + __NL80211_AUTHTYPE_NUM, + NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1, + NL80211_AUTHTYPE_AUTOMATIC +}; + +/** + * enum nl80211_key_type - Key Type + * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key + * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key + * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS) + * @NUM_NL80211_KEYTYPES: number of defined key types + */ +enum nl80211_key_type { + NL80211_KEYTYPE_GROUP, + NL80211_KEYTYPE_PAIRWISE, + NL80211_KEYTYPE_PEERKEY, + + NUM_NL80211_KEYTYPES +}; + +/** + * enum nl80211_mfp - Management frame protection state + * @NL80211_MFP_NO: Management frame protection not used + * @NL80211_MFP_REQUIRED: Management frame protection required + */ +enum nl80211_mfp { + NL80211_MFP_NO, + NL80211_MFP_REQUIRED, +}; + +enum nl80211_wpa_versions { + NL80211_WPA_VERSION_1 = 1 << 0, + NL80211_WPA_VERSION_2 = 1 << 1, +}; + +/** + * enum nl80211_key_default_types - key default types + * @__NL80211_KEY_DEFAULT_TYPE_INVALID: invalid + * @NL80211_KEY_DEFAULT_TYPE_UNICAST: key should be used as default + * unicast key + * @NL80211_KEY_DEFAULT_TYPE_MULTICAST: key should be used as default + * multicast key + * @NUM_NL80211_KEY_DEFAULT_TYPES: number of default types + */ +enum nl80211_key_default_types { + __NL80211_KEY_DEFAULT_TYPE_INVALID, + NL80211_KEY_DEFAULT_TYPE_UNICAST, + NL80211_KEY_DEFAULT_TYPE_MULTICAST, + + NUM_NL80211_KEY_DEFAULT_TYPES +}; + +/** + * enum nl80211_key_attributes - key attributes + * @__NL80211_KEY_INVALID: invalid + * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of + * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC + * keys + * @NL80211_KEY_IDX: key ID (u8, 0-3) + * @NL80211_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 + * section 7.3.2.25.1, e.g. 0x000FAC04) + * @NL80211_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and + * CCMP keys, each six bytes in little endian + * @NL80211_KEY_DEFAULT: flag indicating default key + * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key + * @NL80211_KEY_TYPE: the key type from enum nl80211_key_type, if not + * specified the default depends on whether a MAC address was + * given with the command using the key or not (u32) + * @NL80211_KEY_DEFAULT_TYPES: A nested attribute containing flags + * attributes, specifying what a key should be set as default as. + * See &enum nl80211_key_default_types. + * @__NL80211_KEY_AFTER_LAST: internal + * @NL80211_KEY_MAX: highest key attribute + */ +enum nl80211_key_attributes { + __NL80211_KEY_INVALID, + NL80211_KEY_DATA, + NL80211_KEY_IDX, + NL80211_KEY_CIPHER, + NL80211_KEY_SEQ, + NL80211_KEY_DEFAULT, + NL80211_KEY_DEFAULT_MGMT, + NL80211_KEY_TYPE, + NL80211_KEY_DEFAULT_TYPES, + + /* keep last */ + __NL80211_KEY_AFTER_LAST, + NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1 +}; + +/** + * enum nl80211_tx_rate_attributes - TX rate set attributes + * @__NL80211_TXRATE_INVALID: invalid + * @NL80211_TXRATE_LEGACY: Legacy (non-MCS) rates allowed for TX rate selection + * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with + * 1 = 500 kbps) but without the IE length restriction (at most + * %NL80211_MAX_SUPP_RATES in a single array). + * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection + * in an array of MCS numbers. + * @__NL80211_TXRATE_AFTER_LAST: internal + * @NL80211_TXRATE_MAX: highest TX rate attribute + */ +enum nl80211_tx_rate_attributes { + __NL80211_TXRATE_INVALID, + NL80211_TXRATE_LEGACY, + NL80211_TXRATE_MCS, + + /* keep last */ + __NL80211_TXRATE_AFTER_LAST, + NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 +}; + +/** + * enum nl80211_band - Frequency band + * @NL80211_BAND_2GHZ: 2.4 GHz ISM band + * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) + */ +enum nl80211_band { + NL80211_BAND_2GHZ, + NL80211_BAND_5GHZ, +}; + +enum nl80211_ps_state { + NL80211_PS_DISABLED, + NL80211_PS_ENABLED, +}; + +/** + * enum nl80211_attr_cqm - connection quality monitor attributes + * @__NL80211_ATTR_CQM_INVALID: invalid + * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies + * the threshold for the RSSI level at which an event will be sent. Zero + * to disable. + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies + * the minimum amount the RSSI level must change after an event before a + * new event may be issued (to reduce effects of RSSI oscillation). + * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event + * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many + * consecutive packets were not acknowledged by the peer + * @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures + * during the given %NL80211_ATTR_CQM_TXE_INTVL before an + * %NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and + * %NL80211_ATTR_CQM_TXE_PKTS is generated. + * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given + * %NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is + * checked. + * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic + * interval in which %NL80211_ATTR_CQM_TXE_PKTS and + * %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an + * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting. + * @__NL80211_ATTR_CQM_AFTER_LAST: internal + * @NL80211_ATTR_CQM_MAX: highest key attribute + */ +enum nl80211_attr_cqm { + __NL80211_ATTR_CQM_INVALID, + NL80211_ATTR_CQM_RSSI_THOLD, + NL80211_ATTR_CQM_RSSI_HYST, + NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + NL80211_ATTR_CQM_PKT_LOSS_EVENT, + NL80211_ATTR_CQM_TXE_RATE, + NL80211_ATTR_CQM_TXE_PKTS, + NL80211_ATTR_CQM_TXE_INTVL, + + /* keep last */ + __NL80211_ATTR_CQM_AFTER_LAST, + NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1 +}; + +/** + * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: The RSSI level is lower than the + * configured threshold + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the + * configured threshold + */ +enum nl80211_cqm_rssi_threshold_event { + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, +}; + + +/** + * enum nl80211_tx_power_setting - TX power adjustment + * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power + * @NL80211_TX_POWER_LIMITED: limit TX power by the mBm parameter + * @NL80211_TX_POWER_FIXED: fix TX power to the mBm parameter + */ +enum nl80211_tx_power_setting { + NL80211_TX_POWER_AUTOMATIC, + NL80211_TX_POWER_LIMITED, + NL80211_TX_POWER_FIXED, +}; + +/** + * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute + * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute + * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has + * a zero bit are ignored + * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have + * a bit for each byte in the pattern. The lowest-order bit corresponds + * to the first byte of the pattern, but the bytes of the pattern are + * in a little-endian-like format, i.e. the 9th byte of the pattern + * corresponds to the lowest-order bit in the second byte of the mask. + * For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where + * xx indicates "don't care") would be represented by a pattern of + * twelve zero bytes, and a mask of "0xed,0x07". + * Note that the pattern matching is done as though frames were not + * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked + * first (including SNAP header unpacking) and then matched. + * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes + * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number + */ +enum nl80211_wowlan_packet_pattern_attr { + __NL80211_WOWLAN_PKTPAT_INVALID, + NL80211_WOWLAN_PKTPAT_MASK, + NL80211_WOWLAN_PKTPAT_PATTERN, + + NUM_NL80211_WOWLAN_PKTPAT, + MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1, +}; + +/** + * struct nl80211_wowlan_pattern_support - pattern support information + * @max_patterns: maximum number of patterns supported + * @min_pattern_len: minimum length of each pattern + * @max_pattern_len: maximum length of each pattern + * + * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when + * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the + * capability information given by the kernel to userspace. + */ +struct nl80211_wowlan_pattern_support { + __u32 max_patterns; + __u32 min_pattern_len; + __u32 max_pattern_len; +} __attribute__((packed)); + +/** + * enum nl80211_wowlan_triggers - WoWLAN trigger definitions + * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes + * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put + * the chip into a special state -- works best with chips that have + * support for low-power operation already (flag) + * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect + * is detected is implementation-specific (flag) + * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed + * by 16 repetitions of MAC addr, anywhere in payload) (flag) + * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns + * which are passed in an array of nested attributes, each nested attribute + * defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern. + * Each pattern defines a wakeup packet. The matching is done on the MSDU, + * i.e. as though the packet was an 802.3 packet, so the pattern matching + * is done after the packet is converted to the MSDU. + * + * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute + * carrying a &struct nl80211_wowlan_pattern_support. + * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be + * used when setting, used only to indicate that GTK rekeying is supported + * by the device (flag) + * @NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: wake up on GTK rekey failure (if + * done by the device) (flag) + * @NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: wake up on EAP Identity Request + * packet (flag) + * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag) + * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released + * (on devices that have rfkill in the device) (flag) + * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers + * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number + */ +enum nl80211_wowlan_triggers { + __NL80211_WOWLAN_TRIG_INVALID, + NL80211_WOWLAN_TRIG_ANY, + NL80211_WOWLAN_TRIG_DISCONNECT, + NL80211_WOWLAN_TRIG_MAGIC_PKT, + NL80211_WOWLAN_TRIG_PKT_PATTERN, + NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED, + NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE, + NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, + NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, + NL80211_WOWLAN_TRIG_RFKILL_RELEASE, + + /* keep last */ + NUM_NL80211_WOWLAN_TRIG, + MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1 +}; + +/** + * enum nl80211_iface_limit_attrs - limit attributes + * @NL80211_IFACE_LIMIT_UNSPEC: (reserved) + * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that + * can be chosen from this set of interface types (u32) + * @NL80211_IFACE_LIMIT_TYPES: nested attribute containing a + * flag attribute for each interface type in this set + * @NUM_NL80211_IFACE_LIMIT: number of attributes + * @MAX_NL80211_IFACE_LIMIT: highest attribute number + */ +enum nl80211_iface_limit_attrs { + NL80211_IFACE_LIMIT_UNSPEC, + NL80211_IFACE_LIMIT_MAX, + NL80211_IFACE_LIMIT_TYPES, + + /* keep last */ + NUM_NL80211_IFACE_LIMIT, + MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1 +}; + +/** + * enum nl80211_if_combination_attrs -- interface combination attributes + * + * @NL80211_IFACE_COMB_UNSPEC: (reserved) + * @NL80211_IFACE_COMB_LIMITS: Nested attributes containing the limits + * for given interface types, see &enum nl80211_iface_limit_attrs. + * @NL80211_IFACE_COMB_MAXNUM: u32 attribute giving the total number of + * interfaces that can be created in this group. This number doesn't + * apply to interfaces purely managed in software, which are listed + * in a separate attribute %NL80211_ATTR_INTERFACES_SOFTWARE. + * @NL80211_IFACE_COMB_STA_AP_BI_MATCH: flag attribute specifying that + * beacon intervals within this group must be all the same even for + * infrastructure and AP/GO combinations, i.e. the GO(s) must adopt + * the infrastructure network's beacon interval. + * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many + * different channels may be used within this group. + * @NUM_NL80211_IFACE_COMB: number of attributes + * @MAX_NL80211_IFACE_COMB: highest attribute number + * + * Examples: + * limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2 + * => allows an AP and a STA that must match BIs + * + * numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8 + * => allows 8 of AP/GO + * + * numbers = [ #{STA} <= 2 ], channels = 2, max = 2 + * => allows two STAs on different channels + * + * numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4 + * => allows a STA plus three P2P interfaces + * + * The list of these four possiblities could completely be contained + * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate + * that any of these groups must match. + * + * "Combinations" of just a single interface will not be listed here, + * a single interface of any valid interface type is assumed to always + * be possible by itself. This means that implicitly, for each valid + * interface type, the following group always exists: + * numbers = [ #{} <= 1 ], channels = 1, max = 1 + */ +enum nl80211_if_combination_attrs { + NL80211_IFACE_COMB_UNSPEC, + NL80211_IFACE_COMB_LIMITS, + NL80211_IFACE_COMB_MAXNUM, + NL80211_IFACE_COMB_STA_AP_BI_MATCH, + NL80211_IFACE_COMB_NUM_CHANNELS, + + /* keep last */ + NUM_NL80211_IFACE_COMB, + MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1 +}; + + +/** + * enum nl80211_plink_state - state of a mesh peer link finite state machine + * + * @NL80211_PLINK_LISTEN: initial state, considered the implicit + * state of non existant mesh peer links + * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to + * this mesh peer + * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received + * from this mesh peer + * @NL80211_PLINK_CNF_RCVD: mesh plink confirm frame has been + * received from this mesh peer + * @NL80211_PLINK_ESTAB: mesh peer link is established + * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled + * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh + * plink are discarded + * @NUM_NL80211_PLINK_STATES: number of peer link states + * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states + */ +enum nl80211_plink_state { + NL80211_PLINK_LISTEN, + NL80211_PLINK_OPN_SNT, + NL80211_PLINK_OPN_RCVD, + NL80211_PLINK_CNF_RCVD, + NL80211_PLINK_ESTAB, + NL80211_PLINK_HOLDING, + NL80211_PLINK_BLOCKED, + + /* keep last */ + NUM_NL80211_PLINK_STATES, + MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1 +}; + +#define NL80211_KCK_LEN 16 +#define NL80211_KEK_LEN 16 +#define NL80211_REPLAY_CTR_LEN 8 + +/** + * enum nl80211_rekey_data - attributes for GTK rekey offload + * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes + * @NL80211_REKEY_DATA_KEK: key encryption key (binary) + * @NL80211_REKEY_DATA_KCK: key confirmation key (binary) + * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary) + * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal) + * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal) + */ +enum nl80211_rekey_data { + __NL80211_REKEY_DATA_INVALID, + NL80211_REKEY_DATA_KEK, + NL80211_REKEY_DATA_KCK, + NL80211_REKEY_DATA_REPLAY_CTR, + + /* keep last */ + NUM_NL80211_REKEY_DATA, + MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1 +}; + +/** + * enum nl80211_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID + * @NL80211_HIDDEN_SSID_NOT_IN_USE: do not hide SSID (i.e., broadcast it in + * Beacon frames) + * @NL80211_HIDDEN_SSID_ZERO_LEN: hide SSID by using zero-length SSID element + * in Beacon frames + * @NL80211_HIDDEN_SSID_ZERO_CONTENTS: hide SSID by using correct length of SSID + * element in Beacon frames but zero out each byte in the SSID + */ +enum nl80211_hidden_ssid { + NL80211_HIDDEN_SSID_NOT_IN_USE, + NL80211_HIDDEN_SSID_ZERO_LEN, + NL80211_HIDDEN_SSID_ZERO_CONTENTS +}; + +/** + * enum nl80211_sta_wme_attr - station WME attributes + * @__NL80211_STA_WME_INVALID: invalid number for nested attribute + * @NL80211_STA_WME_UAPSD_QUEUES: bitmap of uapsd queues. the format + * is the same as the AC bitmap in the QoS info field. + * @NL80211_STA_WME_MAX_SP: max service period. the format is the same + * as the MAX_SP field in the QoS info field (but already shifted down). + * @__NL80211_STA_WME_AFTER_LAST: internal + * @NL80211_STA_WME_MAX: highest station WME attribute + */ +enum nl80211_sta_wme_attr { + __NL80211_STA_WME_INVALID, + NL80211_STA_WME_UAPSD_QUEUES, + NL80211_STA_WME_MAX_SP, + + /* keep last */ + __NL80211_STA_WME_AFTER_LAST, + NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1 +}; + +/** + * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates + * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes + * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher + * priority) + * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets) + * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag) + * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes + * (internal) + * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute + * (internal) + */ +enum nl80211_pmksa_candidate_attr { + __NL80211_PMKSA_CANDIDATE_INVALID, + NL80211_PMKSA_CANDIDATE_INDEX, + NL80211_PMKSA_CANDIDATE_BSSID, + NL80211_PMKSA_CANDIDATE_PREAUTH, + + /* keep last */ + NUM_NL80211_PMKSA_CANDIDATE, + MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 +}; + +/** + * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION + * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request + * @NL80211_TDLS_SETUP: Setup TDLS link + * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established + * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link + * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link + */ +enum nl80211_tdls_operation { + NL80211_TDLS_DISCOVERY_REQ, + NL80211_TDLS_SETUP, + NL80211_TDLS_TEARDOWN, + NL80211_TDLS_ENABLE_LINK, + NL80211_TDLS_DISABLE_LINK, +}; + +/* + * enum nl80211_ap_sme_features - device-integrated AP features + * Reserved for future use, no bits are defined in + * NL80211_ATTR_DEVICE_AP_SME yet. +enum nl80211_ap_sme_features { +}; + */ + +/** + * enum nl80211_feature_flags - device/driver features + * @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back + * TX status to the socket error queue when requested with the + * socket option. + * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates. + * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up + * the connected inactive stations in AP mode. + * @NL80211_FEATURE_MAC_ACL: Driver does MAC address based access control + * in AP/P2P GO mode. + */ +enum nl80211_feature_flags { + NL80211_FEATURE_SK_TX_STATUS = 1 << 0, + NL80211_FEATURE_HT_IBSS = 1 << 1, + NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, + NL80211_FEATURE_MAC_ACL = 1 << 3, +}; + +/** + * enum nl80211_probe_resp_offload_support_attr - optional supported + * protocols for probe-response offloading by the driver/FW. + * To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute. + * Each enum value represents a bit in the bitmap of supported + * protocols. Typically a subset of probe-requests belonging to a + * supported protocol will be excluded from offload and uploaded + * to the host. + * + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: Support for WPS ver. 1 + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: Support for WPS ver. 2 + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: Support for P2P + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: Support for 802.11u + */ +enum nl80211_probe_resp_offload_support_attr { + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS = 1<<0, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 = 1<<1, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P = 1<<2, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 1<<3, +}; + +enum nl80211_btcoex_cmds { + NL80211_WMI_SET_BT_STATUS = 0, + NL80211_WMI_SET_BT_PARAMS, + NL80211_WMI_SET_BT_FT_ANT, + NL80211_WMI_SET_COLOCATED_BT_DEV, + NL80211_WMI_SET_BT_INQUIRY_PAGE_CONFIG, + NL80211_WMI_SET_BT_SCO_CONFIG, + NL80211_WMI_SET_BT_A2DP_CONFIG, + NL80211_WMI_SET_BT_ACLCOEX_CONFIG, + NL80211_WMI_SET_BT_DEBUG, + NL80211_WMI_SET_BT_OPSTATUS, + NL80211_WMI_GET_BT_CONFIG, + NL80211_WMI_GET_BT_STATS, + NL80211_WMI_BT_MAX, +}; + +/** + * enum sta_capab_req_options - values for %NL80211_ATTR_STA_CAPA_REQ. + * @NL80211_STA_CAP_REQ_11BONLY: Allow IEEE 802.11b only stations to associate. + * @NL80211_STA_CAP_REQ_11GONLY: Allow IEEE 802.11g only stations to associate. + */ +enum nl80211_sta_capab_req_options { + NL80211_STA_CAP_REQ_11BONLY = 1<<0, + NL80211_STA_CAP_REQ_11GONLY = 1<<1, +}; + +/** + * enum nl80211_acl_policy_attr - The access control policy which needs to be + * applied on an acl list set by %NL80211_CMD_SET_MAC_ACL. To be used + * with %NL80211_ATTR_ACL_POLICY. + * + * @NL80211_ACL_POLICY_ACCEPT: Allow the station to authenticate. + * @NL80211_ACL_POLICY_DENY: Block the station from authentication + */ +enum nl80211_acl_policy_attr { + NL80211_ACL_POLICY_ACCEPT, + NL80211_ACL_POLICY_DENY, +}; + +/** + * enum nl80211_connect_failed_reason - connection request failed reasons + * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be + * handled by the AP is reached. + * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Client's MAC is in the AP's blocklist. + * @NL80211_CONN_FAIL_NEW_CLIENT: Client's MAC is not available either in + * the accept list or in the deny list. + * */ +enum nl80211_connect_failed_reason { + NL80211_CONN_FAIL_MAX_CLIENTS, + NL80211_CONN_FAIL_BLOCKED_CLIENT, + NL80211_CONN_FAIL_NEW_CLIENT, +}; + +#endif /* __LINUX_NL80211_H */ diff --git a/include/compat/linux/of.h b/include/compat/linux/of.h new file mode 100644 index 00000000..c5dc87c2 --- /dev/null +++ b/include/compat/linux/of.h @@ -0,0 +1,16 @@ +#ifndef _COMPAT_LINUX_OF_H +#define _COMPAT_LINUX_OF_H 1 + +#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) +#include_next +#else + +#ifdef CONFIG_OF +#include_next +#endif /* CONFIG_OF */ + +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ + +#endif /* _COMPAT_LINUX_OF_H */ diff --git a/include/compat/linux/pci-aspm.h b/include/compat/linux/pci-aspm.h new file mode 100644 index 00000000..2bc6efb3 --- /dev/null +++ b/include/compat/linux/pci-aspm.h @@ -0,0 +1,5 @@ +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) +#include_next +#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) */ diff --git a/include/compat/linux/pci_ids.h b/include/compat/linux/pci_ids.h new file mode 100644 index 00000000..172ba703 --- /dev/null +++ b/include/compat/linux/pci_ids.h @@ -0,0 +1,2879 @@ +/* + * PCI Class, Vendor and Device IDs + * + * Please keep sorted. + * + * Do not add new entries to this file unless the definitions + * are shared between multiple drivers. + */ + +/* Device classes and subclasses */ + +#define PCI_CLASS_NOT_DEFINED 0x0000 +#define PCI_CLASS_NOT_DEFINED_VGA 0x0001 + +#define PCI_BASE_CLASS_STORAGE 0x01 +#define PCI_CLASS_STORAGE_SCSI 0x0100 +#define PCI_CLASS_STORAGE_IDE 0x0101 +#define PCI_CLASS_STORAGE_FLOPPY 0x0102 +#define PCI_CLASS_STORAGE_IPI 0x0103 +#define PCI_CLASS_STORAGE_RAID 0x0104 +#define PCI_CLASS_STORAGE_SATA 0x0106 +#define PCI_CLASS_STORAGE_SATA_AHCI 0x010601 +#define PCI_CLASS_STORAGE_SAS 0x0107 +#define PCI_CLASS_STORAGE_OTHER 0x0180 + +#define PCI_BASE_CLASS_NETWORK 0x02 +#define PCI_CLASS_NETWORK_ETHERNET 0x0200 +#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201 +#define PCI_CLASS_NETWORK_FDDI 0x0202 +#define PCI_CLASS_NETWORK_ATM 0x0203 +#define PCI_CLASS_NETWORK_OTHER 0x0280 + +#define PCI_BASE_CLASS_DISPLAY 0x03 +#define PCI_CLASS_DISPLAY_VGA 0x0300 +#define PCI_CLASS_DISPLAY_XGA 0x0301 +#define PCI_CLASS_DISPLAY_3D 0x0302 +#define PCI_CLASS_DISPLAY_OTHER 0x0380 + +#define PCI_BASE_CLASS_MULTIMEDIA 0x04 +#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400 +#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 +#define PCI_CLASS_MULTIMEDIA_PHONE 0x0402 +#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 + +#define PCI_BASE_CLASS_MEMORY 0x05 +#define PCI_CLASS_MEMORY_RAM 0x0500 +#define PCI_CLASS_MEMORY_FLASH 0x0501 +#define PCI_CLASS_MEMORY_OTHER 0x0580 + +#define PCI_BASE_CLASS_BRIDGE 0x06 +#define PCI_CLASS_BRIDGE_HOST 0x0600 +#define PCI_CLASS_BRIDGE_ISA 0x0601 +#define PCI_CLASS_BRIDGE_EISA 0x0602 +#define PCI_CLASS_BRIDGE_MC 0x0603 +#define PCI_CLASS_BRIDGE_PCI 0x0604 +#define PCI_CLASS_BRIDGE_PCMCIA 0x0605 +#define PCI_CLASS_BRIDGE_NUBUS 0x0606 +#define PCI_CLASS_BRIDGE_CARDBUS 0x0607 +#define PCI_CLASS_BRIDGE_RACEWAY 0x0608 +#define PCI_CLASS_BRIDGE_OTHER 0x0680 + +#define PCI_BASE_CLASS_COMMUNICATION 0x07 +#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 +#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701 +#define PCI_CLASS_COMMUNICATION_MULTISERIAL 0x0702 +#define PCI_CLASS_COMMUNICATION_MODEM 0x0703 +#define PCI_CLASS_COMMUNICATION_OTHER 0x0780 + +#define PCI_BASE_CLASS_SYSTEM 0x08 +#define PCI_CLASS_SYSTEM_PIC 0x0800 +#define PCI_CLASS_SYSTEM_PIC_IOAPIC 0x080010 +#define PCI_CLASS_SYSTEM_PIC_IOXAPIC 0x080020 +#define PCI_CLASS_SYSTEM_DMA 0x0801 +#define PCI_CLASS_SYSTEM_TIMER 0x0802 +#define PCI_CLASS_SYSTEM_RTC 0x0803 +#define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804 +#define PCI_CLASS_SYSTEM_SDHCI 0x0805 +#define PCI_CLASS_SYSTEM_OTHER 0x0880 + +#define PCI_BASE_CLASS_INPUT 0x09 +#define PCI_CLASS_INPUT_KEYBOARD 0x0900 +#define PCI_CLASS_INPUT_PEN 0x0901 +#define PCI_CLASS_INPUT_MOUSE 0x0902 +#define PCI_CLASS_INPUT_SCANNER 0x0903 +#define PCI_CLASS_INPUT_GAMEPORT 0x0904 +#define PCI_CLASS_INPUT_OTHER 0x0980 + +#define PCI_BASE_CLASS_DOCKING 0x0a +#define PCI_CLASS_DOCKING_GENERIC 0x0a00 +#define PCI_CLASS_DOCKING_OTHER 0x0a80 + +#define PCI_BASE_CLASS_PROCESSOR 0x0b +#define PCI_CLASS_PROCESSOR_386 0x0b00 +#define PCI_CLASS_PROCESSOR_486 0x0b01 +#define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02 +#define PCI_CLASS_PROCESSOR_ALPHA 0x0b10 +#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20 +#define PCI_CLASS_PROCESSOR_MIPS 0x0b30 +#define PCI_CLASS_PROCESSOR_CO 0x0b40 + +#define PCI_BASE_CLASS_SERIAL 0x0c +#define PCI_CLASS_SERIAL_FIREWIRE 0x0c00 +#define PCI_CLASS_SERIAL_FIREWIRE_OHCI 0x0c0010 +#define PCI_CLASS_SERIAL_ACCESS 0x0c01 +#define PCI_CLASS_SERIAL_SSA 0x0c02 +#define PCI_CLASS_SERIAL_USB 0x0c03 +#define PCI_CLASS_SERIAL_USB_UHCI 0x0c0300 +#define PCI_CLASS_SERIAL_USB_OHCI 0x0c0310 +#define PCI_CLASS_SERIAL_USB_EHCI 0x0c0320 +#define PCI_CLASS_SERIAL_USB_XHCI 0x0c0330 +#define PCI_CLASS_SERIAL_FIBER 0x0c04 +#define PCI_CLASS_SERIAL_SMBUS 0x0c05 + +#define PCI_BASE_CLASS_WIRELESS 0x0d +#define PCI_CLASS_WIRELESS_RF_CONTROLLER 0x0d10 +#define PCI_CLASS_WIRELESS_WHCI 0x0d1010 + +#define PCI_BASE_CLASS_INTELLIGENT 0x0e +#define PCI_CLASS_INTELLIGENT_I2O 0x0e00 + +#define PCI_BASE_CLASS_SATELLITE 0x0f +#define PCI_CLASS_SATELLITE_TV 0x0f00 +#define PCI_CLASS_SATELLITE_AUDIO 0x0f01 +#define PCI_CLASS_SATELLITE_VOICE 0x0f03 +#define PCI_CLASS_SATELLITE_DATA 0x0f04 + +#define PCI_BASE_CLASS_CRYPT 0x10 +#define PCI_CLASS_CRYPT_NETWORK 0x1000 +#define PCI_CLASS_CRYPT_ENTERTAINMENT 0x1001 +#define PCI_CLASS_CRYPT_OTHER 0x1080 + +#define PCI_BASE_CLASS_SIGNAL_PROCESSING 0x11 +#define PCI_CLASS_SP_DPIO 0x1100 +#define PCI_CLASS_SP_OTHER 0x1180 + +#define PCI_CLASS_OTHERS 0xff + +/* Vendors and devices. Sort key: vendor first, device next. */ + +#define PCI_VENDOR_ID_TTTECH 0x0357 +#define PCI_DEVICE_ID_TTTECH_MC322 0x000a + +#define PCI_VENDOR_ID_DYNALINK 0x0675 +#define PCI_DEVICE_ID_DYNALINK_IS64PH 0x1702 + +#define PCI_VENDOR_ID_BERKOM 0x0871 +#define PCI_DEVICE_ID_BERKOM_A1T 0xffa1 +#define PCI_DEVICE_ID_BERKOM_T_CONCEPT 0xffa2 +#define PCI_DEVICE_ID_BERKOM_A4T 0xffa4 +#define PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO 0xffa8 + +#define PCI_VENDOR_ID_COMPAQ 0x0e11 +#define PCI_DEVICE_ID_COMPAQ_TOKENRING 0x0508 +#define PCI_DEVICE_ID_COMPAQ_TACHYON 0xa0fc +#define PCI_DEVICE_ID_COMPAQ_SMART2P 0xae10 +#define PCI_DEVICE_ID_COMPAQ_NETEL100 0xae32 +#define PCI_DEVICE_ID_COMPAQ_NETEL10 0xae34 +#define PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE 0xae33 +#define PCI_DEVICE_ID_COMPAQ_NETFLEX3I 0xae35 +#define PCI_DEVICE_ID_COMPAQ_NETEL100D 0xae40 +#define PCI_DEVICE_ID_COMPAQ_NETEL100PI 0xae43 +#define PCI_DEVICE_ID_COMPAQ_NETEL100I 0xb011 +#define PCI_DEVICE_ID_COMPAQ_CISS 0xb060 +#define PCI_DEVICE_ID_COMPAQ_CISSB 0xb178 +#define PCI_DEVICE_ID_COMPAQ_CISSC 0x46 +#define PCI_DEVICE_ID_COMPAQ_THUNDER 0xf130 +#define PCI_DEVICE_ID_COMPAQ_NETFLEX3B 0xf150 + +#define PCI_VENDOR_ID_NCR 0x1000 +#define PCI_VENDOR_ID_LSI_LOGIC 0x1000 +#define PCI_DEVICE_ID_NCR_53C810 0x0001 +#define PCI_DEVICE_ID_NCR_53C820 0x0002 +#define PCI_DEVICE_ID_NCR_53C825 0x0003 +#define PCI_DEVICE_ID_NCR_53C815 0x0004 +#define PCI_DEVICE_ID_LSI_53C810AP 0x0005 +#define PCI_DEVICE_ID_NCR_53C860 0x0006 +#define PCI_DEVICE_ID_LSI_53C1510 0x000a +#define PCI_DEVICE_ID_NCR_53C896 0x000b +#define PCI_DEVICE_ID_NCR_53C895 0x000c +#define PCI_DEVICE_ID_NCR_53C885 0x000d +#define PCI_DEVICE_ID_NCR_53C875 0x000f +#define PCI_DEVICE_ID_NCR_53C1510 0x0010 +#define PCI_DEVICE_ID_LSI_53C895A 0x0012 +#define PCI_DEVICE_ID_LSI_53C875A 0x0013 +#define PCI_DEVICE_ID_LSI_53C1010_33 0x0020 +#define PCI_DEVICE_ID_LSI_53C1010_66 0x0021 +#define PCI_DEVICE_ID_LSI_53C1030 0x0030 +#define PCI_DEVICE_ID_LSI_1030_53C1035 0x0032 +#define PCI_DEVICE_ID_LSI_53C1035 0x0040 +#define PCI_DEVICE_ID_NCR_53C875J 0x008f +#define PCI_DEVICE_ID_LSI_FC909 0x0621 +#define PCI_DEVICE_ID_LSI_FC929 0x0622 +#define PCI_DEVICE_ID_LSI_FC929_LAN 0x0623 +#define PCI_DEVICE_ID_LSI_FC919 0x0624 +#define PCI_DEVICE_ID_LSI_FC919_LAN 0x0625 +#define PCI_DEVICE_ID_LSI_FC929X 0x0626 +#define PCI_DEVICE_ID_LSI_FC939X 0x0642 +#define PCI_DEVICE_ID_LSI_FC949X 0x0640 +#define PCI_DEVICE_ID_LSI_FC949ES 0x0646 +#define PCI_DEVICE_ID_LSI_FC919X 0x0628 +#define PCI_DEVICE_ID_NCR_YELLOWFIN 0x0701 +#define PCI_DEVICE_ID_LSI_61C102 0x0901 +#define PCI_DEVICE_ID_LSI_63C815 0x1000 +#define PCI_DEVICE_ID_LSI_SAS1064 0x0050 +#define PCI_DEVICE_ID_LSI_SAS1064R 0x0411 +#define PCI_DEVICE_ID_LSI_SAS1066 0x005E +#define PCI_DEVICE_ID_LSI_SAS1068 0x0054 +#define PCI_DEVICE_ID_LSI_SAS1064A 0x005C +#define PCI_DEVICE_ID_LSI_SAS1064E 0x0056 +#define PCI_DEVICE_ID_LSI_SAS1066E 0x005A +#define PCI_DEVICE_ID_LSI_SAS1068E 0x0058 +#define PCI_DEVICE_ID_LSI_SAS1078 0x0060 + +#define PCI_VENDOR_ID_ATI 0x1002 +/* Mach64 */ +#define PCI_DEVICE_ID_ATI_68800 0x4158 +#define PCI_DEVICE_ID_ATI_215CT222 0x4354 +#define PCI_DEVICE_ID_ATI_210888CX 0x4358 +#define PCI_DEVICE_ID_ATI_215ET222 0x4554 +/* Mach64 / Rage */ +#define PCI_DEVICE_ID_ATI_215GB 0x4742 +#define PCI_DEVICE_ID_ATI_215GD 0x4744 +#define PCI_DEVICE_ID_ATI_215GI 0x4749 +#define PCI_DEVICE_ID_ATI_215GP 0x4750 +#define PCI_DEVICE_ID_ATI_215GQ 0x4751 +#define PCI_DEVICE_ID_ATI_215XL 0x4752 +#define PCI_DEVICE_ID_ATI_215GT 0x4754 +#define PCI_DEVICE_ID_ATI_215GTB 0x4755 +#define PCI_DEVICE_ID_ATI_215_IV 0x4756 +#define PCI_DEVICE_ID_ATI_215_IW 0x4757 +#define PCI_DEVICE_ID_ATI_215_IZ 0x475A +#define PCI_DEVICE_ID_ATI_210888GX 0x4758 +#define PCI_DEVICE_ID_ATI_215_LB 0x4c42 +#define PCI_DEVICE_ID_ATI_215_LD 0x4c44 +#define PCI_DEVICE_ID_ATI_215_LG 0x4c47 +#define PCI_DEVICE_ID_ATI_215_LI 0x4c49 +#define PCI_DEVICE_ID_ATI_215_LM 0x4c4D +#define PCI_DEVICE_ID_ATI_215_LN 0x4c4E +#define PCI_DEVICE_ID_ATI_215_LR 0x4c52 +#define PCI_DEVICE_ID_ATI_215_LS 0x4c53 +#define PCI_DEVICE_ID_ATI_264_LT 0x4c54 +/* Mach64 VT */ +#define PCI_DEVICE_ID_ATI_264VT 0x5654 +#define PCI_DEVICE_ID_ATI_264VU 0x5655 +#define PCI_DEVICE_ID_ATI_264VV 0x5656 +/* Rage128 GL */ +#define PCI_DEVICE_ID_ATI_RAGE128_RE 0x5245 +#define PCI_DEVICE_ID_ATI_RAGE128_RF 0x5246 +#define PCI_DEVICE_ID_ATI_RAGE128_RG 0x5247 +/* Rage128 VR */ +#define PCI_DEVICE_ID_ATI_RAGE128_RK 0x524b +#define PCI_DEVICE_ID_ATI_RAGE128_RL 0x524c +#define PCI_DEVICE_ID_ATI_RAGE128_SE 0x5345 +#define PCI_DEVICE_ID_ATI_RAGE128_SF 0x5346 +#define PCI_DEVICE_ID_ATI_RAGE128_SG 0x5347 +#define PCI_DEVICE_ID_ATI_RAGE128_SH 0x5348 +#define PCI_DEVICE_ID_ATI_RAGE128_SK 0x534b +#define PCI_DEVICE_ID_ATI_RAGE128_SL 0x534c +#define PCI_DEVICE_ID_ATI_RAGE128_SM 0x534d +#define PCI_DEVICE_ID_ATI_RAGE128_SN 0x534e +/* Rage128 Ultra */ +#define PCI_DEVICE_ID_ATI_RAGE128_TF 0x5446 +#define PCI_DEVICE_ID_ATI_RAGE128_TL 0x544c +#define PCI_DEVICE_ID_ATI_RAGE128_TR 0x5452 +#define PCI_DEVICE_ID_ATI_RAGE128_TS 0x5453 +#define PCI_DEVICE_ID_ATI_RAGE128_TT 0x5454 +#define PCI_DEVICE_ID_ATI_RAGE128_TU 0x5455 +/* Rage128 M3 */ +#define PCI_DEVICE_ID_ATI_RAGE128_LE 0x4c45 +#define PCI_DEVICE_ID_ATI_RAGE128_LF 0x4c46 +/* Rage128 M4 */ +#define PCI_DEVICE_ID_ATI_RAGE128_MF 0x4d46 +#define PCI_DEVICE_ID_ATI_RAGE128_ML 0x4d4c +/* Rage128 Pro GL */ +#define PCI_DEVICE_ID_ATI_RAGE128_PA 0x5041 +#define PCI_DEVICE_ID_ATI_RAGE128_PB 0x5042 +#define PCI_DEVICE_ID_ATI_RAGE128_PC 0x5043 +#define PCI_DEVICE_ID_ATI_RAGE128_PD 0x5044 +#define PCI_DEVICE_ID_ATI_RAGE128_PE 0x5045 +#define PCI_DEVICE_ID_ATI_RAGE128_PF 0x5046 +/* Rage128 Pro VR */ +#define PCI_DEVICE_ID_ATI_RAGE128_PG 0x5047 +#define PCI_DEVICE_ID_ATI_RAGE128_PH 0x5048 +#define PCI_DEVICE_ID_ATI_RAGE128_PI 0x5049 +#define PCI_DEVICE_ID_ATI_RAGE128_PJ 0x504A +#define PCI_DEVICE_ID_ATI_RAGE128_PK 0x504B +#define PCI_DEVICE_ID_ATI_RAGE128_PL 0x504C +#define PCI_DEVICE_ID_ATI_RAGE128_PM 0x504D +#define PCI_DEVICE_ID_ATI_RAGE128_PN 0x504E +#define PCI_DEVICE_ID_ATI_RAGE128_PO 0x504F +#define PCI_DEVICE_ID_ATI_RAGE128_PP 0x5050 +#define PCI_DEVICE_ID_ATI_RAGE128_PQ 0x5051 +#define PCI_DEVICE_ID_ATI_RAGE128_PR 0x5052 +#define PCI_DEVICE_ID_ATI_RAGE128_PS 0x5053 +#define PCI_DEVICE_ID_ATI_RAGE128_PT 0x5054 +#define PCI_DEVICE_ID_ATI_RAGE128_PU 0x5055 +#define PCI_DEVICE_ID_ATI_RAGE128_PV 0x5056 +#define PCI_DEVICE_ID_ATI_RAGE128_PW 0x5057 +#define PCI_DEVICE_ID_ATI_RAGE128_PX 0x5058 +/* Rage128 M4 */ +/* Radeon R100 */ +#define PCI_DEVICE_ID_ATI_RADEON_QD 0x5144 +#define PCI_DEVICE_ID_ATI_RADEON_QE 0x5145 +#define PCI_DEVICE_ID_ATI_RADEON_QF 0x5146 +#define PCI_DEVICE_ID_ATI_RADEON_QG 0x5147 +/* Radeon RV100 (VE) */ +#define PCI_DEVICE_ID_ATI_RADEON_QY 0x5159 +#define PCI_DEVICE_ID_ATI_RADEON_QZ 0x515a +/* Radeon R200 (8500) */ +#define PCI_DEVICE_ID_ATI_RADEON_QL 0x514c +#define PCI_DEVICE_ID_ATI_RADEON_QN 0x514e +#define PCI_DEVICE_ID_ATI_RADEON_QO 0x514f +#define PCI_DEVICE_ID_ATI_RADEON_Ql 0x516c +#define PCI_DEVICE_ID_ATI_RADEON_BB 0x4242 +/* Radeon R200 (9100) */ +#define PCI_DEVICE_ID_ATI_RADEON_QM 0x514d +/* Radeon RV200 (7500) */ +#define PCI_DEVICE_ID_ATI_RADEON_QW 0x5157 +#define PCI_DEVICE_ID_ATI_RADEON_QX 0x5158 +/* Radeon NV-100 */ +/* Radeon RV250 (9000) */ +#define PCI_DEVICE_ID_ATI_RADEON_Id 0x4964 +#define PCI_DEVICE_ID_ATI_RADEON_Ie 0x4965 +#define PCI_DEVICE_ID_ATI_RADEON_If 0x4966 +#define PCI_DEVICE_ID_ATI_RADEON_Ig 0x4967 +/* Radeon RV280 (9200) */ +#define PCI_DEVICE_ID_ATI_RADEON_Ya 0x5961 +#define PCI_DEVICE_ID_ATI_RADEON_Yd 0x5964 +/* Radeon R300 (9500) */ +/* Radeon R300 (9700) */ +#define PCI_DEVICE_ID_ATI_RADEON_ND 0x4e44 +#define PCI_DEVICE_ID_ATI_RADEON_NE 0x4e45 +#define PCI_DEVICE_ID_ATI_RADEON_NF 0x4e46 +#define PCI_DEVICE_ID_ATI_RADEON_NG 0x4e47 +/* Radeon R350 (9800) */ +/* Radeon RV350 (9600) */ +/* Radeon M6 */ +#define PCI_DEVICE_ID_ATI_RADEON_LY 0x4c59 +#define PCI_DEVICE_ID_ATI_RADEON_LZ 0x4c5a +/* Radeon M7 */ +#define PCI_DEVICE_ID_ATI_RADEON_LW 0x4c57 +#define PCI_DEVICE_ID_ATI_RADEON_LX 0x4c58 +/* Radeon M9 */ +#define PCI_DEVICE_ID_ATI_RADEON_Ld 0x4c64 +#define PCI_DEVICE_ID_ATI_RADEON_Le 0x4c65 +#define PCI_DEVICE_ID_ATI_RADEON_Lf 0x4c66 +#define PCI_DEVICE_ID_ATI_RADEON_Lg 0x4c67 +/* Radeon */ +/* RadeonIGP */ +#define PCI_DEVICE_ID_ATI_RS100 0xcab0 +#define PCI_DEVICE_ID_ATI_RS200 0xcab2 +#define PCI_DEVICE_ID_ATI_RS200_B 0xcbb2 +#define PCI_DEVICE_ID_ATI_RS250 0xcab3 +#define PCI_DEVICE_ID_ATI_RS300_100 0x5830 +#define PCI_DEVICE_ID_ATI_RS300_133 0x5831 +#define PCI_DEVICE_ID_ATI_RS300_166 0x5832 +#define PCI_DEVICE_ID_ATI_RS300_200 0x5833 +#define PCI_DEVICE_ID_ATI_RS350_100 0x7830 +#define PCI_DEVICE_ID_ATI_RS350_133 0x7831 +#define PCI_DEVICE_ID_ATI_RS350_166 0x7832 +#define PCI_DEVICE_ID_ATI_RS350_200 0x7833 +#define PCI_DEVICE_ID_ATI_RS400_100 0x5a30 +#define PCI_DEVICE_ID_ATI_RS400_133 0x5a31 +#define PCI_DEVICE_ID_ATI_RS400_166 0x5a32 +#define PCI_DEVICE_ID_ATI_RS400_200 0x5a33 +#define PCI_DEVICE_ID_ATI_RS480 0x5950 +/* ATI IXP Chipset */ +#define PCI_DEVICE_ID_ATI_IXP200_IDE 0x4349 +#define PCI_DEVICE_ID_ATI_IXP200_SMBUS 0x4353 +#define PCI_DEVICE_ID_ATI_IXP300_SMBUS 0x4363 +#define PCI_DEVICE_ID_ATI_IXP300_IDE 0x4369 +#define PCI_DEVICE_ID_ATI_IXP300_SATA 0x436e +#define PCI_DEVICE_ID_ATI_IXP400_SMBUS 0x4372 +#define PCI_DEVICE_ID_ATI_IXP400_IDE 0x4376 +#define PCI_DEVICE_ID_ATI_IXP400_SATA 0x4379 +#define PCI_DEVICE_ID_ATI_IXP400_SATA2 0x437a +#define PCI_DEVICE_ID_ATI_IXP600_SATA 0x4380 +#define PCI_DEVICE_ID_ATI_SBX00_SMBUS 0x4385 +#define PCI_DEVICE_ID_ATI_IXP600_IDE 0x438c +#define PCI_DEVICE_ID_ATI_IXP700_SATA 0x4390 +#define PCI_DEVICE_ID_ATI_IXP700_IDE 0x439c + +#define PCI_VENDOR_ID_VLSI 0x1004 +#define PCI_DEVICE_ID_VLSI_82C592 0x0005 +#define PCI_DEVICE_ID_VLSI_82C593 0x0006 +#define PCI_DEVICE_ID_VLSI_82C594 0x0007 +#define PCI_DEVICE_ID_VLSI_82C597 0x0009 +#define PCI_DEVICE_ID_VLSI_82C541 0x000c +#define PCI_DEVICE_ID_VLSI_82C543 0x000d +#define PCI_DEVICE_ID_VLSI_82C532 0x0101 +#define PCI_DEVICE_ID_VLSI_82C534 0x0102 +#define PCI_DEVICE_ID_VLSI_82C535 0x0104 +#define PCI_DEVICE_ID_VLSI_82C147 0x0105 +#define PCI_DEVICE_ID_VLSI_VAS96011 0x0702 + +/* AMD RD890 Chipset */ +#define PCI_DEVICE_ID_RD890_IOMMU 0x5a23 + +#define PCI_VENDOR_ID_ADL 0x1005 +#define PCI_DEVICE_ID_ADL_2301 0x2301 + +#define PCI_VENDOR_ID_NS 0x100b +#define PCI_DEVICE_ID_NS_87415 0x0002 +#define PCI_DEVICE_ID_NS_87560_LIO 0x000e +#define PCI_DEVICE_ID_NS_87560_USB 0x0012 +#define PCI_DEVICE_ID_NS_83815 0x0020 +#define PCI_DEVICE_ID_NS_83820 0x0022 +#define PCI_DEVICE_ID_NS_CS5535_ISA 0x002b +#define PCI_DEVICE_ID_NS_CS5535_IDE 0x002d +#define PCI_DEVICE_ID_NS_CS5535_AUDIO 0x002e +#define PCI_DEVICE_ID_NS_CS5535_USB 0x002f +#define PCI_DEVICE_ID_NS_GX_VIDEO 0x0030 +#define PCI_DEVICE_ID_NS_SATURN 0x0035 +#define PCI_DEVICE_ID_NS_SCx200_BRIDGE 0x0500 +#define PCI_DEVICE_ID_NS_SCx200_SMI 0x0501 +#define PCI_DEVICE_ID_NS_SCx200_IDE 0x0502 +#define PCI_DEVICE_ID_NS_SCx200_AUDIO 0x0503 +#define PCI_DEVICE_ID_NS_SCx200_VIDEO 0x0504 +#define PCI_DEVICE_ID_NS_SCx200_XBUS 0x0505 +#define PCI_DEVICE_ID_NS_SC1100_BRIDGE 0x0510 +#define PCI_DEVICE_ID_NS_SC1100_SMI 0x0511 +#define PCI_DEVICE_ID_NS_SC1100_XBUS 0x0515 +#define PCI_DEVICE_ID_NS_87410 0xd001 + +#define PCI_DEVICE_ID_NS_GX_HOST_BRIDGE 0x0028 + +#define PCI_VENDOR_ID_TSENG 0x100c +#define PCI_DEVICE_ID_TSENG_W32P_2 0x3202 +#define PCI_DEVICE_ID_TSENG_W32P_b 0x3205 +#define PCI_DEVICE_ID_TSENG_W32P_c 0x3206 +#define PCI_DEVICE_ID_TSENG_W32P_d 0x3207 +#define PCI_DEVICE_ID_TSENG_ET6000 0x3208 + +#define PCI_VENDOR_ID_WEITEK 0x100e +#define PCI_DEVICE_ID_WEITEK_P9000 0x9001 +#define PCI_DEVICE_ID_WEITEK_P9100 0x9100 + +#define PCI_VENDOR_ID_DEC 0x1011 +#define PCI_DEVICE_ID_DEC_BRD 0x0001 +#define PCI_DEVICE_ID_DEC_TULIP 0x0002 +#define PCI_DEVICE_ID_DEC_TGA 0x0004 +#define PCI_DEVICE_ID_DEC_TULIP_FAST 0x0009 +#define PCI_DEVICE_ID_DEC_TGA2 0x000D +#define PCI_DEVICE_ID_DEC_FDDI 0x000F +#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 +#define PCI_DEVICE_ID_DEC_21142 0x0019 +#define PCI_DEVICE_ID_DEC_21052 0x0021 +#define PCI_DEVICE_ID_DEC_21150 0x0022 +#define PCI_DEVICE_ID_DEC_21152 0x0024 +#define PCI_DEVICE_ID_DEC_21153 0x0025 +#define PCI_DEVICE_ID_DEC_21154 0x0026 +#define PCI_DEVICE_ID_DEC_21285 0x1065 +#define PCI_DEVICE_ID_COMPAQ_42XX 0x0046 + +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#define PCI_DEVICE_ID_CIRRUS_7548 0x0038 +#define PCI_DEVICE_ID_CIRRUS_5430 0x00a0 +#define PCI_DEVICE_ID_CIRRUS_5434_4 0x00a4 +#define PCI_DEVICE_ID_CIRRUS_5434_8 0x00a8 +#define PCI_DEVICE_ID_CIRRUS_5436 0x00ac +#define PCI_DEVICE_ID_CIRRUS_5446 0x00b8 +#define PCI_DEVICE_ID_CIRRUS_5480 0x00bc +#define PCI_DEVICE_ID_CIRRUS_5462 0x00d0 +#define PCI_DEVICE_ID_CIRRUS_5464 0x00d4 +#define PCI_DEVICE_ID_CIRRUS_5465 0x00d6 +#define PCI_DEVICE_ID_CIRRUS_6729 0x1100 +#define PCI_DEVICE_ID_CIRRUS_6832 0x1110 +#define PCI_DEVICE_ID_CIRRUS_7543 0x1202 +#define PCI_DEVICE_ID_CIRRUS_4610 0x6001 +#define PCI_DEVICE_ID_CIRRUS_4612 0x6003 +#define PCI_DEVICE_ID_CIRRUS_4615 0x6004 + +#define PCI_VENDOR_ID_IBM 0x1014 +#define PCI_DEVICE_ID_IBM_TR 0x0018 +#define PCI_DEVICE_ID_IBM_TR_WAKE 0x003e +#define PCI_DEVICE_ID_IBM_CPC710_PCI64 0x00fc +#define PCI_DEVICE_ID_IBM_SNIPE 0x0180 +#define PCI_DEVICE_ID_IBM_CITRINE 0x028C +#define PCI_DEVICE_ID_IBM_GEMSTONE 0xB166 +#define PCI_DEVICE_ID_IBM_OBSIDIAN 0x02BD +#define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1 0x0031 +#define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2 0x0219 +#define PCI_DEVICE_ID_IBM_ICOM_V2_TWO_PORTS_RVX 0x021A +#define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM 0x0251 +#define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM_PCIE 0x0361 +#define PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL 0x252 + +#define PCI_SUBVENDOR_ID_IBM 0x1014 +#define PCI_SUBDEVICE_ID_IBM_SATURN_SERIAL_ONE_PORT 0x03d4 + +#define PCI_VENDOR_ID_UNISYS 0x1018 +#define PCI_DEVICE_ID_UNISYS_DMA_DIRECTOR 0x001C + +#define PCI_VENDOR_ID_COMPEX2 0x101a /* pci.ids says "AT&T GIS (NCR)" */ +#define PCI_DEVICE_ID_COMPEX2_100VG 0x0005 + +#define PCI_VENDOR_ID_WD 0x101c +#define PCI_DEVICE_ID_WD_90C 0xc24a + +#define PCI_VENDOR_ID_AMI 0x101e +#define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960 +#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010 +#define PCI_DEVICE_ID_AMI_MEGARAID2 0x9060 + +#define PCI_VENDOR_ID_AMD 0x1022 +#define PCI_DEVICE_ID_AMD_K8_NB 0x1100 +#define PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP 0x1101 +#define PCI_DEVICE_ID_AMD_K8_NB_MEMCTL 0x1102 +#define PCI_DEVICE_ID_AMD_K8_NB_MISC 0x1103 +#define PCI_DEVICE_ID_AMD_10H_NB_HT 0x1200 +#define PCI_DEVICE_ID_AMD_10H_NB_MAP 0x1201 +#define PCI_DEVICE_ID_AMD_10H_NB_DRAM 0x1202 +#define PCI_DEVICE_ID_AMD_10H_NB_MISC 0x1203 +#define PCI_DEVICE_ID_AMD_10H_NB_LINK 0x1204 +#define PCI_DEVICE_ID_AMD_11H_NB_HT 0x1300 +#define PCI_DEVICE_ID_AMD_11H_NB_MAP 0x1301 +#define PCI_DEVICE_ID_AMD_11H_NB_DRAM 0x1302 +#define PCI_DEVICE_ID_AMD_11H_NB_MISC 0x1303 +#define PCI_DEVICE_ID_AMD_11H_NB_LINK 0x1304 +#define PCI_DEVICE_ID_AMD_15H_NB_F3 0x1603 +#define PCI_DEVICE_ID_AMD_15H_NB_F4 0x1604 +#define PCI_DEVICE_ID_AMD_CNB17H_F3 0x1703 +#define PCI_DEVICE_ID_AMD_LANCE 0x2000 +#define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001 +#define PCI_DEVICE_ID_AMD_SCSI 0x2020 +#define PCI_DEVICE_ID_AMD_SERENADE 0x36c0 +#define PCI_DEVICE_ID_AMD_FE_GATE_7006 0x7006 +#define PCI_DEVICE_ID_AMD_FE_GATE_7007 0x7007 +#define PCI_DEVICE_ID_AMD_FE_GATE_700C 0x700C +#define PCI_DEVICE_ID_AMD_FE_GATE_700E 0x700E +#define PCI_DEVICE_ID_AMD_COBRA_7401 0x7401 +#define PCI_DEVICE_ID_AMD_VIPER_7409 0x7409 +#define PCI_DEVICE_ID_AMD_VIPER_740B 0x740B +#define PCI_DEVICE_ID_AMD_VIPER_7410 0x7410 +#define PCI_DEVICE_ID_AMD_VIPER_7411 0x7411 +#define PCI_DEVICE_ID_AMD_VIPER_7413 0x7413 +#define PCI_DEVICE_ID_AMD_VIPER_7440 0x7440 +#define PCI_DEVICE_ID_AMD_OPUS_7441 0x7441 +#define PCI_DEVICE_ID_AMD_OPUS_7443 0x7443 +#define PCI_DEVICE_ID_AMD_VIPER_7443 0x7443 +#define PCI_DEVICE_ID_AMD_OPUS_7445 0x7445 +#define PCI_DEVICE_ID_AMD_8111_PCI 0x7460 +#define PCI_DEVICE_ID_AMD_8111_LPC 0x7468 +#define PCI_DEVICE_ID_AMD_8111_IDE 0x7469 +#define PCI_DEVICE_ID_AMD_8111_SMBUS2 0x746a +#define PCI_DEVICE_ID_AMD_8111_SMBUS 0x746b +#define PCI_DEVICE_ID_AMD_8111_AUDIO 0x746d +#define PCI_DEVICE_ID_AMD_8151_0 0x7454 +#define PCI_DEVICE_ID_AMD_8131_BRIDGE 0x7450 +#define PCI_DEVICE_ID_AMD_8131_APIC 0x7451 +#define PCI_DEVICE_ID_AMD_8132_BRIDGE 0x7458 +#define PCI_DEVICE_ID_AMD_HUDSON2_SMBUS 0x780b +#define PCI_DEVICE_ID_AMD_CS5535_IDE 0x208F +#define PCI_DEVICE_ID_AMD_CS5536_ISA 0x2090 +#define PCI_DEVICE_ID_AMD_CS5536_FLASH 0x2091 +#define PCI_DEVICE_ID_AMD_CS5536_AUDIO 0x2093 +#define PCI_DEVICE_ID_AMD_CS5536_OHC 0x2094 +#define PCI_DEVICE_ID_AMD_CS5536_EHC 0x2095 +#define PCI_DEVICE_ID_AMD_CS5536_UDC 0x2096 +#define PCI_DEVICE_ID_AMD_CS5536_UOC 0x2097 +#define PCI_DEVICE_ID_AMD_CS5536_IDE 0x209A +#define PCI_DEVICE_ID_AMD_LX_VIDEO 0x2081 +#define PCI_DEVICE_ID_AMD_LX_AES 0x2082 +#define PCI_DEVICE_ID_AMD_HUDSON2_IDE 0x780c +#define PCI_DEVICE_ID_AMD_HUDSON2_SATA_IDE 0x7800 + +#define PCI_VENDOR_ID_TRIDENT 0x1023 +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001 +#define PCI_DEVICE_ID_TRIDENT_9320 0x9320 +#define PCI_DEVICE_ID_TRIDENT_9388 0x9388 +#define PCI_DEVICE_ID_TRIDENT_9397 0x9397 +#define PCI_DEVICE_ID_TRIDENT_939A 0x939A +#define PCI_DEVICE_ID_TRIDENT_9520 0x9520 +#define PCI_DEVICE_ID_TRIDENT_9525 0x9525 +#define PCI_DEVICE_ID_TRIDENT_9420 0x9420 +#define PCI_DEVICE_ID_TRIDENT_9440 0x9440 +#define PCI_DEVICE_ID_TRIDENT_9660 0x9660 +#define PCI_DEVICE_ID_TRIDENT_9750 0x9750 +#define PCI_DEVICE_ID_TRIDENT_9850 0x9850 +#define PCI_DEVICE_ID_TRIDENT_9880 0x9880 +#define PCI_DEVICE_ID_TRIDENT_8400 0x8400 +#define PCI_DEVICE_ID_TRIDENT_8420 0x8420 +#define PCI_DEVICE_ID_TRIDENT_8500 0x8500 + +#define PCI_VENDOR_ID_AI 0x1025 +#define PCI_DEVICE_ID_AI_M1435 0x1435 + +#define PCI_VENDOR_ID_DELL 0x1028 +#define PCI_DEVICE_ID_DELL_RACIII 0x0008 +#define PCI_DEVICE_ID_DELL_RAC4 0x0012 +#define PCI_DEVICE_ID_DELL_PERC5 0x0015 + +#define PCI_VENDOR_ID_MATROX 0x102B +#define PCI_DEVICE_ID_MATROX_MGA_2 0x0518 +#define PCI_DEVICE_ID_MATROX_MIL 0x0519 +#define PCI_DEVICE_ID_MATROX_MYS 0x051A +#define PCI_DEVICE_ID_MATROX_MIL_2 0x051b +#define PCI_DEVICE_ID_MATROX_MYS_AGP 0x051e +#define PCI_DEVICE_ID_MATROX_MIL_2_AGP 0x051f +#define PCI_DEVICE_ID_MATROX_MGA_IMP 0x0d10 +#define PCI_DEVICE_ID_MATROX_G100_MM 0x1000 +#define PCI_DEVICE_ID_MATROX_G100_AGP 0x1001 +#define PCI_DEVICE_ID_MATROX_G200_PCI 0x0520 +#define PCI_DEVICE_ID_MATROX_G200_AGP 0x0521 +#define PCI_DEVICE_ID_MATROX_G400 0x0525 +#define PCI_DEVICE_ID_MATROX_G200EV_PCI 0x0530 +#define PCI_DEVICE_ID_MATROX_G550 0x2527 +#define PCI_DEVICE_ID_MATROX_VIA 0x4536 + +#define PCI_VENDOR_ID_MOBILITY_ELECTRONICS 0x14f2 + +#define PCI_VENDOR_ID_CT 0x102c +#define PCI_DEVICE_ID_CT_69000 0x00c0 +#define PCI_DEVICE_ID_CT_65545 0x00d8 +#define PCI_DEVICE_ID_CT_65548 0x00dc +#define PCI_DEVICE_ID_CT_65550 0x00e0 +#define PCI_DEVICE_ID_CT_65554 0x00e4 +#define PCI_DEVICE_ID_CT_65555 0x00e5 + +#define PCI_VENDOR_ID_MIRO 0x1031 +#define PCI_DEVICE_ID_MIRO_36050 0x5601 +#define PCI_DEVICE_ID_MIRO_DC10PLUS 0x7efe +#define PCI_DEVICE_ID_MIRO_DC30PLUS 0xd801 + +#define PCI_VENDOR_ID_NEC 0x1033 +#define PCI_DEVICE_ID_NEC_CBUS_1 0x0001 /* PCI-Cbus Bridge */ +#define PCI_DEVICE_ID_NEC_LOCAL 0x0002 /* Local Bridge */ +#define PCI_DEVICE_ID_NEC_ATM 0x0003 /* ATM LAN Controller */ +#define PCI_DEVICE_ID_NEC_R4000 0x0004 /* R4000 Bridge */ +#define PCI_DEVICE_ID_NEC_486 0x0005 /* 486 Like Peripheral Bus Bridge */ +#define PCI_DEVICE_ID_NEC_ACCEL_1 0x0006 /* Graphic Accelerator */ +#define PCI_DEVICE_ID_NEC_UXBUS 0x0007 /* UX-Bus Bridge */ +#define PCI_DEVICE_ID_NEC_ACCEL_2 0x0008 /* Graphic Accelerator */ +#define PCI_DEVICE_ID_NEC_GRAPH 0x0009 /* PCI-CoreGraph Bridge */ +#define PCI_DEVICE_ID_NEC_VL 0x0016 /* PCI-VL Bridge */ +#define PCI_DEVICE_ID_NEC_STARALPHA2 0x002c /* STAR ALPHA2 */ +#define PCI_DEVICE_ID_NEC_CBUS_2 0x002d /* PCI-Cbus Bridge */ +#define PCI_DEVICE_ID_NEC_USB 0x0035 /* PCI-USB Host */ +#define PCI_DEVICE_ID_NEC_CBUS_3 0x003b +#define PCI_DEVICE_ID_NEC_NAPCCARD 0x003e +#define PCI_DEVICE_ID_NEC_PCX2 0x0046 /* PowerVR */ +#define PCI_DEVICE_ID_NEC_VRC5476 0x009b +#define PCI_DEVICE_ID_NEC_VRC4173 0x00a5 +#define PCI_DEVICE_ID_NEC_VRC5477_AC97 0x00a6 +#define PCI_DEVICE_ID_NEC_PC9821CS01 0x800c /* PC-9821-CS01 */ +#define PCI_DEVICE_ID_NEC_PC9821NRB06 0x800d /* PC-9821NR-B06 */ + +#define PCI_VENDOR_ID_FD 0x1036 +#define PCI_DEVICE_ID_FD_36C70 0x0000 + +#define PCI_VENDOR_ID_SI 0x1039 +#define PCI_DEVICE_ID_SI_5591_AGP 0x0001 +#define PCI_DEVICE_ID_SI_6202 0x0002 +#define PCI_DEVICE_ID_SI_503 0x0008 +#define PCI_DEVICE_ID_SI_ACPI 0x0009 +#define PCI_DEVICE_ID_SI_SMBUS 0x0016 +#define PCI_DEVICE_ID_SI_LPC 0x0018 +#define PCI_DEVICE_ID_SI_5597_VGA 0x0200 +#define PCI_DEVICE_ID_SI_6205 0x0205 +#define PCI_DEVICE_ID_SI_501 0x0406 +#define PCI_DEVICE_ID_SI_496 0x0496 +#define PCI_DEVICE_ID_SI_300 0x0300 +#define PCI_DEVICE_ID_SI_315H 0x0310 +#define PCI_DEVICE_ID_SI_315 0x0315 +#define PCI_DEVICE_ID_SI_315PRO 0x0325 +#define PCI_DEVICE_ID_SI_530 0x0530 +#define PCI_DEVICE_ID_SI_540 0x0540 +#define PCI_DEVICE_ID_SI_550 0x0550 +#define PCI_DEVICE_ID_SI_540_VGA 0x5300 +#define PCI_DEVICE_ID_SI_550_VGA 0x5315 +#define PCI_DEVICE_ID_SI_620 0x0620 +#define PCI_DEVICE_ID_SI_630 0x0630 +#define PCI_DEVICE_ID_SI_633 0x0633 +#define PCI_DEVICE_ID_SI_635 0x0635 +#define PCI_DEVICE_ID_SI_640 0x0640 +#define PCI_DEVICE_ID_SI_645 0x0645 +#define PCI_DEVICE_ID_SI_646 0x0646 +#define PCI_DEVICE_ID_SI_648 0x0648 +#define PCI_DEVICE_ID_SI_650 0x0650 +#define PCI_DEVICE_ID_SI_651 0x0651 +#define PCI_DEVICE_ID_SI_655 0x0655 +#define PCI_DEVICE_ID_SI_661 0x0661 +#define PCI_DEVICE_ID_SI_730 0x0730 +#define PCI_DEVICE_ID_SI_733 0x0733 +#define PCI_DEVICE_ID_SI_630_VGA 0x6300 +#define PCI_DEVICE_ID_SI_735 0x0735 +#define PCI_DEVICE_ID_SI_740 0x0740 +#define PCI_DEVICE_ID_SI_741 0x0741 +#define PCI_DEVICE_ID_SI_745 0x0745 +#define PCI_DEVICE_ID_SI_746 0x0746 +#define PCI_DEVICE_ID_SI_755 0x0755 +#define PCI_DEVICE_ID_SI_760 0x0760 +#define PCI_DEVICE_ID_SI_900 0x0900 +#define PCI_DEVICE_ID_SI_961 0x0961 +#define PCI_DEVICE_ID_SI_962 0x0962 +#define PCI_DEVICE_ID_SI_963 0x0963 +#define PCI_DEVICE_ID_SI_965 0x0965 +#define PCI_DEVICE_ID_SI_966 0x0966 +#define PCI_DEVICE_ID_SI_968 0x0968 +#define PCI_DEVICE_ID_SI_1180 0x1180 +#define PCI_DEVICE_ID_SI_5511 0x5511 +#define PCI_DEVICE_ID_SI_5513 0x5513 +#define PCI_DEVICE_ID_SI_5517 0x5517 +#define PCI_DEVICE_ID_SI_5518 0x5518 +#define PCI_DEVICE_ID_SI_5571 0x5571 +#define PCI_DEVICE_ID_SI_5581 0x5581 +#define PCI_DEVICE_ID_SI_5582 0x5582 +#define PCI_DEVICE_ID_SI_5591 0x5591 +#define PCI_DEVICE_ID_SI_5596 0x5596 +#define PCI_DEVICE_ID_SI_5597 0x5597 +#define PCI_DEVICE_ID_SI_5598 0x5598 +#define PCI_DEVICE_ID_SI_5600 0x5600 +#define PCI_DEVICE_ID_SI_7012 0x7012 +#define PCI_DEVICE_ID_SI_7013 0x7013 +#define PCI_DEVICE_ID_SI_7016 0x7016 +#define PCI_DEVICE_ID_SI_7018 0x7018 + +#define PCI_VENDOR_ID_HP 0x103c +#define PCI_DEVICE_ID_HP_VISUALIZE_EG 0x1005 +#define PCI_DEVICE_ID_HP_VISUALIZE_FX6 0x1006 +#define PCI_DEVICE_ID_HP_VISUALIZE_FX4 0x1008 +#define PCI_DEVICE_ID_HP_VISUALIZE_FX2 0x100a +#define PCI_DEVICE_ID_HP_TACHYON 0x1028 +#define PCI_DEVICE_ID_HP_TACHLITE 0x1029 +#define PCI_DEVICE_ID_HP_J2585A 0x1030 +#define PCI_DEVICE_ID_HP_J2585B 0x1031 +#define PCI_DEVICE_ID_HP_J2973A 0x1040 +#define PCI_DEVICE_ID_HP_J2970A 0x1042 +#define PCI_DEVICE_ID_HP_DIVA 0x1048 +#define PCI_DEVICE_ID_HP_DIVA_TOSCA1 0x1049 +#define PCI_DEVICE_ID_HP_DIVA_TOSCA2 0x104A +#define PCI_DEVICE_ID_HP_DIVA_MAESTRO 0x104B +#define PCI_DEVICE_ID_HP_REO_IOC 0x10f1 +#define PCI_DEVICE_ID_HP_VISUALIZE_FXE 0x108b +#define PCI_DEVICE_ID_HP_DIVA_HALFDOME 0x1223 +#define PCI_DEVICE_ID_HP_DIVA_KEYSTONE 0x1226 +#define PCI_DEVICE_ID_HP_DIVA_POWERBAR 0x1227 +#define PCI_DEVICE_ID_HP_ZX1_IOC 0x122a +#define PCI_DEVICE_ID_HP_PCIX_LBA 0x122e +#define PCI_DEVICE_ID_HP_SX1000_IOC 0x127c +#define PCI_DEVICE_ID_HP_DIVA_EVEREST 0x1282 +#define PCI_DEVICE_ID_HP_DIVA_AUX 0x1290 +#define PCI_DEVICE_ID_HP_DIVA_RMP3 0x1301 +#define PCI_DEVICE_ID_HP_DIVA_HURRICANE 0x132a +#define PCI_DEVICE_ID_HP_CISSA 0x3220 +#define PCI_DEVICE_ID_HP_CISSC 0x3230 +#define PCI_DEVICE_ID_HP_CISSD 0x3238 +#define PCI_DEVICE_ID_HP_CISSE 0x323a +#define PCI_DEVICE_ID_HP_CISSF 0x323b +#define PCI_DEVICE_ID_HP_ZX2_IOC 0x4031 + +#define PCI_VENDOR_ID_PCTECH 0x1042 +#define PCI_DEVICE_ID_PCTECH_RZ1000 0x1000 +#define PCI_DEVICE_ID_PCTECH_RZ1001 0x1001 +#define PCI_DEVICE_ID_PCTECH_SAMURAI_IDE 0x3020 + +#define PCI_VENDOR_ID_ASUSTEK 0x1043 +#define PCI_DEVICE_ID_ASUSTEK_0675 0x0675 + +#define PCI_VENDOR_ID_DPT 0x1044 +#define PCI_DEVICE_ID_DPT 0xa400 + +#define PCI_VENDOR_ID_OPTI 0x1045 +#define PCI_DEVICE_ID_OPTI_82C558 0xc558 +#define PCI_DEVICE_ID_OPTI_82C621 0xc621 +#define PCI_DEVICE_ID_OPTI_82C700 0xc700 +#define PCI_DEVICE_ID_OPTI_82C825 0xd568 + +#define PCI_VENDOR_ID_ELSA 0x1048 +#define PCI_DEVICE_ID_ELSA_MICROLINK 0x1000 +#define PCI_DEVICE_ID_ELSA_QS3000 0x3000 + +#define PCI_VENDOR_ID_STMICRO 0x104A + +#define PCI_VENDOR_ID_BUSLOGIC 0x104B +#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC 0x0140 +#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER 0x1040 +#define PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT 0x8130 + +#define PCI_VENDOR_ID_TI 0x104c +#define PCI_DEVICE_ID_TI_TVP4020 0x3d07 +#define PCI_DEVICE_ID_TI_4450 0x8011 +#define PCI_DEVICE_ID_TI_XX21_XX11 0x8031 +#define PCI_DEVICE_ID_TI_XX21_XX11_FM 0x8033 +#define PCI_DEVICE_ID_TI_XX21_XX11_SD 0x8034 +#define PCI_DEVICE_ID_TI_X515 0x8036 +#define PCI_DEVICE_ID_TI_XX12 0x8039 +#define PCI_DEVICE_ID_TI_XX12_FM 0x803b +#define PCI_DEVICE_ID_TI_XIO2000A 0x8231 +#define PCI_DEVICE_ID_TI_1130 0xac12 +#define PCI_DEVICE_ID_TI_1031 0xac13 +#define PCI_DEVICE_ID_TI_1131 0xac15 +#define PCI_DEVICE_ID_TI_1250 0xac16 +#define PCI_DEVICE_ID_TI_1220 0xac17 +#define PCI_DEVICE_ID_TI_1221 0xac19 +#define PCI_DEVICE_ID_TI_1210 0xac1a +#define PCI_DEVICE_ID_TI_1450 0xac1b +#define PCI_DEVICE_ID_TI_1225 0xac1c +#define PCI_DEVICE_ID_TI_1251A 0xac1d +#define PCI_DEVICE_ID_TI_1211 0xac1e +#define PCI_DEVICE_ID_TI_1251B 0xac1f +#define PCI_DEVICE_ID_TI_4410 0xac41 +#define PCI_DEVICE_ID_TI_4451 0xac42 +#define PCI_DEVICE_ID_TI_4510 0xac44 +#define PCI_DEVICE_ID_TI_4520 0xac46 +#define PCI_DEVICE_ID_TI_7510 0xac47 +#define PCI_DEVICE_ID_TI_7610 0xac48 +#define PCI_DEVICE_ID_TI_7410 0xac49 +#define PCI_DEVICE_ID_TI_1410 0xac50 +#define PCI_DEVICE_ID_TI_1420 0xac51 +#define PCI_DEVICE_ID_TI_1451A 0xac52 +#define PCI_DEVICE_ID_TI_1620 0xac54 +#define PCI_DEVICE_ID_TI_1520 0xac55 +#define PCI_DEVICE_ID_TI_1510 0xac56 +#define PCI_DEVICE_ID_TI_X620 0xac8d +#define PCI_DEVICE_ID_TI_X420 0xac8e +#define PCI_DEVICE_ID_TI_XX20_FM 0xac8f + +#define PCI_VENDOR_ID_SONY 0x104d + +/* Winbond have two vendor IDs! See 0x10ad as well */ +#define PCI_VENDOR_ID_WINBOND2 0x1050 +#define PCI_DEVICE_ID_WINBOND2_89C940F 0x5a5a +#define PCI_DEVICE_ID_WINBOND2_6692 0x6692 + +#define PCI_VENDOR_ID_ANIGMA 0x1051 +#define PCI_DEVICE_ID_ANIGMA_MC145575 0x0100 + +#define PCI_VENDOR_ID_EFAR 0x1055 +#define PCI_DEVICE_ID_EFAR_SLC90E66_1 0x9130 +#define PCI_DEVICE_ID_EFAR_SLC90E66_3 0x9463 + +#define PCI_VENDOR_ID_MOTOROLA 0x1057 +#define PCI_DEVICE_ID_MOTOROLA_MPC105 0x0001 +#define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002 +#define PCI_DEVICE_ID_MOTOROLA_MPC107 0x0004 +#define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801 +#define PCI_DEVICE_ID_MOTOROLA_FALCON 0x4802 +#define PCI_DEVICE_ID_MOTOROLA_HAWK 0x4803 +#define PCI_DEVICE_ID_MOTOROLA_HARRIER 0x480b +#define PCI_DEVICE_ID_MOTOROLA_MPC5200 0x5803 +#define PCI_DEVICE_ID_MOTOROLA_MPC5200B 0x5809 + +#define PCI_VENDOR_ID_PROMISE 0x105a +#define PCI_DEVICE_ID_PROMISE_20265 0x0d30 +#define PCI_DEVICE_ID_PROMISE_20267 0x4d30 +#define PCI_DEVICE_ID_PROMISE_20246 0x4d33 +#define PCI_DEVICE_ID_PROMISE_20262 0x4d38 +#define PCI_DEVICE_ID_PROMISE_20263 0x0D38 +#define PCI_DEVICE_ID_PROMISE_20268 0x4d68 +#define PCI_DEVICE_ID_PROMISE_20269 0x4d69 +#define PCI_DEVICE_ID_PROMISE_20270 0x6268 +#define PCI_DEVICE_ID_PROMISE_20271 0x6269 +#define PCI_DEVICE_ID_PROMISE_20275 0x1275 +#define PCI_DEVICE_ID_PROMISE_20276 0x5275 +#define PCI_DEVICE_ID_PROMISE_20277 0x7275 + +#define PCI_VENDOR_ID_FOXCONN 0x105b + +#define PCI_VENDOR_ID_UMC 0x1060 +#define PCI_DEVICE_ID_UMC_UM8673F 0x0101 +#define PCI_DEVICE_ID_UMC_UM8886BF 0x673a +#define PCI_DEVICE_ID_UMC_UM8886A 0x886a + +#define PCI_VENDOR_ID_PICOPOWER 0x1066 +#define PCI_DEVICE_ID_PICOPOWER_PT86C523 0x0002 +#define PCI_DEVICE_ID_PICOPOWER_PT86C523BBP 0x8002 + +#define PCI_VENDOR_ID_MYLEX 0x1069 +#define PCI_DEVICE_ID_MYLEX_DAC960_P 0x0001 +#define PCI_DEVICE_ID_MYLEX_DAC960_PD 0x0002 +#define PCI_DEVICE_ID_MYLEX_DAC960_PG 0x0010 +#define PCI_DEVICE_ID_MYLEX_DAC960_LA 0x0020 +#define PCI_DEVICE_ID_MYLEX_DAC960_LP 0x0050 +#define PCI_DEVICE_ID_MYLEX_DAC960_BA 0xBA56 +#define PCI_DEVICE_ID_MYLEX_DAC960_GEM 0xB166 + +#define PCI_VENDOR_ID_APPLE 0x106b +#define PCI_DEVICE_ID_APPLE_BANDIT 0x0001 +#define PCI_DEVICE_ID_APPLE_HYDRA 0x000e +#define PCI_DEVICE_ID_APPLE_UNI_N_FW 0x0018 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020 +#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC 0x0021 +#define PCI_DEVICE_ID_APPLE_UNI_N_GMACP 0x0024 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP_P 0x0027 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP15 0x002d +#define PCI_DEVICE_ID_APPLE_UNI_N_PCI15 0x002e +#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC2 0x0032 +#define PCI_DEVICE_ID_APPLE_UNI_N_ATA 0x0033 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP2 0x0034 +#define PCI_DEVICE_ID_APPLE_IPID_ATA100 0x003b +#define PCI_DEVICE_ID_APPLE_K2_ATA100 0x0043 +#define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b +#define PCI_DEVICE_ID_APPLE_K2_GMAC 0x004c +#define PCI_DEVICE_ID_APPLE_SH_ATA 0x0050 +#define PCI_DEVICE_ID_APPLE_SH_SUNGEM 0x0051 +#define PCI_DEVICE_ID_APPLE_U3L_AGP 0x0058 +#define PCI_DEVICE_ID_APPLE_U3H_AGP 0x0059 +#define PCI_DEVICE_ID_APPLE_U4_PCIE 0x005b +#define PCI_DEVICE_ID_APPLE_IPID2_AGP 0x0066 +#define PCI_DEVICE_ID_APPLE_IPID2_ATA 0x0069 +#define PCI_DEVICE_ID_APPLE_IPID2_FW 0x006a +#define PCI_DEVICE_ID_APPLE_IPID2_GMAC 0x006b +#define PCI_DEVICE_ID_APPLE_TIGON3 0x1645 + +#define PCI_VENDOR_ID_YAMAHA 0x1073 +#define PCI_DEVICE_ID_YAMAHA_724 0x0004 +#define PCI_DEVICE_ID_YAMAHA_724F 0x000d +#define PCI_DEVICE_ID_YAMAHA_740 0x000a +#define PCI_DEVICE_ID_YAMAHA_740C 0x000c +#define PCI_DEVICE_ID_YAMAHA_744 0x0010 +#define PCI_DEVICE_ID_YAMAHA_754 0x0012 + +#define PCI_VENDOR_ID_QLOGIC 0x1077 +#define PCI_DEVICE_ID_QLOGIC_ISP10160 0x1016 +#define PCI_DEVICE_ID_QLOGIC_ISP1020 0x1020 +#define PCI_DEVICE_ID_QLOGIC_ISP1080 0x1080 +#define PCI_DEVICE_ID_QLOGIC_ISP12160 0x1216 +#define PCI_DEVICE_ID_QLOGIC_ISP1240 0x1240 +#define PCI_DEVICE_ID_QLOGIC_ISP1280 0x1280 +#define PCI_DEVICE_ID_QLOGIC_ISP2100 0x2100 +#define PCI_DEVICE_ID_QLOGIC_ISP2200 0x2200 +#define PCI_DEVICE_ID_QLOGIC_ISP2300 0x2300 +#define PCI_DEVICE_ID_QLOGIC_ISP2312 0x2312 +#define PCI_DEVICE_ID_QLOGIC_ISP2322 0x2322 +#define PCI_DEVICE_ID_QLOGIC_ISP6312 0x6312 +#define PCI_DEVICE_ID_QLOGIC_ISP6322 0x6322 +#define PCI_DEVICE_ID_QLOGIC_ISP2422 0x2422 +#define PCI_DEVICE_ID_QLOGIC_ISP2432 0x2432 +#define PCI_DEVICE_ID_QLOGIC_ISP2512 0x2512 +#define PCI_DEVICE_ID_QLOGIC_ISP2522 0x2522 +#define PCI_DEVICE_ID_QLOGIC_ISP5422 0x5422 +#define PCI_DEVICE_ID_QLOGIC_ISP5432 0x5432 + +#define PCI_VENDOR_ID_CYRIX 0x1078 +#define PCI_DEVICE_ID_CYRIX_5510 0x0000 +#define PCI_DEVICE_ID_CYRIX_PCI_MASTER 0x0001 +#define PCI_DEVICE_ID_CYRIX_5520 0x0002 +#define PCI_DEVICE_ID_CYRIX_5530_LEGACY 0x0100 +#define PCI_DEVICE_ID_CYRIX_5530_IDE 0x0102 +#define PCI_DEVICE_ID_CYRIX_5530_AUDIO 0x0103 +#define PCI_DEVICE_ID_CYRIX_5530_VIDEO 0x0104 + +#define PCI_VENDOR_ID_CONTAQ 0x1080 +#define PCI_DEVICE_ID_CONTAQ_82C693 0xc693 + +#define PCI_VENDOR_ID_OLICOM 0x108d +#define PCI_DEVICE_ID_OLICOM_OC2325 0x0012 +#define PCI_DEVICE_ID_OLICOM_OC2183 0x0013 +#define PCI_DEVICE_ID_OLICOM_OC2326 0x0014 + +#define PCI_VENDOR_ID_SUN 0x108e +#define PCI_DEVICE_ID_SUN_EBUS 0x1000 +#define PCI_DEVICE_ID_SUN_HAPPYMEAL 0x1001 +#define PCI_DEVICE_ID_SUN_RIO_EBUS 0x1100 +#define PCI_DEVICE_ID_SUN_RIO_GEM 0x1101 +#define PCI_DEVICE_ID_SUN_RIO_1394 0x1102 +#define PCI_DEVICE_ID_SUN_RIO_USB 0x1103 +#define PCI_DEVICE_ID_SUN_GEM 0x2bad +#define PCI_DEVICE_ID_SUN_SIMBA 0x5000 +#define PCI_DEVICE_ID_SUN_PBM 0x8000 +#define PCI_DEVICE_ID_SUN_SCHIZO 0x8001 +#define PCI_DEVICE_ID_SUN_SABRE 0xa000 +#define PCI_DEVICE_ID_SUN_HUMMINGBIRD 0xa001 +#define PCI_DEVICE_ID_SUN_TOMATILLO 0xa801 +#define PCI_DEVICE_ID_SUN_CASSINI 0xabba + +#define PCI_VENDOR_ID_NI 0x1093 +#define PCI_DEVICE_ID_NI_PCI2322 0xd130 +#define PCI_DEVICE_ID_NI_PCI2324 0xd140 +#define PCI_DEVICE_ID_NI_PCI2328 0xd150 +#define PCI_DEVICE_ID_NI_PXI8422_2322 0xd190 +#define PCI_DEVICE_ID_NI_PXI8422_2324 0xd1a0 +#define PCI_DEVICE_ID_NI_PXI8420_2322 0xd1d0 +#define PCI_DEVICE_ID_NI_PXI8420_2324 0xd1e0 +#define PCI_DEVICE_ID_NI_PXI8420_2328 0xd1f0 +#define PCI_DEVICE_ID_NI_PXI8420_23216 0xd1f1 +#define PCI_DEVICE_ID_NI_PCI2322I 0xd250 +#define PCI_DEVICE_ID_NI_PCI2324I 0xd270 +#define PCI_DEVICE_ID_NI_PCI23216 0xd2b0 +#define PCI_DEVICE_ID_NI_PXI8430_2322 0x7080 +#define PCI_DEVICE_ID_NI_PCI8430_2322 0x70db +#define PCI_DEVICE_ID_NI_PXI8430_2324 0x70dd +#define PCI_DEVICE_ID_NI_PCI8430_2324 0x70df +#define PCI_DEVICE_ID_NI_PXI8430_2328 0x70e2 +#define PCI_DEVICE_ID_NI_PCI8430_2328 0x70e4 +#define PCI_DEVICE_ID_NI_PXI8430_23216 0x70e6 +#define PCI_DEVICE_ID_NI_PCI8430_23216 0x70e7 +#define PCI_DEVICE_ID_NI_PXI8432_2322 0x70e8 +#define PCI_DEVICE_ID_NI_PCI8432_2322 0x70ea +#define PCI_DEVICE_ID_NI_PXI8432_2324 0x70ec +#define PCI_DEVICE_ID_NI_PCI8432_2324 0x70ee + +#define PCI_VENDOR_ID_CMD 0x1095 +#define PCI_DEVICE_ID_CMD_643 0x0643 +#define PCI_DEVICE_ID_CMD_646 0x0646 +#define PCI_DEVICE_ID_CMD_648 0x0648 +#define PCI_DEVICE_ID_CMD_649 0x0649 + +#define PCI_DEVICE_ID_SII_680 0x0680 +#define PCI_DEVICE_ID_SII_3112 0x3112 +#define PCI_DEVICE_ID_SII_1210SA 0x0240 + +#define PCI_VENDOR_ID_BROOKTREE 0x109e +#define PCI_DEVICE_ID_BROOKTREE_878 0x0878 +#define PCI_DEVICE_ID_BROOKTREE_879 0x0879 + +#define PCI_VENDOR_ID_SGI 0x10a9 +#define PCI_DEVICE_ID_SGI_IOC3 0x0003 +#define PCI_DEVICE_ID_SGI_LITHIUM 0x1002 +#define PCI_DEVICE_ID_SGI_IOC4 0x100a + +#define PCI_VENDOR_ID_WINBOND 0x10ad +#define PCI_DEVICE_ID_WINBOND_82C105 0x0105 +#define PCI_DEVICE_ID_WINBOND_83C553 0x0565 + +#define PCI_VENDOR_ID_PLX 0x10b5 +#define PCI_DEVICE_ID_PLX_R685 0x1030 +#define PCI_DEVICE_ID_PLX_ROMULUS 0x106a +#define PCI_DEVICE_ID_PLX_SPCOM800 0x1076 +#define PCI_DEVICE_ID_PLX_1077 0x1077 +#define PCI_DEVICE_ID_PLX_SPCOM200 0x1103 +#define PCI_DEVICE_ID_PLX_DJINN_ITOO 0x1151 +#define PCI_DEVICE_ID_PLX_R753 0x1152 +#define PCI_DEVICE_ID_PLX_OLITEC 0x1187 +#define PCI_DEVICE_ID_PLX_PCI200SYN 0x3196 +#define PCI_DEVICE_ID_PLX_9030 0x9030 +#define PCI_DEVICE_ID_PLX_9050 0x9050 +#define PCI_DEVICE_ID_PLX_9056 0x9056 +#define PCI_DEVICE_ID_PLX_9080 0x9080 +#define PCI_DEVICE_ID_PLX_GTEK_SERIAL2 0xa001 + +#define PCI_VENDOR_ID_MADGE 0x10b6 +#define PCI_DEVICE_ID_MADGE_MK2 0x0002 + +#define PCI_VENDOR_ID_3COM 0x10b7 +#define PCI_DEVICE_ID_3COM_3C985 0x0001 +#define PCI_DEVICE_ID_3COM_3C940 0x1700 +#define PCI_DEVICE_ID_3COM_3C339 0x3390 +#define PCI_DEVICE_ID_3COM_3C359 0x3590 +#define PCI_DEVICE_ID_3COM_3C940B 0x80eb +#define PCI_DEVICE_ID_3COM_3CR990 0x9900 +#define PCI_DEVICE_ID_3COM_3CR990_TX_95 0x9902 +#define PCI_DEVICE_ID_3COM_3CR990_TX_97 0x9903 +#define PCI_DEVICE_ID_3COM_3CR990B 0x9904 +#define PCI_DEVICE_ID_3COM_3CR990_FX 0x9905 +#define PCI_DEVICE_ID_3COM_3CR990SVR95 0x9908 +#define PCI_DEVICE_ID_3COM_3CR990SVR97 0x9909 +#define PCI_DEVICE_ID_3COM_3CR990SVR 0x990a + +#define PCI_VENDOR_ID_AL 0x10b9 +#define PCI_DEVICE_ID_AL_M1533 0x1533 +#define PCI_DEVICE_ID_AL_M1535 0x1535 +#define PCI_DEVICE_ID_AL_M1541 0x1541 +#define PCI_DEVICE_ID_AL_M1563 0x1563 +#define PCI_DEVICE_ID_AL_M1621 0x1621 +#define PCI_DEVICE_ID_AL_M1631 0x1631 +#define PCI_DEVICE_ID_AL_M1632 0x1632 +#define PCI_DEVICE_ID_AL_M1641 0x1641 +#define PCI_DEVICE_ID_AL_M1644 0x1644 +#define PCI_DEVICE_ID_AL_M1647 0x1647 +#define PCI_DEVICE_ID_AL_M1651 0x1651 +#define PCI_DEVICE_ID_AL_M1671 0x1671 +#define PCI_DEVICE_ID_AL_M1681 0x1681 +#define PCI_DEVICE_ID_AL_M1683 0x1683 +#define PCI_DEVICE_ID_AL_M1689 0x1689 +#define PCI_DEVICE_ID_AL_M5219 0x5219 +#define PCI_DEVICE_ID_AL_M5228 0x5228 +#define PCI_DEVICE_ID_AL_M5229 0x5229 +#define PCI_DEVICE_ID_AL_M5451 0x5451 +#define PCI_DEVICE_ID_AL_M7101 0x7101 + +#define PCI_VENDOR_ID_NEOMAGIC 0x10c8 +#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005 +#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006 +#define PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO 0x8016 + +#define PCI_VENDOR_ID_TCONRAD 0x10da +#define PCI_DEVICE_ID_TCONRAD_TOKENRING 0x0508 + +#define PCI_VENDOR_ID_NVIDIA 0x10de +#define PCI_DEVICE_ID_NVIDIA_TNT 0x0020 +#define PCI_DEVICE_ID_NVIDIA_TNT2 0x0028 +#define PCI_DEVICE_ID_NVIDIA_UTNT2 0x0029 +#define PCI_DEVICE_ID_NVIDIA_TNT_UNKNOWN 0x002a +#define PCI_DEVICE_ID_NVIDIA_VTNT2 0x002C +#define PCI_DEVICE_ID_NVIDIA_UVTNT2 0x002D +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS 0x0034 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE 0x0035 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA 0x0036 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2 0x003e +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_ULTRA 0x0040 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800 0x0041 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_LE 0x0042 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_GT 0x0045 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_4000 0x004E +#define PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS 0x0052 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE 0x0053 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA 0x0054 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2 0x0055 +#define PCI_DEVICE_ID_NVIDIA_CK804_AUDIO 0x0059 +#define PCI_DEVICE_ID_NVIDIA_CK804_PCIE 0x005d +#define PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS 0x0064 +#define PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE 0x0065 +#define PCI_DEVICE_ID_NVIDIA_MCP2_MODEM 0x0069 +#define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO 0x006a +#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS 0x0084 +#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE 0x0085 +#define PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM 0x0089 +#define PCI_DEVICE_ID_NVIDIA_CK8_AUDIO 0x008a +#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA 0x008e +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GT 0x0090 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GTX 0x0091 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800 0x0098 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800_GTX 0x0099 +#define PCI_DEVICE_ID_NVIDIA_ITNT2 0x00A0 +#define PCI_DEVICE_ID_GEFORCE_6800A 0x00c1 +#define PCI_DEVICE_ID_GEFORCE_6800A_LE 0x00c2 +#define PCI_DEVICE_ID_GEFORCE_GO_6800 0x00c8 +#define PCI_DEVICE_ID_GEFORCE_GO_6800_ULTRA 0x00c9 +#define PCI_DEVICE_ID_QUADRO_FX_GO1400 0x00cc +#define PCI_DEVICE_ID_QUADRO_FX_1400 0x00ce +#define PCI_DEVICE_ID_NVIDIA_NFORCE3 0x00d1 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS 0x00d4 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE 0x00d5 +#define PCI_DEVICE_ID_NVIDIA_MCP3_MODEM 0x00d9 +#define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S 0x00e1 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA 0x00e3 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS 0x00e4 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE 0x00e5 +#define PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO 0x00ea +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2 0x00ee +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_ALT1 0x00f0 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT1 0x00f1 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT2 0x00f2 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6200_ALT1 0x00f3 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_GT 0x00f9 +#define PCIE_DEVICE_ID_NVIDIA_QUADRO_NVS280 0x00fd +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR 0x0100 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR 0x0101 +#define PCI_DEVICE_ID_NVIDIA_QUADRO 0x0103 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX 0x0110 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX2 0x0111 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO 0x0112 +#define PCI_DEVICE_ID_NVIDIA_QUADRO2_MXR 0x0113 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6600_GT 0x0140 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6600 0x0141 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6610_XL 0x0145 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_540 0x014E +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6200 0x014F +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS 0x0150 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS2 0x0151 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_ULTRA 0x0152 +#define PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO 0x0153 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6200_TURBOCACHE 0x0161 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200 0x0164 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250 0x0166 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200_1 0x0167 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250_1 0x0168 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_460 0x0170 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440 0x0171 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420 0x0172 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_SE 0x0173 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO 0x0174 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO 0x0175 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO_M32 0x0176 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_460_GO 0x0177 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_500XGL 0x0178 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO_M64 0x0179 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_200 0x017A +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_550XGL 0x017B +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_500_GOGL 0x017C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_410_GO_M16 0x017D +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_8X 0x0181 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440SE_8X 0x0182 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420_8X 0x0183 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_4000 0x0185 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_448_GO 0x0186 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_488_GO 0x0187 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_580_XGL 0x0188 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_MAC 0x0189 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_280_NVS 0x018A +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_380_XGL 0x018B +#define PCI_DEVICE_ID_NVIDIA_IGEFORCE2 0x01a0 +#define PCI_DEVICE_ID_NVIDIA_NFORCE 0x01a4 +#define PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO 0x01b1 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS 0x01b4 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_IDE 0x01bc +#define PCI_DEVICE_ID_NVIDIA_MCP1_MODEM 0x01c1 +#define PCI_DEVICE_ID_NVIDIA_NFORCE2 0x01e0 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE3 0x0200 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_1 0x0201 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_2 0x0202 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_DDC 0x0203 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B 0x0211 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_LE 0x0212 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_GT 0x0215 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4600 0x0250 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4400 0x0251 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4200 0x0253 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL 0x0258 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL 0x0259 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL 0x025B +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS 0x0264 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE 0x0265 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA 0x0266 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2 0x0267 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS 0x0368 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE 0x036E +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x037E +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2 0x037F +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800 0x0280 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800_8X 0x0281 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800SE 0x0282 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_4200_GO 0x0286 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_980_XGL 0x0288 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_780_XGL 0x0289 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_700_GOGL 0x028C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800_ULTRA 0x0301 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800 0x0302 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_2000 0x0308 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1000 0x0309 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600_ULTRA 0x0311 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600 0x0312 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600SE 0x0314 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5600 0x031A +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5650 0x031B +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO700 0x031C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200 0x0320 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_ULTRA 0x0321 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_1 0x0322 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200SE 0x0323 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5200 0x0324 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250 0x0325 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5500 0x0326 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5100 0x0327 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250_32 0x0328 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO_5200 0x0329 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_NVS_280_PCI 0x032A +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_500 0x032B +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5300 0x032C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5100 0x032D +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900_ULTRA 0x0330 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900 0x0331 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900XT 0x0332 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5950_ULTRA 0x0333 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900ZT 0x0334 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_3000 0x0338 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_700 0x033F +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700_ULTRA 0x0341 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700 0x0342 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700LE 0x0343 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700VE 0x0344 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_1 0x0347 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_2 0x0348 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO1000 0x034C +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1100 0x034E +#define PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V0 0x0360 +#define PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V4 0x0364 +#define PCI_DEVICE_ID_NVIDIA_NVENET_15 0x0373 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA 0x03E7 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SMBUS 0x03EB +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE 0x03EC +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2 0x03F6 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3 0x03F7 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_SMBUS 0x0446 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE 0x0448 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_SMBUS 0x0542 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE 0x0560 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE 0x056C +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP78S_SMBUS 0x0752 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE 0x0759 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_SMBUS 0x07D8 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS 0x0AA2 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP89_SATA 0x0D85 + +#define PCI_VENDOR_ID_IMS 0x10e0 +#define PCI_DEVICE_ID_IMS_TT128 0x9128 +#define PCI_DEVICE_ID_IMS_TT3D 0x9135 + +#define PCI_VENDOR_ID_INTERG 0x10ea +#define PCI_DEVICE_ID_INTERG_1682 0x1682 +#define PCI_DEVICE_ID_INTERG_2000 0x2000 +#define PCI_DEVICE_ID_INTERG_2010 0x2010 +#define PCI_DEVICE_ID_INTERG_5000 0x5000 +#define PCI_DEVICE_ID_INTERG_5050 0x5050 + +#define PCI_VENDOR_ID_REALTEK 0x10ec +#define PCI_DEVICE_ID_REALTEK_8139 0x8139 + +#define PCI_VENDOR_ID_XILINX 0x10ee +#define PCI_DEVICE_ID_RME_DIGI96 0x3fc0 +#define PCI_DEVICE_ID_RME_DIGI96_8 0x3fc1 +#define PCI_DEVICE_ID_RME_DIGI96_8_PRO 0x3fc2 +#define PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST 0x3fc3 +#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP 0x3fc5 +#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI 0x3fc6 + +#define PCI_VENDOR_ID_INIT 0x1101 + +#define PCI_VENDOR_ID_CREATIVE 0x1102 /* duplicate: ECTIVA */ +#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002 +#define PCI_DEVICE_ID_CREATIVE_20K1 0x0005 +#define PCI_DEVICE_ID_CREATIVE_20K2 0x000b +#define PCI_SUBDEVICE_ID_CREATIVE_SB0760 0x0024 +#define PCI_SUBDEVICE_ID_CREATIVE_SB08801 0x0041 +#define PCI_SUBDEVICE_ID_CREATIVE_SB08802 0x0042 +#define PCI_SUBDEVICE_ID_CREATIVE_SB08803 0x0043 +#define PCI_SUBDEVICE_ID_CREATIVE_SB1270 0x0062 +#define PCI_SUBDEVICE_ID_CREATIVE_HENDRIX 0x6000 + +#define PCI_VENDOR_ID_ECTIVA 0x1102 /* duplicate: CREATIVE */ +#define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938 + +#define PCI_VENDOR_ID_TTI 0x1103 +#define PCI_DEVICE_ID_TTI_HPT343 0x0003 +#define PCI_DEVICE_ID_TTI_HPT366 0x0004 +#define PCI_DEVICE_ID_TTI_HPT372 0x0005 +#define PCI_DEVICE_ID_TTI_HPT302 0x0006 +#define PCI_DEVICE_ID_TTI_HPT371 0x0007 +#define PCI_DEVICE_ID_TTI_HPT374 0x0008 +#define PCI_DEVICE_ID_TTI_HPT372N 0x0009 /* apparently a 372N variant? */ + +#define PCI_VENDOR_ID_VIA 0x1106 +#define PCI_DEVICE_ID_VIA_8763_0 0x0198 +#define PCI_DEVICE_ID_VIA_8380_0 0x0204 +#define PCI_DEVICE_ID_VIA_3238_0 0x0238 +#define PCI_DEVICE_ID_VIA_PT880 0x0258 +#define PCI_DEVICE_ID_VIA_PT880ULTRA 0x0308 +#define PCI_DEVICE_ID_VIA_PX8X0_0 0x0259 +#define PCI_DEVICE_ID_VIA_3269_0 0x0269 +#define PCI_DEVICE_ID_VIA_K8T800PRO_0 0x0282 +#define PCI_DEVICE_ID_VIA_3296_0 0x0296 +#define PCI_DEVICE_ID_VIA_8363_0 0x0305 +#define PCI_DEVICE_ID_VIA_P4M800CE 0x0314 +#define PCI_DEVICE_ID_VIA_P4M890 0x0327 +#define PCI_DEVICE_ID_VIA_VT3324 0x0324 +#define PCI_DEVICE_ID_VIA_VT3336 0x0336 +#define PCI_DEVICE_ID_VIA_VT3351 0x0351 +#define PCI_DEVICE_ID_VIA_VT3364 0x0364 +#define PCI_DEVICE_ID_VIA_8371_0 0x0391 +#define PCI_DEVICE_ID_VIA_6415 0x0415 +#define PCI_DEVICE_ID_VIA_8501_0 0x0501 +#define PCI_DEVICE_ID_VIA_82C561 0x0561 +#define PCI_DEVICE_ID_VIA_82C586_1 0x0571 +#define PCI_DEVICE_ID_VIA_82C576 0x0576 +#define PCI_DEVICE_ID_VIA_82C586_0 0x0586 +#define PCI_DEVICE_ID_VIA_82C596 0x0596 +#define PCI_DEVICE_ID_VIA_82C597_0 0x0597 +#define PCI_DEVICE_ID_VIA_82C598_0 0x0598 +#define PCI_DEVICE_ID_VIA_8601_0 0x0601 +#define PCI_DEVICE_ID_VIA_8605_0 0x0605 +#define PCI_DEVICE_ID_VIA_82C686 0x0686 +#define PCI_DEVICE_ID_VIA_82C691_0 0x0691 +#define PCI_DEVICE_ID_VIA_82C576_1 0x1571 +#define PCI_DEVICE_ID_VIA_82C586_2 0x3038 +#define PCI_DEVICE_ID_VIA_82C586_3 0x3040 +#define PCI_DEVICE_ID_VIA_82C596_3 0x3050 +#define PCI_DEVICE_ID_VIA_82C596B_3 0x3051 +#define PCI_DEVICE_ID_VIA_82C686_4 0x3057 +#define PCI_DEVICE_ID_VIA_82C686_5 0x3058 +#define PCI_DEVICE_ID_VIA_8233_5 0x3059 +#define PCI_DEVICE_ID_VIA_8233_0 0x3074 +#define PCI_DEVICE_ID_VIA_8633_0 0x3091 +#define PCI_DEVICE_ID_VIA_8367_0 0x3099 +#define PCI_DEVICE_ID_VIA_8653_0 0x3101 +#define PCI_DEVICE_ID_VIA_8622 0x3102 +#define PCI_DEVICE_ID_VIA_8235_USB_2 0x3104 +#define PCI_DEVICE_ID_VIA_8233C_0 0x3109 +#define PCI_DEVICE_ID_VIA_8361 0x3112 +#define PCI_DEVICE_ID_VIA_XM266 0x3116 +#define PCI_DEVICE_ID_VIA_612X 0x3119 +#define PCI_DEVICE_ID_VIA_862X_0 0x3123 +#define PCI_DEVICE_ID_VIA_8753_0 0x3128 +#define PCI_DEVICE_ID_VIA_8233A 0x3147 +#define PCI_DEVICE_ID_VIA_8703_51_0 0x3148 +#define PCI_DEVICE_ID_VIA_8237_SATA 0x3149 +#define PCI_DEVICE_ID_VIA_XN266 0x3156 +#define PCI_DEVICE_ID_VIA_6410 0x3164 +#define PCI_DEVICE_ID_VIA_8754C_0 0x3168 +#define PCI_DEVICE_ID_VIA_8235 0x3177 +#define PCI_DEVICE_ID_VIA_8385_0 0x3188 +#define PCI_DEVICE_ID_VIA_8377_0 0x3189 +#define PCI_DEVICE_ID_VIA_8378_0 0x3205 +#define PCI_DEVICE_ID_VIA_8783_0 0x3208 +#define PCI_DEVICE_ID_VIA_8237 0x3227 +#define PCI_DEVICE_ID_VIA_8251 0x3287 +#define PCI_DEVICE_ID_VIA_8261 0x3402 +#define PCI_DEVICE_ID_VIA_8237A 0x3337 +#define PCI_DEVICE_ID_VIA_8237S 0x3372 +#define PCI_DEVICE_ID_VIA_SATA_EIDE 0x5324 +#define PCI_DEVICE_ID_VIA_8231 0x8231 +#define PCI_DEVICE_ID_VIA_8231_4 0x8235 +#define PCI_DEVICE_ID_VIA_8365_1 0x8305 +#define PCI_DEVICE_ID_VIA_CX700 0x8324 +#define PCI_DEVICE_ID_VIA_CX700_IDE 0x0581 +#define PCI_DEVICE_ID_VIA_VX800 0x8353 +#define PCI_DEVICE_ID_VIA_VX855 0x8409 +#define PCI_DEVICE_ID_VIA_8371_1 0x8391 +#define PCI_DEVICE_ID_VIA_82C598_1 0x8598 +#define PCI_DEVICE_ID_VIA_838X_1 0xB188 +#define PCI_DEVICE_ID_VIA_83_87XX_1 0xB198 +#define PCI_DEVICE_ID_VIA_VX855_IDE 0xC409 +#define PCI_DEVICE_ID_VIA_ANON 0xFFFF + +#define PCI_VENDOR_ID_SIEMENS 0x110A +#define PCI_DEVICE_ID_SIEMENS_DSCC4 0x2102 + +#define PCI_VENDOR_ID_VORTEX 0x1119 +#define PCI_DEVICE_ID_VORTEX_GDT60x0 0x0000 +#define PCI_DEVICE_ID_VORTEX_GDT6000B 0x0001 +#define PCI_DEVICE_ID_VORTEX_GDT6x10 0x0002 +#define PCI_DEVICE_ID_VORTEX_GDT6x20 0x0003 +#define PCI_DEVICE_ID_VORTEX_GDT6530 0x0004 +#define PCI_DEVICE_ID_VORTEX_GDT6550 0x0005 +#define PCI_DEVICE_ID_VORTEX_GDT6x17 0x0006 +#define PCI_DEVICE_ID_VORTEX_GDT6x27 0x0007 +#define PCI_DEVICE_ID_VORTEX_GDT6537 0x0008 +#define PCI_DEVICE_ID_VORTEX_GDT6557 0x0009 +#define PCI_DEVICE_ID_VORTEX_GDT6x15 0x000a +#define PCI_DEVICE_ID_VORTEX_GDT6x25 0x000b +#define PCI_DEVICE_ID_VORTEX_GDT6535 0x000c +#define PCI_DEVICE_ID_VORTEX_GDT6555 0x000d +#define PCI_DEVICE_ID_VORTEX_GDT6x17RP 0x0100 +#define PCI_DEVICE_ID_VORTEX_GDT6x27RP 0x0101 +#define PCI_DEVICE_ID_VORTEX_GDT6537RP 0x0102 +#define PCI_DEVICE_ID_VORTEX_GDT6557RP 0x0103 +#define PCI_DEVICE_ID_VORTEX_GDT6x11RP 0x0104 +#define PCI_DEVICE_ID_VORTEX_GDT6x21RP 0x0105 + +#define PCI_VENDOR_ID_EF 0x111a +#define PCI_DEVICE_ID_EF_ATM_FPGA 0x0000 +#define PCI_DEVICE_ID_EF_ATM_ASIC 0x0002 +#define PCI_DEVICE_ID_EF_ATM_LANAI2 0x0003 +#define PCI_DEVICE_ID_EF_ATM_LANAIHB 0x0005 + +#define PCI_VENDOR_ID_IDT 0x111d +#define PCI_DEVICE_ID_IDT_IDT77201 0x0001 + +#define PCI_VENDOR_ID_FORE 0x1127 +#define PCI_DEVICE_ID_FORE_PCA200E 0x0300 + +#define PCI_VENDOR_ID_PHILIPS 0x1131 +#define PCI_DEVICE_ID_PHILIPS_SAA7146 0x7146 +#define PCI_DEVICE_ID_PHILIPS_SAA9730 0x9730 + +#define PCI_VENDOR_ID_EICON 0x1133 +#define PCI_DEVICE_ID_EICON_DIVA20 0xe002 +#define PCI_DEVICE_ID_EICON_DIVA20_U 0xe004 +#define PCI_DEVICE_ID_EICON_DIVA201 0xe005 +#define PCI_DEVICE_ID_EICON_DIVA202 0xe00b +#define PCI_DEVICE_ID_EICON_MAESTRA 0xe010 +#define PCI_DEVICE_ID_EICON_MAESTRAQ 0xe012 +#define PCI_DEVICE_ID_EICON_MAESTRAQ_U 0xe013 +#define PCI_DEVICE_ID_EICON_MAESTRAP 0xe014 + +#define PCI_VENDOR_ID_CISCO 0x1137 + +#define PCI_VENDOR_ID_ZIATECH 0x1138 +#define PCI_DEVICE_ID_ZIATECH_5550_HC 0x5550 + + +#define PCI_VENDOR_ID_SYSKONNECT 0x1148 +#define PCI_DEVICE_ID_SYSKONNECT_TR 0x4200 +#define PCI_DEVICE_ID_SYSKONNECT_GE 0x4300 +#define PCI_DEVICE_ID_SYSKONNECT_YU 0x4320 +#define PCI_DEVICE_ID_SYSKONNECT_9DXX 0x4400 +#define PCI_DEVICE_ID_SYSKONNECT_9MXX 0x4500 + +#define PCI_VENDOR_ID_DIGI 0x114f +#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_E 0x0070 +#define PCI_DEVICE_ID_DIGI_DF_M_E 0x0071 +#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_A 0x0072 +#define PCI_DEVICE_ID_DIGI_DF_M_A 0x0073 +#define PCI_DEVICE_ID_DIGI_NEO_8 0x00B1 +#define PCI_DEVICE_ID_NEO_2DB9 0x00C8 +#define PCI_DEVICE_ID_NEO_2DB9PRI 0x00C9 +#define PCI_DEVICE_ID_NEO_2RJ45 0x00CA +#define PCI_DEVICE_ID_NEO_2RJ45PRI 0x00CB +#define PCIE_DEVICE_ID_NEO_4_IBM 0x00F4 + +#define PCI_VENDOR_ID_XIRCOM 0x115d +#define PCI_DEVICE_ID_XIRCOM_RBM56G 0x0101 +#define PCI_DEVICE_ID_XIRCOM_X3201_MDM 0x0103 + +#define PCI_VENDOR_ID_SERVERWORKS 0x1166 +#define PCI_DEVICE_ID_SERVERWORKS_HE 0x0008 +#define PCI_DEVICE_ID_SERVERWORKS_LE 0x0009 +#define PCI_DEVICE_ID_SERVERWORKS_GCNB_LE 0x0017 +#define PCI_DEVICE_ID_SERVERWORKS_HT1000_PXB 0x0036 +#define PCI_DEVICE_ID_SERVERWORKS_EPB 0x0103 +#define PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE 0x0132 +#define PCI_DEVICE_ID_SERVERWORKS_OSB4 0x0200 +#define PCI_DEVICE_ID_SERVERWORKS_CSB5 0x0201 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6 0x0203 +#define PCI_DEVICE_ID_SERVERWORKS_HT1000SB 0x0205 +#define PCI_DEVICE_ID_SERVERWORKS_OSB4IDE 0x0211 +#define PCI_DEVICE_ID_SERVERWORKS_CSB5IDE 0x0212 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE 0x0213 +#define PCI_DEVICE_ID_SERVERWORKS_HT1000IDE 0x0214 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2 0x0217 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6LPC 0x0227 +#define PCI_DEVICE_ID_SERVERWORKS_HT1100LD 0x0408 + +#define PCI_VENDOR_ID_SBE 0x1176 +#define PCI_DEVICE_ID_SBE_WANXL100 0x0301 +#define PCI_DEVICE_ID_SBE_WANXL200 0x0302 +#define PCI_DEVICE_ID_SBE_WANXL400 0x0104 +#define PCI_SUBDEVICE_ID_SBE_T3E3 0x0009 +#define PCI_SUBDEVICE_ID_SBE_2T3E3_P0 0x0901 +#define PCI_SUBDEVICE_ID_SBE_2T3E3_P1 0x0902 + +#define PCI_VENDOR_ID_TOSHIBA 0x1179 +#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_1 0x0101 +#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_2 0x0102 +#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_3 0x0103 +#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_5 0x0105 +#define PCI_DEVICE_ID_TOSHIBA_TOPIC95 0x060a +#define PCI_DEVICE_ID_TOSHIBA_TOPIC97 0x060f +#define PCI_DEVICE_ID_TOSHIBA_TOPIC100 0x0617 + +#define PCI_VENDOR_ID_TOSHIBA_2 0x102f +#define PCI_DEVICE_ID_TOSHIBA_TC35815CF 0x0030 +#define PCI_DEVICE_ID_TOSHIBA_TC35815_NWU 0x0031 +#define PCI_DEVICE_ID_TOSHIBA_TC35815_TX4939 0x0032 +#define PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE 0x0105 +#define PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC 0x0108 +#define PCI_DEVICE_ID_TOSHIBA_SPIDER_NET 0x01b3 + +#define PCI_VENDOR_ID_ATTO 0x117c + +#define PCI_VENDOR_ID_RICOH 0x1180 +#define PCI_DEVICE_ID_RICOH_RL5C465 0x0465 +#define PCI_DEVICE_ID_RICOH_RL5C466 0x0466 +#define PCI_DEVICE_ID_RICOH_RL5C475 0x0475 +#define PCI_DEVICE_ID_RICOH_RL5C476 0x0476 +#define PCI_DEVICE_ID_RICOH_RL5C478 0x0478 +#define PCI_DEVICE_ID_RICOH_R5C822 0x0822 +#define PCI_DEVICE_ID_RICOH_R5CE823 0xe823 +#define PCI_DEVICE_ID_RICOH_R5C832 0x0832 +#define PCI_DEVICE_ID_RICOH_R5C843 0x0843 + +#define PCI_VENDOR_ID_DLINK 0x1186 +#define PCI_DEVICE_ID_DLINK_DGE510T 0x4c00 + +#define PCI_VENDOR_ID_ARTOP 0x1191 +#define PCI_DEVICE_ID_ARTOP_ATP850UF 0x0005 +#define PCI_DEVICE_ID_ARTOP_ATP860 0x0006 +#define PCI_DEVICE_ID_ARTOP_ATP860R 0x0007 +#define PCI_DEVICE_ID_ARTOP_ATP865 0x0008 +#define PCI_DEVICE_ID_ARTOP_ATP865R 0x0009 +#define PCI_DEVICE_ID_ARTOP_ATP867A 0x000A +#define PCI_DEVICE_ID_ARTOP_ATP867B 0x000B +#define PCI_DEVICE_ID_ARTOP_AEC7610 0x8002 +#define PCI_DEVICE_ID_ARTOP_AEC7612UW 0x8010 +#define PCI_DEVICE_ID_ARTOP_AEC7612U 0x8020 +#define PCI_DEVICE_ID_ARTOP_AEC7612S 0x8030 +#define PCI_DEVICE_ID_ARTOP_AEC7612D 0x8040 +#define PCI_DEVICE_ID_ARTOP_AEC7612SUW 0x8050 +#define PCI_DEVICE_ID_ARTOP_8060 0x8060 + +#define PCI_VENDOR_ID_ZEITNET 0x1193 +#define PCI_DEVICE_ID_ZEITNET_1221 0x0001 +#define PCI_DEVICE_ID_ZEITNET_1225 0x0002 + +#define PCI_VENDOR_ID_FUJITSU_ME 0x119e +#define PCI_DEVICE_ID_FUJITSU_FS155 0x0001 +#define PCI_DEVICE_ID_FUJITSU_FS50 0x0003 + +#define PCI_SUBVENDOR_ID_KEYSPAN 0x11a9 +#define PCI_SUBDEVICE_ID_KEYSPAN_SX2 0x5334 + +#define PCI_VENDOR_ID_MARVELL 0x11ab +#define PCI_DEVICE_ID_MARVELL_GT64111 0x4146 +#define PCI_DEVICE_ID_MARVELL_GT64260 0x6430 +#define PCI_DEVICE_ID_MARVELL_MV64360 0x6460 +#define PCI_DEVICE_ID_MARVELL_MV64460 0x6480 +#define PCI_DEVICE_ID_MARVELL_88ALP01_NAND 0x4100 +#define PCI_DEVICE_ID_MARVELL_88ALP01_SD 0x4101 +#define PCI_DEVICE_ID_MARVELL_88ALP01_CCIC 0x4102 + +#define PCI_VENDOR_ID_V3 0x11b0 +#define PCI_DEVICE_ID_V3_V960 0x0001 +#define PCI_DEVICE_ID_V3_V351 0x0002 + +#define PCI_VENDOR_ID_ATT 0x11c1 +#define PCI_DEVICE_ID_ATT_VENUS_MODEM 0x480 + +#define PCI_VENDOR_ID_SPECIALIX 0x11cb +#define PCI_DEVICE_ID_SPECIALIX_IO8 0x2000 +#define PCI_DEVICE_ID_SPECIALIX_RIO 0x8000 +#define PCI_SUBDEVICE_ID_SPECIALIX_SPEED4 0xa004 + +#define PCI_VENDOR_ID_ANALOG_DEVICES 0x11d4 +#define PCI_DEVICE_ID_AD1889JS 0x1889 + +#define PCI_DEVICE_ID_SEGA_BBA 0x1234 + +#define PCI_VENDOR_ID_ZORAN 0x11de +#define PCI_DEVICE_ID_ZORAN_36057 0x6057 +#define PCI_DEVICE_ID_ZORAN_36120 0x6120 + +#define PCI_VENDOR_ID_COMPEX 0x11f6 +#define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112 + +#define PCI_VENDOR_ID_PMC_Sierra 0x11f8 + +#define PCI_VENDOR_ID_RP 0x11fe +#define PCI_DEVICE_ID_RP32INTF 0x0001 +#define PCI_DEVICE_ID_RP8INTF 0x0002 +#define PCI_DEVICE_ID_RP16INTF 0x0003 +#define PCI_DEVICE_ID_RP4QUAD 0x0004 +#define PCI_DEVICE_ID_RP8OCTA 0x0005 +#define PCI_DEVICE_ID_RP8J 0x0006 +#define PCI_DEVICE_ID_RP4J 0x0007 +#define PCI_DEVICE_ID_RP8SNI 0x0008 +#define PCI_DEVICE_ID_RP16SNI 0x0009 +#define PCI_DEVICE_ID_RPP4 0x000A +#define PCI_DEVICE_ID_RPP8 0x000B +#define PCI_DEVICE_ID_RP4M 0x000D +#define PCI_DEVICE_ID_RP2_232 0x000E +#define PCI_DEVICE_ID_RP2_422 0x000F +#define PCI_DEVICE_ID_URP32INTF 0x0801 +#define PCI_DEVICE_ID_URP8INTF 0x0802 +#define PCI_DEVICE_ID_URP16INTF 0x0803 +#define PCI_DEVICE_ID_URP8OCTA 0x0805 +#define PCI_DEVICE_ID_UPCI_RM3_8PORT 0x080C +#define PCI_DEVICE_ID_UPCI_RM3_4PORT 0x080D +#define PCI_DEVICE_ID_CRP16INTF 0x0903 + +#define PCI_VENDOR_ID_CYCLADES 0x120e +#define PCI_DEVICE_ID_CYCLOM_Y_Lo 0x0100 +#define PCI_DEVICE_ID_CYCLOM_Y_Hi 0x0101 +#define PCI_DEVICE_ID_CYCLOM_4Y_Lo 0x0102 +#define PCI_DEVICE_ID_CYCLOM_4Y_Hi 0x0103 +#define PCI_DEVICE_ID_CYCLOM_8Y_Lo 0x0104 +#define PCI_DEVICE_ID_CYCLOM_8Y_Hi 0x0105 +#define PCI_DEVICE_ID_CYCLOM_Z_Lo 0x0200 +#define PCI_DEVICE_ID_CYCLOM_Z_Hi 0x0201 +#define PCI_DEVICE_ID_PC300_RX_2 0x0300 +#define PCI_DEVICE_ID_PC300_RX_1 0x0301 +#define PCI_DEVICE_ID_PC300_TE_2 0x0310 +#define PCI_DEVICE_ID_PC300_TE_1 0x0311 +#define PCI_DEVICE_ID_PC300_TE_M_2 0x0320 +#define PCI_DEVICE_ID_PC300_TE_M_1 0x0321 + +#define PCI_VENDOR_ID_ESSENTIAL 0x120f +#define PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER 0x0001 + +#define PCI_VENDOR_ID_O2 0x1217 +#define PCI_DEVICE_ID_O2_6729 0x6729 +#define PCI_DEVICE_ID_O2_6730 0x673a +#define PCI_DEVICE_ID_O2_6832 0x6832 +#define PCI_DEVICE_ID_O2_6836 0x6836 +#define PCI_DEVICE_ID_O2_6812 0x6872 +#define PCI_DEVICE_ID_O2_6933 0x6933 +#define PCI_DEVICE_ID_O2_8120 0x8120 +#define PCI_DEVICE_ID_O2_8220 0x8220 +#define PCI_DEVICE_ID_O2_8221 0x8221 +#define PCI_DEVICE_ID_O2_8320 0x8320 +#define PCI_DEVICE_ID_O2_8321 0x8321 + +#define PCI_VENDOR_ID_3DFX 0x121a +#define PCI_DEVICE_ID_3DFX_VOODOO 0x0001 +#define PCI_DEVICE_ID_3DFX_VOODOO2 0x0002 +#define PCI_DEVICE_ID_3DFX_BANSHEE 0x0003 +#define PCI_DEVICE_ID_3DFX_VOODOO3 0x0005 +#define PCI_DEVICE_ID_3DFX_VOODOO5 0x0009 + +#define PCI_VENDOR_ID_AVM 0x1244 +#define PCI_DEVICE_ID_AVM_B1 0x0700 +#define PCI_DEVICE_ID_AVM_C4 0x0800 +#define PCI_DEVICE_ID_AVM_A1 0x0a00 +#define PCI_DEVICE_ID_AVM_A1_V2 0x0e00 +#define PCI_DEVICE_ID_AVM_C2 0x1100 +#define PCI_DEVICE_ID_AVM_T1 0x1200 + +#define PCI_VENDOR_ID_STALLION 0x124d + +/* Allied Telesyn */ +#define PCI_VENDOR_ID_AT 0x1259 +#define PCI_SUBDEVICE_ID_AT_2700FX 0x2701 +#define PCI_SUBDEVICE_ID_AT_2701FX 0x2703 + +#define PCI_VENDOR_ID_ESS 0x125d +#define PCI_DEVICE_ID_ESS_ESS1968 0x1968 +#define PCI_DEVICE_ID_ESS_ESS1978 0x1978 +#define PCI_DEVICE_ID_ESS_ALLEGRO_1 0x1988 +#define PCI_DEVICE_ID_ESS_ALLEGRO 0x1989 +#define PCI_DEVICE_ID_ESS_CANYON3D_2LE 0x1990 +#define PCI_DEVICE_ID_ESS_CANYON3D_2 0x1992 +#define PCI_DEVICE_ID_ESS_MAESTRO3 0x1998 +#define PCI_DEVICE_ID_ESS_MAESTRO3_1 0x1999 +#define PCI_DEVICE_ID_ESS_MAESTRO3_HW 0x199a +#define PCI_DEVICE_ID_ESS_MAESTRO3_2 0x199b + +#define PCI_VENDOR_ID_SATSAGEM 0x1267 +#define PCI_DEVICE_ID_SATSAGEM_NICCY 0x1016 + +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880 +#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 +#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 + +#define PCI_VENDOR_ID_TRANSMETA 0x1279 +#define PCI_DEVICE_ID_EFFICEON 0x0060 + +#define PCI_VENDOR_ID_ROCKWELL 0x127A + +#define PCI_VENDOR_ID_ITE 0x1283 +#define PCI_DEVICE_ID_ITE_8172 0x8172 +#define PCI_DEVICE_ID_ITE_8211 0x8211 +#define PCI_DEVICE_ID_ITE_8212 0x8212 +#define PCI_DEVICE_ID_ITE_8213 0x8213 +#define PCI_DEVICE_ID_ITE_8152 0x8152 +#define PCI_DEVICE_ID_ITE_8872 0x8872 +#define PCI_DEVICE_ID_ITE_IT8330G_0 0xe886 + +/* formerly Platform Tech */ +#define PCI_DEVICE_ID_ESS_ESS0100 0x0100 + +#define PCI_VENDOR_ID_ALTEON 0x12ae + +#define PCI_SUBVENDOR_ID_CONNECT_TECH 0x12c4 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232 0x0001 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232 0x0002 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232 0x0003 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485 0x0004 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4 0x0005 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485 0x0006 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2 0x0007 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485 0x0008 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6 0x0009 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1 0x000A +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1 0x000B +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ 0x000C +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_PTM 0x000D +#define PCI_SUBDEVICE_ID_CONNECT_TECH_NT960PCI 0x0100 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2 0x0201 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4 0x0202 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232 0x0300 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232 0x0301 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232 0x0302 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1 0x0310 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2 0x0311 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4 0x0312 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2 0x0320 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4 0x0321 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8 0x0322 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485 0x0330 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485 0x0331 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485 0x0332 + +#define PCI_VENDOR_ID_NVIDIA_SGS 0x12d2 +#define PCI_DEVICE_ID_NVIDIA_SGS_RIVA128 0x0018 + +#define PCI_SUBVENDOR_ID_CHASE_PCIFAST 0x12E0 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST4 0x0031 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST8 0x0021 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST16 0x0011 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC 0x0041 +#define PCI_SUBVENDOR_ID_CHASE_PCIRAS 0x124D +#define PCI_SUBDEVICE_ID_CHASE_PCIRAS4 0xF001 +#define PCI_SUBDEVICE_ID_CHASE_PCIRAS8 0xF010 + +#define PCI_VENDOR_ID_AUREAL 0x12eb +#define PCI_DEVICE_ID_AUREAL_VORTEX_1 0x0001 +#define PCI_DEVICE_ID_AUREAL_VORTEX_2 0x0002 +#define PCI_DEVICE_ID_AUREAL_ADVANTAGE 0x0003 + +#define PCI_VENDOR_ID_ELECTRONICDESIGNGMBH 0x12f8 +#define PCI_DEVICE_ID_LML_33R10 0x8a02 + +#define PCI_VENDOR_ID_ESDGMBH 0x12fe +#define PCI_DEVICE_ID_ESDGMBH_CPCIASIO4 0x0111 + +#define PCI_VENDOR_ID_SIIG 0x131f +#define PCI_SUBVENDOR_ID_SIIG 0x131f +#define PCI_DEVICE_ID_SIIG_1S_10x_550 0x1000 +#define PCI_DEVICE_ID_SIIG_1S_10x_650 0x1001 +#define PCI_DEVICE_ID_SIIG_1S_10x_850 0x1002 +#define PCI_DEVICE_ID_SIIG_1S1P_10x_550 0x1010 +#define PCI_DEVICE_ID_SIIG_1S1P_10x_650 0x1011 +#define PCI_DEVICE_ID_SIIG_1S1P_10x_850 0x1012 +#define PCI_DEVICE_ID_SIIG_1P_10x 0x1020 +#define PCI_DEVICE_ID_SIIG_2P_10x 0x1021 +#define PCI_DEVICE_ID_SIIG_2S_10x_550 0x1030 +#define PCI_DEVICE_ID_SIIG_2S_10x_650 0x1031 +#define PCI_DEVICE_ID_SIIG_2S_10x_850 0x1032 +#define PCI_DEVICE_ID_SIIG_2S1P_10x_550 0x1034 +#define PCI_DEVICE_ID_SIIG_2S1P_10x_650 0x1035 +#define PCI_DEVICE_ID_SIIG_2S1P_10x_850 0x1036 +#define PCI_DEVICE_ID_SIIG_4S_10x_550 0x1050 +#define PCI_DEVICE_ID_SIIG_4S_10x_650 0x1051 +#define PCI_DEVICE_ID_SIIG_4S_10x_850 0x1052 +#define PCI_DEVICE_ID_SIIG_1S_20x_550 0x2000 +#define PCI_DEVICE_ID_SIIG_1S_20x_650 0x2001 +#define PCI_DEVICE_ID_SIIG_1S_20x_850 0x2002 +#define PCI_DEVICE_ID_SIIG_1P_20x 0x2020 +#define PCI_DEVICE_ID_SIIG_2P_20x 0x2021 +#define PCI_DEVICE_ID_SIIG_2S_20x_550 0x2030 +#define PCI_DEVICE_ID_SIIG_2S_20x_650 0x2031 +#define PCI_DEVICE_ID_SIIG_2S_20x_850 0x2032 +#define PCI_DEVICE_ID_SIIG_2P1S_20x_550 0x2040 +#define PCI_DEVICE_ID_SIIG_2P1S_20x_650 0x2041 +#define PCI_DEVICE_ID_SIIG_2P1S_20x_850 0x2042 +#define PCI_DEVICE_ID_SIIG_1S1P_20x_550 0x2010 +#define PCI_DEVICE_ID_SIIG_1S1P_20x_650 0x2011 +#define PCI_DEVICE_ID_SIIG_1S1P_20x_850 0x2012 +#define PCI_DEVICE_ID_SIIG_4S_20x_550 0x2050 +#define PCI_DEVICE_ID_SIIG_4S_20x_650 0x2051 +#define PCI_DEVICE_ID_SIIG_4S_20x_850 0x2052 +#define PCI_DEVICE_ID_SIIG_2S1P_20x_550 0x2060 +#define PCI_DEVICE_ID_SIIG_2S1P_20x_650 0x2061 +#define PCI_DEVICE_ID_SIIG_2S1P_20x_850 0x2062 +#define PCI_DEVICE_ID_SIIG_8S_20x_550 0x2080 +#define PCI_DEVICE_ID_SIIG_8S_20x_650 0x2081 +#define PCI_DEVICE_ID_SIIG_8S_20x_850 0x2082 +#define PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL 0x2050 +#define PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL 0x2530 + +#define PCI_VENDOR_ID_RADISYS 0x1331 + +#define PCI_VENDOR_ID_MICRO_MEMORY 0x1332 +#define PCI_DEVICE_ID_MICRO_MEMORY_5415CN 0x5415 +#define PCI_DEVICE_ID_MICRO_MEMORY_5425CN 0x5425 +#define PCI_DEVICE_ID_MICRO_MEMORY_6155 0x6155 + +#define PCI_VENDOR_ID_DOMEX 0x134a +#define PCI_DEVICE_ID_DOMEX_DMX3191D 0x0001 + +#define PCI_VENDOR_ID_INTASHIELD 0x135a +#define PCI_DEVICE_ID_INTASHIELD_IS200 0x0d80 +#define PCI_DEVICE_ID_INTASHIELD_IS400 0x0dc0 + +#define PCI_VENDOR_ID_QUATECH 0x135C +#define PCI_DEVICE_ID_QUATECH_QSC100 0x0010 +#define PCI_DEVICE_ID_QUATECH_DSC100 0x0020 +#define PCI_DEVICE_ID_QUATECH_ESC100D 0x0050 +#define PCI_DEVICE_ID_QUATECH_ESC100M 0x0060 +#define PCI_DEVICE_ID_QUATECH_SPPXP_100 0x0278 + +#define PCI_VENDOR_ID_SEALEVEL 0x135e +#define PCI_DEVICE_ID_SEALEVEL_U530 0x7101 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM2 0x7201 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM422 0x7402 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM232 0x7202 +#define PCI_DEVICE_ID_SEALEVEL_COMM4 0x7401 +#define PCI_DEVICE_ID_SEALEVEL_COMM8 0x7801 +#define PCI_DEVICE_ID_SEALEVEL_7803 0x7803 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM8 0x7804 + +#define PCI_VENDOR_ID_HYPERCOPE 0x1365 +#define PCI_DEVICE_ID_HYPERCOPE_PLX 0x9050 +#define PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO 0x0104 +#define PCI_SUBDEVICE_ID_HYPERCOPE_ERGO 0x0106 +#define PCI_SUBDEVICE_ID_HYPERCOPE_METRO 0x0107 +#define PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2 0x0108 + +#define PCI_VENDOR_ID_DIGIGRAM 0x1369 +#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM 0xc001 +#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM 0xc002 + +#define PCI_VENDOR_ID_KAWASAKI 0x136b +#define PCI_DEVICE_ID_MCHIP_KL5A72002 0xff01 + +#define PCI_VENDOR_ID_CNET 0x1371 +#define PCI_DEVICE_ID_CNET_GIGACARD 0x434e + +#define PCI_VENDOR_ID_LMC 0x1376 +#define PCI_DEVICE_ID_LMC_HSSI 0x0003 +#define PCI_DEVICE_ID_LMC_DS3 0x0004 +#define PCI_DEVICE_ID_LMC_SSI 0x0005 +#define PCI_DEVICE_ID_LMC_T1 0x0006 + +#define PCI_VENDOR_ID_NETGEAR 0x1385 +#define PCI_DEVICE_ID_NETGEAR_GA620 0x620a + +#define PCI_VENDOR_ID_APPLICOM 0x1389 +#define PCI_DEVICE_ID_APPLICOM_PCIGENERIC 0x0001 +#define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002 +#define PCI_DEVICE_ID_APPLICOM_PCI2000PFB 0x0003 + +#define PCI_VENDOR_ID_MOXA 0x1393 +#define PCI_DEVICE_ID_MOXA_RC7000 0x0001 +#define PCI_DEVICE_ID_MOXA_CP102 0x1020 +#define PCI_DEVICE_ID_MOXA_CP102UL 0x1021 +#define PCI_DEVICE_ID_MOXA_CP102U 0x1022 +#define PCI_DEVICE_ID_MOXA_C104 0x1040 +#define PCI_DEVICE_ID_MOXA_CP104U 0x1041 +#define PCI_DEVICE_ID_MOXA_CP104JU 0x1042 +#define PCI_DEVICE_ID_MOXA_CP104EL 0x1043 +#define PCI_DEVICE_ID_MOXA_CT114 0x1140 +#define PCI_DEVICE_ID_MOXA_CP114 0x1141 +#define PCI_DEVICE_ID_MOXA_CP118U 0x1180 +#define PCI_DEVICE_ID_MOXA_CP118EL 0x1181 +#define PCI_DEVICE_ID_MOXA_CP132 0x1320 +#define PCI_DEVICE_ID_MOXA_CP132U 0x1321 +#define PCI_DEVICE_ID_MOXA_CP134U 0x1340 +#define PCI_DEVICE_ID_MOXA_C168 0x1680 +#define PCI_DEVICE_ID_MOXA_CP168U 0x1681 +#define PCI_DEVICE_ID_MOXA_CP168EL 0x1682 +#define PCI_DEVICE_ID_MOXA_CP204J 0x2040 +#define PCI_DEVICE_ID_MOXA_C218 0x2180 +#define PCI_DEVICE_ID_MOXA_C320 0x3200 + +#define PCI_VENDOR_ID_CCD 0x1397 +#define PCI_DEVICE_ID_CCD_HFC4S 0x08B4 +#define PCI_SUBDEVICE_ID_CCD_PMX2S 0x1234 +#define PCI_DEVICE_ID_CCD_HFC8S 0x16B8 +#define PCI_DEVICE_ID_CCD_2BD0 0x2bd0 +#define PCI_DEVICE_ID_CCD_HFCE1 0x30B1 +#define PCI_SUBDEVICE_ID_CCD_SPD4S 0x3136 +#define PCI_SUBDEVICE_ID_CCD_SPDE1 0x3137 +#define PCI_DEVICE_ID_CCD_B000 0xb000 +#define PCI_DEVICE_ID_CCD_B006 0xb006 +#define PCI_DEVICE_ID_CCD_B007 0xb007 +#define PCI_DEVICE_ID_CCD_B008 0xb008 +#define PCI_DEVICE_ID_CCD_B009 0xb009 +#define PCI_DEVICE_ID_CCD_B00A 0xb00a +#define PCI_DEVICE_ID_CCD_B00B 0xb00b +#define PCI_DEVICE_ID_CCD_B00C 0xb00c +#define PCI_DEVICE_ID_CCD_B100 0xb100 +#define PCI_SUBDEVICE_ID_CCD_IOB4ST 0xB520 +#define PCI_SUBDEVICE_ID_CCD_IOB8STR 0xB521 +#define PCI_SUBDEVICE_ID_CCD_IOB8ST 0xB522 +#define PCI_SUBDEVICE_ID_CCD_IOB1E1 0xB523 +#define PCI_SUBDEVICE_ID_CCD_SWYX4S 0xB540 +#define PCI_SUBDEVICE_ID_CCD_JH4S20 0xB550 +#define PCI_SUBDEVICE_ID_CCD_IOB8ST_1 0xB552 +#define PCI_SUBDEVICE_ID_CCD_JHSE1 0xB553 +#define PCI_SUBDEVICE_ID_CCD_JH8S 0xB55B +#define PCI_SUBDEVICE_ID_CCD_BN4S 0xB560 +#define PCI_SUBDEVICE_ID_CCD_BN8S 0xB562 +#define PCI_SUBDEVICE_ID_CCD_BNE1 0xB563 +#define PCI_SUBDEVICE_ID_CCD_BNE1D 0xB564 +#define PCI_SUBDEVICE_ID_CCD_BNE1DP 0xB565 +#define PCI_SUBDEVICE_ID_CCD_BN2S 0xB566 +#define PCI_SUBDEVICE_ID_CCD_BN1SM 0xB567 +#define PCI_SUBDEVICE_ID_CCD_BN4SM 0xB568 +#define PCI_SUBDEVICE_ID_CCD_BN2SM 0xB569 +#define PCI_SUBDEVICE_ID_CCD_BNE1M 0xB56A +#define PCI_SUBDEVICE_ID_CCD_BN8SP 0xB56B +#define PCI_SUBDEVICE_ID_CCD_HFC4S 0xB620 +#define PCI_SUBDEVICE_ID_CCD_HFC8S 0xB622 +#define PCI_DEVICE_ID_CCD_B700 0xb700 +#define PCI_DEVICE_ID_CCD_B701 0xb701 +#define PCI_SUBDEVICE_ID_CCD_HFCE1 0xC523 +#define PCI_SUBDEVICE_ID_CCD_OV2S 0xE884 +#define PCI_SUBDEVICE_ID_CCD_OV4S 0xE888 +#define PCI_SUBDEVICE_ID_CCD_OV8S 0xE998 + +#define PCI_VENDOR_ID_EXAR 0x13a8 +#define PCI_DEVICE_ID_EXAR_XR17C152 0x0152 +#define PCI_DEVICE_ID_EXAR_XR17C154 0x0154 +#define PCI_DEVICE_ID_EXAR_XR17C158 0x0158 + +#define PCI_VENDOR_ID_MICROGATE 0x13c0 +#define PCI_DEVICE_ID_MICROGATE_USC 0x0010 +#define PCI_DEVICE_ID_MICROGATE_SCA 0x0030 + +#define PCI_VENDOR_ID_3WARE 0x13C1 +#define PCI_DEVICE_ID_3WARE_1000 0x1000 +#define PCI_DEVICE_ID_3WARE_7000 0x1001 +#define PCI_DEVICE_ID_3WARE_9000 0x1002 + +#define PCI_VENDOR_ID_IOMEGA 0x13ca +#define PCI_DEVICE_ID_IOMEGA_BUZ 0x4231 + +#define PCI_VENDOR_ID_ABOCOM 0x13D1 +#define PCI_DEVICE_ID_ABOCOM_2BD1 0x2BD1 + +#define PCI_VENDOR_ID_SUNDANCE 0x13f0 + +#define PCI_VENDOR_ID_CMEDIA 0x13f6 +#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100 +#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101 +#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111 +#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112 + +#define PCI_VENDOR_ID_LAVA 0x1407 +#define PCI_DEVICE_ID_LAVA_DSERIAL 0x0100 /* 2x 16550 */ +#define PCI_DEVICE_ID_LAVA_QUATRO_A 0x0101 /* 2x 16550, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_QUATRO_B 0x0102 /* 2x 16550, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_QUATTRO_A 0x0120 /* 2x 16550A, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_QUATTRO_B 0x0121 /* 2x 16550A, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_OCTO_A 0x0180 /* 4x 16550A, half of 8 port */ +#define PCI_DEVICE_ID_LAVA_OCTO_B 0x0181 /* 4x 16550A, half of 8 port */ +#define PCI_DEVICE_ID_LAVA_PORT_PLUS 0x0200 /* 2x 16650 */ +#define PCI_DEVICE_ID_LAVA_QUAD_A 0x0201 /* 2x 16650, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_QUAD_B 0x0202 /* 2x 16650, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_SSERIAL 0x0500 /* 1x 16550 */ +#define PCI_DEVICE_ID_LAVA_PORT_650 0x0600 /* 1x 16650 */ +#define PCI_DEVICE_ID_LAVA_PARALLEL 0x8000 +#define PCI_DEVICE_ID_LAVA_DUAL_PAR_A 0x8002 /* The Lava Dual Parallel is */ +#define PCI_DEVICE_ID_LAVA_DUAL_PAR_B 0x8003 /* two PCI devices on a card */ +#define PCI_DEVICE_ID_LAVA_BOCA_IOPPAR 0x8800 + +#define PCI_VENDOR_ID_TIMEDIA 0x1409 +#define PCI_DEVICE_ID_TIMEDIA_1889 0x7168 + +#define PCI_VENDOR_ID_ICE 0x1412 +#define PCI_DEVICE_ID_ICE_1712 0x1712 +#define PCI_DEVICE_ID_VT1724 0x1724 + +#define PCI_VENDOR_ID_OXSEMI 0x1415 +#define PCI_DEVICE_ID_OXSEMI_12PCI840 0x8403 +#define PCI_DEVICE_ID_OXSEMI_PCIe840 0xC000 +#define PCI_DEVICE_ID_OXSEMI_PCIe840_G 0xC004 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_0 0xC100 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_0_G 0xC104 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1 0xC110 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_G 0xC114 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_U 0xC118 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU 0xC11C +#define PCI_DEVICE_ID_OXSEMI_16PCI954 0x9501 +#define PCI_DEVICE_ID_OXSEMI_C950 0x950B +#define PCI_DEVICE_ID_OXSEMI_16PCI95N 0x9511 +#define PCI_DEVICE_ID_OXSEMI_16PCI954PP 0x9513 +#define PCI_DEVICE_ID_OXSEMI_16PCI952 0x9521 +#define PCI_DEVICE_ID_OXSEMI_16PCI952PP 0x9523 +#define PCI_SUBDEVICE_ID_OXSEMI_C950 0x0001 + +#define PCI_VENDOR_ID_CHELSIO 0x1425 + +#define PCI_VENDOR_ID_SAMSUNG 0x144d + +#define PCI_VENDOR_ID_GIGABYTE 0x1458 + +#define PCI_VENDOR_ID_AMBIT 0x1468 + +#define PCI_VENDOR_ID_MYRICOM 0x14c1 + +#define PCI_VENDOR_ID_TITAN 0x14D2 +#define PCI_DEVICE_ID_TITAN_010L 0x8001 +#define PCI_DEVICE_ID_TITAN_100L 0x8010 +#define PCI_DEVICE_ID_TITAN_110L 0x8011 +#define PCI_DEVICE_ID_TITAN_200L 0x8020 +#define PCI_DEVICE_ID_TITAN_210L 0x8021 +#define PCI_DEVICE_ID_TITAN_400L 0x8040 +#define PCI_DEVICE_ID_TITAN_800L 0x8080 +#define PCI_DEVICE_ID_TITAN_100 0xA001 +#define PCI_DEVICE_ID_TITAN_200 0xA005 +#define PCI_DEVICE_ID_TITAN_400 0xA003 +#define PCI_DEVICE_ID_TITAN_800B 0xA004 + +#define PCI_VENDOR_ID_PANACOM 0x14d4 +#define PCI_DEVICE_ID_PANACOM_QUADMODEM 0x0400 +#define PCI_DEVICE_ID_PANACOM_DUALMODEM 0x0402 + +#define PCI_VENDOR_ID_SIPACKETS 0x14d9 +#define PCI_DEVICE_ID_SP1011 0x0010 + +#define PCI_VENDOR_ID_AFAVLAB 0x14db +#define PCI_DEVICE_ID_AFAVLAB_P028 0x2180 +#define PCI_DEVICE_ID_AFAVLAB_P030 0x2182 +#define PCI_SUBDEVICE_ID_AFAVLAB_P061 0x2150 + +#define PCI_VENDOR_ID_BCM_GVC 0x14a4 +#define PCI_VENDOR_ID_BROADCOM 0x14e4 +#define PCI_DEVICE_ID_TIGON3_5752 0x1600 +#define PCI_DEVICE_ID_TIGON3_5752M 0x1601 +#define PCI_DEVICE_ID_NX2_5709 0x1639 +#define PCI_DEVICE_ID_NX2_5709S 0x163a +#define PCI_DEVICE_ID_TIGON3_5700 0x1644 +#define PCI_DEVICE_ID_TIGON3_5701 0x1645 +#define PCI_DEVICE_ID_TIGON3_5702 0x1646 +#define PCI_DEVICE_ID_TIGON3_5703 0x1647 +#define PCI_DEVICE_ID_TIGON3_5704 0x1648 +#define PCI_DEVICE_ID_TIGON3_5704S_2 0x1649 +#define PCI_DEVICE_ID_NX2_5706 0x164a +#define PCI_DEVICE_ID_NX2_5708 0x164c +#define PCI_DEVICE_ID_TIGON3_5702FE 0x164d +#define PCI_DEVICE_ID_NX2_57710 0x164e +#define PCI_DEVICE_ID_NX2_57711 0x164f +#define PCI_DEVICE_ID_NX2_57711E 0x1650 +#define PCI_DEVICE_ID_TIGON3_5705 0x1653 +#define PCI_DEVICE_ID_TIGON3_5705_2 0x1654 +#define PCI_DEVICE_ID_TIGON3_5721 0x1659 +#define PCI_DEVICE_ID_TIGON3_5722 0x165a +#define PCI_DEVICE_ID_TIGON3_5723 0x165b +#define PCI_DEVICE_ID_TIGON3_5705M 0x165d +#define PCI_DEVICE_ID_TIGON3_5705M_2 0x165e +#define PCI_DEVICE_ID_NX2_57712 0x1662 +#define PCI_DEVICE_ID_NX2_57712E 0x1663 +#define PCI_DEVICE_ID_TIGON3_5714 0x1668 +#define PCI_DEVICE_ID_TIGON3_5714S 0x1669 +#define PCI_DEVICE_ID_TIGON3_5780 0x166a +#define PCI_DEVICE_ID_TIGON3_5780S 0x166b +#define PCI_DEVICE_ID_TIGON3_5705F 0x166e +#define PCI_DEVICE_ID_TIGON3_5754M 0x1672 +#define PCI_DEVICE_ID_TIGON3_5755M 0x1673 +#define PCI_DEVICE_ID_TIGON3_5756 0x1674 +#define PCI_DEVICE_ID_TIGON3_5751 0x1677 +#define PCI_DEVICE_ID_TIGON3_5715 0x1678 +#define PCI_DEVICE_ID_TIGON3_5715S 0x1679 +#define PCI_DEVICE_ID_TIGON3_5754 0x167a +#define PCI_DEVICE_ID_TIGON3_5755 0x167b +#define PCI_DEVICE_ID_TIGON3_5751M 0x167d +#define PCI_DEVICE_ID_TIGON3_5751F 0x167e +#define PCI_DEVICE_ID_TIGON3_5787F 0x167f +#define PCI_DEVICE_ID_TIGON3_5761E 0x1680 +#define PCI_DEVICE_ID_TIGON3_5761 0x1681 +#define PCI_DEVICE_ID_TIGON3_5764 0x1684 +#define PCI_DEVICE_ID_NX2_57800 0x168a +#define PCI_DEVICE_ID_NX2_57840 0x168d +#define PCI_DEVICE_ID_NX2_57810 0x168e +#define PCI_DEVICE_ID_TIGON3_5787M 0x1693 +#define PCI_DEVICE_ID_TIGON3_5782 0x1696 +#define PCI_DEVICE_ID_TIGON3_5784 0x1698 +#define PCI_DEVICE_ID_TIGON3_5786 0x169a +#define PCI_DEVICE_ID_TIGON3_5787 0x169b +#define PCI_DEVICE_ID_TIGON3_5788 0x169c +#define PCI_DEVICE_ID_TIGON3_5789 0x169d +#define PCI_DEVICE_ID_NX2_57800_MF 0x16a5 +#define PCI_DEVICE_ID_TIGON3_5702X 0x16a6 +#define PCI_DEVICE_ID_TIGON3_5703X 0x16a7 +#define PCI_DEVICE_ID_TIGON3_5704S 0x16a8 +#define PCI_DEVICE_ID_NX2_57800_VF 0x16a9 +#define PCI_DEVICE_ID_NX2_5706S 0x16aa +#define PCI_DEVICE_ID_NX2_57840_MF 0x16ab +#define PCI_DEVICE_ID_NX2_5708S 0x16ac +#define PCI_DEVICE_ID_NX2_57840_VF 0x16ad +#define PCI_DEVICE_ID_NX2_57810_MF 0x16ae +#define PCI_DEVICE_ID_NX2_57810_VF 0x16af +#define PCI_DEVICE_ID_TIGON3_5702A3 0x16c6 +#define PCI_DEVICE_ID_TIGON3_5703A3 0x16c7 +#define PCI_DEVICE_ID_TIGON3_5781 0x16dd +#define PCI_DEVICE_ID_TIGON3_5753 0x16f7 +#define PCI_DEVICE_ID_TIGON3_5753M 0x16fd +#define PCI_DEVICE_ID_TIGON3_5753F 0x16fe +#define PCI_DEVICE_ID_TIGON3_5901 0x170d +#define PCI_DEVICE_ID_BCM4401B1 0x170c +#define PCI_DEVICE_ID_TIGON3_5901_2 0x170e +#define PCI_DEVICE_ID_TIGON3_5906 0x1712 +#define PCI_DEVICE_ID_TIGON3_5906M 0x1713 +#define PCI_DEVICE_ID_BCM4401 0x4401 +#define PCI_DEVICE_ID_BCM4401B0 0x4402 + +#define PCI_VENDOR_ID_TOPIC 0x151f +#define PCI_DEVICE_ID_TOPIC_TP560 0x0000 + +#define PCI_VENDOR_ID_MAINPINE 0x1522 +#define PCI_DEVICE_ID_MAINPINE_PBRIDGE 0x0100 +#define PCI_VENDOR_ID_ENE 0x1524 +#define PCI_DEVICE_ID_ENE_CB710_FLASH 0x0510 +#define PCI_DEVICE_ID_ENE_CB712_SD 0x0550 +#define PCI_DEVICE_ID_ENE_CB712_SD_2 0x0551 +#define PCI_DEVICE_ID_ENE_CB714_SD 0x0750 +#define PCI_DEVICE_ID_ENE_CB714_SD_2 0x0751 +#define PCI_DEVICE_ID_ENE_1211 0x1211 +#define PCI_DEVICE_ID_ENE_1225 0x1225 +#define PCI_DEVICE_ID_ENE_1410 0x1410 +#define PCI_DEVICE_ID_ENE_710 0x1411 +#define PCI_DEVICE_ID_ENE_712 0x1412 +#define PCI_DEVICE_ID_ENE_1420 0x1420 +#define PCI_DEVICE_ID_ENE_720 0x1421 +#define PCI_DEVICE_ID_ENE_722 0x1422 + +#define PCI_SUBVENDOR_ID_PERLE 0x155f +#define PCI_SUBDEVICE_ID_PCI_RAS4 0xf001 +#define PCI_SUBDEVICE_ID_PCI_RAS8 0xf010 + +#define PCI_VENDOR_ID_SYBA 0x1592 +#define PCI_DEVICE_ID_SYBA_2P_EPP 0x0782 +#define PCI_DEVICE_ID_SYBA_1P_ECP 0x0783 + +#define PCI_VENDOR_ID_MORETON 0x15aa +#define PCI_DEVICE_ID_RASTEL_2PORT 0x2000 + +#define PCI_VENDOR_ID_ZOLTRIX 0x15b0 +#define PCI_DEVICE_ID_ZOLTRIX_2BD0 0x2bd0 + +#define PCI_VENDOR_ID_MELLANOX 0x15b3 +#define PCI_DEVICE_ID_MELLANOX_TAVOR 0x5a44 +#define PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE 0x5a46 +#define PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT 0x6278 +#define PCI_DEVICE_ID_MELLANOX_ARBEL 0x6282 +#define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c +#define PCI_DEVICE_ID_MELLANOX_SINAI 0x6274 + +#define PCI_VENDOR_ID_DFI 0x15bd + +#define PCI_VENDOR_ID_QUICKNET 0x15e2 +#define PCI_DEVICE_ID_QUICKNET_XJ 0x0500 + +/* + * ADDI-DATA GmbH communication cards + */ +#define PCI_VENDOR_ID_ADDIDATA_OLD 0x10E8 +#define PCI_VENDOR_ID_ADDIDATA 0x15B8 +#define PCI_DEVICE_ID_ADDIDATA_APCI7500 0x7000 +#define PCI_DEVICE_ID_ADDIDATA_APCI7420 0x7001 +#define PCI_DEVICE_ID_ADDIDATA_APCI7300 0x7002 +#define PCI_DEVICE_ID_ADDIDATA_APCI7800 0x818E +#define PCI_DEVICE_ID_ADDIDATA_APCI7500_2 0x7009 +#define PCI_DEVICE_ID_ADDIDATA_APCI7420_2 0x700A +#define PCI_DEVICE_ID_ADDIDATA_APCI7300_2 0x700B +#define PCI_DEVICE_ID_ADDIDATA_APCI7500_3 0x700C +#define PCI_DEVICE_ID_ADDIDATA_APCI7420_3 0x700D +#define PCI_DEVICE_ID_ADDIDATA_APCI7300_3 0x700E +#define PCI_DEVICE_ID_ADDIDATA_APCI7800_3 0x700F +#define PCI_DEVICE_ID_ADDIDATA_APCIe7300 0x7010 +#define PCI_DEVICE_ID_ADDIDATA_APCIe7420 0x7011 +#define PCI_DEVICE_ID_ADDIDATA_APCIe7500 0x7012 +#define PCI_DEVICE_ID_ADDIDATA_APCIe7800 0x7013 + +#define PCI_VENDOR_ID_PDC 0x15e9 + +#define PCI_VENDOR_ID_FARSITE 0x1619 +#define PCI_DEVICE_ID_FARSITE_T2P 0x0400 +#define PCI_DEVICE_ID_FARSITE_T4P 0x0440 +#define PCI_DEVICE_ID_FARSITE_T1U 0x0610 +#define PCI_DEVICE_ID_FARSITE_T2U 0x0620 +#define PCI_DEVICE_ID_FARSITE_T4U 0x0640 +#define PCI_DEVICE_ID_FARSITE_TE1 0x1610 +#define PCI_DEVICE_ID_FARSITE_TE1C 0x1612 + +#define PCI_VENDOR_ID_ARIMA 0x161f + +#define PCI_VENDOR_ID_BROCADE 0x1657 +#define PCI_DEVICE_ID_BROCADE_CT 0x0014 +#define PCI_DEVICE_ID_BROCADE_FC_8G1P 0x0017 +#define PCI_DEVICE_ID_BROCADE_CT_FC 0x0021 + +#define PCI_VENDOR_ID_SIBYTE 0x166d +#define PCI_DEVICE_ID_BCM1250_PCI 0x0001 +#define PCI_DEVICE_ID_BCM1250_HT 0x0002 + +#define PCI_VENDOR_ID_ATHEROS 0x168c + +#define PCI_VENDOR_ID_NETCELL 0x169c +#define PCI_DEVICE_ID_REVOLUTION 0x0044 + +#define PCI_VENDOR_ID_CENATEK 0x16CA +#define PCI_DEVICE_ID_CENATEK_IDE 0x0001 + +#define PCI_VENDOR_ID_VITESSE 0x1725 +#define PCI_DEVICE_ID_VITESSE_VSC7174 0x7174 + +#define PCI_VENDOR_ID_LINKSYS 0x1737 +#define PCI_DEVICE_ID_LINKSYS_EG1064 0x1064 + +#define PCI_VENDOR_ID_ALTIMA 0x173b +#define PCI_DEVICE_ID_ALTIMA_AC1000 0x03e8 +#define PCI_DEVICE_ID_ALTIMA_AC1001 0x03e9 +#define PCI_DEVICE_ID_ALTIMA_AC9100 0x03ea +#define PCI_DEVICE_ID_ALTIMA_AC1003 0x03eb + +#define PCI_VENDOR_ID_BELKIN 0x1799 +#define PCI_DEVICE_ID_BELKIN_F5D7010V7 0x701f + +#define PCI_VENDOR_ID_RDC 0x17f3 +#define PCI_DEVICE_ID_RDC_R6020 0x6020 +#define PCI_DEVICE_ID_RDC_R6030 0x6030 +#define PCI_DEVICE_ID_RDC_R6040 0x6040 +#define PCI_DEVICE_ID_RDC_R6060 0x6060 +#define PCI_DEVICE_ID_RDC_R6061 0x6061 +#define PCI_DEVICE_ID_RDC_D1010 0x1010 + +#define PCI_VENDOR_ID_LENOVO 0x17aa + +#define PCI_VENDOR_ID_ARECA 0x17d3 +#define PCI_DEVICE_ID_ARECA_1110 0x1110 +#define PCI_DEVICE_ID_ARECA_1120 0x1120 +#define PCI_DEVICE_ID_ARECA_1130 0x1130 +#define PCI_DEVICE_ID_ARECA_1160 0x1160 +#define PCI_DEVICE_ID_ARECA_1170 0x1170 +#define PCI_DEVICE_ID_ARECA_1200 0x1200 +#define PCI_DEVICE_ID_ARECA_1201 0x1201 +#define PCI_DEVICE_ID_ARECA_1202 0x1202 +#define PCI_DEVICE_ID_ARECA_1210 0x1210 +#define PCI_DEVICE_ID_ARECA_1220 0x1220 +#define PCI_DEVICE_ID_ARECA_1230 0x1230 +#define PCI_DEVICE_ID_ARECA_1260 0x1260 +#define PCI_DEVICE_ID_ARECA_1270 0x1270 +#define PCI_DEVICE_ID_ARECA_1280 0x1280 +#define PCI_DEVICE_ID_ARECA_1380 0x1380 +#define PCI_DEVICE_ID_ARECA_1381 0x1381 +#define PCI_DEVICE_ID_ARECA_1680 0x1680 +#define PCI_DEVICE_ID_ARECA_1681 0x1681 + +#define PCI_VENDOR_ID_S2IO 0x17d5 +#define PCI_DEVICE_ID_S2IO_WIN 0x5731 +#define PCI_DEVICE_ID_S2IO_UNI 0x5831 +#define PCI_DEVICE_ID_HERC_WIN 0x5732 +#define PCI_DEVICE_ID_HERC_UNI 0x5832 + +#define PCI_VENDOR_ID_SITECOM 0x182d +#define PCI_DEVICE_ID_SITECOM_DC105V2 0x3069 + +#define PCI_VENDOR_ID_TOPSPIN 0x1867 + +#define PCI_VENDOR_ID_SILAN 0x1904 + +#define PCI_VENDOR_ID_RENESAS 0x1912 +#define PCI_DEVICE_ID_RENESAS_SH7781 0x0001 +#define PCI_DEVICE_ID_RENESAS_SH7780 0x0002 +#define PCI_DEVICE_ID_RENESAS_SH7763 0x0004 +#define PCI_DEVICE_ID_RENESAS_SH7785 0x0007 +#define PCI_DEVICE_ID_RENESAS_SH7786 0x0010 + +#define PCI_VENDOR_ID_SOLARFLARE 0x1924 +#define PCI_DEVICE_ID_SOLARFLARE_SFC4000A_0 0x0703 +#define PCI_DEVICE_ID_SOLARFLARE_SFC4000A_1 0x6703 +#define PCI_DEVICE_ID_SOLARFLARE_SFC4000B 0x0710 + +#define PCI_VENDOR_ID_TDI 0x192E +#define PCI_DEVICE_ID_TDI_EHCI 0x0101 + +#define PCI_VENDOR_ID_FREESCALE 0x1957 +#define PCI_DEVICE_ID_MPC8308 0xc006 +#define PCI_DEVICE_ID_MPC8315E 0x00b4 +#define PCI_DEVICE_ID_MPC8315 0x00b5 +#define PCI_DEVICE_ID_MPC8314E 0x00b6 +#define PCI_DEVICE_ID_MPC8314 0x00b7 +#define PCI_DEVICE_ID_MPC8378E 0x00c4 +#define PCI_DEVICE_ID_MPC8378 0x00c5 +#define PCI_DEVICE_ID_MPC8377E 0x00c6 +#define PCI_DEVICE_ID_MPC8377 0x00c7 +#define PCI_DEVICE_ID_MPC8548E 0x0012 +#define PCI_DEVICE_ID_MPC8548 0x0013 +#define PCI_DEVICE_ID_MPC8543E 0x0014 +#define PCI_DEVICE_ID_MPC8543 0x0015 +#define PCI_DEVICE_ID_MPC8547E 0x0018 +#define PCI_DEVICE_ID_MPC8545E 0x0019 +#define PCI_DEVICE_ID_MPC8545 0x001a +#define PCI_DEVICE_ID_MPC8569E 0x0061 +#define PCI_DEVICE_ID_MPC8569 0x0060 +#define PCI_DEVICE_ID_MPC8568E 0x0020 +#define PCI_DEVICE_ID_MPC8568 0x0021 +#define PCI_DEVICE_ID_MPC8567E 0x0022 +#define PCI_DEVICE_ID_MPC8567 0x0023 +#define PCI_DEVICE_ID_MPC8533E 0x0030 +#define PCI_DEVICE_ID_MPC8533 0x0031 +#define PCI_DEVICE_ID_MPC8544E 0x0032 +#define PCI_DEVICE_ID_MPC8544 0x0033 +#define PCI_DEVICE_ID_MPC8572E 0x0040 +#define PCI_DEVICE_ID_MPC8572 0x0041 +#define PCI_DEVICE_ID_MPC8536E 0x0050 +#define PCI_DEVICE_ID_MPC8536 0x0051 +#define PCI_DEVICE_ID_P2020E 0x0070 +#define PCI_DEVICE_ID_P2020 0x0071 +#define PCI_DEVICE_ID_P2010E 0x0078 +#define PCI_DEVICE_ID_P2010 0x0079 +#define PCI_DEVICE_ID_P1020E 0x0100 +#define PCI_DEVICE_ID_P1020 0x0101 +#define PCI_DEVICE_ID_P1021E 0x0102 +#define PCI_DEVICE_ID_P1021 0x0103 +#define PCI_DEVICE_ID_P1011E 0x0108 +#define PCI_DEVICE_ID_P1011 0x0109 +#define PCI_DEVICE_ID_P1022E 0x0110 +#define PCI_DEVICE_ID_P1022 0x0111 +#define PCI_DEVICE_ID_P1013E 0x0118 +#define PCI_DEVICE_ID_P1013 0x0119 +#define PCI_DEVICE_ID_P4080E 0x0400 +#define PCI_DEVICE_ID_P4080 0x0401 +#define PCI_DEVICE_ID_P4040E 0x0408 +#define PCI_DEVICE_ID_P4040 0x0409 +#define PCI_DEVICE_ID_P2040E 0x0410 +#define PCI_DEVICE_ID_P2040 0x0411 +#define PCI_DEVICE_ID_P3041E 0x041E +#define PCI_DEVICE_ID_P3041 0x041F +#define PCI_DEVICE_ID_P5020E 0x0420 +#define PCI_DEVICE_ID_P5020 0x0421 +#define PCI_DEVICE_ID_P5010E 0x0428 +#define PCI_DEVICE_ID_P5010 0x0429 +#define PCI_DEVICE_ID_MPC8641 0x7010 +#define PCI_DEVICE_ID_MPC8641D 0x7011 +#define PCI_DEVICE_ID_MPC8610 0x7018 + +#define PCI_VENDOR_ID_PASEMI 0x1959 + +#define PCI_VENDOR_ID_ATTANSIC 0x1969 +#define PCI_DEVICE_ID_ATTANSIC_L1 0x1048 +#define PCI_DEVICE_ID_ATTANSIC_L2 0x2048 + +#define PCI_VENDOR_ID_JMICRON 0x197B +#define PCI_DEVICE_ID_JMICRON_JMB360 0x2360 +#define PCI_DEVICE_ID_JMICRON_JMB361 0x2361 +#define PCI_DEVICE_ID_JMICRON_JMB362 0x2362 +#define PCI_DEVICE_ID_JMICRON_JMB363 0x2363 +#define PCI_DEVICE_ID_JMICRON_JMB364 0x2364 +#define PCI_DEVICE_ID_JMICRON_JMB365 0x2365 +#define PCI_DEVICE_ID_JMICRON_JMB366 0x2366 +#define PCI_DEVICE_ID_JMICRON_JMB368 0x2368 +#define PCI_DEVICE_ID_JMICRON_JMB369 0x2369 +#define PCI_DEVICE_ID_JMICRON_JMB38X_SD 0x2381 +#define PCI_DEVICE_ID_JMICRON_JMB38X_MMC 0x2382 +#define PCI_DEVICE_ID_JMICRON_JMB38X_MS 0x2383 +#define PCI_DEVICE_ID_JMICRON_JMB385_MS 0x2388 +#define PCI_DEVICE_ID_JMICRON_JMB388_SD 0x2391 +#define PCI_DEVICE_ID_JMICRON_JMB388_ESD 0x2392 +#define PCI_DEVICE_ID_JMICRON_JMB390_MS 0x2393 + +#define PCI_VENDOR_ID_KORENIX 0x1982 +#define PCI_DEVICE_ID_KORENIX_JETCARDF0 0x1600 +#define PCI_DEVICE_ID_KORENIX_JETCARDF1 0x16ff +#define PCI_DEVICE_ID_KORENIX_JETCARDF2 0x1700 +#define PCI_DEVICE_ID_KORENIX_JETCARDF3 0x17ff + +#define PCI_VENDOR_ID_QMI 0x1a32 + +#define PCI_VENDOR_ID_AZWAVE 0x1a3b + +#define PCI_VENDOR_ID_ASMEDIA 0x1b21 + +#define PCI_VENDOR_ID_TEKRAM 0x1de1 +#define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29 + +#define PCI_VENDOR_ID_TEHUTI 0x1fc9 +#define PCI_DEVICE_ID_TEHUTI_3009 0x3009 +#define PCI_DEVICE_ID_TEHUTI_3010 0x3010 +#define PCI_DEVICE_ID_TEHUTI_3014 0x3014 + +#define PCI_VENDOR_ID_HINT 0x3388 +#define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013 + +#define PCI_VENDOR_ID_3DLABS 0x3d3d +#define PCI_DEVICE_ID_3DLABS_PERMEDIA2 0x0007 +#define PCI_DEVICE_ID_3DLABS_PERMEDIA2V 0x0009 + +#define PCI_VENDOR_ID_NETXEN 0x4040 +#define PCI_DEVICE_ID_NX2031_10GXSR 0x0001 +#define PCI_DEVICE_ID_NX2031_10GCX4 0x0002 +#define PCI_DEVICE_ID_NX2031_4GCU 0x0003 +#define PCI_DEVICE_ID_NX2031_IMEZ 0x0004 +#define PCI_DEVICE_ID_NX2031_HMEZ 0x0005 +#define PCI_DEVICE_ID_NX2031_XG_MGMT 0x0024 +#define PCI_DEVICE_ID_NX2031_XG_MGMT2 0x0025 +#define PCI_DEVICE_ID_NX3031 0x0100 + +#define PCI_VENDOR_ID_AKS 0x416c +#define PCI_DEVICE_ID_AKS_ALADDINCARD 0x0100 + +#define PCI_VENDOR_ID_ACCESSIO 0x494f +#define PCI_DEVICE_ID_ACCESSIO_WDG_CSM 0x22c0 + +#define PCI_VENDOR_ID_S3 0x5333 +#define PCI_DEVICE_ID_S3_TRIO 0x8811 +#define PCI_DEVICE_ID_S3_868 0x8880 +#define PCI_DEVICE_ID_S3_968 0x88f0 +#define PCI_DEVICE_ID_S3_SAVAGE4 0x8a25 +#define PCI_DEVICE_ID_S3_PROSAVAGE8 0x8d04 +#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 + +#define PCI_VENDOR_ID_DUNORD 0x5544 +#define PCI_DEVICE_ID_DUNORD_I3000 0x0001 + +#define PCI_VENDOR_ID_DCI 0x6666 +#define PCI_DEVICE_ID_DCI_PCCOM4 0x0001 +#define PCI_DEVICE_ID_DCI_PCCOM8 0x0002 +#define PCI_DEVICE_ID_DCI_PCCOM2 0x0004 + +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_EESSC 0x0008 +#define PCI_DEVICE_ID_INTEL_PXHD_0 0x0320 +#define PCI_DEVICE_ID_INTEL_PXHD_1 0x0321 +#define PCI_DEVICE_ID_INTEL_PXH_0 0x0329 +#define PCI_DEVICE_ID_INTEL_PXH_1 0x032A +#define PCI_DEVICE_ID_INTEL_PXHV 0x032C +#define PCI_DEVICE_ID_INTEL_80332_0 0x0330 +#define PCI_DEVICE_ID_INTEL_80332_1 0x0332 +#define PCI_DEVICE_ID_INTEL_80333_0 0x0370 +#define PCI_DEVICE_ID_INTEL_80333_1 0x0372 +#define PCI_DEVICE_ID_INTEL_82375 0x0482 +#define PCI_DEVICE_ID_INTEL_82424 0x0483 +#define PCI_DEVICE_ID_INTEL_82378 0x0484 +#define PCI_DEVICE_ID_INTEL_MRST_SD0 0x0807 +#define PCI_DEVICE_ID_INTEL_MRST_SD1 0x0808 +#define PCI_DEVICE_ID_INTEL_MFD_SD 0x0820 +#define PCI_DEVICE_ID_INTEL_MFD_SDIO1 0x0821 +#define PCI_DEVICE_ID_INTEL_MFD_SDIO2 0x0822 +#define PCI_DEVICE_ID_INTEL_MFD_EMMC0 0x0823 +#define PCI_DEVICE_ID_INTEL_MFD_EMMC1 0x0824 +#define PCI_DEVICE_ID_INTEL_MRST_SD2 0x084F +#define PCI_DEVICE_ID_INTEL_I960 0x0960 +#define PCI_DEVICE_ID_INTEL_I960RM 0x0962 +#define PCI_DEVICE_ID_INTEL_8257X_SOL 0x1062 +#define PCI_DEVICE_ID_INTEL_82573E_SOL 0x1085 +#define PCI_DEVICE_ID_INTEL_82573L_SOL 0x108F +#define PCI_DEVICE_ID_INTEL_82815_MC 0x1130 +#define PCI_DEVICE_ID_INTEL_82815_CGC 0x1132 +#define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221 +#define PCI_DEVICE_ID_INTEL_7505_0 0x2550 +#define PCI_DEVICE_ID_INTEL_7205_0 0x255d +#define PCI_DEVICE_ID_INTEL_82437 0x122d +#define PCI_DEVICE_ID_INTEL_82371FB_0 0x122e +#define PCI_DEVICE_ID_INTEL_82371FB_1 0x1230 +#define PCI_DEVICE_ID_INTEL_82371MX 0x1234 +#define PCI_DEVICE_ID_INTEL_82441 0x1237 +#define PCI_DEVICE_ID_INTEL_82380FB 0x124b +#define PCI_DEVICE_ID_INTEL_82439 0x1250 +#define PCI_DEVICE_ID_INTEL_80960_RP 0x1960 +#define PCI_DEVICE_ID_INTEL_82840_HB 0x1a21 +#define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30 +#define PCI_DEVICE_ID_INTEL_IOAT 0x1a38 +#define PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN 0x1c41 +#define PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX 0x1c5f +#define PCI_DEVICE_ID_INTEL_PATSBURG_LPC_0 0x1d40 +#define PCI_DEVICE_ID_INTEL_PATSBURG_LPC_1 0x1d41 +#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI 0x1e31 +#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_LPC_MIN 0x1e40 +#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_LPC_MAX 0x1e5f +#define PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MIN 0x2310 +#define PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MAX 0x231f +#define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410 +#define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411 +#define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 +#define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415 +#define PCI_DEVICE_ID_INTEL_82801AA_6 0x2416 +#define PCI_DEVICE_ID_INTEL_82801AA_8 0x2418 +#define PCI_DEVICE_ID_INTEL_82801AB_0 0x2420 +#define PCI_DEVICE_ID_INTEL_82801AB_1 0x2421 +#define PCI_DEVICE_ID_INTEL_82801AB_3 0x2423 +#define PCI_DEVICE_ID_INTEL_82801AB_5 0x2425 +#define PCI_DEVICE_ID_INTEL_82801AB_6 0x2426 +#define PCI_DEVICE_ID_INTEL_82801AB_8 0x2428 +#define PCI_DEVICE_ID_INTEL_82801BA_0 0x2440 +#define PCI_DEVICE_ID_INTEL_82801BA_2 0x2443 +#define PCI_DEVICE_ID_INTEL_82801BA_4 0x2445 +#define PCI_DEVICE_ID_INTEL_82801BA_6 0x2448 +#define PCI_DEVICE_ID_INTEL_82801BA_8 0x244a +#define PCI_DEVICE_ID_INTEL_82801BA_9 0x244b +#define PCI_DEVICE_ID_INTEL_82801BA_10 0x244c +#define PCI_DEVICE_ID_INTEL_82801BA_11 0x244e +#define PCI_DEVICE_ID_INTEL_82801E_0 0x2450 +#define PCI_DEVICE_ID_INTEL_82801E_11 0x245b +#define PCI_DEVICE_ID_INTEL_82801CA_0 0x2480 +#define PCI_DEVICE_ID_INTEL_82801CA_3 0x2483 +#define PCI_DEVICE_ID_INTEL_82801CA_5 0x2485 +#define PCI_DEVICE_ID_INTEL_82801CA_6 0x2486 +#define PCI_DEVICE_ID_INTEL_82801CA_10 0x248a +#define PCI_DEVICE_ID_INTEL_82801CA_11 0x248b +#define PCI_DEVICE_ID_INTEL_82801CA_12 0x248c +#define PCI_DEVICE_ID_INTEL_82801DB_0 0x24c0 +#define PCI_DEVICE_ID_INTEL_82801DB_1 0x24c1 +#define PCI_DEVICE_ID_INTEL_82801DB_2 0x24c2 +#define PCI_DEVICE_ID_INTEL_82801DB_3 0x24c3 +#define PCI_DEVICE_ID_INTEL_82801DB_5 0x24c5 +#define PCI_DEVICE_ID_INTEL_82801DB_6 0x24c6 +#define PCI_DEVICE_ID_INTEL_82801DB_9 0x24c9 +#define PCI_DEVICE_ID_INTEL_82801DB_10 0x24ca +#define PCI_DEVICE_ID_INTEL_82801DB_11 0x24cb +#define PCI_DEVICE_ID_INTEL_82801DB_12 0x24cc +#define PCI_DEVICE_ID_INTEL_82801EB_0 0x24d0 +#define PCI_DEVICE_ID_INTEL_82801EB_1 0x24d1 +#define PCI_DEVICE_ID_INTEL_82801EB_3 0x24d3 +#define PCI_DEVICE_ID_INTEL_82801EB_5 0x24d5 +#define PCI_DEVICE_ID_INTEL_82801EB_6 0x24d6 +#define PCI_DEVICE_ID_INTEL_82801EB_11 0x24db +#define PCI_DEVICE_ID_INTEL_82801EB_12 0x24dc +#define PCI_DEVICE_ID_INTEL_82801EB_13 0x24dd +#define PCI_DEVICE_ID_INTEL_ESB_1 0x25a1 +#define PCI_DEVICE_ID_INTEL_ESB_2 0x25a2 +#define PCI_DEVICE_ID_INTEL_ESB_4 0x25a4 +#define PCI_DEVICE_ID_INTEL_ESB_5 0x25a6 +#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab +#define PCI_DEVICE_ID_INTEL_ESB_10 0x25ac +#define PCI_DEVICE_ID_INTEL_82820_HB 0x2500 +#define PCI_DEVICE_ID_INTEL_82820_UP_HB 0x2501 +#define PCI_DEVICE_ID_INTEL_82850_HB 0x2530 +#define PCI_DEVICE_ID_INTEL_82860_HB 0x2531 +#define PCI_DEVICE_ID_INTEL_E7501_MCH 0x254c +#define PCI_DEVICE_ID_INTEL_82845G_HB 0x2560 +#define PCI_DEVICE_ID_INTEL_82845G_IG 0x2562 +#define PCI_DEVICE_ID_INTEL_82865_HB 0x2570 +#define PCI_DEVICE_ID_INTEL_82865_IG 0x2572 +#define PCI_DEVICE_ID_INTEL_82875_HB 0x2578 +#define PCI_DEVICE_ID_INTEL_82915G_HB 0x2580 +#define PCI_DEVICE_ID_INTEL_82915G_IG 0x2582 +#define PCI_DEVICE_ID_INTEL_82915GM_HB 0x2590 +#define PCI_DEVICE_ID_INTEL_82915GM_IG 0x2592 +#define PCI_DEVICE_ID_INTEL_5000_ERR 0x25F0 +#define PCI_DEVICE_ID_INTEL_5000_FBD0 0x25F5 +#define PCI_DEVICE_ID_INTEL_5000_FBD1 0x25F6 +#define PCI_DEVICE_ID_INTEL_82945G_HB 0x2770 +#define PCI_DEVICE_ID_INTEL_82945G_IG 0x2772 +#define PCI_DEVICE_ID_INTEL_3000_HB 0x2778 +#define PCI_DEVICE_ID_INTEL_82945GM_HB 0x27A0 +#define PCI_DEVICE_ID_INTEL_82945GM_IG 0x27A2 +#define PCI_DEVICE_ID_INTEL_ICH6_0 0x2640 +#define PCI_DEVICE_ID_INTEL_ICH6_1 0x2641 +#define PCI_DEVICE_ID_INTEL_ICH6_2 0x2642 +#define PCI_DEVICE_ID_INTEL_ICH6_16 0x266a +#define PCI_DEVICE_ID_INTEL_ICH6_17 0x266d +#define PCI_DEVICE_ID_INTEL_ICH6_18 0x266e +#define PCI_DEVICE_ID_INTEL_ICH6_19 0x266f +#define PCI_DEVICE_ID_INTEL_ESB2_0 0x2670 +#define PCI_DEVICE_ID_INTEL_ESB2_14 0x2698 +#define PCI_DEVICE_ID_INTEL_ESB2_17 0x269b +#define PCI_DEVICE_ID_INTEL_ESB2_18 0x269e +#define PCI_DEVICE_ID_INTEL_ICH7_0 0x27b8 +#define PCI_DEVICE_ID_INTEL_ICH7_1 0x27b9 +#define PCI_DEVICE_ID_INTEL_ICH7_30 0x27b0 +#define PCI_DEVICE_ID_INTEL_TGP_LPC 0x27bc +#define PCI_DEVICE_ID_INTEL_ICH7_31 0x27bd +#define PCI_DEVICE_ID_INTEL_ICH7_17 0x27da +#define PCI_DEVICE_ID_INTEL_ICH7_19 0x27dd +#define PCI_DEVICE_ID_INTEL_ICH7_20 0x27de +#define PCI_DEVICE_ID_INTEL_ICH7_21 0x27df +#define PCI_DEVICE_ID_INTEL_ICH8_0 0x2810 +#define PCI_DEVICE_ID_INTEL_ICH8_1 0x2811 +#define PCI_DEVICE_ID_INTEL_ICH8_2 0x2812 +#define PCI_DEVICE_ID_INTEL_ICH8_3 0x2814 +#define PCI_DEVICE_ID_INTEL_ICH8_4 0x2815 +#define PCI_DEVICE_ID_INTEL_ICH8_5 0x283e +#define PCI_DEVICE_ID_INTEL_ICH8_6 0x2850 +#define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910 +#define PCI_DEVICE_ID_INTEL_ICH9_1 0x2917 +#define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912 +#define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913 +#define PCI_DEVICE_ID_INTEL_ICH9_4 0x2914 +#define PCI_DEVICE_ID_INTEL_ICH9_5 0x2919 +#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 +#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 +#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 +#define PCI_DEVICE_ID_INTEL_I7_MCR 0x2c18 +#define PCI_DEVICE_ID_INTEL_I7_MC_TAD 0x2c19 +#define PCI_DEVICE_ID_INTEL_I7_MC_RAS 0x2c1a +#define PCI_DEVICE_ID_INTEL_I7_MC_TEST 0x2c1c +#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL 0x2c20 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR 0x2c21 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK 0x2c22 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC 0x2c23 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL 0x2c28 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR 0x2c29 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK 0x2c2a +#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC 0x2c2b +#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL 0x2c30 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR 0x2c31 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK 0x2c32 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC 0x2c33 +#define PCI_DEVICE_ID_INTEL_I7_NONCORE 0x2c41 +#define PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT 0x2c40 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE 0x2c50 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT 0x2c51 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2 0x2c70 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_SAD 0x2c81 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0 0x2c90 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_PHY0 0x2c91 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR 0x2c98 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD 0x2c99 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST 0x2c9C +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL 0x2ca0 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR 0x2ca1 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK 0x2ca2 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC 0x2ca3 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL 0x2ca8 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR 0x2ca9 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK 0x2caa +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC 0x2cab +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2 0x2d98 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2 0x2d99 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_RAS_REV2 0x2d9a +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST_REV2 0x2d9c +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL_REV2 0x2da0 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR_REV2 0x2da1 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK_REV2 0x2da2 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC_REV2 0x2da3 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL_REV2 0x2da8 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR_REV2 0x2da9 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK_REV2 0x2daa +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC_REV2 0x2dab +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_CTRL_REV2 0x2db0 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2 0x2db1 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2 0x2db2 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2 0x2db3 +#define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a +#define PCI_DEVICE_ID_INTEL_IOAT_TBG6 0x342b +#define PCI_DEVICE_ID_INTEL_IOAT_TBG7 0x342c +#define PCI_DEVICE_ID_INTEL_X58_HUB_MGMT 0x342e +#define PCI_DEVICE_ID_INTEL_IOAT_TBG0 0x3430 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG1 0x3431 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG2 0x3432 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG3 0x3433 +#define PCI_DEVICE_ID_INTEL_82830_HB 0x3575 +#define PCI_DEVICE_ID_INTEL_82830_CGC 0x3577 +#define PCI_DEVICE_ID_INTEL_82854_HB 0x358c +#define PCI_DEVICE_ID_INTEL_82854_IG 0x358e +#define PCI_DEVICE_ID_INTEL_82855GM_HB 0x3580 +#define PCI_DEVICE_ID_INTEL_82855GM_IG 0x3582 +#define PCI_DEVICE_ID_INTEL_E7520_MCH 0x3590 +#define PCI_DEVICE_ID_INTEL_E7320_MCH 0x3592 +#define PCI_DEVICE_ID_INTEL_MCH_PA 0x3595 +#define PCI_DEVICE_ID_INTEL_MCH_PA1 0x3596 +#define PCI_DEVICE_ID_INTEL_MCH_PB 0x3597 +#define PCI_DEVICE_ID_INTEL_MCH_PB1 0x3598 +#define PCI_DEVICE_ID_INTEL_MCH_PC 0x3599 +#define PCI_DEVICE_ID_INTEL_MCH_PC1 0x359a +#define PCI_DEVICE_ID_INTEL_E7525_MCH 0x359e +#define PCI_DEVICE_ID_INTEL_I7300_MCH_ERR 0x360c +#define PCI_DEVICE_ID_INTEL_I7300_MCH_FB0 0x360f +#define PCI_DEVICE_ID_INTEL_I7300_MCH_FB1 0x3610 +#define PCI_DEVICE_ID_INTEL_IOAT_CNB 0x360b +#define PCI_DEVICE_ID_INTEL_FBD_CNB 0x360c +#define PCI_DEVICE_ID_INTEL_IOAT_JSF0 0x3710 +#define PCI_DEVICE_ID_INTEL_IOAT_JSF1 0x3711 +#define PCI_DEVICE_ID_INTEL_IOAT_JSF2 0x3712 +#define PCI_DEVICE_ID_INTEL_IOAT_JSF3 0x3713 +#define PCI_DEVICE_ID_INTEL_IOAT_JSF4 0x3714 +#define PCI_DEVICE_ID_INTEL_IOAT_JSF5 0x3715 +#define PCI_DEVICE_ID_INTEL_IOAT_JSF6 0x3716 +#define PCI_DEVICE_ID_INTEL_IOAT_JSF7 0x3717 +#define PCI_DEVICE_ID_INTEL_IOAT_JSF8 0x3718 +#define PCI_DEVICE_ID_INTEL_IOAT_JSF9 0x3719 +#define PCI_DEVICE_ID_INTEL_ICH10_0 0x3a14 +#define PCI_DEVICE_ID_INTEL_ICH10_1 0x3a16 +#define PCI_DEVICE_ID_INTEL_ICH10_2 0x3a18 +#define PCI_DEVICE_ID_INTEL_ICH10_3 0x3a1a +#define PCI_DEVICE_ID_INTEL_ICH10_4 0x3a30 +#define PCI_DEVICE_ID_INTEL_ICH10_5 0x3a60 +#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MIN 0x3b00 +#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MAX 0x3b1f +#define PCI_DEVICE_ID_INTEL_IOAT_SNB0 0x3c20 +#define PCI_DEVICE_ID_INTEL_IOAT_SNB1 0x3c21 +#define PCI_DEVICE_ID_INTEL_IOAT_SNB2 0x3c22 +#define PCI_DEVICE_ID_INTEL_IOAT_SNB3 0x3c23 +#define PCI_DEVICE_ID_INTEL_IOAT_SNB4 0x3c24 +#define PCI_DEVICE_ID_INTEL_IOAT_SNB5 0x3c25 +#define PCI_DEVICE_ID_INTEL_IOAT_SNB6 0x3c26 +#define PCI_DEVICE_ID_INTEL_IOAT_SNB7 0x3c27 +#define PCI_DEVICE_ID_INTEL_IOAT_SNB8 0x3c2e +#define PCI_DEVICE_ID_INTEL_IOAT_SNB9 0x3c2f +#define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f +#define PCI_DEVICE_ID_INTEL_5100_16 0x65f0 +#define PCI_DEVICE_ID_INTEL_5100_21 0x65f5 +#define PCI_DEVICE_ID_INTEL_5100_22 0x65f6 +#define PCI_DEVICE_ID_INTEL_5400_ERR 0x4030 +#define PCI_DEVICE_ID_INTEL_5400_FBD0 0x4035 +#define PCI_DEVICE_ID_INTEL_5400_FBD1 0x4036 +#define PCI_DEVICE_ID_INTEL_IOAT_SCNB 0x65ff +#define PCI_DEVICE_ID_INTEL_EP80579_0 0x5031 +#define PCI_DEVICE_ID_INTEL_EP80579_1 0x5032 +#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 +#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 +#define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 +#define PCI_DEVICE_ID_INTEL_82437VX 0x7030 +#define PCI_DEVICE_ID_INTEL_82439TX 0x7100 +#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110 +#define PCI_DEVICE_ID_INTEL_82371AB 0x7111 +#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 +#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 +#define PCI_DEVICE_ID_INTEL_82810_MC1 0x7120 +#define PCI_DEVICE_ID_INTEL_82810_IG1 0x7121 +#define PCI_DEVICE_ID_INTEL_82810_MC3 0x7122 +#define PCI_DEVICE_ID_INTEL_82810_IG3 0x7123 +#define PCI_DEVICE_ID_INTEL_82810E_MC 0x7124 +#define PCI_DEVICE_ID_INTEL_82810E_IG 0x7125 +#define PCI_DEVICE_ID_INTEL_82443LX_0 0x7180 +#define PCI_DEVICE_ID_INTEL_82443LX_1 0x7181 +#define PCI_DEVICE_ID_INTEL_82443BX_0 0x7190 +#define PCI_DEVICE_ID_INTEL_82443BX_1 0x7191 +#define PCI_DEVICE_ID_INTEL_82443BX_2 0x7192 +#define PCI_DEVICE_ID_INTEL_440MX 0x7195 +#define PCI_DEVICE_ID_INTEL_440MX_6 0x7196 +#define PCI_DEVICE_ID_INTEL_82443MX_0 0x7198 +#define PCI_DEVICE_ID_INTEL_82443MX_1 0x7199 +#define PCI_DEVICE_ID_INTEL_82443MX_3 0x719b +#define PCI_DEVICE_ID_INTEL_82443GX_0 0x71a0 +#define PCI_DEVICE_ID_INTEL_82443GX_2 0x71a2 +#define PCI_DEVICE_ID_INTEL_82372FB_1 0x7601 +#define PCI_DEVICE_ID_INTEL_SCH_LPC 0x8119 +#define PCI_DEVICE_ID_INTEL_SCH_IDE 0x811a +#define PCI_DEVICE_ID_INTEL_ITC_LPC 0x8186 +#define PCI_DEVICE_ID_INTEL_82454GX 0x84c4 +#define PCI_DEVICE_ID_INTEL_82450GX 0x84c5 +#define PCI_DEVICE_ID_INTEL_82451NX 0x84ca +#define PCI_DEVICE_ID_INTEL_82454NX 0x84cb +#define PCI_DEVICE_ID_INTEL_84460GX 0x84ea +#define PCI_DEVICE_ID_INTEL_IXP4XX 0x8500 +#define PCI_DEVICE_ID_INTEL_IXP2800 0x9004 +#define PCI_DEVICE_ID_INTEL_S21152BB 0xb152 + +#define PCI_VENDOR_ID_SCALEMP 0x8686 +#define PCI_DEVICE_ID_SCALEMP_VSMP_CTL 0x1010 + +#define PCI_VENDOR_ID_COMPUTONE 0x8e0e +#define PCI_DEVICE_ID_COMPUTONE_IP2EX 0x0291 +#define PCI_DEVICE_ID_COMPUTONE_PG 0x0302 +#define PCI_SUBVENDOR_ID_COMPUTONE 0x8e0e +#define PCI_SUBDEVICE_ID_COMPUTONE_PG4 0x0001 +#define PCI_SUBDEVICE_ID_COMPUTONE_PG8 0x0002 +#define PCI_SUBDEVICE_ID_COMPUTONE_PG6 0x0003 + +#define PCI_VENDOR_ID_KTI 0x8e2e + +#define PCI_VENDOR_ID_ADAPTEC 0x9004 +#define PCI_DEVICE_ID_ADAPTEC_7810 0x1078 +#define PCI_DEVICE_ID_ADAPTEC_7821 0x2178 +#define PCI_DEVICE_ID_ADAPTEC_38602 0x3860 +#define PCI_DEVICE_ID_ADAPTEC_7850 0x5078 +#define PCI_DEVICE_ID_ADAPTEC_7855 0x5578 +#define PCI_DEVICE_ID_ADAPTEC_3860 0x6038 +#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075 +#define PCI_DEVICE_ID_ADAPTEC_7860 0x6078 +#define PCI_DEVICE_ID_ADAPTEC_7861 0x6178 +#define PCI_DEVICE_ID_ADAPTEC_7870 0x7078 +#define PCI_DEVICE_ID_ADAPTEC_7871 0x7178 +#define PCI_DEVICE_ID_ADAPTEC_7872 0x7278 +#define PCI_DEVICE_ID_ADAPTEC_7873 0x7378 +#define PCI_DEVICE_ID_ADAPTEC_7874 0x7478 +#define PCI_DEVICE_ID_ADAPTEC_7895 0x7895 +#define PCI_DEVICE_ID_ADAPTEC_7880 0x8078 +#define PCI_DEVICE_ID_ADAPTEC_7881 0x8178 +#define PCI_DEVICE_ID_ADAPTEC_7882 0x8278 +#define PCI_DEVICE_ID_ADAPTEC_7883 0x8378 +#define PCI_DEVICE_ID_ADAPTEC_7884 0x8478 +#define PCI_DEVICE_ID_ADAPTEC_7885 0x8578 +#define PCI_DEVICE_ID_ADAPTEC_7886 0x8678 +#define PCI_DEVICE_ID_ADAPTEC_7887 0x8778 +#define PCI_DEVICE_ID_ADAPTEC_7888 0x8878 + +#define PCI_VENDOR_ID_ADAPTEC2 0x9005 +#define PCI_DEVICE_ID_ADAPTEC2_2940U2 0x0010 +#define PCI_DEVICE_ID_ADAPTEC2_2930U2 0x0011 +#define PCI_DEVICE_ID_ADAPTEC2_7890B 0x0013 +#define PCI_DEVICE_ID_ADAPTEC2_7890 0x001f +#define PCI_DEVICE_ID_ADAPTEC2_3940U2 0x0050 +#define PCI_DEVICE_ID_ADAPTEC2_3950U2D 0x0051 +#define PCI_DEVICE_ID_ADAPTEC2_7896 0x005f +#define PCI_DEVICE_ID_ADAPTEC2_7892A 0x0080 +#define PCI_DEVICE_ID_ADAPTEC2_7892B 0x0081 +#define PCI_DEVICE_ID_ADAPTEC2_7892D 0x0083 +#define PCI_DEVICE_ID_ADAPTEC2_7892P 0x008f +#define PCI_DEVICE_ID_ADAPTEC2_7899A 0x00c0 +#define PCI_DEVICE_ID_ADAPTEC2_7899B 0x00c1 +#define PCI_DEVICE_ID_ADAPTEC2_7899D 0x00c3 +#define PCI_DEVICE_ID_ADAPTEC2_7899P 0x00cf +#define PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN 0x0500 +#define PCI_DEVICE_ID_ADAPTEC2_SCAMP 0x0503 + +#define PCI_VENDOR_ID_HOLTEK 0x9412 +#define PCI_DEVICE_ID_HOLTEK_6565 0x6565 + +#define PCI_VENDOR_ID_NETMOS 0x9710 +#define PCI_DEVICE_ID_NETMOS_9705 0x9705 +#define PCI_DEVICE_ID_NETMOS_9715 0x9715 +#define PCI_DEVICE_ID_NETMOS_9735 0x9735 +#define PCI_DEVICE_ID_NETMOS_9745 0x9745 +#define PCI_DEVICE_ID_NETMOS_9755 0x9755 +#define PCI_DEVICE_ID_NETMOS_9805 0x9805 +#define PCI_DEVICE_ID_NETMOS_9815 0x9815 +#define PCI_DEVICE_ID_NETMOS_9835 0x9835 +#define PCI_DEVICE_ID_NETMOS_9845 0x9845 +#define PCI_DEVICE_ID_NETMOS_9855 0x9855 +#define PCI_DEVICE_ID_NETMOS_9865 0x9865 +#define PCI_DEVICE_ID_NETMOS_9900 0x9900 +#define PCI_DEVICE_ID_NETMOS_9901 0x9901 +#define PCI_DEVICE_ID_NETMOS_9904 0x9904 +#define PCI_DEVICE_ID_NETMOS_9912 0x9912 +#define PCI_DEVICE_ID_NETMOS_9922 0x9922 + +#define PCI_VENDOR_ID_3COM_2 0xa727 + +#define PCI_VENDOR_ID_DIGIUM 0xd161 +#define PCI_DEVICE_ID_DIGIUM_HFC4S 0xb410 + +#define PCI_SUBVENDOR_ID_EXSYS 0xd84d +#define PCI_SUBDEVICE_ID_EXSYS_4014 0x4014 +#define PCI_SUBDEVICE_ID_EXSYS_4055 0x4055 + +#define PCI_VENDOR_ID_TIGERJET 0xe159 +#define PCI_DEVICE_ID_TIGERJET_300 0x0001 +#define PCI_DEVICE_ID_TIGERJET_100 0x0002 + +#define PCI_VENDOR_ID_XILINX_RME 0xea60 +#define PCI_DEVICE_ID_RME_DIGI32 0x9896 +#define PCI_DEVICE_ID_RME_DIGI32_PRO 0x9897 +#define PCI_DEVICE_ID_RME_DIGI32_8 0x9898 + +#define PCI_VENDOR_ID_XEN 0x5853 +#define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 + +#define PCI_VENDOR_ID_OCZ 0x1b85 diff --git a/include/compat/linux/pm_qos.h b/include/compat/linux/pm_qos.h new file mode 100644 index 00000000..c58c1c1f --- /dev/null +++ b/include/compat/linux/pm_qos.h @@ -0,0 +1,12 @@ +#ifndef _COMPAT_LINUX_PM_QOS_H +#define _COMPAT_LINUX_PM_QOS_H 1 + +#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)) +#include_next +#else +#include +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)) */ + +#endif /* _COMPAT_LINUX_PM_QOS_H */ diff --git a/include/compat/linux/pm_qos_params.h b/include/compat/linux/pm_qos_params.h new file mode 100644 index 00000000..d0e3ad86 --- /dev/null +++ b/include/compat/linux/pm_qos_params.h @@ -0,0 +1,36 @@ +#include + +#ifndef __COMPAT_LINUX_PM_QOS_PARAMS_H +#define __COMPAT_LINUX_PM_QOS_PARAMS_H + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) +#include_next +#else +/* interface for the pm_qos_power infrastructure of the linux kernel. + * + * Mark Gross + */ +#include +#include +#include + +#define PM_QOS_RESERVED 0 +#define PM_QOS_CPU_DMA_LATENCY 1 +#define PM_QOS_NETWORK_LATENCY 2 +#define PM_QOS_NETWORK_THROUGHPUT 3 +#define PM_QOS_SYSTEM_BUS_FREQ 4 + +#define PM_QOS_NUM_CLASSES 5 +#define PM_QOS_DEFAULT_VALUE -1 + +int pm_qos_add_requirement(int qos, char *name, s32 value); +int pm_qos_update_requirement(int qos, char *name, s32 new_value); +void pm_qos_remove_requirement(int qos, char *name); + +int pm_qos_requirement(int qos); + +int pm_qos_add_notifier(int qos, struct notifier_block *notifier); +int pm_qos_remove_notifier(int qos, struct notifier_block *notifier); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) */ + +#endif diff --git a/include/compat/linux/pm_runtime.h b/include/compat/linux/pm_runtime.h new file mode 100644 index 00000000..eec965ba --- /dev/null +++ b/include/compat/linux/pm_runtime.h @@ -0,0 +1,14 @@ +#include + +#ifndef __COMPAT_LINUX_PM_RUNTIME_H +#define __COMPAT_LINUX_PM_RUNTIME_H + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) +#include_next +#else + +static inline void pm_runtime_enable(struct device *dev) {} + +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) */ + +#endif diff --git a/include/compat/linux/printk.h b/include/compat/linux/printk.h new file mode 100644 index 00000000..c0822ac4 --- /dev/null +++ b/include/compat/linux/printk.h @@ -0,0 +1,12 @@ +#ifndef _COMPAT_LINUX_PRINTK_H +#define _COMPAT_LINUX_PRINTK_H 1 + +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36)) +#include_next +#else +#include +#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)) */ + +#endif /* _COMPAT_LINUX_PRINTK_H */ diff --git a/include/compat/linux/rfkill.h b/include/compat/linux/rfkill.h new file mode 100644 index 00000000..d615ac9f --- /dev/null +++ b/include/compat/linux/rfkill.h @@ -0,0 +1,32 @@ +#ifndef __COMPAT_RFKILL_H +#define __COMPAT_RFKILL_H + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) + +#include_next + +#else + +#include + +#undef CONFIG_RFKILL +#undef CONFIG_RFKILL_INPUT +#undef CONFIG_RFKILL_LEDS + +#ifdef CONFIG_RFKILL_BACKPORT +#define CONFIG_RFKILL 1 +#endif + +#ifdef CONFIG_RFKILL_BACKPORT_INPUT +#define CONFIG_RFKILL_INPUT +#endif + +#ifdef CONFIG_RFKILL_BACKPORT_LEDS +#define CONFIG_RFKILL_LEDS +#endif + +#include + +#endif + +#endif diff --git a/include/compat/linux/rfkill_backport.h b/include/compat/linux/rfkill_backport.h new file mode 100644 index 00000000..c6c60848 --- /dev/null +++ b/include/compat/linux/rfkill_backport.h @@ -0,0 +1,359 @@ +#ifndef __RFKILL_H +#define __RFKILL_H + +/* + * Copyright (C) 2006 - 2007 Ivo van Doorn + * Copyright (C) 2007 Dmitry Torokhov + * Copyright 2009 Johannes Berg + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +/* define userspace visible states */ +#define RFKILL_STATE_SOFT_BLOCKED 0 +#define RFKILL_STATE_UNBLOCKED 1 +#define RFKILL_STATE_HARD_BLOCKED 2 + +/** + * enum rfkill_type - type of rfkill switch. + * + * @RFKILL_TYPE_ALL: toggles all switches (requests only - not a switch type) + * @RFKILL_TYPE_WLAN: switch is on a 802.11 wireless network device. + * @RFKILL_TYPE_BLUETOOTH: switch is on a bluetooth device. + * @RFKILL_TYPE_UWB: switch is on a ultra wideband device. + * @RFKILL_TYPE_WIMAX: switch is on a WiMAX device. + * @RFKILL_TYPE_WWAN: switch is on a wireless WAN device. + * @RFKILL_TYPE_GPS: switch is on a GPS device. + * @RFKILL_TYPE_FM: switch is on a FM radio device. + * @NUM_RFKILL_TYPES: number of defined rfkill types + */ +enum rfkill_type { + RFKILL_TYPE_ALL = 0, + RFKILL_TYPE_WLAN, + RFKILL_TYPE_BLUETOOTH, + RFKILL_TYPE_UWB, + RFKILL_TYPE_WIMAX, + RFKILL_TYPE_WWAN, + RFKILL_TYPE_GPS, + RFKILL_TYPE_FM, + NUM_RFKILL_TYPES, +}; + +/** + * enum rfkill_operation - operation types + * @RFKILL_OP_ADD: a device was added + * @RFKILL_OP_DEL: a device was removed + * @RFKILL_OP_CHANGE: a device's state changed -- userspace changes one device + * @RFKILL_OP_CHANGE_ALL: userspace changes all devices (of a type, or all) + */ +enum rfkill_operation { + RFKILL_OP_ADD = 0, + RFKILL_OP_DEL, + RFKILL_OP_CHANGE, + RFKILL_OP_CHANGE_ALL, +}; + +/** + * struct rfkill_event - events for userspace on /dev/rfkill + * @idx: index of dev rfkill + * @type: type of the rfkill struct + * @op: operation code + * @hard: hard state (0/1) + * @soft: soft state (0/1) + * + * Structure used for userspace communication on /dev/rfkill, + * used for events from the kernel and control to the kernel. + */ +struct rfkill_event { + __u32 idx; + __u8 type; + __u8 op; + __u8 soft, hard; +} __attribute__((packed)); + +/* + * We are planning to be backward and forward compatible with changes + * to the event struct, by adding new, optional, members at the end. + * When reading an event (whether the kernel from userspace or vice + * versa) we need to accept anything that's at least as large as the + * version 1 event size, but might be able to accept other sizes in + * the future. + * + * One exception is the kernel -- we already have two event sizes in + * that we've made the 'hard' member optional since our only option + * is to ignore it anyway. + */ +#define RFKILL_EVENT_SIZE_V1 8 + +/* ioctl for turning off rfkill-input (if present) */ +#define RFKILL_IOC_MAGIC 'R' +#define RFKILL_IOC_NOINPUT 1 +#define RFKILL_IOCTL_NOINPUT _IO(RFKILL_IOC_MAGIC, RFKILL_IOC_NOINPUT) + +/* and that's all userspace gets */ +#ifdef __KERNEL__ +/* don't allow anyone to use these in the kernel */ +enum rfkill_user_states { + RFKILL_USER_STATE_SOFT_BLOCKED = RFKILL_STATE_SOFT_BLOCKED, + RFKILL_USER_STATE_UNBLOCKED = RFKILL_STATE_UNBLOCKED, + RFKILL_USER_STATE_HARD_BLOCKED = RFKILL_STATE_HARD_BLOCKED, +}; +#undef RFKILL_STATE_SOFT_BLOCKED +#undef RFKILL_STATE_UNBLOCKED +#undef RFKILL_STATE_HARD_BLOCKED + +#include +#include +#include +#include +#include +#include + +/* this is opaque */ +struct rfkill; + +/** + * struct rfkill_ops - rfkill driver methods + * + * @poll: poll the rfkill block state(s) -- only assign this method + * when you need polling. When called, simply call one of the + * rfkill_set{,_hw,_sw}_state family of functions. If the hw + * is getting unblocked you need to take into account the return + * value of those functions to make sure the software block is + * properly used. + * @query: query the rfkill block state(s) and call exactly one of the + * rfkill_set{,_hw,_sw}_state family of functions. Assign this + * method if input events can cause hardware state changes to make + * the rfkill core query your driver before setting a requested + * block. + * @set_block: turn the transmitter on (blocked == false) or off + * (blocked == true) -- ignore and return 0 when hard blocked. + * This callback must be assigned. + */ +struct rfkill_ops { + void (*poll)(struct rfkill *rfkill, void *data); + void (*query)(struct rfkill *rfkill, void *data); + int (*set_block)(void *data, bool blocked); +}; + +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) +/** + * rfkill_alloc - allocate rfkill structure + * @name: name of the struct -- the string is not copied internally + * @parent: device that has rf switch on it + * @type: type of the switch (RFKILL_TYPE_*) + * @ops: rfkill methods + * @ops_data: data passed to each method + * + * This function should be called by the transmitter driver to allocate an + * rfkill structure. Returns %NULL on failure. + */ +struct rfkill * __must_check rfkill_alloc(const char *name, + struct device *parent, + const enum rfkill_type type, + const struct rfkill_ops *ops, + void *ops_data); + +/** + * rfkill_register - Register a rfkill structure. + * @rfkill: rfkill structure to be registered + * + * This function should be called by the transmitter driver to register + * the rfkill structure. Before calling this function the driver needs + * to be ready to service method calls from rfkill. + * + * If rfkill_init_sw_state() is not called before registration, + * set_block() will be called to initialize the software blocked state + * to a default value. + * + * If the hardware blocked state is not set before registration, + * it is assumed to be unblocked. + */ +int __must_check rfkill_register(struct rfkill *rfkill); + +/** + * rfkill_pause_polling(struct rfkill *rfkill) + * + * Pause polling -- say transmitter is off for other reasons. + * NOTE: not necessary for suspend/resume -- in that case the + * core stops polling anyway + */ +void rfkill_pause_polling(struct rfkill *rfkill); + +/** + * rfkill_resume_polling(struct rfkill *rfkill) + * + * Pause polling -- say transmitter is off for other reasons. + * NOTE: not necessary for suspend/resume -- in that case the + * core stops polling anyway + */ +void rfkill_resume_polling(struct rfkill *rfkill); + + +/** + * rfkill_unregister - Unregister a rfkill structure. + * @rfkill: rfkill structure to be unregistered + * + * This function should be called by the network driver during device + * teardown to destroy rfkill structure. Until it returns, the driver + * needs to be able to service method calls. + */ +void rfkill_unregister(struct rfkill *rfkill); + +/** + * rfkill_destroy - free rfkill structure + * @rfkill: rfkill structure to be destroyed + * + * Destroys the rfkill structure. + */ +void rfkill_destroy(struct rfkill *rfkill); + +/** + * rfkill_set_hw_state - Set the internal rfkill hardware block state + * @rfkill: pointer to the rfkill class to modify. + * @state: the current hardware block state to set + * + * rfkill drivers that get events when the hard-blocked state changes + * use this function to notify the rfkill core (and through that also + * userspace) of the current state. They should also use this after + * resume if the state could have changed. + * + * You need not (but may) call this function if poll_state is assigned. + * + * This function can be called in any context, even from within rfkill + * callbacks. + * + * The function returns the combined block state (true if transmitter + * should be blocked) so that drivers need not keep track of the soft + * block state -- which they might not be able to. + */ +bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked); + +/** + * rfkill_set_sw_state - Set the internal rfkill software block state + * @rfkill: pointer to the rfkill class to modify. + * @state: the current software block state to set + * + * rfkill drivers that get events when the soft-blocked state changes + * (yes, some platforms directly act on input but allow changing again) + * use this function to notify the rfkill core (and through that also + * userspace) of the current state. + * + * Drivers should also call this function after resume if the state has + * been changed by the user. This only makes sense for "persistent" + * devices (see rfkill_init_sw_state()). + * + * This function can be called in any context, even from within rfkill + * callbacks. + * + * The function returns the combined block state (true if transmitter + * should be blocked). + */ +bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked); + +/** + * rfkill_init_sw_state - Initialize persistent software block state + * @rfkill: pointer to the rfkill class to modify. + * @state: the current software block state to set + * + * rfkill drivers that preserve their software block state over power off + * use this function to notify the rfkill core (and through that also + * userspace) of their initial state. It should only be used before + * registration. + * + * In addition, it marks the device as "persistent", an attribute which + * can be read by userspace. Persistent devices are expected to preserve + * their own state when suspended. + */ +void rfkill_init_sw_state(struct rfkill *rfkill, bool blocked); + +/** + * rfkill_set_states - Set the internal rfkill block states + * @rfkill: pointer to the rfkill class to modify. + * @sw: the current software block state to set + * @hw: the current hardware block state to set + * + * This function can be called in any context, even from within rfkill + * callbacks. + */ +void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw); + +/** + * rfkill_blocked - query rfkill block + * + * @rfkill: rfkill struct to query + */ +bool rfkill_blocked(struct rfkill *rfkill); +#else /* !RFKILL */ +static inline struct rfkill * __must_check +rfkill_alloc(const char *name, + struct device *parent, + const enum rfkill_type type, + const struct rfkill_ops *ops, + void *ops_data) +{ + return ERR_PTR(-ENODEV); +} + +static inline int __must_check rfkill_register(struct rfkill *rfkill) +{ + if (rfkill == ERR_PTR(-ENODEV)) + return 0; + return -EINVAL; +} + +static inline void rfkill_pause_polling(struct rfkill *rfkill) +{ +} + +static inline void rfkill_resume_polling(struct rfkill *rfkill) +{ +} + +static inline void rfkill_unregister(struct rfkill *rfkill) +{ +} + +static inline void rfkill_destroy(struct rfkill *rfkill) +{ +} + +static inline bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked) +{ + return blocked; +} + +static inline bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked) +{ + return blocked; +} + +static inline void rfkill_init_sw_state(struct rfkill *rfkill, bool blocked) +{ +} + +static inline void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw) +{ +} + +static inline bool rfkill_blocked(struct rfkill *rfkill) +{ + return false; +} +#endif /* RFKILL || RFKILL_MODULE */ + +#endif /* __KERNEL__ */ + +#endif /* RFKILL_H */ diff --git a/include/compat/linux/semaphore.h b/include/compat/linux/semaphore.h new file mode 100644 index 00000000..78af4db3 --- /dev/null +++ b/include/compat/linux/semaphore.h @@ -0,0 +1,12 @@ +#ifndef _COMPAT_LINUX_SEMAPHORE_H +#define _COMPAT_LINUX_SEMAPHORE_H 1 + +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) +#include_next +#else +#include +#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) */ + +#endif /* _COMPAT_LINUX_SEMAPHORE_H */ diff --git a/include/compat/linux/spi/libertas_spi.h b/include/compat/linux/spi/libertas_spi.h new file mode 100644 index 00000000..1b5d5384 --- /dev/null +++ b/include/compat/linux/spi/libertas_spi.h @@ -0,0 +1,29 @@ +/* + * board-specific data for the libertas_spi driver. + * + * Copyright 2008 Analog Devices 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. + */ +#ifndef _LIBERTAS_SPI_H_ +#define _LIBERTAS_SPI_H_ + +struct spi_device; + +struct libertas_spi_platform_data { + /* There are two ways to read data from the WLAN module's SPI + * interface. Setting 0 or 1 here controls which one is used. + * + * Usually you want to set use_dummy_writes = 1. + * However, if that doesn't work or if you are using a slow SPI clock + * speed, you may want to use 0 here. */ + u16 use_dummy_writes; + + /* Board specific setup/teardown */ + int (*setup)(struct spi_device *spi); + int (*teardown)(struct spi_device *spi); +}; +#endif diff --git a/include/compat/linux/ssb/ssb.h b/include/compat/linux/ssb/ssb.h new file mode 100644 index 00000000..061e5602 --- /dev/null +++ b/include/compat/linux/ssb/ssb.h @@ -0,0 +1,538 @@ +#ifndef LINUX_SSB_H_ +#define LINUX_SSB_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include + + +struct pcmcia_device; +struct ssb_bus; +struct ssb_driver; + +struct ssb_sprom { + u8 revision; + u8 il0mac[6]; /* MAC address for 802.11b/g */ + u8 et0mac[6]; /* MAC address for Ethernet */ + u8 et1mac[6]; /* MAC address for 802.11a */ + u8 et0phyaddr; /* MII address for enet0 */ + u8 et1phyaddr; /* MII address for enet1 */ + u8 et0mdcport; /* MDIO for enet0 */ + u8 et1mdcport; /* MDIO for enet1 */ + u16 board_rev; /* Board revision number from SPROM. */ + u8 country_code; /* Country Code */ + u16 leddc_on_time; /* LED Powersave Duty Cycle On Count */ + u16 leddc_off_time; /* LED Powersave Duty Cycle Off Count */ + u8 ant_available_a; /* 2GHz antenna available bits (up to 4) */ + u8 ant_available_bg; /* 5GHz antenna available bits (up to 4) */ + u16 pa0b0; + u16 pa0b1; + u16 pa0b2; + u16 pa1b0; + u16 pa1b1; + u16 pa1b2; + u16 pa1lob0; + u16 pa1lob1; + u16 pa1lob2; + u16 pa1hib0; + u16 pa1hib1; + u16 pa1hib2; + u8 gpio0; /* GPIO pin 0 */ + u8 gpio1; /* GPIO pin 1 */ + u8 gpio2; /* GPIO pin 2 */ + u8 gpio3; /* GPIO pin 3 */ + u16 maxpwr_bg; /* 2.4GHz Amplifier Max Power (in dBm Q5.2) */ + u16 maxpwr_al; /* 5.2GHz Amplifier Max Power (in dBm Q5.2) */ + u16 maxpwr_a; /* 5.3GHz Amplifier Max Power (in dBm Q5.2) */ + u16 maxpwr_ah; /* 5.8GHz Amplifier Max Power (in dBm Q5.2) */ + u8 itssi_a; /* Idle TSSI Target for A-PHY */ + u8 itssi_bg; /* Idle TSSI Target for B/G-PHY */ + u8 tri2g; /* 2.4GHz TX isolation */ + u8 tri5gl; /* 5.2GHz TX isolation */ + u8 tri5g; /* 5.3GHz TX isolation */ + u8 tri5gh; /* 5.8GHz TX isolation */ + u8 txpid2g[4]; /* 2GHz TX power index */ + u8 txpid5gl[4]; /* 4.9 - 5.1GHz TX power index */ + u8 txpid5g[4]; /* 5.1 - 5.5GHz TX power index */ + u8 txpid5gh[4]; /* 5.5 - ...GHz TX power index */ + u8 rxpo2g; /* 2GHz RX power offset */ + u8 rxpo5g; /* 5GHz RX power offset */ + u8 rssisav2g; /* 2GHz RSSI params */ + u8 rssismc2g; + u8 rssismf2g; + u8 bxa2g; /* 2GHz BX arch */ + u8 rssisav5g; /* 5GHz RSSI params */ + u8 rssismc5g; + u8 rssismf5g; + u8 bxa5g; /* 5GHz BX arch */ + u16 cck2gpo; /* CCK power offset */ + u32 ofdm2gpo; /* 2.4GHz OFDM power offset */ + u32 ofdm5glpo; /* 5.2GHz OFDM power offset */ + u32 ofdm5gpo; /* 5.3GHz OFDM power offset */ + u32 ofdm5ghpo; /* 5.8GHz OFDM power offset */ + u16 boardflags_lo; /* Board flags (bits 0-15) */ + u16 boardflags_hi; /* Board flags (bits 16-31) */ + u16 boardflags2_lo; /* Board flags (bits 32-47) */ + u16 boardflags2_hi; /* Board flags (bits 48-63) */ + /* TODO store board flags in a single u64 */ + + /* Antenna gain values for up to 4 antennas + * on each band. Values in dBm/4 (Q5.2). Negative gain means the + * loss in the connectors is bigger than the gain. */ + struct { + struct { + s8 a0, a1, a2, a3; + } ghz24; /* 2.4GHz band */ + struct { + s8 a0, a1, a2, a3; + } ghz5; /* 5GHz band */ + } antenna_gain; + + /* TODO - add any parameters needed from rev 2, 3, 4, 5 or 8 SPROMs */ +}; + +/* Information about the PCB the circuitry is soldered on. */ +struct ssb_boardinfo { + u16 vendor; + u16 type; + u8 rev; +}; + + +struct ssb_device; +/* Lowlevel read/write operations on the device MMIO. + * Internal, don't use that outside of ssb. */ +struct ssb_bus_ops { + u8 (*read8)(struct ssb_device *dev, u16 offset); + u16 (*read16)(struct ssb_device *dev, u16 offset); + u32 (*read32)(struct ssb_device *dev, u16 offset); + void (*write8)(struct ssb_device *dev, u16 offset, u8 value); + void (*write16)(struct ssb_device *dev, u16 offset, u16 value); + void (*write32)(struct ssb_device *dev, u16 offset, u32 value); +#ifdef CONFIG_SSB_BLOCKIO + void (*block_read)(struct ssb_device *dev, void *buffer, + size_t count, u16 offset, u8 reg_width); + void (*block_write)(struct ssb_device *dev, const void *buffer, + size_t count, u16 offset, u8 reg_width); +#endif +}; + + +/* Core-ID values. */ +#define SSB_DEV_CHIPCOMMON 0x800 +#define SSB_DEV_ILINE20 0x801 +#define SSB_DEV_SDRAM 0x803 +#define SSB_DEV_PCI 0x804 +#define SSB_DEV_MIPS 0x805 +#define SSB_DEV_ETHERNET 0x806 +#define SSB_DEV_V90 0x807 +#define SSB_DEV_USB11_HOSTDEV 0x808 +#define SSB_DEV_ADSL 0x809 +#define SSB_DEV_ILINE100 0x80A +#define SSB_DEV_IPSEC 0x80B +#define SSB_DEV_PCMCIA 0x80D +#define SSB_DEV_INTERNAL_MEM 0x80E +#define SSB_DEV_MEMC_SDRAM 0x80F +#define SSB_DEV_EXTIF 0x811 +#define SSB_DEV_80211 0x812 +#define SSB_DEV_MIPS_3302 0x816 +#define SSB_DEV_USB11_HOST 0x817 +#define SSB_DEV_USB11_DEV 0x818 +#define SSB_DEV_USB20_HOST 0x819 +#define SSB_DEV_USB20_DEV 0x81A +#define SSB_DEV_SDIO_HOST 0x81B +#define SSB_DEV_ROBOSWITCH 0x81C +#define SSB_DEV_PARA_ATA 0x81D +#define SSB_DEV_SATA_XORDMA 0x81E +#define SSB_DEV_ETHERNET_GBIT 0x81F +#define SSB_DEV_PCIE 0x820 +#define SSB_DEV_MIMO_PHY 0x821 +#define SSB_DEV_SRAM_CTRLR 0x822 +#define SSB_DEV_MINI_MACPHY 0x823 +#define SSB_DEV_ARM_1176 0x824 +#define SSB_DEV_ARM_7TDMI 0x825 + +/* Vendor-ID values */ +#define SSB_VENDOR_BROADCOM 0x4243 + +/* Some kernel subsystems poke with dev->drvdata, so we must use the + * following ugly workaround to get from struct device to struct ssb_device */ +struct __ssb_dev_wrapper { + struct device dev; + struct ssb_device *sdev; +}; + +struct ssb_device { + /* Having a copy of the ops pointer in each dev struct + * is an optimization. */ + const struct ssb_bus_ops *ops; + + struct device *dev, *dma_dev; + + struct ssb_bus *bus; + struct ssb_device_id id; + + u8 core_index; + unsigned int irq; + + /* Internal-only stuff follows. */ + void *drvdata; /* Per-device data */ + void *devtypedata; /* Per-devicetype (eg 802.11) data */ +}; + +/* Go from struct device to struct ssb_device. */ +static inline +struct ssb_device * dev_to_ssb_dev(struct device *dev) +{ + struct __ssb_dev_wrapper *wrap; + wrap = container_of(dev, struct __ssb_dev_wrapper, dev); + return wrap->sdev; +} + +/* Device specific user data */ +static inline +void ssb_set_drvdata(struct ssb_device *dev, void *data) +{ + dev->drvdata = data; +} +static inline +void * ssb_get_drvdata(struct ssb_device *dev) +{ + return dev->drvdata; +} + +/* Devicetype specific user data. This is per device-type (not per device) */ +void ssb_set_devtypedata(struct ssb_device *dev, void *data); +static inline +void * ssb_get_devtypedata(struct ssb_device *dev) +{ + return dev->devtypedata; +} + + +struct ssb_driver { + const char *name; + const struct ssb_device_id *id_table; + + int (*probe)(struct ssb_device *dev, const struct ssb_device_id *id); + void (*remove)(struct ssb_device *dev); + int (*suspend)(struct ssb_device *dev, pm_message_t state); + int (*resume)(struct ssb_device *dev); + void (*shutdown)(struct ssb_device *dev); + + struct device_driver drv; +}; +#define drv_to_ssb_drv(_drv) container_of(_drv, struct ssb_driver, drv) + +extern int __ssb_driver_register(struct ssb_driver *drv, struct module *owner); +#define ssb_driver_register(drv) \ + __ssb_driver_register(drv, THIS_MODULE) + +extern void ssb_driver_unregister(struct ssb_driver *drv); + + + + +enum ssb_bustype { + SSB_BUSTYPE_SSB, /* This SSB bus is the system bus */ + SSB_BUSTYPE_PCI, /* SSB is connected to PCI bus */ + SSB_BUSTYPE_PCMCIA, /* SSB is connected to PCMCIA bus */ + SSB_BUSTYPE_SDIO, /* SSB is connected to SDIO bus */ +}; + +/* board_vendor */ +#define SSB_BOARDVENDOR_BCM 0x14E4 /* Broadcom */ +#define SSB_BOARDVENDOR_DELL 0x1028 /* Dell */ +#define SSB_BOARDVENDOR_HP 0x0E11 /* HP */ +/* board_type */ +#define SSB_BOARD_BCM94306MP 0x0418 +#define SSB_BOARD_BCM4309G 0x0421 +#define SSB_BOARD_BCM4306CB 0x0417 +#define SSB_BOARD_BCM4309MP 0x040C +#define SSB_BOARD_MP4318 0x044A +#define SSB_BOARD_BU4306 0x0416 +#define SSB_BOARD_BU4309 0x040A +/* chip_package */ +#define SSB_CHIPPACK_BCM4712S 1 /* Small 200pin 4712 */ +#define SSB_CHIPPACK_BCM4712M 2 /* Medium 225pin 4712 */ +#define SSB_CHIPPACK_BCM4712L 0 /* Large 340pin 4712 */ + +#include +#include +#include +#include + +struct ssb_bus { + /* The MMIO area. */ + void __iomem *mmio; + + const struct ssb_bus_ops *ops; + + /* The core currently mapped into the MMIO window. + * Not valid on all host-buses. So don't use outside of SSB. */ + struct ssb_device *mapped_device; + union { + /* Currently mapped PCMCIA segment. (bustype == SSB_BUSTYPE_PCMCIA only) */ + u8 mapped_pcmcia_seg; + /* Current SSB base address window for SDIO. */ + u32 sdio_sbaddr; + }; + /* Lock for core and segment switching. + * On PCMCIA-host busses this is used to protect the whole MMIO access. */ + spinlock_t bar_lock; + + /* The host-bus this backplane is running on. */ + enum ssb_bustype bustype; + /* Pointers to the host-bus. Check bustype before using any of these pointers. */ + union { + /* Pointer to the PCI bus (only valid if bustype == SSB_BUSTYPE_PCI). */ + struct pci_dev *host_pci; + /* Pointer to the PCMCIA device (only if bustype == SSB_BUSTYPE_PCMCIA). */ + struct pcmcia_device *host_pcmcia; + /* Pointer to the SDIO device (only if bustype == SSB_BUSTYPE_SDIO). */ + struct sdio_func *host_sdio; + }; + + /* See enum ssb_quirks */ + unsigned int quirks; + +#ifdef CONFIG_SSB_SPROM + /* Mutex to protect the SPROM writing. */ + struct mutex sprom_mutex; +#endif + + /* ID information about the Chip. */ + u16 chip_id; + u8 chip_rev; + u16 sprom_offset; + u16 sprom_size; /* number of words in sprom */ + u8 chip_package; + + /* List of devices (cores) on the backplane. */ + struct ssb_device devices[SSB_MAX_NR_CORES]; + u8 nr_devices; + + /* Software ID number for this bus. */ + unsigned int busnumber; + + /* The ChipCommon device (if available). */ + struct ssb_chipcommon chipco; + /* The PCI-core device (if available). */ + struct ssb_pcicore pcicore; + /* The MIPS-core device (if available). */ + struct ssb_mipscore mipscore; + /* The EXTif-core device (if available). */ + struct ssb_extif extif; + + /* The following structure elements are not available in early + * SSB initialization. Though, they are available for regular + * registered drivers at any stage. So be careful when + * using them in the ssb core code. */ + + /* ID information about the PCB. */ + struct ssb_boardinfo boardinfo; + /* Contents of the SPROM. */ + struct ssb_sprom sprom; + /* If the board has a cardbus slot, this is set to true. */ + bool has_cardbus_slot; + +#ifdef CONFIG_SSB_EMBEDDED + /* Lock for GPIO register access. */ + spinlock_t gpio_lock; +#endif /* EMBEDDED */ + + /* Internal-only stuff follows. Do not touch. */ + struct list_head list; +#ifdef CONFIG_SSB_DEBUG + /* Is the bus already powered up? */ + bool powered_up; + int power_warn_count; +#endif /* DEBUG */ +}; + +enum ssb_quirks { + /* SDIO connected card requires performing a read after writing a 32-bit value */ + SSB_QUIRK_SDIO_READ_AFTER_WRITE32 = (1 << 0), +}; + +/* The initialization-invariants. */ +struct ssb_init_invariants { + /* Versioning information about the PCB. */ + struct ssb_boardinfo boardinfo; + /* The SPROM information. That's either stored in an + * EEPROM or NVRAM on the board. */ + struct ssb_sprom sprom; + /* If the board has a cardbus slot, this is set to true. */ + bool has_cardbus_slot; +}; +/* Type of function to fetch the invariants. */ +typedef int (*ssb_invariants_func_t)(struct ssb_bus *bus, + struct ssb_init_invariants *iv); + +/* Register a SSB system bus. get_invariants() is called after the + * basic system devices are initialized. + * The invariants are usually fetched from some NVRAM. + * Put the invariants into the struct pointed to by iv. */ +extern int ssb_bus_ssbbus_register(struct ssb_bus *bus, + unsigned long baseaddr, + ssb_invariants_func_t get_invariants); +#ifdef CONFIG_SSB_PCIHOST +extern int ssb_bus_pcibus_register(struct ssb_bus *bus, + struct pci_dev *host_pci); +#endif /* CONFIG_SSB_PCIHOST */ +#ifdef CONFIG_SSB_PCMCIAHOST +extern int ssb_bus_pcmciabus_register(struct ssb_bus *bus, + struct pcmcia_device *pcmcia_dev, + unsigned long baseaddr); +#endif /* CONFIG_SSB_PCMCIAHOST */ +#ifdef CONFIG_SSB_SDIOHOST +extern int ssb_bus_sdiobus_register(struct ssb_bus *bus, + struct sdio_func *sdio_func, + unsigned int quirks); +#endif /* CONFIG_SSB_SDIOHOST */ + + +extern void ssb_bus_unregister(struct ssb_bus *bus); + +/* Does the device have an SPROM? */ +extern bool ssb_is_sprom_available(struct ssb_bus *bus); + +/* Set a fallback SPROM. + * See kdoc at the function definition for complete documentation. */ +extern int ssb_arch_register_fallback_sprom( + int (*sprom_callback)(struct ssb_bus *bus, + struct ssb_sprom *out)); + +/* Suspend a SSB bus. + * Call this from the parent bus suspend routine. */ +extern int ssb_bus_suspend(struct ssb_bus *bus); +/* Resume a SSB bus. + * Call this from the parent bus resume routine. */ +extern int ssb_bus_resume(struct ssb_bus *bus); + +extern u32 ssb_clockspeed(struct ssb_bus *bus); + +/* Is the device enabled in hardware? */ +int ssb_device_is_enabled(struct ssb_device *dev); +/* Enable a device and pass device-specific SSB_TMSLOW flags. + * If no device-specific flags are available, use 0. */ +void ssb_device_enable(struct ssb_device *dev, u32 core_specific_flags); +/* Disable a device in hardware and pass SSB_TMSLOW flags (if any). */ +void ssb_device_disable(struct ssb_device *dev, u32 core_specific_flags); + + +/* Device MMIO register read/write functions. */ +static inline u8 ssb_read8(struct ssb_device *dev, u16 offset) +{ + return dev->ops->read8(dev, offset); +} +static inline u16 ssb_read16(struct ssb_device *dev, u16 offset) +{ + return dev->ops->read16(dev, offset); +} +static inline u32 ssb_read32(struct ssb_device *dev, u16 offset) +{ + return dev->ops->read32(dev, offset); +} +static inline void ssb_write8(struct ssb_device *dev, u16 offset, u8 value) +{ + dev->ops->write8(dev, offset, value); +} +static inline void ssb_write16(struct ssb_device *dev, u16 offset, u16 value) +{ + dev->ops->write16(dev, offset, value); +} +static inline void ssb_write32(struct ssb_device *dev, u16 offset, u32 value) +{ + dev->ops->write32(dev, offset, value); +} +#ifdef CONFIG_SSB_BLOCKIO +static inline void ssb_block_read(struct ssb_device *dev, void *buffer, + size_t count, u16 offset, u8 reg_width) +{ + dev->ops->block_read(dev, buffer, count, offset, reg_width); +} + +static inline void ssb_block_write(struct ssb_device *dev, const void *buffer, + size_t count, u16 offset, u8 reg_width) +{ + dev->ops->block_write(dev, buffer, count, offset, reg_width); +} +#endif /* CONFIG_SSB_BLOCKIO */ + + +/* The SSB DMA API. Use this API for any DMA operation on the device. + * This API basically is a wrapper that calls the correct DMA API for + * the host device type the SSB device is attached to. */ + +/* Translation (routing) bits that need to be ORed to DMA + * addresses before they are given to a device. */ +extern u32 ssb_dma_translation(struct ssb_device *dev); +#define SSB_DMA_TRANSLATION_MASK 0xC0000000 +#define SSB_DMA_TRANSLATION_SHIFT 30 + +static inline void __cold __ssb_dma_not_implemented(struct ssb_device *dev) +{ +#ifdef CONFIG_SSB_DEBUG + printk(KERN_ERR "SSB: BUG! Calling DMA API for " + "unsupported bustype %d\n", dev->bus->bustype); +#endif /* DEBUG */ +} + +#ifdef CONFIG_SSB_PCIHOST +/* PCI-host wrapper driver */ +extern int ssb_pcihost_register(struct pci_driver *driver); +static inline void ssb_pcihost_unregister(struct pci_driver *driver) +{ + pci_unregister_driver(driver); +} + +static inline +void ssb_pcihost_set_power_state(struct ssb_device *sdev, pci_power_t state) +{ + if (sdev->bus->bustype == SSB_BUSTYPE_PCI) + pci_set_power_state(sdev->bus->host_pci, state); +} +#else +static inline void ssb_pcihost_unregister(struct pci_driver *driver) +{ +} + +static inline +void ssb_pcihost_set_power_state(struct ssb_device *sdev, pci_power_t state) +{ +} +#endif /* CONFIG_SSB_PCIHOST */ + + +/* If a driver is shutdown or suspended, call this to signal + * that the bus may be completely powered down. SSB will decide, + * if it's really time to power down the bus, based on if there + * are other devices that want to run. */ +extern int ssb_bus_may_powerdown(struct ssb_bus *bus); +/* Before initializing and enabling a device, call this to power-up the bus. + * If you want to allow use of dynamic-power-control, pass the flag. + * Otherwise static always-on powercontrol will be used. */ +extern int ssb_bus_powerup(struct ssb_bus *bus, bool dynamic_pctl); + +extern void ssb_commit_settings(struct ssb_bus *bus); + +/* Various helper functions */ +extern u32 ssb_admatch_base(u32 adm); +extern u32 ssb_admatch_size(u32 adm); + +/* PCI device mapping and fixup routines. + * Called from the architecture pcibios init code. + * These are only available on SSB_EMBEDDED configurations. */ +#ifdef CONFIG_SSB_EMBEDDED +int ssb_pcibios_plat_dev_init(struct pci_dev *dev); +int ssb_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin); +#endif /* CONFIG_SSB_EMBEDDED */ + +#endif /* LINUX_SSB_H_ */ diff --git a/include/compat/linux/ssb/ssb_driver_chipcommon.h b/include/compat/linux/ssb/ssb_driver_chipcommon.h new file mode 100644 index 00000000..1a6b0045 --- /dev/null +++ b/include/compat/linux/ssb/ssb_driver_chipcommon.h @@ -0,0 +1,665 @@ +#ifndef LINUX_SSB_CHIPCO_H_ +#define LINUX_SSB_CHIPCO_H_ + +/* SonicsSiliconBackplane 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. + * + * Copyright 2005, Broadcom Corporation + * Copyright 2006, Michael Buesch + * + * Licensed under the GPL version 2. See COPYING for details. + */ + +/** ChipCommon core registers. **/ + +#define SSB_CHIPCO_CHIPID 0x0000 +#define SSB_CHIPCO_IDMASK 0x0000FFFF +#define SSB_CHIPCO_REVMASK 0x000F0000 +#define SSB_CHIPCO_REVSHIFT 16 +#define SSB_CHIPCO_PACKMASK 0x00F00000 +#define SSB_CHIPCO_PACKSHIFT 20 +#define SSB_CHIPCO_NRCORESMASK 0x0F000000 +#define SSB_CHIPCO_NRCORESSHIFT 24 +#define SSB_CHIPCO_CAP 0x0004 /* Capabilities */ +#define SSB_CHIPCO_CAP_NRUART 0x00000003 /* # of UARTs */ +#define SSB_CHIPCO_CAP_MIPSEB 0x00000004 /* MIPS in BigEndian Mode */ +#define SSB_CHIPCO_CAP_UARTCLK 0x00000018 /* UART clock select */ +#define SSB_CHIPCO_CAP_UARTCLK_INT 0x00000008 /* UARTs are driven by internal divided clock */ +#define SSB_CHIPCO_CAP_UARTGPIO 0x00000020 /* UARTs on GPIO 15-12 */ +#define SSB_CHIPCO_CAP_EXTBUS 0x000000C0 /* External buses present */ +#define SSB_CHIPCO_CAP_FLASHT 0x00000700 /* Flash Type */ +#define SSB_CHIPCO_FLASHT_NONE 0x00000000 /* No flash */ +#define SSB_CHIPCO_FLASHT_STSER 0x00000100 /* ST serial flash */ +#define SSB_CHIPCO_FLASHT_ATSER 0x00000200 /* Atmel serial flash */ +#define SSB_CHIPCO_FLASHT_PARA 0x00000700 /* Parallel flash */ +#define SSB_CHIPCO_CAP_PLLT 0x00038000 /* PLL Type */ +#define SSB_PLLTYPE_NONE 0x00000000 +#define SSB_PLLTYPE_1 0x00010000 /* 48Mhz base, 3 dividers */ +#define SSB_PLLTYPE_2 0x00020000 /* 48Mhz, 4 dividers */ +#define SSB_PLLTYPE_3 0x00030000 /* 25Mhz, 2 dividers */ +#define SSB_PLLTYPE_4 0x00008000 /* 48Mhz, 4 dividers */ +#define SSB_PLLTYPE_5 0x00018000 /* 25Mhz, 4 dividers */ +#define SSB_PLLTYPE_6 0x00028000 /* 100/200 or 120/240 only */ +#define SSB_PLLTYPE_7 0x00038000 /* 25Mhz, 4 dividers */ +#define SSB_CHIPCO_CAP_PCTL 0x00040000 /* Power Control */ +#define SSB_CHIPCO_CAP_OTPS 0x00380000 /* OTP size */ +#define SSB_CHIPCO_CAP_OTPS_SHIFT 19 +#define SSB_CHIPCO_CAP_OTPS_BASE 5 +#define SSB_CHIPCO_CAP_JTAGM 0x00400000 /* JTAG master present */ +#define SSB_CHIPCO_CAP_BROM 0x00800000 /* Internal boot ROM active */ +#define SSB_CHIPCO_CAP_64BIT 0x08000000 /* 64-bit Backplane */ +#define SSB_CHIPCO_CAP_PMU 0x10000000 /* PMU available (rev >= 20) */ +#define SSB_CHIPCO_CAP_ECI 0x20000000 /* ECI available (rev >= 20) */ +#define SSB_CHIPCO_CAP_SPROM 0x40000000 /* SPROM present */ +#define SSB_CHIPCO_CORECTL 0x0008 +#define SSB_CHIPCO_CORECTL_UARTCLK0 0x00000001 /* Drive UART with internal clock */ +#define SSB_CHIPCO_CORECTL_SE 0x00000002 /* sync clk out enable (corerev >= 3) */ +#define SSB_CHIPCO_CORECTL_UARTCLKEN 0x00000008 /* UART clock enable (rev >= 21) */ +#define SSB_CHIPCO_BIST 0x000C +#define SSB_CHIPCO_OTPS 0x0010 /* OTP status */ +#define SSB_CHIPCO_OTPS_PROGFAIL 0x80000000 +#define SSB_CHIPCO_OTPS_PROTECT 0x00000007 +#define SSB_CHIPCO_OTPS_HW_PROTECT 0x00000001 +#define SSB_CHIPCO_OTPS_SW_PROTECT 0x00000002 +#define SSB_CHIPCO_OTPS_CID_PROTECT 0x00000004 +#define SSB_CHIPCO_OTPC 0x0014 /* OTP control */ +#define SSB_CHIPCO_OTPC_RECWAIT 0xFF000000 +#define SSB_CHIPCO_OTPC_PROGWAIT 0x00FFFF00 +#define SSB_CHIPCO_OTPC_PRW_SHIFT 8 +#define SSB_CHIPCO_OTPC_MAXFAIL 0x00000038 +#define SSB_CHIPCO_OTPC_VSEL 0x00000006 +#define SSB_CHIPCO_OTPC_SELVL 0x00000001 +#define SSB_CHIPCO_OTPP 0x0018 /* OTP prog */ +#define SSB_CHIPCO_OTPP_COL 0x000000FF +#define SSB_CHIPCO_OTPP_ROW 0x0000FF00 +#define SSB_CHIPCO_OTPP_ROW_SHIFT 8 +#define SSB_CHIPCO_OTPP_READERR 0x10000000 +#define SSB_CHIPCO_OTPP_VALUE 0x20000000 +#define SSB_CHIPCO_OTPP_READ 0x40000000 +#define SSB_CHIPCO_OTPP_START 0x80000000 +#define SSB_CHIPCO_OTPP_BUSY 0x80000000 +#define SSB_CHIPCO_IRQSTAT 0x0020 +#define SSB_CHIPCO_IRQMASK 0x0024 +#define SSB_CHIPCO_IRQ_GPIO 0x00000001 /* gpio intr */ +#define SSB_CHIPCO_IRQ_EXT 0x00000002 /* ro: ext intr pin (corerev >= 3) */ +#define SSB_CHIPCO_IRQ_WDRESET 0x80000000 /* watchdog reset occurred */ +#define SSB_CHIPCO_CHIPCTL 0x0028 /* Rev >= 11 only */ +#define SSB_CHIPCO_CHIPSTAT 0x002C /* Rev >= 11 only */ +#define SSB_CHIPCO_JCMD 0x0030 /* Rev >= 10 only */ +#define SSB_CHIPCO_JCMD_START 0x80000000 +#define SSB_CHIPCO_JCMD_BUSY 0x80000000 +#define SSB_CHIPCO_JCMD_PAUSE 0x40000000 +#define SSB_CHIPCO_JCMD0_ACC_MASK 0x0000F000 +#define SSB_CHIPCO_JCMD0_ACC_IRDR 0x00000000 +#define SSB_CHIPCO_JCMD0_ACC_DR 0x00001000 +#define SSB_CHIPCO_JCMD0_ACC_IR 0x00002000 +#define SSB_CHIPCO_JCMD0_ACC_RESET 0x00003000 +#define SSB_CHIPCO_JCMD0_ACC_IRPDR 0x00004000 +#define SSB_CHIPCO_JCMD0_ACC_PDR 0x00005000 +#define SSB_CHIPCO_JCMD0_IRW_MASK 0x00000F00 +#define SSB_CHIPCO_JCMD_ACC_MASK 0x000F0000 /* Changes for corerev 11 */ +#define SSB_CHIPCO_JCMD_ACC_IRDR 0x00000000 +#define SSB_CHIPCO_JCMD_ACC_DR 0x00010000 +#define SSB_CHIPCO_JCMD_ACC_IR 0x00020000 +#define SSB_CHIPCO_JCMD_ACC_RESET 0x00030000 +#define SSB_CHIPCO_JCMD_ACC_IRPDR 0x00040000 +#define SSB_CHIPCO_JCMD_ACC_PDR 0x00050000 +#define SSB_CHIPCO_JCMD_IRW_MASK 0x00001F00 +#define SSB_CHIPCO_JCMD_IRW_SHIFT 8 +#define SSB_CHIPCO_JCMD_DRW_MASK 0x0000003F +#define SSB_CHIPCO_JIR 0x0034 /* Rev >= 10 only */ +#define SSB_CHIPCO_JDR 0x0038 /* Rev >= 10 only */ +#define SSB_CHIPCO_JCTL 0x003C /* Rev >= 10 only */ +#define SSB_CHIPCO_JCTL_FORCE_CLK 4 /* Force clock */ +#define SSB_CHIPCO_JCTL_EXT_EN 2 /* Enable external targets */ +#define SSB_CHIPCO_JCTL_EN 1 /* Enable Jtag master */ +#define SSB_CHIPCO_FLASHCTL 0x0040 +#define SSB_CHIPCO_FLASHCTL_START 0x80000000 +#define SSB_CHIPCO_FLASHCTL_BUSY SSB_CHIPCO_FLASHCTL_START +#define SSB_CHIPCO_FLASHADDR 0x0044 +#define SSB_CHIPCO_FLASHDATA 0x0048 +#define SSB_CHIPCO_BCAST_ADDR 0x0050 +#define SSB_CHIPCO_BCAST_DATA 0x0054 +#define SSB_CHIPCO_GPIOPULLUP 0x0058 /* Rev >= 20 only */ +#define SSB_CHIPCO_GPIOPULLDOWN 0x005C /* Rev >= 20 only */ +#define SSB_CHIPCO_GPIOIN 0x0060 +#define SSB_CHIPCO_GPIOOUT 0x0064 +#define SSB_CHIPCO_GPIOOUTEN 0x0068 +#define SSB_CHIPCO_GPIOCTL 0x006C +#define SSB_CHIPCO_GPIOPOL 0x0070 +#define SSB_CHIPCO_GPIOIRQ 0x0074 +#define SSB_CHIPCO_WATCHDOG 0x0080 +#define SSB_CHIPCO_GPIOTIMER 0x0088 /* LED powersave (corerev >= 16) */ +#define SSB_CHIPCO_GPIOTIMER_OFFTIME 0x0000FFFF +#define SSB_CHIPCO_GPIOTIMER_OFFTIME_SHIFT 0 +#define SSB_CHIPCO_GPIOTIMER_ONTIME 0xFFFF0000 +#define SSB_CHIPCO_GPIOTIMER_ONTIME_SHIFT 16 +#define SSB_CHIPCO_GPIOTOUTM 0x008C /* LED powersave (corerev >= 16) */ +#define SSB_CHIPCO_CLOCK_N 0x0090 +#define SSB_CHIPCO_CLOCK_SB 0x0094 +#define SSB_CHIPCO_CLOCK_PCI 0x0098 +#define SSB_CHIPCO_CLOCK_M2 0x009C +#define SSB_CHIPCO_CLOCK_MIPS 0x00A0 +#define SSB_CHIPCO_CLKDIV 0x00A4 /* Rev >= 3 only */ +#define SSB_CHIPCO_CLKDIV_SFLASH 0x0F000000 +#define SSB_CHIPCO_CLKDIV_SFLASH_SHIFT 24 +#define SSB_CHIPCO_CLKDIV_OTP 0x000F0000 +#define SSB_CHIPCO_CLKDIV_OTP_SHIFT 16 +#define SSB_CHIPCO_CLKDIV_JTAG 0x00000F00 +#define SSB_CHIPCO_CLKDIV_JTAG_SHIFT 8 +#define SSB_CHIPCO_CLKDIV_UART 0x000000FF +#define SSB_CHIPCO_PLLONDELAY 0x00B0 /* Rev >= 4 only */ +#define SSB_CHIPCO_FREFSELDELAY 0x00B4 /* Rev >= 4 only */ +#define SSB_CHIPCO_SLOWCLKCTL 0x00B8 /* 6 <= Rev <= 9 only */ +#define SSB_CHIPCO_SLOWCLKCTL_SRC 0x00000007 /* slow clock source mask */ +#define SSB_CHIPCO_SLOWCLKCTL_SRC_LPO 0x00000000 /* source of slow clock is LPO */ +#define SSB_CHIPCO_SLOWCLKCTL_SRC_XTAL 0x00000001 /* source of slow clock is crystal */ +#define SSB_CHIPCO_SLOECLKCTL_SRC_PCI 0x00000002 /* source of slow clock is PCI */ +#define SSB_CHIPCO_SLOWCLKCTL_LPOFREQ 0x00000200 /* LPOFreqSel, 1: 160Khz, 0: 32KHz */ +#define SSB_CHIPCO_SLOWCLKCTL_LPOPD 0x00000400 /* LPOPowerDown, 1: LPO is disabled, 0: LPO is enabled */ +#define SSB_CHIPCO_SLOWCLKCTL_FSLOW 0x00000800 /* ForceSlowClk, 1: sb/cores running on slow clock, 0: power logic control */ +#define SSB_CHIPCO_SLOWCLKCTL_IPLL 0x00001000 /* IgnorePllOffReq, 1/0: power logic ignores/honors PLL clock disable requests from core */ +#define SSB_CHIPCO_SLOWCLKCTL_ENXTAL 0x00002000 /* XtalControlEn, 1/0: power logic does/doesn't disable crystal when appropriate */ +#define SSB_CHIPCO_SLOWCLKCTL_XTALPU 0x00004000 /* XtalPU (RO), 1/0: crystal running/disabled */ +#define SSB_CHIPCO_SLOWCLKCTL_CLKDIV 0xFFFF0000 /* ClockDivider (SlowClk = 1/(4+divisor)) */ +#define SSB_CHIPCO_SLOWCLKCTL_CLKDIV_SHIFT 16 +#define SSB_CHIPCO_SYSCLKCTL 0x00C0 /* Rev >= 3 only */ +#define SSB_CHIPCO_SYSCLKCTL_IDLPEN 0x00000001 /* ILPen: Enable Idle Low Power */ +#define SSB_CHIPCO_SYSCLKCTL_ALPEN 0x00000002 /* ALPen: Enable Active Low Power */ +#define SSB_CHIPCO_SYSCLKCTL_PLLEN 0x00000004 /* ForcePLLOn */ +#define SSB_CHIPCO_SYSCLKCTL_FORCEALP 0x00000008 /* Force ALP (or HT if ALPen is not set */ +#define SSB_CHIPCO_SYSCLKCTL_FORCEHT 0x00000010 /* Force HT */ +#define SSB_CHIPCO_SYSCLKCTL_CLKDIV 0xFFFF0000 /* ClkDiv (ILP = 1/(4+divisor)) */ +#define SSB_CHIPCO_SYSCLKCTL_CLKDIV_SHIFT 16 +#define SSB_CHIPCO_CLKSTSTR 0x00C4 /* Rev >= 3 only */ +#define SSB_CHIPCO_PCMCIA_CFG 0x0100 +#define SSB_CHIPCO_PCMCIA_MEMWAIT 0x0104 +#define SSB_CHIPCO_PCMCIA_ATTRWAIT 0x0108 +#define SSB_CHIPCO_PCMCIA_IOWAIT 0x010C +#define SSB_CHIPCO_IDE_CFG 0x0110 +#define SSB_CHIPCO_IDE_MEMWAIT 0x0114 +#define SSB_CHIPCO_IDE_ATTRWAIT 0x0118 +#define SSB_CHIPCO_IDE_IOWAIT 0x011C +#define SSB_CHIPCO_PROG_CFG 0x0120 +#define SSB_CHIPCO_PROG_WAITCNT 0x0124 +#define SSB_CHIPCO_FLASH_CFG 0x0128 +#define SSB_CHIPCO_FLASH_WAITCNT 0x012C +#define SSB_CHIPCO_CLKCTLST 0x01E0 /* Clock control and status (rev >= 20) */ +#define SSB_CHIPCO_CLKCTLST_FORCEALP 0x00000001 /* Force ALP request */ +#define SSB_CHIPCO_CLKCTLST_FORCEHT 0x00000002 /* Force HT request */ +#define SSB_CHIPCO_CLKCTLST_FORCEILP 0x00000004 /* Force ILP request */ +#define SSB_CHIPCO_CLKCTLST_HAVEALPREQ 0x00000008 /* ALP available request */ +#define SSB_CHIPCO_CLKCTLST_HAVEHTREQ 0x00000010 /* HT available request */ +#define SSB_CHIPCO_CLKCTLST_HWCROFF 0x00000020 /* Force HW clock request off */ +#define SSB_CHIPCO_CLKCTLST_HAVEALP 0x00010000 /* ALP available */ +#define SSB_CHIPCO_CLKCTLST_HAVEHT 0x00020000 /* HT available */ +#define SSB_CHIPCO_CLKCTLST_4328A0_HAVEHT 0x00010000 /* 4328a0 has reversed bits */ +#define SSB_CHIPCO_CLKCTLST_4328A0_HAVEALP 0x00020000 /* 4328a0 has reversed bits */ +#define SSB_CHIPCO_HW_WORKAROUND 0x01E4 /* Hardware workaround (rev >= 20) */ +#define SSB_CHIPCO_UART0_DATA 0x0300 +#define SSB_CHIPCO_UART0_IMR 0x0304 +#define SSB_CHIPCO_UART0_FCR 0x0308 +#define SSB_CHIPCO_UART0_LCR 0x030C +#define SSB_CHIPCO_UART0_MCR 0x0310 +#define SSB_CHIPCO_UART0_LSR 0x0314 +#define SSB_CHIPCO_UART0_MSR 0x0318 +#define SSB_CHIPCO_UART0_SCRATCH 0x031C +#define SSB_CHIPCO_UART1_DATA 0x0400 +#define SSB_CHIPCO_UART1_IMR 0x0404 +#define SSB_CHIPCO_UART1_FCR 0x0408 +#define SSB_CHIPCO_UART1_LCR 0x040C +#define SSB_CHIPCO_UART1_MCR 0x0410 +#define SSB_CHIPCO_UART1_LSR 0x0414 +#define SSB_CHIPCO_UART1_MSR 0x0418 +#define SSB_CHIPCO_UART1_SCRATCH 0x041C +/* PMU registers (rev >= 20) */ +#define SSB_CHIPCO_PMU_CTL 0x0600 /* PMU control */ +#define SSB_CHIPCO_PMU_CTL_ILP_DIV 0xFFFF0000 /* ILP div mask */ +#define SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT 16 +#define SSB_CHIPCO_PMU_CTL_NOILPONW 0x00000200 /* No ILP on wait */ +#define SSB_CHIPCO_PMU_CTL_HTREQEN 0x00000100 /* HT req enable */ +#define SSB_CHIPCO_PMU_CTL_ALPREQEN 0x00000080 /* ALP req enable */ +#define SSB_CHIPCO_PMU_CTL_XTALFREQ 0x0000007C /* Crystal freq */ +#define SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT 2 +#define SSB_CHIPCO_PMU_CTL_ILPDIVEN 0x00000002 /* ILP div enable */ +#define SSB_CHIPCO_PMU_CTL_LPOSEL 0x00000001 /* LPO sel */ +#define SSB_CHIPCO_PMU_CAP 0x0604 /* PMU capabilities */ +#define SSB_CHIPCO_PMU_CAP_REVISION 0x000000FF /* Revision mask */ +#define SSB_CHIPCO_PMU_STAT 0x0608 /* PMU status */ +#define SSB_CHIPCO_PMU_STAT_INTPEND 0x00000040 /* Interrupt pending */ +#define SSB_CHIPCO_PMU_STAT_SBCLKST 0x00000030 /* Backplane clock status? */ +#define SSB_CHIPCO_PMU_STAT_HAVEALP 0x00000008 /* ALP available */ +#define SSB_CHIPCO_PMU_STAT_HAVEHT 0x00000004 /* HT available */ +#define SSB_CHIPCO_PMU_STAT_RESINIT 0x00000003 /* Res init */ +#define SSB_CHIPCO_PMU_RES_STAT 0x060C /* PMU res status */ +#define SSB_CHIPCO_PMU_RES_PEND 0x0610 /* PMU res pending */ +#define SSB_CHIPCO_PMU_TIMER 0x0614 /* PMU timer */ +#define SSB_CHIPCO_PMU_MINRES_MSK 0x0618 /* PMU min res mask */ +#define SSB_CHIPCO_PMU_MAXRES_MSK 0x061C /* PMU max res mask */ +#define SSB_CHIPCO_PMU_RES_TABSEL 0x0620 /* PMU res table sel */ +#define SSB_CHIPCO_PMU_RES_DEPMSK 0x0624 /* PMU res dep mask */ +#define SSB_CHIPCO_PMU_RES_UPDNTM 0x0628 /* PMU res updown timer */ +#define SSB_CHIPCO_PMU_RES_TIMER 0x062C /* PMU res timer */ +#define SSB_CHIPCO_PMU_CLKSTRETCH 0x0630 /* PMU clockstretch */ +#define SSB_CHIPCO_PMU_WATCHDOG 0x0634 /* PMU watchdog */ +#define SSB_CHIPCO_PMU_RES_REQTS 0x0640 /* PMU res req timer sel */ +#define SSB_CHIPCO_PMU_RES_REQT 0x0644 /* PMU res req timer */ +#define SSB_CHIPCO_PMU_RES_REQM 0x0648 /* PMU res req mask */ +#define SSB_CHIPCO_CHIPCTL_ADDR 0x0650 +#define SSB_CHIPCO_CHIPCTL_DATA 0x0654 +#define SSB_CHIPCO_REGCTL_ADDR 0x0658 +#define SSB_CHIPCO_REGCTL_DATA 0x065C +#define SSB_CHIPCO_PLLCTL_ADDR 0x0660 +#define SSB_CHIPCO_PLLCTL_DATA 0x0664 + + + +/** PMU PLL registers */ + +/* PMU rev 0 PLL registers */ +#define SSB_PMU0_PLLCTL0 0 +#define SSB_PMU0_PLLCTL0_PDIV_MSK 0x00000001 +#define SSB_PMU0_PLLCTL0_PDIV_FREQ 25000 /* kHz */ +#define SSB_PMU0_PLLCTL1 1 +#define SSB_PMU0_PLLCTL1_WILD_IMSK 0xF0000000 /* Wild int mask (low nibble) */ +#define SSB_PMU0_PLLCTL1_WILD_IMSK_SHIFT 28 +#define SSB_PMU0_PLLCTL1_WILD_FMSK 0x0FFFFF00 /* Wild frac mask */ +#define SSB_PMU0_PLLCTL1_WILD_FMSK_SHIFT 8 +#define SSB_PMU0_PLLCTL1_STOPMOD 0x00000040 /* Stop mod */ +#define SSB_PMU0_PLLCTL2 2 +#define SSB_PMU0_PLLCTL2_WILD_IMSKHI 0x0000000F /* Wild int mask (high nibble) */ +#define SSB_PMU0_PLLCTL2_WILD_IMSKHI_SHIFT 0 + +/* PMU rev 1 PLL registers */ +#define SSB_PMU1_PLLCTL0 0 +#define SSB_PMU1_PLLCTL0_P1DIV 0x00F00000 /* P1 div */ +#define SSB_PMU1_PLLCTL0_P1DIV_SHIFT 20 +#define SSB_PMU1_PLLCTL0_P2DIV 0x0F000000 /* P2 div */ +#define SSB_PMU1_PLLCTL0_P2DIV_SHIFT 24 +#define SSB_PMU1_PLLCTL1 1 +#define SSB_PMU1_PLLCTL1_M1DIV 0x000000FF /* M1 div */ +#define SSB_PMU1_PLLCTL1_M1DIV_SHIFT 0 +#define SSB_PMU1_PLLCTL1_M2DIV 0x0000FF00 /* M2 div */ +#define SSB_PMU1_PLLCTL1_M2DIV_SHIFT 8 +#define SSB_PMU1_PLLCTL1_M3DIV 0x00FF0000 /* M3 div */ +#define SSB_PMU1_PLLCTL1_M3DIV_SHIFT 16 +#define SSB_PMU1_PLLCTL1_M4DIV 0xFF000000 /* M4 div */ +#define SSB_PMU1_PLLCTL1_M4DIV_SHIFT 24 +#define SSB_PMU1_PLLCTL2 2 +#define SSB_PMU1_PLLCTL2_M5DIV 0x000000FF /* M5 div */ +#define SSB_PMU1_PLLCTL2_M5DIV_SHIFT 0 +#define SSB_PMU1_PLLCTL2_M6DIV 0x0000FF00 /* M6 div */ +#define SSB_PMU1_PLLCTL2_M6DIV_SHIFT 8 +#define SSB_PMU1_PLLCTL2_NDIVMODE 0x000E0000 /* NDIV mode */ +#define SSB_PMU1_PLLCTL2_NDIVMODE_SHIFT 17 +#define SSB_PMU1_PLLCTL2_NDIVINT 0x1FF00000 /* NDIV int */ +#define SSB_PMU1_PLLCTL2_NDIVINT_SHIFT 20 +#define SSB_PMU1_PLLCTL3 3 +#define SSB_PMU1_PLLCTL3_NDIVFRAC 0x00FFFFFF /* NDIV frac */ +#define SSB_PMU1_PLLCTL3_NDIVFRAC_SHIFT 0 +#define SSB_PMU1_PLLCTL4 4 +#define SSB_PMU1_PLLCTL5 5 +#define SSB_PMU1_PLLCTL5_CLKDRV 0xFFFFFF00 /* clk drv */ +#define SSB_PMU1_PLLCTL5_CLKDRV_SHIFT 8 + +/* BCM4312 PLL resource numbers. */ +#define SSB_PMURES_4312_SWITCHER_BURST 0 +#define SSB_PMURES_4312_SWITCHER_PWM 1 +#define SSB_PMURES_4312_PA_REF_LDO 2 +#define SSB_PMURES_4312_CORE_LDO_BURST 3 +#define SSB_PMURES_4312_CORE_LDO_PWM 4 +#define SSB_PMURES_4312_RADIO_LDO 5 +#define SSB_PMURES_4312_ILP_REQUEST 6 +#define SSB_PMURES_4312_BG_FILTBYP 7 +#define SSB_PMURES_4312_TX_FILTBYP 8 +#define SSB_PMURES_4312_RX_FILTBYP 9 +#define SSB_PMURES_4312_XTAL_PU 10 +#define SSB_PMURES_4312_ALP_AVAIL 11 +#define SSB_PMURES_4312_BB_PLL_FILTBYP 12 +#define SSB_PMURES_4312_RF_PLL_FILTBYP 13 +#define SSB_PMURES_4312_HT_AVAIL 14 + +/* BCM4325 PLL resource numbers. */ +#define SSB_PMURES_4325_BUCK_BOOST_BURST 0 +#define SSB_PMURES_4325_CBUCK_BURST 1 +#define SSB_PMURES_4325_CBUCK_PWM 2 +#define SSB_PMURES_4325_CLDO_CBUCK_BURST 3 +#define SSB_PMURES_4325_CLDO_CBUCK_PWM 4 +#define SSB_PMURES_4325_BUCK_BOOST_PWM 5 +#define SSB_PMURES_4325_ILP_REQUEST 6 +#define SSB_PMURES_4325_ABUCK_BURST 7 +#define SSB_PMURES_4325_ABUCK_PWM 8 +#define SSB_PMURES_4325_LNLDO1_PU 9 +#define SSB_PMURES_4325_LNLDO2_PU 10 +#define SSB_PMURES_4325_LNLDO3_PU 11 +#define SSB_PMURES_4325_LNLDO4_PU 12 +#define SSB_PMURES_4325_XTAL_PU 13 +#define SSB_PMURES_4325_ALP_AVAIL 14 +#define SSB_PMURES_4325_RX_PWRSW_PU 15 +#define SSB_PMURES_4325_TX_PWRSW_PU 16 +#define SSB_PMURES_4325_RFPLL_PWRSW_PU 17 +#define SSB_PMURES_4325_LOGEN_PWRSW_PU 18 +#define SSB_PMURES_4325_AFE_PWRSW_PU 19 +#define SSB_PMURES_4325_BBPLL_PWRSW_PU 20 +#define SSB_PMURES_4325_HT_AVAIL 21 + +/* BCM4328 PLL resource numbers. */ +#define SSB_PMURES_4328_EXT_SWITCHER_PWM 0 +#define SSB_PMURES_4328_BB_SWITCHER_PWM 1 +#define SSB_PMURES_4328_BB_SWITCHER_BURST 2 +#define SSB_PMURES_4328_BB_EXT_SWITCHER_BURST 3 +#define SSB_PMURES_4328_ILP_REQUEST 4 +#define SSB_PMURES_4328_RADIO_SWITCHER_PWM 5 +#define SSB_PMURES_4328_RADIO_SWITCHER_BURST 6 +#define SSB_PMURES_4328_ROM_SWITCH 7 +#define SSB_PMURES_4328_PA_REF_LDO 8 +#define SSB_PMURES_4328_RADIO_LDO 9 +#define SSB_PMURES_4328_AFE_LDO 10 +#define SSB_PMURES_4328_PLL_LDO 11 +#define SSB_PMURES_4328_BG_FILTBYP 12 +#define SSB_PMURES_4328_TX_FILTBYP 13 +#define SSB_PMURES_4328_RX_FILTBYP 14 +#define SSB_PMURES_4328_XTAL_PU 15 +#define SSB_PMURES_4328_XTAL_EN 16 +#define SSB_PMURES_4328_BB_PLL_FILTBYP 17 +#define SSB_PMURES_4328_RF_PLL_FILTBYP 18 +#define SSB_PMURES_4328_BB_PLL_PU 19 + +/* BCM5354 PLL resource numbers. */ +#define SSB_PMURES_5354_EXT_SWITCHER_PWM 0 +#define SSB_PMURES_5354_BB_SWITCHER_PWM 1 +#define SSB_PMURES_5354_BB_SWITCHER_BURST 2 +#define SSB_PMURES_5354_BB_EXT_SWITCHER_BURST 3 +#define SSB_PMURES_5354_ILP_REQUEST 4 +#define SSB_PMURES_5354_RADIO_SWITCHER_PWM 5 +#define SSB_PMURES_5354_RADIO_SWITCHER_BURST 6 +#define SSB_PMURES_5354_ROM_SWITCH 7 +#define SSB_PMURES_5354_PA_REF_LDO 8 +#define SSB_PMURES_5354_RADIO_LDO 9 +#define SSB_PMURES_5354_AFE_LDO 10 +#define SSB_PMURES_5354_PLL_LDO 11 +#define SSB_PMURES_5354_BG_FILTBYP 12 +#define SSB_PMURES_5354_TX_FILTBYP 13 +#define SSB_PMURES_5354_RX_FILTBYP 14 +#define SSB_PMURES_5354_XTAL_PU 15 +#define SSB_PMURES_5354_XTAL_EN 16 +#define SSB_PMURES_5354_BB_PLL_FILTBYP 17 +#define SSB_PMURES_5354_RF_PLL_FILTBYP 18 +#define SSB_PMURES_5354_BB_PLL_PU 19 + + + +/** Chip specific Chip-Status register contents. */ +#define SSB_CHIPCO_CHST_4322_SPROM_EXISTS 0x00000040 /* SPROM present */ +#define SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL 0x00000003 +#define SSB_CHIPCO_CHST_4325_DEFCIS_SEL 0 /* OTP is powered up, use def. CIS, no SPROM */ +#define SSB_CHIPCO_CHST_4325_SPROM_SEL 1 /* OTP is powered up, SPROM is present */ +#define SSB_CHIPCO_CHST_4325_OTP_SEL 2 /* OTP is powered up, no SPROM */ +#define SSB_CHIPCO_CHST_4325_OTP_PWRDN 3 /* OTP is powered down, SPROM is present */ +#define SSB_CHIPCO_CHST_4325_SDIO_USB_MODE 0x00000004 +#define SSB_CHIPCO_CHST_4325_SDIO_USB_MODE_SHIFT 2 +#define SSB_CHIPCO_CHST_4325_RCAL_VALID 0x00000008 +#define SSB_CHIPCO_CHST_4325_RCAL_VALID_SHIFT 3 +#define SSB_CHIPCO_CHST_4325_RCAL_VALUE 0x000001F0 +#define SSB_CHIPCO_CHST_4325_RCAL_VALUE_SHIFT 4 +#define SSB_CHIPCO_CHST_4325_PMUTOP_2B 0x00000200 /* 1 for 2b, 0 for to 2a */ + +/** Macros to determine SPROM presence based on Chip-Status register. */ +#define SSB_CHIPCO_CHST_4312_SPROM_PRESENT(status) \ + ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \ + SSB_CHIPCO_CHST_4325_OTP_SEL) +#define SSB_CHIPCO_CHST_4322_SPROM_PRESENT(status) \ + (status & SSB_CHIPCO_CHST_4322_SPROM_EXISTS) +#define SSB_CHIPCO_CHST_4325_SPROM_PRESENT(status) \ + (((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \ + SSB_CHIPCO_CHST_4325_DEFCIS_SEL) && \ + ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \ + SSB_CHIPCO_CHST_4325_OTP_SEL)) + + + +/** Clockcontrol masks and values **/ + +/* SSB_CHIPCO_CLOCK_N */ +#define SSB_CHIPCO_CLK_N1 0x0000003F /* n1 control */ +#define SSB_CHIPCO_CLK_N2 0x00003F00 /* n2 control */ +#define SSB_CHIPCO_CLK_N2_SHIFT 8 +#define SSB_CHIPCO_CLK_PLLC 0x000F0000 /* pll control */ +#define SSB_CHIPCO_CLK_PLLC_SHIFT 16 + +/* SSB_CHIPCO_CLOCK_SB/PCI/UART */ +#define SSB_CHIPCO_CLK_M1 0x0000003F /* m1 control */ +#define SSB_CHIPCO_CLK_M2 0x00003F00 /* m2 control */ +#define SSB_CHIPCO_CLK_M2_SHIFT 8 +#define SSB_CHIPCO_CLK_M3 0x003F0000 /* m3 control */ +#define SSB_CHIPCO_CLK_M3_SHIFT 16 +#define SSB_CHIPCO_CLK_MC 0x1F000000 /* mux control */ +#define SSB_CHIPCO_CLK_MC_SHIFT 24 + +/* N3M Clock control magic field values */ +#define SSB_CHIPCO_CLK_F6_2 0x02 /* A factor of 2 in */ +#define SSB_CHIPCO_CLK_F6_3 0x03 /* 6-bit fields like */ +#define SSB_CHIPCO_CLK_F6_4 0x05 /* N1, M1 or M3 */ +#define SSB_CHIPCO_CLK_F6_5 0x09 +#define SSB_CHIPCO_CLK_F6_6 0x11 +#define SSB_CHIPCO_CLK_F6_7 0x21 + +#define SSB_CHIPCO_CLK_F5_BIAS 5 /* 5-bit fields get this added */ + +#define SSB_CHIPCO_CLK_MC_BYPASS 0x08 +#define SSB_CHIPCO_CLK_MC_M1 0x04 +#define SSB_CHIPCO_CLK_MC_M1M2 0x02 +#define SSB_CHIPCO_CLK_MC_M1M2M3 0x01 +#define SSB_CHIPCO_CLK_MC_M1M3 0x11 + +/* Type 2 Clock control magic field values */ +#define SSB_CHIPCO_CLK_T2_BIAS 2 /* n1, n2, m1 & m3 bias */ +#define SSB_CHIPCO_CLK_T2M2_BIAS 3 /* m2 bias */ + +#define SSB_CHIPCO_CLK_T2MC_M1BYP 1 +#define SSB_CHIPCO_CLK_T2MC_M2BYP 2 +#define SSB_CHIPCO_CLK_T2MC_M3BYP 4 + +/* Type 6 Clock control magic field values */ +#define SSB_CHIPCO_CLK_T6_MMASK 1 /* bits of interest in m */ +#define SSB_CHIPCO_CLK_T6_M0 120000000 /* sb clock for m = 0 */ +#define SSB_CHIPCO_CLK_T6_M1 100000000 /* sb clock for m = 1 */ +#define SSB_CHIPCO_CLK_SB2MIPS_T6(sb) (2 * (sb)) + +/* Common clock base */ +#define SSB_CHIPCO_CLK_BASE1 24000000 /* Half the clock freq */ +#define SSB_CHIPCO_CLK_BASE2 12500000 /* Alternate crystal on some PLL's */ + +/* Clock control values for 200Mhz in 5350 */ +#define SSB_CHIPCO_CLK_5350_N 0x0311 +#define SSB_CHIPCO_CLK_5350_M 0x04020009 + + +/** Bits in the config registers **/ + +#define SSB_CHIPCO_CFG_EN 0x0001 /* Enable */ +#define SSB_CHIPCO_CFG_EXTM 0x000E /* Extif Mode */ +#define SSB_CHIPCO_CFG_EXTM_ASYNC 0x0002 /* Async/Parallel flash */ +#define SSB_CHIPCO_CFG_EXTM_SYNC 0x0004 /* Synchronous */ +#define SSB_CHIPCO_CFG_EXTM_PCMCIA 0x0008 /* PCMCIA */ +#define SSB_CHIPCO_CFG_EXTM_IDE 0x000A /* IDE */ +#define SSB_CHIPCO_CFG_DS16 0x0010 /* Data size, 0=8bit, 1=16bit */ +#define SSB_CHIPCO_CFG_CLKDIV 0x0060 /* Sync: Clock divisor */ +#define SSB_CHIPCO_CFG_CLKEN 0x0080 /* Sync: Clock enable */ +#define SSB_CHIPCO_CFG_BSTRO 0x0100 /* Sync: Size/Bytestrobe */ + + +/** Flash-specific control/status values */ + +/* flashcontrol opcodes for ST flashes */ +#define SSB_CHIPCO_FLASHCTL_ST_WREN 0x0006 /* Write Enable */ +#define SSB_CHIPCO_FLASHCTL_ST_WRDIS 0x0004 /* Write Disable */ +#define SSB_CHIPCO_FLASHCTL_ST_RDSR 0x0105 /* Read Status Register */ +#define SSB_CHIPCO_FLASHCTL_ST_WRSR 0x0101 /* Write Status Register */ +#define SSB_CHIPCO_FLASHCTL_ST_READ 0x0303 /* Read Data Bytes */ +#define SSB_CHIPCO_FLASHCTL_ST_PP 0x0302 /* Page Program */ +#define SSB_CHIPCO_FLASHCTL_ST_SE 0x02D8 /* Sector Erase */ +#define SSB_CHIPCO_FLASHCTL_ST_BE 0x00C7 /* Bulk Erase */ +#define SSB_CHIPCO_FLASHCTL_ST_DP 0x00B9 /* Deep Power-down */ +#define SSB_CHIPCO_FLASHCTL_ST_RSIG 0x03AB /* Read Electronic Signature */ + +/* Status register bits for ST flashes */ +#define SSB_CHIPCO_FLASHSTA_ST_WIP 0x01 /* Write In Progress */ +#define SSB_CHIPCO_FLASHSTA_ST_WEL 0x02 /* Write Enable Latch */ +#define SSB_CHIPCO_FLASHSTA_ST_BP 0x1C /* Block Protect */ +#define SSB_CHIPCO_FLASHSTA_ST_BP_SHIFT 2 +#define SSB_CHIPCO_FLASHSTA_ST_SRWD 0x80 /* Status Register Write Disable */ + +/* flashcontrol opcodes for Atmel flashes */ +#define SSB_CHIPCO_FLASHCTL_AT_READ 0x07E8 +#define SSB_CHIPCO_FLASHCTL_AT_PAGE_READ 0x07D2 +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_READ /* FIXME */ +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_READ /* FIXME */ +#define SSB_CHIPCO_FLASHCTL_AT_STATUS 0x01D7 +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_WRITE 0x0384 +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_WRITE 0x0387 +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_ERASE_PRGM 0x0283 /* Erase program */ +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_ERASE_PRGM 0x0286 /* Erase program */ +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_PROGRAM 0x0288 +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_PROGRAM 0x0289 +#define SSB_CHIPCO_FLASHCTL_AT_PAGE_ERASE 0x0281 +#define SSB_CHIPCO_FLASHCTL_AT_BLOCK_ERASE 0x0250 +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_WRER_PRGM 0x0382 /* Write erase program */ +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_WRER_PRGM 0x0385 /* Write erase program */ +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_LOAD 0x0253 +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_LOAD 0x0255 +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_COMPARE 0x0260 +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_COMPARE 0x0261 +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_REPROGRAM 0x0258 +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_REPROGRAM 0x0259 + +/* Status register bits for Atmel flashes */ +#define SSB_CHIPCO_FLASHSTA_AT_READY 0x80 +#define SSB_CHIPCO_FLASHSTA_AT_MISMATCH 0x40 +#define SSB_CHIPCO_FLASHSTA_AT_ID 0x38 +#define SSB_CHIPCO_FLASHSTA_AT_ID_SHIFT 3 + + +/** OTP **/ + +/* OTP regions */ +#define SSB_CHIPCO_OTP_HW_REGION SSB_CHIPCO_OTPS_HW_PROTECT +#define SSB_CHIPCO_OTP_SW_REGION SSB_CHIPCO_OTPS_SW_PROTECT +#define SSB_CHIPCO_OTP_CID_REGION SSB_CHIPCO_OTPS_CID_PROTECT + +/* OTP regions (Byte offsets from otp size) */ +#define SSB_CHIPCO_OTP_SWLIM_OFF (-8) +#define SSB_CHIPCO_OTP_CIDBASE_OFF 0 +#define SSB_CHIPCO_OTP_CIDLIM_OFF 8 + +/* Predefined OTP words (Word offset from otp size) */ +#define SSB_CHIPCO_OTP_BOUNDARY_OFF (-4) +#define SSB_CHIPCO_OTP_HWSIGN_OFF (-3) +#define SSB_CHIPCO_OTP_SWSIGN_OFF (-2) +#define SSB_CHIPCO_OTP_CIDSIGN_OFF (-1) + +#define SSB_CHIPCO_OTP_CID_OFF 0 +#define SSB_CHIPCO_OTP_PKG_OFF 1 +#define SSB_CHIPCO_OTP_FID_OFF 2 +#define SSB_CHIPCO_OTP_RSV_OFF 3 +#define SSB_CHIPCO_OTP_LIM_OFF 4 + +#define SSB_CHIPCO_OTP_SIGNATURE 0x578A +#define SSB_CHIPCO_OTP_MAGIC 0x4E56 + + +struct ssb_device; +struct ssb_serial_port; + +/* Data for the PMU, if available. + * Check availability with ((struct ssb_chipcommon)->capabilities & SSB_CHIPCO_CAP_PMU) + */ +struct ssb_chipcommon_pmu { + u8 rev; /* PMU revision */ + u32 crystalfreq; /* The active crystal frequency (in kHz) */ +}; + +struct ssb_chipcommon { + struct ssb_device *dev; + u32 capabilities; + u32 status; + /* Fast Powerup Delay constant */ + u16 fast_pwrup_delay; + struct ssb_chipcommon_pmu pmu; +}; + +static inline bool ssb_chipco_available(struct ssb_chipcommon *cc) +{ + return (cc->dev != NULL); +} + +/* Register access */ +#define chipco_read32(cc, offset) ssb_read32((cc)->dev, offset) +#define chipco_write32(cc, offset, val) ssb_write32((cc)->dev, offset, val) + +#define chipco_mask32(cc, offset, mask) \ + chipco_write32(cc, offset, chipco_read32(cc, offset) & (mask)) +#define chipco_set32(cc, offset, set) \ + chipco_write32(cc, offset, chipco_read32(cc, offset) | (set)) +#define chipco_maskset32(cc, offset, mask, set) \ + chipco_write32(cc, offset, (chipco_read32(cc, offset) & (mask)) | (set)) + +extern void ssb_chipcommon_init(struct ssb_chipcommon *cc); + +extern void ssb_chipco_suspend(struct ssb_chipcommon *cc); +extern void ssb_chipco_resume(struct ssb_chipcommon *cc); + +extern void ssb_chipco_get_clockcpu(struct ssb_chipcommon *cc, + u32 *plltype, u32 *n, u32 *m); +extern void ssb_chipco_get_clockcontrol(struct ssb_chipcommon *cc, + u32 *plltype, u32 *n, u32 *m); +extern void ssb_chipco_timing_init(struct ssb_chipcommon *cc, + unsigned long ns_per_cycle); + +enum ssb_clkmode { + SSB_CLKMODE_SLOW, + SSB_CLKMODE_FAST, + SSB_CLKMODE_DYNAMIC, +}; + +extern void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc, + enum ssb_clkmode mode); + +extern void ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, + u32 ticks); + +void ssb_chipco_irq_mask(struct ssb_chipcommon *cc, u32 mask, u32 value); + +u32 ssb_chipco_irq_status(struct ssb_chipcommon *cc, u32 mask); + +/* Chipcommon GPIO pin access. */ +u32 ssb_chipco_gpio_in(struct ssb_chipcommon *cc, u32 mask); +u32 ssb_chipco_gpio_out(struct ssb_chipcommon *cc, u32 mask, u32 value); +u32 ssb_chipco_gpio_outen(struct ssb_chipcommon *cc, u32 mask, u32 value); +u32 ssb_chipco_gpio_control(struct ssb_chipcommon *cc, u32 mask, u32 value); +u32 ssb_chipco_gpio_intmask(struct ssb_chipcommon *cc, u32 mask, u32 value); +u32 ssb_chipco_gpio_polarity(struct ssb_chipcommon *cc, u32 mask, u32 value); + +#ifdef CONFIG_SSB_SERIAL +extern int ssb_chipco_serial_init(struct ssb_chipcommon *cc, + struct ssb_serial_port *ports); +#endif /* CONFIG_SSB_SERIAL */ + +/* PMU support */ +extern void ssb_pmu_init(struct ssb_chipcommon *cc); + +enum ssb_pmu_ldo_volt_id { + LDO_PAREF = 0, + LDO_VOLT1, + LDO_VOLT2, + LDO_VOLT3, +}; + +void ssb_pmu_set_ldo_voltage(struct ssb_chipcommon *cc, + enum ssb_pmu_ldo_volt_id id, u32 voltage); +void ssb_pmu_set_ldo_paref(struct ssb_chipcommon *cc, bool on); + +#endif /* LINUX_SSB_CHIPCO_H_ */ diff --git a/include/compat/linux/ssb/ssb_driver_extif.h b/include/compat/linux/ssb/ssb_driver_extif.h new file mode 100644 index 00000000..91161f0a --- /dev/null +++ b/include/compat/linux/ssb/ssb_driver_extif.h @@ -0,0 +1,214 @@ +/* + * Hardware-specific External Interface I/O core definitions + * for the BCM47xx family of SiliconBackplane-based chips. + * + * The External Interface core supports a total of three external chip selects + * supporting external interfaces. One of the external chip selects is + * used for Flash, one is used for PCMCIA, and the other may be + * programmed to support either a synchronous interface or an + * asynchronous interface. The asynchronous interface can be used to + * support external devices such as UARTs and the BCM2019 Bluetooth + * baseband processor. + * The external interface core also contains 2 on-chip 16550 UARTs, clock + * frequency control, a watchdog interrupt timer, and a GPIO interface. + * + * Copyright 2005, Broadcom Corporation + * Copyright 2006, Michael Buesch + * + * Licensed under the GPL version 2. See COPYING for details. + */ +#ifndef LINUX_SSB_EXTIFCORE_H_ +#define LINUX_SSB_EXTIFCORE_H_ + +/* external interface address space */ +#define SSB_EXTIF_PCMCIA_MEMBASE(x) (x) +#define SSB_EXTIF_PCMCIA_IOBASE(x) ((x) + 0x100000) +#define SSB_EXTIF_PCMCIA_CFGBASE(x) ((x) + 0x200000) +#define SSB_EXTIF_CFGIF_BASE(x) ((x) + 0x800000) +#define SSB_EXTIF_FLASH_BASE(x) ((x) + 0xc00000) + +#define SSB_EXTIF_NR_GPIOOUT 5 +/* GPIO NOTE: + * The multiple instances of output and output enable registers + * are present to allow driver software for multiple cores to control + * gpio outputs without needing to share a single register pair. + * Use the following helper macro to get a register offset value. + */ +#define SSB_EXTIF_GPIO_OUT(index) ({ \ + BUILD_BUG_ON(index >= SSB_EXTIF_NR_GPIOOUT); \ + SSB_EXTIF_GPIO_OUT_BASE + ((index) * 8); \ + }) +#define SSB_EXTIF_GPIO_OUTEN(index) ({ \ + BUILD_BUG_ON(index >= SSB_EXTIF_NR_GPIOOUT); \ + SSB_EXTIF_GPIO_OUTEN_BASE + ((index) * 8); \ + }) + +/** EXTIF core registers **/ + +#define SSB_EXTIF_CTL 0x0000 +#define SSB_EXTIF_CTL_UARTEN (1 << 0) /* UART enable */ +#define SSB_EXTIF_EXTSTAT 0x0004 +#define SSB_EXTIF_EXTSTAT_EMODE (1 << 0) /* Endian mode (ro) */ +#define SSB_EXTIF_EXTSTAT_EIRQPIN (1 << 1) /* External interrupt pin (ro) */ +#define SSB_EXTIF_EXTSTAT_GPIOIRQPIN (1 << 2) /* GPIO interrupt pin (ro) */ +#define SSB_EXTIF_PCMCIA_CFG 0x0010 +#define SSB_EXTIF_PCMCIA_MEMWAIT 0x0014 +#define SSB_EXTIF_PCMCIA_ATTRWAIT 0x0018 +#define SSB_EXTIF_PCMCIA_IOWAIT 0x001C +#define SSB_EXTIF_PROG_CFG 0x0020 +#define SSB_EXTIF_PROG_WAITCNT 0x0024 +#define SSB_EXTIF_FLASH_CFG 0x0028 +#define SSB_EXTIF_FLASH_WAITCNT 0x002C +#define SSB_EXTIF_WATCHDOG 0x0040 +#define SSB_EXTIF_CLOCK_N 0x0044 +#define SSB_EXTIF_CLOCK_SB 0x0048 +#define SSB_EXTIF_CLOCK_PCI 0x004C +#define SSB_EXTIF_CLOCK_MII 0x0050 +#define SSB_EXTIF_GPIO_IN 0x0060 +#define SSB_EXTIF_GPIO_OUT_BASE 0x0064 +#define SSB_EXTIF_GPIO_OUTEN_BASE 0x0068 +#define SSB_EXTIF_EJTAG_OUTEN 0x0090 +#define SSB_EXTIF_GPIO_INTPOL 0x0094 +#define SSB_EXTIF_GPIO_INTMASK 0x0098 +#define SSB_EXTIF_UART_DATA 0x0300 +#define SSB_EXTIF_UART_TIMER 0x0310 +#define SSB_EXTIF_UART_FCR 0x0320 +#define SSB_EXTIF_UART_LCR 0x0330 +#define SSB_EXTIF_UART_MCR 0x0340 +#define SSB_EXTIF_UART_LSR 0x0350 +#define SSB_EXTIF_UART_MSR 0x0360 +#define SSB_EXTIF_UART_SCRATCH 0x0370 + + + + +/* pcmcia/prog/flash_config */ +#define SSB_EXTCFG_EN (1 << 0) /* enable */ +#define SSB_EXTCFG_MODE 0xE /* mode */ +#define SSB_EXTCFG_MODE_SHIFT 1 +#define SSB_EXTCFG_MODE_FLASH 0x0 /* flash/asynchronous mode */ +#define SSB_EXTCFG_MODE_SYNC 0x2 /* synchronous mode */ +#define SSB_EXTCFG_MODE_PCMCIA 0x4 /* pcmcia mode */ +#define SSB_EXTCFG_DS16 (1 << 4) /* destsize: 0=8bit, 1=16bit */ +#define SSB_EXTCFG_BSWAP (1 << 5) /* byteswap */ +#define SSB_EXTCFG_CLKDIV 0xC0 /* clock divider */ +#define SSB_EXTCFG_CLKDIV_SHIFT 6 +#define SSB_EXTCFG_CLKDIV_2 0x0 /* backplane/2 */ +#define SSB_EXTCFG_CLKDIV_3 0x40 /* backplane/3 */ +#define SSB_EXTCFG_CLKDIV_4 0x80 /* backplane/4 */ +#define SSB_EXTCFG_CLKEN (1 << 8) /* clock enable */ +#define SSB_EXTCFG_STROBE (1 << 9) /* size/bytestrobe (synch only) */ + +/* pcmcia_memwait */ +#define SSB_PCMCIA_MEMW_0 0x0000003F /* waitcount0 */ +#define SSB_PCMCIA_MEMW_1 0x00001F00 /* waitcount1 */ +#define SSB_PCMCIA_MEMW_1_SHIFT 8 +#define SSB_PCMCIA_MEMW_2 0x001F0000 /* waitcount2 */ +#define SSB_PCMCIA_MEMW_2_SHIFT 16 +#define SSB_PCMCIA_MEMW_3 0x1F000000 /* waitcount3 */ +#define SSB_PCMCIA_MEMW_3_SHIFT 24 + +/* pcmcia_attrwait */ +#define SSB_PCMCIA_ATTW_0 0x0000003F /* waitcount0 */ +#define SSB_PCMCIA_ATTW_1 0x00001F00 /* waitcount1 */ +#define SSB_PCMCIA_ATTW_1_SHIFT 8 +#define SSB_PCMCIA_ATTW_2 0x001F0000 /* waitcount2 */ +#define SSB_PCMCIA_ATTW_2_SHIFT 16 +#define SSB_PCMCIA_ATTW_3 0x1F000000 /* waitcount3 */ +#define SSB_PCMCIA_ATTW_3_SHIFT 24 + +/* pcmcia_iowait */ +#define SSB_PCMCIA_IOW_0 0x0000003F /* waitcount0 */ +#define SSB_PCMCIA_IOW_1 0x00001F00 /* waitcount1 */ +#define SSB_PCMCIA_IOW_1_SHIFT 8 +#define SSB_PCMCIA_IOW_2 0x001F0000 /* waitcount2 */ +#define SSB_PCMCIA_IOW_2_SHIFT 16 +#define SSB_PCMCIA_IOW_3 0x1F000000 /* waitcount3 */ +#define SSB_PCMCIA_IOW_3_SHIFT 24 + +/* prog_waitcount */ +#define SSB_PROG_WCNT_0 0x0000001F /* waitcount0 */ +#define SSB_PROG_WCNT_1 0x00001F00 /* waitcount1 */ +#define SSB_PROG_WCNT_1_SHIFT 8 +#define SSB_PROG_WCNT_2 0x001F0000 /* waitcount2 */ +#define SSB_PROG_WCNT_2_SHIFT 16 +#define SSB_PROG_WCNT_3 0x1F000000 /* waitcount3 */ +#define SSB_PROG_WCNT_3_SHIFT 24 + +#define SSB_PROG_W0 0x0000000C +#define SSB_PROG_W1 0x00000A00 +#define SSB_PROG_W2 0x00020000 +#define SSB_PROG_W3 0x01000000 + +/* flash_waitcount */ +#define SSB_FLASH_WCNT_0 0x0000001F /* waitcount0 */ +#define SSB_FLASH_WCNT_1 0x00001F00 /* waitcount1 */ +#define SSB_FLASH_WCNT_1_SHIFT 8 +#define SSB_FLASH_WCNT_2 0x001F0000 /* waitcount2 */ +#define SSB_FLASH_WCNT_2_SHIFT 16 +#define SSB_FLASH_WCNT_3 0x1F000000 /* waitcount3 */ +#define SSB_FLASH_WCNT_3_SHIFT 24 + +/* watchdog */ +#define SSB_EXTIF_WATCHDOG_CLK 48000000 /* Hz */ + + + +#ifdef CONFIG_SSB_DRIVER_EXTIF + +struct ssb_extif { + struct ssb_device *dev; +}; + +static inline bool ssb_extif_available(struct ssb_extif *extif) +{ + return (extif->dev != NULL); +} + +extern void ssb_extif_get_clockcontrol(struct ssb_extif *extif, + u32 *plltype, u32 *n, u32 *m); + +extern void ssb_extif_timing_init(struct ssb_extif *extif, + unsigned long ns); + +extern void ssb_extif_watchdog_timer_set(struct ssb_extif *extif, + u32 ticks); + +/* Extif GPIO pin access */ +u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask); +u32 ssb_extif_gpio_out(struct ssb_extif *extif, u32 mask, u32 value); +u32 ssb_extif_gpio_outen(struct ssb_extif *extif, u32 mask, u32 value); +u32 ssb_extif_gpio_polarity(struct ssb_extif *extif, u32 mask, u32 value); +u32 ssb_extif_gpio_intmask(struct ssb_extif *extif, u32 mask, u32 value); + +#ifdef CONFIG_SSB_SERIAL +extern int ssb_extif_serial_init(struct ssb_extif *extif, + struct ssb_serial_port *ports); +#endif /* CONFIG_SSB_SERIAL */ + + +#else /* CONFIG_SSB_DRIVER_EXTIF */ +/* extif disabled */ + +struct ssb_extif { +}; + +static inline bool ssb_extif_available(struct ssb_extif *extif) +{ + return 0; +} + +static inline +void ssb_extif_get_clockcontrol(struct ssb_extif *extif, + u32 *plltype, u32 *n, u32 *m) +{ +} + +static inline +void ssb_extif_watchdog_timer_set(struct ssb_extif *extif, + u32 ticks) +{ +} + +#endif /* CONFIG_SSB_DRIVER_EXTIF */ +#endif /* LINUX_SSB_EXTIFCORE_H_ */ diff --git a/include/compat/linux/ssb/ssb_driver_gige.h b/include/compat/linux/ssb/ssb_driver_gige.h new file mode 100644 index 00000000..eba52a10 --- /dev/null +++ b/include/compat/linux/ssb/ssb_driver_gige.h @@ -0,0 +1,179 @@ +#ifndef LINUX_SSB_DRIVER_GIGE_H_ +#define LINUX_SSB_DRIVER_GIGE_H_ + +#include +#include +#include + + +#ifdef CONFIG_SSB_DRIVER_GIGE + + +#define SSB_GIGE_PCIIO 0x0000 /* PCI I/O Registers (1024 bytes) */ +#define SSB_GIGE_RESERVED 0x0400 /* Reserved (1024 bytes) */ +#define SSB_GIGE_PCICFG 0x0800 /* PCI config space (256 bytes) */ +#define SSB_GIGE_SHIM_FLUSHSTAT 0x0C00 /* PCI to OCP: Flush status control (32bit) */ +#define SSB_GIGE_SHIM_FLUSHRDA 0x0C04 /* PCI to OCP: Flush read address (32bit) */ +#define SSB_GIGE_SHIM_FLUSHTO 0x0C08 /* PCI to OCP: Flush timeout counter (32bit) */ +#define SSB_GIGE_SHIM_BARRIER 0x0C0C /* PCI to OCP: Barrier register (32bit) */ +#define SSB_GIGE_SHIM_MAOCPSI 0x0C10 /* PCI to OCP: MaocpSI Control (32bit) */ +#define SSB_GIGE_SHIM_SIOCPMA 0x0C14 /* PCI to OCP: SiocpMa Control (32bit) */ + +/* TM Status High flags */ +#define SSB_GIGE_TMSHIGH_RGMII 0x00010000 /* Have an RGMII PHY-bus */ +/* TM Status Low flags */ +#define SSB_GIGE_TMSLOW_TXBYPASS 0x00080000 /* TX bypass (no delay) */ +#define SSB_GIGE_TMSLOW_RXBYPASS 0x00100000 /* RX bypass (no delay) */ +#define SSB_GIGE_TMSLOW_DLLEN 0x01000000 /* Enable DLL controls */ + +/* Boardflags (low) */ +#define SSB_GIGE_BFL_ROBOSWITCH 0x0010 + + +#define SSB_GIGE_MEM_RES_NAME "SSB Broadcom 47xx GigE memory" +#define SSB_GIGE_IO_RES_NAME "SSB Broadcom 47xx GigE I/O" + +struct ssb_gige { + struct ssb_device *dev; + + spinlock_t lock; + + /* True, if the device has an RGMII bus. + * False, if the device has a GMII bus. */ + bool has_rgmii; + + /* The PCI controller device. */ + struct pci_controller pci_controller; + struct pci_ops pci_ops; + struct resource mem_resource; + struct resource io_resource; +}; + +/* Check whether a PCI device is a SSB Gigabit Ethernet core. */ +extern bool pdev_is_ssb_gige_core(struct pci_dev *pdev); + +/* Convert a pci_dev pointer to a ssb_gige pointer. */ +static inline struct ssb_gige * pdev_to_ssb_gige(struct pci_dev *pdev) +{ + if (!pdev_is_ssb_gige_core(pdev)) + return NULL; + return container_of(pdev->bus->ops, struct ssb_gige, pci_ops); +} + +/* Returns whether the PHY is connected by an RGMII bus. */ +static inline bool ssb_gige_is_rgmii(struct pci_dev *pdev) +{ + struct ssb_gige *dev = pdev_to_ssb_gige(pdev); + return (dev ? dev->has_rgmii : 0); +} + +/* Returns whether we have a Roboswitch. */ +static inline bool ssb_gige_have_roboswitch(struct pci_dev *pdev) +{ + struct ssb_gige *dev = pdev_to_ssb_gige(pdev); + if (dev) + return !!(dev->dev->bus->sprom.boardflags_lo & + SSB_GIGE_BFL_ROBOSWITCH); + return 0; +} + +/* Returns whether we can only do one DMA at once. */ +static inline bool ssb_gige_one_dma_at_once(struct pci_dev *pdev) +{ + struct ssb_gige *dev = pdev_to_ssb_gige(pdev); + if (dev) + return ((dev->dev->bus->chip_id == 0x4785) && + (dev->dev->bus->chip_rev < 2)); + return 0; +} + +/* Returns whether we must flush posted writes. */ +static inline bool ssb_gige_must_flush_posted_writes(struct pci_dev *pdev) +{ + struct ssb_gige *dev = pdev_to_ssb_gige(pdev); + if (dev) + return (dev->dev->bus->chip_id == 0x4785); + return 0; +} + +#ifdef CONFIG_BCM47XX +#include +/* Get the device MAC address */ +static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr) +{ + char buf[20]; + if (nvram_getenv("et0macaddr", buf, sizeof(buf)) < 0) + return; + nvram_parse_macaddr(buf, macaddr); +} +#else +static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr) +{ +} +#endif + +extern int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev, + struct pci_dev *pdev); +extern int ssb_gige_map_irq(struct ssb_device *sdev, + const struct pci_dev *pdev); + +/* The GigE driver is not a standalone module, because we don't have support + * for unregistering the driver. So we could not unload the module anyway. */ +extern int ssb_gige_init(void); +static inline void ssb_gige_exit(void) +{ + /* Currently we can not unregister the GigE driver, + * because we can not unregister the PCI bridge. */ + BUG(); +} + + +#else /* CONFIG_SSB_DRIVER_GIGE */ +/* Gigabit Ethernet driver disabled */ + + +static inline int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev, + struct pci_dev *pdev) +{ + return -ENOSYS; +} +static inline int ssb_gige_map_irq(struct ssb_device *sdev, + const struct pci_dev *pdev) +{ + return -ENOSYS; +} +static inline int ssb_gige_init(void) +{ + return 0; +} +static inline void ssb_gige_exit(void) +{ +} + +static inline bool pdev_is_ssb_gige_core(struct pci_dev *pdev) +{ + return 0; +} +static inline struct ssb_gige * pdev_to_ssb_gige(struct pci_dev *pdev) +{ + return NULL; +} +static inline bool ssb_gige_is_rgmii(struct pci_dev *pdev) +{ + return 0; +} +static inline bool ssb_gige_have_roboswitch(struct pci_dev *pdev) +{ + return 0; +} +static inline bool ssb_gige_one_dma_at_once(struct pci_dev *pdev) +{ + return 0; +} +static inline bool ssb_gige_must_flush_posted_writes(struct pci_dev *pdev) +{ + return 0; +} + +#endif /* CONFIG_SSB_DRIVER_GIGE */ +#endif /* LINUX_SSB_DRIVER_GIGE_H_ */ diff --git a/include/compat/linux/ssb/ssb_driver_mips.h b/include/compat/linux/ssb/ssb_driver_mips.h new file mode 100644 index 00000000..5f44e974 --- /dev/null +++ b/include/compat/linux/ssb/ssb_driver_mips.h @@ -0,0 +1,46 @@ +#ifndef LINUX_SSB_MIPSCORE_H_ +#define LINUX_SSB_MIPSCORE_H_ + +#ifdef CONFIG_SSB_DRIVER_MIPS + +struct ssb_device; + +struct ssb_serial_port { + void *regs; + unsigned long clockspeed; + unsigned int irq; + unsigned int baud_base; + unsigned int reg_shift; +}; + + +struct ssb_mipscore { + struct ssb_device *dev; + + int nr_serial_ports; + struct ssb_serial_port serial_ports[4]; + + u8 flash_buswidth; + u32 flash_window; + u32 flash_window_size; +}; + +extern void ssb_mipscore_init(struct ssb_mipscore *mcore); +extern u32 ssb_cpu_clock(struct ssb_mipscore *mcore); + +extern unsigned int ssb_mips_irq(struct ssb_device *dev); + + +#else /* CONFIG_SSB_DRIVER_MIPS */ + +struct ssb_mipscore { +}; + +static inline +void ssb_mipscore_init(struct ssb_mipscore *mcore) +{ +} + +#endif /* CONFIG_SSB_DRIVER_MIPS */ + +#endif /* LINUX_SSB_MIPSCORE_H_ */ diff --git a/include/compat/linux/ssb/ssb_driver_pci.h b/include/compat/linux/ssb/ssb_driver_pci.h new file mode 100644 index 00000000..41e330e5 --- /dev/null +++ b/include/compat/linux/ssb/ssb_driver_pci.h @@ -0,0 +1,130 @@ +#ifndef LINUX_SSB_PCICORE_H_ +#define LINUX_SSB_PCICORE_H_ + +#include + +struct pci_dev; + + +#ifdef CONFIG_SSB_DRIVER_PCICORE + +/* PCI core registers. */ +#define SSB_PCICORE_CTL 0x0000 /* PCI Control */ +#define SSB_PCICORE_CTL_RST_OE 0x00000001 /* PCI_RESET Output Enable */ +#define SSB_PCICORE_CTL_RST 0x00000002 /* PCI_RESET driven out to pin */ +#define SSB_PCICORE_CTL_CLK_OE 0x00000004 /* Clock gate Output Enable */ +#define SSB_PCICORE_CTL_CLK 0x00000008 /* Gate for clock driven out to pin */ +#define SSB_PCICORE_ARBCTL 0x0010 /* PCI Arbiter Control */ +#define SSB_PCICORE_ARBCTL_INTERN 0x00000001 /* Use internal arbiter */ +#define SSB_PCICORE_ARBCTL_EXTERN 0x00000002 /* Use external arbiter */ +#define SSB_PCICORE_ARBCTL_PARKID 0x00000006 /* Mask, selects which agent is parked on an idle bus */ +#define SSB_PCICORE_ARBCTL_PARKID_LAST 0x00000000 /* Last requestor */ +#define SSB_PCICORE_ARBCTL_PARKID_4710 0x00000002 /* 4710 */ +#define SSB_PCICORE_ARBCTL_PARKID_EXT0 0x00000004 /* External requestor 0 */ +#define SSB_PCICORE_ARBCTL_PARKID_EXT1 0x00000006 /* External requestor 1 */ +#define SSB_PCICORE_ISTAT 0x0020 /* Interrupt status */ +#define SSB_PCICORE_ISTAT_INTA 0x00000001 /* PCI INTA# */ +#define SSB_PCICORE_ISTAT_INTB 0x00000002 /* PCI INTB# */ +#define SSB_PCICORE_ISTAT_SERR 0x00000004 /* PCI SERR# (write to clear) */ +#define SSB_PCICORE_ISTAT_PERR 0x00000008 /* PCI PERR# (write to clear) */ +#define SSB_PCICORE_ISTAT_PME 0x00000010 /* PCI PME# */ +#define SSB_PCICORE_IMASK 0x0024 /* Interrupt mask */ +#define SSB_PCICORE_IMASK_INTA 0x00000001 /* PCI INTA# */ +#define SSB_PCICORE_IMASK_INTB 0x00000002 /* PCI INTB# */ +#define SSB_PCICORE_IMASK_SERR 0x00000004 /* PCI SERR# */ +#define SSB_PCICORE_IMASK_PERR 0x00000008 /* PCI PERR# */ +#define SSB_PCICORE_IMASK_PME 0x00000010 /* PCI PME# */ +#define SSB_PCICORE_MBOX 0x0028 /* Backplane to PCI Mailbox */ +#define SSB_PCICORE_MBOX_F0_0 0x00000100 /* PCI function 0, INT 0 */ +#define SSB_PCICORE_MBOX_F0_1 0x00000200 /* PCI function 0, INT 1 */ +#define SSB_PCICORE_MBOX_F1_0 0x00000400 /* PCI function 1, INT 0 */ +#define SSB_PCICORE_MBOX_F1_1 0x00000800 /* PCI function 1, INT 1 */ +#define SSB_PCICORE_MBOX_F2_0 0x00001000 /* PCI function 2, INT 0 */ +#define SSB_PCICORE_MBOX_F2_1 0x00002000 /* PCI function 2, INT 1 */ +#define SSB_PCICORE_MBOX_F3_0 0x00004000 /* PCI function 3, INT 0 */ +#define SSB_PCICORE_MBOX_F3_1 0x00008000 /* PCI function 3, INT 1 */ +#define SSB_PCICORE_BCAST_ADDR 0x0050 /* Backplane Broadcast Address */ +#define SSB_PCICORE_BCAST_ADDR_MASK 0x000000FF +#define SSB_PCICORE_BCAST_DATA 0x0054 /* Backplane Broadcast Data */ +#define SSB_PCICORE_GPIO_IN 0x0060 /* rev >= 2 only */ +#define SSB_PCICORE_GPIO_OUT 0x0064 /* rev >= 2 only */ +#define SSB_PCICORE_GPIO_ENABLE 0x0068 /* rev >= 2 only */ +#define SSB_PCICORE_GPIO_CTL 0x006C /* rev >= 2 only */ +#define SSB_PCICORE_SBTOPCI0 0x0100 /* Backplane to PCI translation 0 (sbtopci0) */ +#define SSB_PCICORE_SBTOPCI0_MASK 0xFC000000 +#define SSB_PCICORE_SBTOPCI1 0x0104 /* Backplane to PCI translation 1 (sbtopci1) */ +#define SSB_PCICORE_SBTOPCI1_MASK 0xFC000000 +#define SSB_PCICORE_SBTOPCI2 0x0108 /* Backplane to PCI translation 2 (sbtopci2) */ +#define SSB_PCICORE_SBTOPCI2_MASK 0xC0000000 +#define SSB_PCICORE_PCICFG0 0x0400 /* PCI config space 0 (rev >= 8) */ +#define SSB_PCICORE_PCICFG1 0x0500 /* PCI config space 1 (rev >= 8) */ +#define SSB_PCICORE_PCICFG2 0x0600 /* PCI config space 2 (rev >= 8) */ +#define SSB_PCICORE_PCICFG3 0x0700 /* PCI config space 3 (rev >= 8) */ +#define SSB_PCICORE_SPROM(wordoffset) (0x0800 + ((wordoffset) * 2)) /* SPROM shadow area (72 bytes) */ + +/* SBtoPCIx */ +#define SSB_PCICORE_SBTOPCI_MEM 0x00000000 +#define SSB_PCICORE_SBTOPCI_IO 0x00000001 +#define SSB_PCICORE_SBTOPCI_CFG0 0x00000002 +#define SSB_PCICORE_SBTOPCI_CFG1 0x00000003 +#define SSB_PCICORE_SBTOPCI_PREF 0x00000004 /* Prefetch enable */ +#define SSB_PCICORE_SBTOPCI_BURST 0x00000008 /* Burst enable */ +#define SSB_PCICORE_SBTOPCI_MRM 0x00000020 /* Memory Read Multiple */ +#define SSB_PCICORE_SBTOPCI_RC 0x00000030 /* Read Command mask (rev >= 11) */ +#define SSB_PCICORE_SBTOPCI_RC_READ 0x00000000 /* Memory read */ +#define SSB_PCICORE_SBTOPCI_RC_READL 0x00000010 /* Memory read line */ +#define SSB_PCICORE_SBTOPCI_RC_READM 0x00000020 /* Memory read multiple */ + + +/* PCIcore specific boardflags */ +#define SSB_PCICORE_BFL_NOPCI 0x00000400 /* Board leaves PCI floating */ + + +struct ssb_pcicore { + struct ssb_device *dev; + u8 setup_done:1; + u8 hostmode:1; + u8 cardbusmode:1; +}; + +extern void ssb_pcicore_init(struct ssb_pcicore *pc); + +/* Enable IRQ routing for a specific device */ +extern int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc, + struct ssb_device *dev); + +int ssb_pcicore_plat_dev_init(struct pci_dev *d); +int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin); + + +#else /* CONFIG_SSB_DRIVER_PCICORE */ + + +struct ssb_pcicore { +}; + +static inline +void ssb_pcicore_init(struct ssb_pcicore *pc) +{ +} + +static inline +int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc, + struct ssb_device *dev) +{ + return 0; +} + +static inline +int ssb_pcicore_plat_dev_init(struct pci_dev *d) +{ + return -ENODEV; +} +static inline +int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + return -ENODEV; +} + +#endif /* CONFIG_SSB_DRIVER_PCICORE */ +#endif /* LINUX_SSB_PCICORE_H_ */ diff --git a/include/compat/linux/ssb/ssb_embedded.h b/include/compat/linux/ssb/ssb_embedded.h new file mode 100644 index 00000000..8d8dedff --- /dev/null +++ b/include/compat/linux/ssb/ssb_embedded.h @@ -0,0 +1,18 @@ +#ifndef LINUX_SSB_EMBEDDED_H_ +#define LINUX_SSB_EMBEDDED_H_ + +#include +#include + + +extern int ssb_watchdog_timer_set(struct ssb_bus *bus, u32 ticks); + +/* Generic GPIO API */ +u32 ssb_gpio_in(struct ssb_bus *bus, u32 mask); +u32 ssb_gpio_out(struct ssb_bus *bus, u32 mask, u32 value); +u32 ssb_gpio_outen(struct ssb_bus *bus, u32 mask, u32 value); +u32 ssb_gpio_control(struct ssb_bus *bus, u32 mask, u32 value); +u32 ssb_gpio_intmask(struct ssb_bus *bus, u32 mask, u32 value); +u32 ssb_gpio_polarity(struct ssb_bus *bus, u32 mask, u32 value); + +#endif /* LINUX_SSB_EMBEDDED_H_ */ diff --git a/include/compat/linux/ssb/ssb_regs.h b/include/compat/linux/ssb/ssb_regs.h new file mode 100644 index 00000000..98941203 --- /dev/null +++ b/include/compat/linux/ssb/ssb_regs.h @@ -0,0 +1,543 @@ +#ifndef LINUX_SSB_REGS_H_ +#define LINUX_SSB_REGS_H_ + + +/* SiliconBackplane Address Map. + * All regions may not exist on all chips. + */ +#define SSB_SDRAM_BASE 0x00000000U /* Physical SDRAM */ +#define SSB_PCI_MEM 0x08000000U /* Host Mode sb2pcitranslation0 (64 MB) */ +#define SSB_PCI_CFG 0x0c000000U /* Host Mode sb2pcitranslation1 (64 MB) */ +#define SSB_SDRAM_SWAPPED 0x10000000U /* Byteswapped Physical SDRAM */ +#define SSB_ENUM_BASE 0x18000000U /* Enumeration space base */ +#define SSB_ENUM_LIMIT 0x18010000U /* Enumeration space limit */ + +#define SSB_FLASH2 0x1c000000U /* Flash Region 2 (region 1 shadowed here) */ +#define SSB_FLASH2_SZ 0x02000000U /* Size of Flash Region 2 */ + +#define SSB_EXTIF_BASE 0x1f000000U /* External Interface region base address */ +#define SSB_FLASH1 0x1fc00000U /* Flash Region 1 */ +#define SSB_FLASH1_SZ 0x00400000U /* Size of Flash Region 1 */ + +#define SSB_PCI_DMA 0x40000000U /* Client Mode sb2pcitranslation2 (1 GB) */ +#define SSB_PCI_DMA_SZ 0x40000000U /* Client Mode sb2pcitranslation2 size in bytes */ +#define SSB_PCIE_DMA_L32 0x00000000U /* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), low 32 bits */ +#define SSB_PCIE_DMA_H32 0x80000000U /* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), high 32 bits */ +#define SSB_EUART (SSB_EXTIF_BASE + 0x00800000) +#define SSB_LED (SSB_EXTIF_BASE + 0x00900000) + + +/* Enumeration space constants */ +#define SSB_CORE_SIZE 0x1000 /* Size of a core MMIO area */ +#define SSB_MAX_NR_CORES ((SSB_ENUM_LIMIT - SSB_ENUM_BASE) / SSB_CORE_SIZE) + + +/* mips address */ +#define SSB_EJTAG 0xff200000 /* MIPS EJTAG space (2M) */ + + +/* SSB PCI config space registers. */ +#define SSB_PMCSR 0x44 +#define SSB_PE 0x100 +#define SSB_BAR0_WIN 0x80 /* Backplane address space 0 */ +#define SSB_BAR1_WIN 0x84 /* Backplane address space 1 */ +#define SSB_SPROMCTL 0x88 /* SPROM control */ +#define SSB_SPROMCTL_WE 0x10 /* SPROM write enable */ +#define SSB_BAR1_CONTROL 0x8c /* Address space 1 burst control */ +#define SSB_PCI_IRQS 0x90 /* PCI interrupts */ +#define SSB_PCI_IRQMASK 0x94 /* PCI IRQ control and mask (pcirev >= 6 only) */ +#define SSB_BACKPLANE_IRQS 0x98 /* Backplane Interrupts */ +#define SSB_GPIO_IN 0xB0 /* GPIO Input (pcirev >= 3 only) */ +#define SSB_GPIO_OUT 0xB4 /* GPIO Output (pcirev >= 3 only) */ +#define SSB_GPIO_OUT_ENABLE 0xB8 /* GPIO Output Enable/Disable (pcirev >= 3 only) */ +#define SSB_GPIO_SCS 0x10 /* PCI config space bit 4 for 4306c0 slow clock source */ +#define SSB_GPIO_HWRAD 0x20 /* PCI config space GPIO 13 for hw radio disable */ +#define SSB_GPIO_XTAL 0x40 /* PCI config space GPIO 14 for Xtal powerup */ +#define SSB_GPIO_PLL 0x80 /* PCI config space GPIO 15 for PLL powerdown */ + + +#define SSB_BAR0_MAX_RETRIES 50 + +/* Silicon backplane configuration register definitions */ +#define SSB_IPSFLAG 0x0F08 +#define SSB_IPSFLAG_IRQ1 0x0000003F /* which sbflags get routed to mips interrupt 1 */ +#define SSB_IPSFLAG_IRQ1_SHIFT 0 +#define SSB_IPSFLAG_IRQ2 0x00003F00 /* which sbflags get routed to mips interrupt 2 */ +#define SSB_IPSFLAG_IRQ2_SHIFT 8 +#define SSB_IPSFLAG_IRQ3 0x003F0000 /* which sbflags get routed to mips interrupt 3 */ +#define SSB_IPSFLAG_IRQ3_SHIFT 16 +#define SSB_IPSFLAG_IRQ4 0x3F000000 /* which sbflags get routed to mips interrupt 4 */ +#define SSB_IPSFLAG_IRQ4_SHIFT 24 +#define SSB_TPSFLAG 0x0F18 +#define SSB_TPSFLAG_BPFLAG 0x0000003F /* Backplane flag # */ +#define SSB_TPSFLAG_ALWAYSIRQ 0x00000040 /* IRQ is always sent on the Backplane */ +#define SSB_TMERRLOGA 0x0F48 +#define SSB_TMERRLOG 0x0F50 +#define SSB_ADMATCH3 0x0F60 +#define SSB_ADMATCH2 0x0F68 +#define SSB_ADMATCH1 0x0F70 +#define SSB_IMSTATE 0x0F90 /* SB Initiator Agent State */ +#define SSB_IMSTATE_PC 0x0000000f /* Pipe Count */ +#define SSB_IMSTATE_AP_MASK 0x00000030 /* Arbitration Priority */ +#define SSB_IMSTATE_AP_BOTH 0x00000000 /* Use both timeslices and token */ +#define SSB_IMSTATE_AP_TS 0x00000010 /* Use timeslices only */ +#define SSB_IMSTATE_AP_TK 0x00000020 /* Use token only */ +#define SSB_IMSTATE_AP_RSV 0x00000030 /* Reserved */ +#define SSB_IMSTATE_IBE 0x00020000 /* In Band Error */ +#define SSB_IMSTATE_TO 0x00040000 /* Timeout */ +#define SSB_IMSTATE_BUSY 0x01800000 /* Busy (Backplane rev >= 2.3 only) */ +#define SSB_IMSTATE_REJECT 0x02000000 /* Reject (Backplane rev >= 2.3 only) */ +#define SSB_INTVEC 0x0F94 /* SB Interrupt Mask */ +#define SSB_INTVEC_PCI 0x00000001 /* Enable interrupts for PCI */ +#define SSB_INTVEC_ENET0 0x00000002 /* Enable interrupts for enet 0 */ +#define SSB_INTVEC_ILINE20 0x00000004 /* Enable interrupts for iline20 */ +#define SSB_INTVEC_CODEC 0x00000008 /* Enable interrupts for v90 codec */ +#define SSB_INTVEC_USB 0x00000010 /* Enable interrupts for usb */ +#define SSB_INTVEC_EXTIF 0x00000020 /* Enable interrupts for external i/f */ +#define SSB_INTVEC_ENET1 0x00000040 /* Enable interrupts for enet 1 */ +#define SSB_TMSLOW 0x0F98 /* SB Target State Low */ +#define SSB_TMSLOW_RESET 0x00000001 /* Reset */ +#define SSB_TMSLOW_REJECT 0x00000002 /* Reject (Standard Backplane) */ +#define SSB_TMSLOW_REJECT_23 0x00000004 /* Reject (Backplane rev 2.3) */ +#define SSB_TMSLOW_CLOCK 0x00010000 /* Clock Enable */ +#define SSB_TMSLOW_FGC 0x00020000 /* Force Gated Clocks On */ +#define SSB_TMSLOW_PE 0x40000000 /* Power Management Enable */ +#define SSB_TMSLOW_BE 0x80000000 /* BIST Enable */ +#define SSB_TMSHIGH 0x0F9C /* SB Target State High */ +#define SSB_TMSHIGH_SERR 0x00000001 /* S-error */ +#define SSB_TMSHIGH_INT 0x00000002 /* Interrupt */ +#define SSB_TMSHIGH_BUSY 0x00000004 /* Busy */ +#define SSB_TMSHIGH_TO 0x00000020 /* Timeout. Backplane rev >= 2.3 only */ +#define SSB_TMSHIGH_COREFL 0x1FFF0000 /* Core specific flags */ +#define SSB_TMSHIGH_COREFL_SHIFT 16 +#define SSB_TMSHIGH_DMA64 0x10000000 /* 64bit DMA supported */ +#define SSB_TMSHIGH_GCR 0x20000000 /* Gated Clock Request */ +#define SSB_TMSHIGH_BISTF 0x40000000 /* BIST Failed */ +#define SSB_TMSHIGH_BISTD 0x80000000 /* BIST Done */ +#define SSB_BWA0 0x0FA0 +#define SSB_IMCFGLO 0x0FA8 +#define SSB_IMCFGLO_SERTO 0x00000007 /* Service timeout */ +#define SSB_IMCFGLO_REQTO 0x00000070 /* Request timeout */ +#define SSB_IMCFGLO_REQTO_SHIFT 4 +#define SSB_IMCFGLO_CONNID 0x00FF0000 /* Connection ID */ +#define SSB_IMCFGLO_CONNID_SHIFT 16 +#define SSB_IMCFGHI 0x0FAC +#define SSB_ADMATCH0 0x0FB0 +#define SSB_TMCFGLO 0x0FB8 +#define SSB_TMCFGHI 0x0FBC +#define SSB_BCONFIG 0x0FC0 +#define SSB_BSTATE 0x0FC8 +#define SSB_ACTCFG 0x0FD8 +#define SSB_FLAGST 0x0FE8 +#define SSB_IDLOW 0x0FF8 +#define SSB_IDLOW_CFGSP 0x00000003 /* Config Space */ +#define SSB_IDLOW_ADDRNGE 0x00000038 /* Address Ranges supported */ +#define SSB_IDLOW_ADDRNGE_SHIFT 3 +#define SSB_IDLOW_SYNC 0x00000040 +#define SSB_IDLOW_INITIATOR 0x00000080 +#define SSB_IDLOW_MIBL 0x00000F00 /* Minimum Backplane latency */ +#define SSB_IDLOW_MIBL_SHIFT 8 +#define SSB_IDLOW_MABL 0x0000F000 /* Maximum Backplane latency */ +#define SSB_IDLOW_MABL_SHIFT 12 +#define SSB_IDLOW_TIF 0x00010000 /* This Initiator is first */ +#define SSB_IDLOW_CCW 0x000C0000 /* Cycle counter width */ +#define SSB_IDLOW_CCW_SHIFT 18 +#define SSB_IDLOW_TPT 0x00F00000 /* Target ports */ +#define SSB_IDLOW_TPT_SHIFT 20 +#define SSB_IDLOW_INITP 0x0F000000 /* Initiator ports */ +#define SSB_IDLOW_INITP_SHIFT 24 +#define SSB_IDLOW_SSBREV 0xF0000000 /* Sonics Backplane Revision code */ +#define SSB_IDLOW_SSBREV_22 0x00000000 /* <= 2.2 */ +#define SSB_IDLOW_SSBREV_23 0x10000000 /* 2.3 */ +#define SSB_IDLOW_SSBREV_24 0x40000000 /* ?? Found in BCM4328 */ +#define SSB_IDLOW_SSBREV_25 0x50000000 /* ?? Not Found yet */ +#define SSB_IDLOW_SSBREV_26 0x60000000 /* ?? Found in some BCM4311/2 */ +#define SSB_IDLOW_SSBREV_27 0x70000000 /* ?? Found in some BCM4311/2 */ +#define SSB_IDHIGH 0x0FFC /* SB Identification High */ +#define SSB_IDHIGH_RCLO 0x0000000F /* Revision Code (low part) */ +#define SSB_IDHIGH_CC 0x00008FF0 /* Core Code */ +#define SSB_IDHIGH_CC_SHIFT 4 +#define SSB_IDHIGH_RCHI 0x00007000 /* Revision Code (high part) */ +#define SSB_IDHIGH_RCHI_SHIFT 8 /* yes, shift 8 is right */ +#define SSB_IDHIGH_VC 0xFFFF0000 /* Vendor Code */ +#define SSB_IDHIGH_VC_SHIFT 16 + +/* SPROM shadow area. If not otherwise noted, fields are + * two bytes wide. Note that the SPROM can _only_ be read + * in two-byte quantities. + */ +#define SSB_SPROMSIZE_WORDS 64 +#define SSB_SPROMSIZE_BYTES (SSB_SPROMSIZE_WORDS * sizeof(u16)) +#define SSB_SPROMSIZE_WORDS_R123 64 +#define SSB_SPROMSIZE_WORDS_R4 220 +#define SSB_SPROMSIZE_BYTES_R123 (SSB_SPROMSIZE_WORDS_R123 * sizeof(u16)) +#define SSB_SPROMSIZE_BYTES_R4 (SSB_SPROMSIZE_WORDS_R4 * sizeof(u16)) +#define SSB_SPROM_BASE1 0x1000 +#define SSB_SPROM_BASE31 0x0800 +#define SSB_SPROM_REVISION 0x007E +#define SSB_SPROM_REVISION_REV 0x00FF /* SPROM Revision number */ +#define SSB_SPROM_REVISION_CRC 0xFF00 /* SPROM CRC8 value */ +#define SSB_SPROM_REVISION_CRC_SHIFT 8 + +/* SPROM Revision 1 */ +#define SSB_SPROM1_SPID 0x0004 /* Subsystem Product ID for PCI */ +#define SSB_SPROM1_SVID 0x0006 /* Subsystem Vendor ID for PCI */ +#define SSB_SPROM1_PID 0x0008 /* Product ID for PCI */ +#define SSB_SPROM1_IL0MAC 0x0048 /* 6 bytes MAC address for 802.11b/g */ +#define SSB_SPROM1_ET0MAC 0x004E /* 6 bytes MAC address for Ethernet */ +#define SSB_SPROM1_ET1MAC 0x0054 /* 6 bytes MAC address for 802.11a */ +#define SSB_SPROM1_ETHPHY 0x005A /* Ethernet PHY settings */ +#define SSB_SPROM1_ETHPHY_ET0A 0x001F /* MII Address for enet0 */ +#define SSB_SPROM1_ETHPHY_ET1A 0x03E0 /* MII Address for enet1 */ +#define SSB_SPROM1_ETHPHY_ET1A_SHIFT 5 +#define SSB_SPROM1_ETHPHY_ET0M (1<<14) /* MDIO for enet0 */ +#define SSB_SPROM1_ETHPHY_ET1M (1<<15) /* MDIO for enet1 */ +#define SSB_SPROM1_BINF 0x005C /* Board info */ +#define SSB_SPROM1_BINF_BREV 0x00FF /* Board Revision */ +#define SSB_SPROM1_BINF_CCODE 0x0F00 /* Country Code */ +#define SSB_SPROM1_BINF_CCODE_SHIFT 8 +#define SSB_SPROM1_BINF_ANTBG 0x3000 /* Available B-PHY and G-PHY antennas */ +#define SSB_SPROM1_BINF_ANTBG_SHIFT 12 +#define SSB_SPROM1_BINF_ANTA 0xC000 /* Available A-PHY antennas */ +#define SSB_SPROM1_BINF_ANTA_SHIFT 14 +#define SSB_SPROM1_PA0B0 0x005E +#define SSB_SPROM1_PA0B1 0x0060 +#define SSB_SPROM1_PA0B2 0x0062 +#define SSB_SPROM1_GPIOA 0x0064 /* General Purpose IO pins 0 and 1 */ +#define SSB_SPROM1_GPIOA_P0 0x00FF /* Pin 0 */ +#define SSB_SPROM1_GPIOA_P1 0xFF00 /* Pin 1 */ +#define SSB_SPROM1_GPIOA_P1_SHIFT 8 +#define SSB_SPROM1_GPIOB 0x0066 /* General Purpuse IO pins 2 and 3 */ +#define SSB_SPROM1_GPIOB_P2 0x00FF /* Pin 2 */ +#define SSB_SPROM1_GPIOB_P3 0xFF00 /* Pin 3 */ +#define SSB_SPROM1_GPIOB_P3_SHIFT 8 +#define SSB_SPROM1_MAXPWR 0x0068 /* Power Amplifier Max Power */ +#define SSB_SPROM1_MAXPWR_BG 0x00FF /* B-PHY and G-PHY (in dBm Q5.2) */ +#define SSB_SPROM1_MAXPWR_A 0xFF00 /* A-PHY (in dBm Q5.2) */ +#define SSB_SPROM1_MAXPWR_A_SHIFT 8 +#define SSB_SPROM1_PA1B0 0x006A +#define SSB_SPROM1_PA1B1 0x006C +#define SSB_SPROM1_PA1B2 0x006E +#define SSB_SPROM1_ITSSI 0x0070 /* Idle TSSI Target */ +#define SSB_SPROM1_ITSSI_BG 0x00FF /* B-PHY and G-PHY*/ +#define SSB_SPROM1_ITSSI_A 0xFF00 /* A-PHY */ +#define SSB_SPROM1_ITSSI_A_SHIFT 8 +#define SSB_SPROM1_BFLLO 0x0072 /* Boardflags (low 16 bits) */ +#define SSB_SPROM1_AGAIN 0x0074 /* Antenna Gain (in dBm Q5.2) */ +#define SSB_SPROM1_AGAIN_BG 0x00FF /* B-PHY and G-PHY */ +#define SSB_SPROM1_AGAIN_BG_SHIFT 0 +#define SSB_SPROM1_AGAIN_A 0xFF00 /* A-PHY */ +#define SSB_SPROM1_AGAIN_A_SHIFT 8 + +/* SPROM Revision 2 (inherits from rev 1) */ +#define SSB_SPROM2_BFLHI 0x0038 /* Boardflags (high 16 bits) */ +#define SSB_SPROM2_MAXP_A 0x003A /* A-PHY Max Power */ +#define SSB_SPROM2_MAXP_A_HI 0x00FF /* Max Power High */ +#define SSB_SPROM2_MAXP_A_LO 0xFF00 /* Max Power Low */ +#define SSB_SPROM2_MAXP_A_LO_SHIFT 8 +#define SSB_SPROM2_PA1LOB0 0x003C /* A-PHY PowerAmplifier Low Settings */ +#define SSB_SPROM2_PA1LOB1 0x003E /* A-PHY PowerAmplifier Low Settings */ +#define SSB_SPROM2_PA1LOB2 0x0040 /* A-PHY PowerAmplifier Low Settings */ +#define SSB_SPROM2_PA1HIB0 0x0042 /* A-PHY PowerAmplifier High Settings */ +#define SSB_SPROM2_PA1HIB1 0x0044 /* A-PHY PowerAmplifier High Settings */ +#define SSB_SPROM2_PA1HIB2 0x0046 /* A-PHY PowerAmplifier High Settings */ +#define SSB_SPROM2_OPO 0x0078 /* OFDM Power Offset from CCK Level */ +#define SSB_SPROM2_OPO_VALUE 0x00FF +#define SSB_SPROM2_OPO_UNUSED 0xFF00 +#define SSB_SPROM2_CCODE 0x007C /* Two char Country Code */ + +/* SPROM Revision 3 (inherits most data from rev 2) */ +#define SSB_SPROM3_OFDMAPO 0x002C /* A-PHY OFDM Mid Power Offset (4 bytes, BigEndian) */ +#define SSB_SPROM3_OFDMALPO 0x0030 /* A-PHY OFDM Low Power Offset (4 bytes, BigEndian) */ +#define SSB_SPROM3_OFDMAHPO 0x0034 /* A-PHY OFDM High Power Offset (4 bytes, BigEndian) */ +#define SSB_SPROM3_GPIOLDC 0x0042 /* GPIO LED Powersave Duty Cycle (4 bytes, BigEndian) */ +#define SSB_SPROM3_GPIOLDC_OFF 0x0000FF00 /* Off Count */ +#define SSB_SPROM3_GPIOLDC_OFF_SHIFT 8 +#define SSB_SPROM3_GPIOLDC_ON 0x00FF0000 /* On Count */ +#define SSB_SPROM3_GPIOLDC_ON_SHIFT 16 +#define SSB_SPROM3_IL0MAC 0x004A /* 6 bytes MAC address for 802.11b/g */ +#define SSB_SPROM3_CCKPO 0x0078 /* CCK Power Offset */ +#define SSB_SPROM3_CCKPO_1M 0x000F /* 1M Rate PO */ +#define SSB_SPROM3_CCKPO_2M 0x00F0 /* 2M Rate PO */ +#define SSB_SPROM3_CCKPO_2M_SHIFT 4 +#define SSB_SPROM3_CCKPO_55M 0x0F00 /* 5.5M Rate PO */ +#define SSB_SPROM3_CCKPO_55M_SHIFT 8 +#define SSB_SPROM3_CCKPO_11M 0xF000 /* 11M Rate PO */ +#define SSB_SPROM3_CCKPO_11M_SHIFT 12 +#define SSB_SPROM3_OFDMGPO 0x107A /* G-PHY OFDM Power Offset (4 bytes, BigEndian) */ + +/* SPROM Revision 4 */ +#define SSB_SPROM4_BFLLO 0x0044 /* Boardflags (low 16 bits) */ +#define SSB_SPROM4_BFLHI 0x0046 /* Board Flags Hi */ +#define SSB_SPROM4_BFL2LO 0x0048 /* Board flags 2 (low 16 bits) */ +#define SSB_SPROM4_BFL2HI 0x004A /* Board flags 2 Hi */ +#define SSB_SPROM4_IL0MAC 0x004C /* 6 byte MAC address for a/b/g/n */ +#define SSB_SPROM4_CCODE 0x0052 /* Country Code (2 bytes) */ +#define SSB_SPROM4_GPIOA 0x0056 /* Gen. Purpose IO # 0 and 1 */ +#define SSB_SPROM4_GPIOA_P0 0x00FF /* Pin 0 */ +#define SSB_SPROM4_GPIOA_P1 0xFF00 /* Pin 1 */ +#define SSB_SPROM4_GPIOA_P1_SHIFT 8 +#define SSB_SPROM4_GPIOB 0x0058 /* Gen. Purpose IO # 2 and 3 */ +#define SSB_SPROM4_GPIOB_P2 0x00FF /* Pin 2 */ +#define SSB_SPROM4_GPIOB_P3 0xFF00 /* Pin 3 */ +#define SSB_SPROM4_GPIOB_P3_SHIFT 8 +#define SSB_SPROM4_ETHPHY 0x005A /* Ethernet PHY settings ?? */ +#define SSB_SPROM4_ETHPHY_ET0A 0x001F /* MII Address for enet0 */ +#define SSB_SPROM4_ETHPHY_ET1A 0x03E0 /* MII Address for enet1 */ +#define SSB_SPROM4_ETHPHY_ET1A_SHIFT 5 +#define SSB_SPROM4_ETHPHY_ET0M (1<<14) /* MDIO for enet0 */ +#define SSB_SPROM4_ETHPHY_ET1M (1<<15) /* MDIO for enet1 */ +#define SSB_SPROM4_ANTAVAIL 0x005D /* Antenna available bitfields */ +#define SSB_SPROM4_ANTAVAIL_A 0x00FF /* A-PHY bitfield */ +#define SSB_SPROM4_ANTAVAIL_A_SHIFT 0 +#define SSB_SPROM4_ANTAVAIL_BG 0xFF00 /* B-PHY and G-PHY bitfield */ +#define SSB_SPROM4_ANTAVAIL_BG_SHIFT 8 +#define SSB_SPROM4_AGAIN01 0x005E /* Antenna Gain (in dBm Q5.2) */ +#define SSB_SPROM4_AGAIN0 0x00FF /* Antenna 0 */ +#define SSB_SPROM4_AGAIN0_SHIFT 0 +#define SSB_SPROM4_AGAIN1 0xFF00 /* Antenna 1 */ +#define SSB_SPROM4_AGAIN1_SHIFT 8 +#define SSB_SPROM4_AGAIN23 0x0060 +#define SSB_SPROM4_AGAIN2 0x00FF /* Antenna 2 */ +#define SSB_SPROM4_AGAIN2_SHIFT 0 +#define SSB_SPROM4_AGAIN3 0xFF00 /* Antenna 3 */ +#define SSB_SPROM4_AGAIN3_SHIFT 8 +#define SSB_SPROM4_TXPID2G01 0x0062 /* TX Power Index 2GHz */ +#define SSB_SPROM4_TXPID2G0 0x00FF +#define SSB_SPROM4_TXPID2G0_SHIFT 0 +#define SSB_SPROM4_TXPID2G1 0xFF00 +#define SSB_SPROM4_TXPID2G1_SHIFT 8 +#define SSB_SPROM4_TXPID2G23 0x0064 /* TX Power Index 2GHz */ +#define SSB_SPROM4_TXPID2G2 0x00FF +#define SSB_SPROM4_TXPID2G2_SHIFT 0 +#define SSB_SPROM4_TXPID2G3 0xFF00 +#define SSB_SPROM4_TXPID2G3_SHIFT 8 +#define SSB_SPROM4_TXPID5G01 0x0066 /* TX Power Index 5GHz middle subband */ +#define SSB_SPROM4_TXPID5G0 0x00FF +#define SSB_SPROM4_TXPID5G0_SHIFT 0 +#define SSB_SPROM4_TXPID5G1 0xFF00 +#define SSB_SPROM4_TXPID5G1_SHIFT 8 +#define SSB_SPROM4_TXPID5G23 0x0068 /* TX Power Index 5GHz middle subband */ +#define SSB_SPROM4_TXPID5G2 0x00FF +#define SSB_SPROM4_TXPID5G2_SHIFT 0 +#define SSB_SPROM4_TXPID5G3 0xFF00 +#define SSB_SPROM4_TXPID5G3_SHIFT 8 +#define SSB_SPROM4_TXPID5GL01 0x006A /* TX Power Index 5GHz low subband */ +#define SSB_SPROM4_TXPID5GL0 0x00FF +#define SSB_SPROM4_TXPID5GL0_SHIFT 0 +#define SSB_SPROM4_TXPID5GL1 0xFF00 +#define SSB_SPROM4_TXPID5GL1_SHIFT 8 +#define SSB_SPROM4_TXPID5GL23 0x006C /* TX Power Index 5GHz low subband */ +#define SSB_SPROM4_TXPID5GL2 0x00FF +#define SSB_SPROM4_TXPID5GL2_SHIFT 0 +#define SSB_SPROM4_TXPID5GL3 0xFF00 +#define SSB_SPROM4_TXPID5GL3_SHIFT 8 +#define SSB_SPROM4_TXPID5GH01 0x006E /* TX Power Index 5GHz high subband */ +#define SSB_SPROM4_TXPID5GH0 0x00FF +#define SSB_SPROM4_TXPID5GH0_SHIFT 0 +#define SSB_SPROM4_TXPID5GH1 0xFF00 +#define SSB_SPROM4_TXPID5GH1_SHIFT 8 +#define SSB_SPROM4_TXPID5GH23 0x0070 /* TX Power Index 5GHz high subband */ +#define SSB_SPROM4_TXPID5GH2 0x00FF +#define SSB_SPROM4_TXPID5GH2_SHIFT 0 +#define SSB_SPROM4_TXPID5GH3 0xFF00 +#define SSB_SPROM4_TXPID5GH3_SHIFT 8 +#define SSB_SPROM4_MAXP_BG 0x0080 /* Max Power BG in path 1 */ +#define SSB_SPROM4_MAXP_BG_MASK 0x00FF /* Mask for Max Power BG */ +#define SSB_SPROM4_ITSSI_BG 0xFF00 /* Mask for path 1 itssi_bg */ +#define SSB_SPROM4_ITSSI_BG_SHIFT 8 +#define SSB_SPROM4_MAXP_A 0x008A /* Max Power A in path 1 */ +#define SSB_SPROM4_MAXP_A_MASK 0x00FF /* Mask for Max Power A */ +#define SSB_SPROM4_ITSSI_A 0xFF00 /* Mask for path 1 itssi_a */ +#define SSB_SPROM4_ITSSI_A_SHIFT 8 +#define SSB_SPROM4_PA0B0 0x0082 /* The paXbY locations are */ +#define SSB_SPROM4_PA0B1 0x0084 /* only guesses */ +#define SSB_SPROM4_PA0B2 0x0086 +#define SSB_SPROM4_PA1B0 0x008E +#define SSB_SPROM4_PA1B1 0x0090 +#define SSB_SPROM4_PA1B2 0x0092 + +/* SPROM Revision 5 (inherits most data from rev 4) */ +#define SSB_SPROM5_CCODE 0x0044 /* Country Code (2 bytes) */ +#define SSB_SPROM5_BFLLO 0x004A /* Boardflags (low 16 bits) */ +#define SSB_SPROM5_BFLHI 0x004C /* Board Flags Hi */ +#define SSB_SPROM5_BFL2LO 0x004E /* Board flags 2 (low 16 bits) */ +#define SSB_SPROM5_BFL2HI 0x0050 /* Board flags 2 Hi */ +#define SSB_SPROM5_IL0MAC 0x0052 /* 6 byte MAC address for a/b/g/n */ +#define SSB_SPROM5_GPIOA 0x0076 /* Gen. Purpose IO # 0 and 1 */ +#define SSB_SPROM5_GPIOA_P0 0x00FF /* Pin 0 */ +#define SSB_SPROM5_GPIOA_P1 0xFF00 /* Pin 1 */ +#define SSB_SPROM5_GPIOA_P1_SHIFT 8 +#define SSB_SPROM5_GPIOB 0x0078 /* Gen. Purpose IO # 2 and 3 */ +#define SSB_SPROM5_GPIOB_P2 0x00FF /* Pin 2 */ +#define SSB_SPROM5_GPIOB_P3 0xFF00 /* Pin 3 */ +#define SSB_SPROM5_GPIOB_P3_SHIFT 8 + +/* SPROM Revision 8 */ +#define SSB_SPROM8_BOARDREV 0x0082 /* Board revision */ +#define SSB_SPROM8_BFLLO 0x0084 /* Board flags (bits 0-15) */ +#define SSB_SPROM8_BFLHI 0x0086 /* Board flags (bits 16-31) */ +#define SSB_SPROM8_BFL2LO 0x0088 /* Board flags (bits 32-47) */ +#define SSB_SPROM8_BFL2HI 0x008A /* Board flags (bits 48-63) */ +#define SSB_SPROM8_IL0MAC 0x008C /* 6 byte MAC address */ +#define SSB_SPROM8_CCODE 0x0092 /* 2 byte country code */ +#define SSB_SPROM8_GPIOA 0x0096 /*Gen. Purpose IO # 0 and 1 */ +#define SSB_SPROM8_GPIOA_P0 0x00FF /* Pin 0 */ +#define SSB_SPROM8_GPIOA_P1 0xFF00 /* Pin 1 */ +#define SSB_SPROM8_GPIOA_P1_SHIFT 8 +#define SSB_SPROM8_GPIOB 0x0098 /* Gen. Purpose IO # 2 and 3 */ +#define SSB_SPROM8_GPIOB_P2 0x00FF /* Pin 2 */ +#define SSB_SPROM8_GPIOB_P3 0xFF00 /* Pin 3 */ +#define SSB_SPROM8_GPIOB_P3_SHIFT 8 +#define SSB_SPROM8_ANTAVAIL 0x009C /* Antenna available bitfields*/ +#define SSB_SPROM8_ANTAVAIL_A 0xFF00 /* A-PHY bitfield */ +#define SSB_SPROM8_ANTAVAIL_A_SHIFT 8 +#define SSB_SPROM8_ANTAVAIL_BG 0x00FF /* B-PHY and G-PHY bitfield */ +#define SSB_SPROM8_ANTAVAIL_BG_SHIFT 0 +#define SSB_SPROM8_AGAIN01 0x009E /* Antenna Gain (in dBm Q5.2) */ +#define SSB_SPROM8_AGAIN0 0x00FF /* Antenna 0 */ +#define SSB_SPROM8_AGAIN0_SHIFT 0 +#define SSB_SPROM8_AGAIN1 0xFF00 /* Antenna 1 */ +#define SSB_SPROM8_AGAIN1_SHIFT 8 +#define SSB_SPROM8_AGAIN23 0x00A0 +#define SSB_SPROM8_AGAIN2 0x00FF /* Antenna 2 */ +#define SSB_SPROM8_AGAIN2_SHIFT 0 +#define SSB_SPROM8_AGAIN3 0xFF00 /* Antenna 3 */ +#define SSB_SPROM8_AGAIN3_SHIFT 8 +#define SSB_SPROM8_RSSIPARM2G 0x00A4 /* RSSI params for 2GHz */ +#define SSB_SPROM8_RSSISMF2G 0x000F +#define SSB_SPROM8_RSSISMC2G 0x00F0 +#define SSB_SPROM8_RSSISMC2G_SHIFT 4 +#define SSB_SPROM8_RSSISAV2G 0x0700 +#define SSB_SPROM8_RSSISAV2G_SHIFT 8 +#define SSB_SPROM8_BXA2G 0x1800 +#define SSB_SPROM8_BXA2G_SHIFT 11 +#define SSB_SPROM8_RSSIPARM5G 0x00A6 /* RSSI params for 5GHz */ +#define SSB_SPROM8_RSSISMF5G 0x000F +#define SSB_SPROM8_RSSISMC5G 0x00F0 +#define SSB_SPROM8_RSSISMC5G_SHIFT 4 +#define SSB_SPROM8_RSSISAV5G 0x0700 +#define SSB_SPROM8_RSSISAV5G_SHIFT 8 +#define SSB_SPROM8_BXA5G 0x1800 +#define SSB_SPROM8_BXA5G_SHIFT 11 +#define SSB_SPROM8_TRI25G 0x00A8 /* TX isolation 2.4&5.3GHz */ +#define SSB_SPROM8_TRI2G 0x00FF /* TX isolation 2.4GHz */ +#define SSB_SPROM8_TRI5G 0xFF00 /* TX isolation 5.3GHz */ +#define SSB_SPROM8_TRI5G_SHIFT 8 +#define SSB_SPROM8_TRI5GHL 0x00AA /* TX isolation 5.2/5.8GHz */ +#define SSB_SPROM8_TRI5GL 0x00FF /* TX isolation 5.2GHz */ +#define SSB_SPROM8_TRI5GH 0xFF00 /* TX isolation 5.8GHz */ +#define SSB_SPROM8_TRI5GH_SHIFT 8 +#define SSB_SPROM8_RXPO 0x00AC /* RX power offsets */ +#define SSB_SPROM8_RXPO2G 0x00FF /* 2GHz RX power offset */ +#define SSB_SPROM8_RXPO5G 0xFF00 /* 5GHz RX power offset */ +#define SSB_SPROM8_RXPO5G_SHIFT 8 +#define SSB_SPROM8_MAXP_BG 0x00C0 /* Max Power 2GHz in path 1 */ +#define SSB_SPROM8_MAXP_BG_MASK 0x00FF /* Mask for Max Power 2GHz */ +#define SSB_SPROM8_ITSSI_BG 0xFF00 /* Mask for path 1 itssi_bg */ +#define SSB_SPROM8_ITSSI_BG_SHIFT 8 +#define SSB_SPROM8_PA0B0 0x00C2 /* 2GHz power amp settings */ +#define SSB_SPROM8_PA0B1 0x00C4 +#define SSB_SPROM8_PA0B2 0x00C6 +#define SSB_SPROM8_MAXP_A 0x00C8 /* Max Power 5.3GHz */ +#define SSB_SPROM8_MAXP_A_MASK 0x00FF /* Mask for Max Power 5.3GHz */ +#define SSB_SPROM8_ITSSI_A 0xFF00 /* Mask for path 1 itssi_a */ +#define SSB_SPROM8_ITSSI_A_SHIFT 8 +#define SSB_SPROM8_MAXP_AHL 0x00CA /* Max Power 5.2/5.8GHz */ +#define SSB_SPROM8_MAXP_AH_MASK 0x00FF /* Mask for Max Power 5.8GHz */ +#define SSB_SPROM8_MAXP_AL_MASK 0xFF00 /* Mask for Max Power 5.2GHz */ +#define SSB_SPROM8_MAXP_AL_SHIFT 8 +#define SSB_SPROM8_PA1B0 0x00CC /* 5.3GHz power amp settings */ +#define SSB_SPROM8_PA1B1 0x00CE +#define SSB_SPROM8_PA1B2 0x00D0 +#define SSB_SPROM8_PA1LOB0 0x00D2 /* 5.2GHz power amp settings */ +#define SSB_SPROM8_PA1LOB1 0x00D4 +#define SSB_SPROM8_PA1LOB2 0x00D6 +#define SSB_SPROM8_PA1HIB0 0x00D8 /* 5.8GHz power amp settings */ +#define SSB_SPROM8_PA1HIB1 0x00DA +#define SSB_SPROM8_PA1HIB2 0x00DC +#define SSB_SPROM8_CCK2GPO 0x0140 /* CCK power offset */ +#define SSB_SPROM8_OFDM2GPO 0x0142 /* 2.4GHz OFDM power offset */ +#define SSB_SPROM8_OFDM5GPO 0x0146 /* 5.3GHz OFDM power offset */ +#define SSB_SPROM8_OFDM5GLPO 0x014A /* 5.2GHz OFDM power offset */ +#define SSB_SPROM8_OFDM5GHPO 0x014E /* 5.8GHz OFDM power offset */ + +/* Values for boardflags_lo read from SPROM */ +#define SSB_BFL_BTCOEXIST 0x0001 /* implements Bluetooth coexistance */ +#define SSB_BFL_PACTRL 0x0002 /* GPIO 9 controlling the PA */ +#define SSB_BFL_AIRLINEMODE 0x0004 /* implements GPIO 13 radio disable indication */ +#define SSB_BFL_RSSI 0x0008 /* software calculates nrssi slope. */ +#define SSB_BFL_ENETSPI 0x0010 /* has ephy roboswitch spi */ +#define SSB_BFL_XTAL_NOSLOW 0x0020 /* no slow clock available */ +#define SSB_BFL_CCKHIPWR 0x0040 /* can do high power CCK transmission */ +#define SSB_BFL_ENETADM 0x0080 /* has ADMtek switch */ +#define SSB_BFL_ENETVLAN 0x0100 /* can do vlan */ +#define SSB_BFL_AFTERBURNER 0x0200 /* supports Afterburner mode */ +#define SSB_BFL_NOPCI 0x0400 /* board leaves PCI floating */ +#define SSB_BFL_FEM 0x0800 /* supports the Front End Module */ +#define SSB_BFL_EXTLNA 0x1000 /* has an external LNA */ +#define SSB_BFL_HGPA 0x2000 /* had high gain PA */ +#define SSB_BFL_BTCMOD 0x4000 /* BFL_BTCOEXIST is given in alternate GPIOs */ +#define SSB_BFL_ALTIQ 0x8000 /* alternate I/Q settings */ + +/* Values for boardflags_hi read from SPROM */ +#define SSB_BFH_NOPA 0x0001 /* has no PA */ +#define SSB_BFH_RSSIINV 0x0002 /* RSSI uses positive slope (not TSSI) */ +#define SSB_BFH_PAREF 0x0004 /* uses the PARef LDO */ +#define SSB_BFH_3TSWITCH 0x0008 /* uses a triple throw switch shared with bluetooth */ +#define SSB_BFH_PHASESHIFT 0x0010 /* can support phase shifter */ +#define SSB_BFH_BUCKBOOST 0x0020 /* has buck/booster */ +#define SSB_BFH_FEM_BT 0x0040 /* has FEM and switch to share antenna with bluetooth */ + +/* Values for boardflags2_lo read from SPROM */ +#define SSB_BFL2_RXBB_INT_REG_DIS 0x0001 /* external RX BB regulator present */ +#define SSB_BFL2_APLL_WAR 0x0002 /* alternative A-band PLL settings implemented */ +#define SSB_BFL2_TXPWRCTRL_EN 0x0004 /* permits enabling TX Power Control */ +#define SSB_BFL2_2X4_DIV 0x0008 /* 2x4 diversity switch */ +#define SSB_BFL2_5G_PWRGAIN 0x0010 /* supports 5G band power gain */ +#define SSB_BFL2_PCIEWAR_OVR 0x0020 /* overrides ASPM and Clkreq settings */ +#define SSB_BFL2_CAESERS_BRD 0x0040 /* is Caesers board (unused) */ +#define SSB_BFL2_BTC3WIRE 0x0080 /* used 3-wire bluetooth coexist */ +#define SSB_BFL2_SKWRKFEM_BRD 0x0100 /* 4321mcm93 uses Skyworks FEM */ +#define SSB_BFL2_SPUR_WAR 0x0200 /* has a workaround for clock-harmonic spurs */ +#define SSB_BFL2_GPLL_WAR 0x0400 /* altenative G-band PLL settings implemented */ + +/* Values for SSB_SPROM1_BINF_CCODE */ +enum { + SSB_SPROM1CCODE_WORLD = 0, + SSB_SPROM1CCODE_THAILAND, + SSB_SPROM1CCODE_ISRAEL, + SSB_SPROM1CCODE_JORDAN, + SSB_SPROM1CCODE_CHINA, + SSB_SPROM1CCODE_JAPAN, + SSB_SPROM1CCODE_USA_CANADA_ANZ, + SSB_SPROM1CCODE_EUROPE, + SSB_SPROM1CCODE_USA_LOW, + SSB_SPROM1CCODE_JAPAN_HIGH, + SSB_SPROM1CCODE_ALL, + SSB_SPROM1CCODE_NONE, +}; + +/* Address-Match values and masks (SSB_ADMATCHxxx) */ +#define SSB_ADM_TYPE 0x00000003 /* Address type */ +#define SSB_ADM_TYPE0 0 +#define SSB_ADM_TYPE1 1 +#define SSB_ADM_TYPE2 2 +#define SSB_ADM_AD64 0x00000004 +#define SSB_ADM_SZ0 0x000000F8 /* Type0 size */ +#define SSB_ADM_SZ0_SHIFT 3 +#define SSB_ADM_SZ1 0x000001F8 /* Type1 size */ +#define SSB_ADM_SZ1_SHIFT 3 +#define SSB_ADM_SZ2 0x000001F8 /* Type2 size */ +#define SSB_ADM_SZ2_SHIFT 3 +#define SSB_ADM_EN 0x00000400 /* Enable */ +#define SSB_ADM_NEG 0x00000800 /* Negative decode */ +#define SSB_ADM_BASE0 0xFFFFFF00 /* Type0 base address */ +#define SSB_ADM_BASE0_SHIFT 8 +#define SSB_ADM_BASE1 0xFFFFF000 /* Type1 base address for the core */ +#define SSB_ADM_BASE1_SHIFT 12 +#define SSB_ADM_BASE2 0xFFFF0000 /* Type2 base address for the core */ +#define SSB_ADM_BASE2_SHIFT 16 + + +#endif /* LINUX_SSB_REGS_H_ */ diff --git a/include/compat/linux/tracepoint.h b/include/compat/linux/tracepoint.h new file mode 100644 index 00000000..9f5add1c --- /dev/null +++ b/include/compat/linux/tracepoint.h @@ -0,0 +1,10 @@ +#ifndef _COMPAT_LINUX_TRACEPOINT_H +#define _COMPAT_LINUX_TRACEPOINT_H 1 + +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)) +#include_next +#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)) */ + +#endif /* _COMPAT_LINUX_TRACEPOINT_H */ diff --git a/include/compat/linux/unaligned/access_ok.h b/include/compat/linux/unaligned/access_ok.h new file mode 100644 index 00000000..99c1b4d2 --- /dev/null +++ b/include/compat/linux/unaligned/access_ok.h @@ -0,0 +1,67 @@ +#ifndef _LINUX_UNALIGNED_ACCESS_OK_H +#define _LINUX_UNALIGNED_ACCESS_OK_H + +#include +#include + +static inline u16 get_unaligned_le16(const void *p) +{ + return le16_to_cpup((__le16 *)p); +} + +static inline u32 get_unaligned_le32(const void *p) +{ + return le32_to_cpup((__le32 *)p); +} + +static inline u64 get_unaligned_le64(const void *p) +{ + return le64_to_cpup((__le64 *)p); +} + +static inline u16 get_unaligned_be16(const void *p) +{ + return be16_to_cpup((__be16 *)p); +} + +static inline u32 get_unaligned_be32(const void *p) +{ + return be32_to_cpup((__be32 *)p); +} + +static inline u64 get_unaligned_be64(const void *p) +{ + return be64_to_cpup((__be64 *)p); +} + +static inline void put_unaligned_le16(u16 val, void *p) +{ + *((__le16 *)p) = cpu_to_le16(val); +} + +static inline void put_unaligned_le32(u32 val, void *p) +{ + *((__le32 *)p) = cpu_to_le32(val); +} + +static inline void put_unaligned_le64(u64 val, void *p) +{ + *((__le64 *)p) = cpu_to_le64(val); +} + +static inline void put_unaligned_be16(u16 val, void *p) +{ + *((__be16 *)p) = cpu_to_be16(val); +} + +static inline void put_unaligned_be32(u32 val, void *p) +{ + *((__be32 *)p) = cpu_to_be32(val); +} + +static inline void put_unaligned_be64(u64 val, void *p) +{ + *((__be64 *)p) = cpu_to_be64(val); +} + +#endif /* _LINUX_UNALIGNED_ACCESS_OK_H */ diff --git a/include/compat/linux/unaligned/be_byteshift.h b/include/compat/linux/unaligned/be_byteshift.h new file mode 100644 index 00000000..9356b242 --- /dev/null +++ b/include/compat/linux/unaligned/be_byteshift.h @@ -0,0 +1,70 @@ +#ifndef _LINUX_UNALIGNED_BE_BYTESHIFT_H +#define _LINUX_UNALIGNED_BE_BYTESHIFT_H + +#include + +static inline u16 __get_unaligned_be16(const u8 *p) +{ + return p[0] << 8 | p[1]; +} + +static inline u32 __get_unaligned_be32(const u8 *p) +{ + return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +} + +static inline u64 __get_unaligned_be64(const u8 *p) +{ + return (u64)__get_unaligned_be32(p) << 32 | + __get_unaligned_be32(p + 4); +} + +static inline void __put_unaligned_be16(u16 val, u8 *p) +{ + *p++ = val >> 8; + *p++ = val; +} + +static inline void __put_unaligned_be32(u32 val, u8 *p) +{ + __put_unaligned_be16(val >> 16, p); + __put_unaligned_be16(val, p + 2); +} + +static inline void __put_unaligned_be64(u64 val, u8 *p) +{ + __put_unaligned_be32(val >> 32, p); + __put_unaligned_be32(val, p + 4); +} + +static inline u16 get_unaligned_be16(const void *p) +{ + return __get_unaligned_be16((const u8 *)p); +} + +static inline u32 get_unaligned_be32(const void *p) +{ + return __get_unaligned_be32((const u8 *)p); +} + +static inline u64 get_unaligned_be64(const void *p) +{ + return __get_unaligned_be64((const u8 *)p); +} + +static inline void put_unaligned_be16(u16 val, void *p) +{ + __put_unaligned_be16(val, p); +} + +static inline void put_unaligned_be32(u32 val, void *p) +{ + __put_unaligned_be32(val, p); +} + +static inline void put_unaligned_be64(u64 val, void *p) +{ + __put_unaligned_be64(val, p); +} + +#endif /* _LINUX_UNALIGNED_BE_BYTESHIFT_H */ diff --git a/include/compat/linux/unaligned/be_memmove.h b/include/compat/linux/unaligned/be_memmove.h new file mode 100644 index 00000000..c2a76c5c --- /dev/null +++ b/include/compat/linux/unaligned/be_memmove.h @@ -0,0 +1,36 @@ +#ifndef _LINUX_UNALIGNED_BE_MEMMOVE_H +#define _LINUX_UNALIGNED_BE_MEMMOVE_H + +#include + +static inline u16 get_unaligned_be16(const void *p) +{ + return __get_unaligned_memmove16((const u8 *)p); +} + +static inline u32 get_unaligned_be32(const void *p) +{ + return __get_unaligned_memmove32((const u8 *)p); +} + +static inline u64 get_unaligned_be64(const void *p) +{ + return __get_unaligned_memmove64((const u8 *)p); +} + +static inline void put_unaligned_be16(u16 val, void *p) +{ + __put_unaligned_memmove16(val, p); +} + +static inline void put_unaligned_be32(u32 val, void *p) +{ + __put_unaligned_memmove32(val, p); +} + +static inline void put_unaligned_be64(u64 val, void *p) +{ + __put_unaligned_memmove64(val, p); +} + +#endif /* _LINUX_UNALIGNED_LE_MEMMOVE_H */ diff --git a/include/compat/linux/unaligned/be_struct.h b/include/compat/linux/unaligned/be_struct.h new file mode 100644 index 00000000..13241583 --- /dev/null +++ b/include/compat/linux/unaligned/be_struct.h @@ -0,0 +1,36 @@ +#ifndef _LINUX_UNALIGNED_BE_STRUCT_H +#define _LINUX_UNALIGNED_BE_STRUCT_H + +#include + +static inline u16 get_unaligned_be16(const void *p) +{ + return __get_unaligned_cpu16((const u8 *)p); +} + +static inline u32 get_unaligned_be32(const void *p) +{ + return __get_unaligned_cpu32((const u8 *)p); +} + +static inline u64 get_unaligned_be64(const void *p) +{ + return __get_unaligned_cpu64((const u8 *)p); +} + +static inline void put_unaligned_be16(u16 val, void *p) +{ + __put_unaligned_cpu16(val, p); +} + +static inline void put_unaligned_be32(u32 val, void *p) +{ + __put_unaligned_cpu32(val, p); +} + +static inline void put_unaligned_be64(u64 val, void *p) +{ + __put_unaligned_cpu64(val, p); +} + +#endif /* _LINUX_UNALIGNED_BE_STRUCT_H */ diff --git a/include/compat/linux/unaligned/generic.h b/include/compat/linux/unaligned/generic.h new file mode 100644 index 00000000..02d97ff3 --- /dev/null +++ b/include/compat/linux/unaligned/generic.h @@ -0,0 +1,68 @@ +#ifndef _LINUX_UNALIGNED_GENERIC_H +#define _LINUX_UNALIGNED_GENERIC_H + +/* + * Cause a link-time error if we try an unaligned access other than + * 1,2,4 or 8 bytes long + */ +extern void __bad_unaligned_access_size(void); + +#define __get_unaligned_le(ptr) ((__force typeof(*(ptr)))({ \ + __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr), \ + __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_le16((ptr)), \ + __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_le32((ptr)), \ + __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_le64((ptr)), \ + __bad_unaligned_access_size())))); \ + })) + +#define __get_unaligned_be(ptr) ((__force typeof(*(ptr)))({ \ + __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr), \ + __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_be16((ptr)), \ + __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_be32((ptr)), \ + __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_be64((ptr)), \ + __bad_unaligned_access_size())))); \ + })) + +#define __put_unaligned_le(val, ptr) ({ \ + void *__gu_p = (ptr); \ + switch (sizeof(*(ptr))) { \ + case 1: \ + *(u8 *)__gu_p = (__force u8)(val); \ + break; \ + case 2: \ + put_unaligned_le16((__force u16)(val), __gu_p); \ + break; \ + case 4: \ + put_unaligned_le32((__force u32)(val), __gu_p); \ + break; \ + case 8: \ + put_unaligned_le64((__force u64)(val), __gu_p); \ + break; \ + default: \ + __bad_unaligned_access_size(); \ + break; \ + } \ + (void)0; }) + +#define __put_unaligned_be(val, ptr) ({ \ + void *__gu_p = (ptr); \ + switch (sizeof(*(ptr))) { \ + case 1: \ + *(u8 *)__gu_p = (__force u8)(val); \ + break; \ + case 2: \ + put_unaligned_be16((__force u16)(val), __gu_p); \ + break; \ + case 4: \ + put_unaligned_be32((__force u32)(val), __gu_p); \ + break; \ + case 8: \ + put_unaligned_be64((__force u64)(val), __gu_p); \ + break; \ + default: \ + __bad_unaligned_access_size(); \ + break; \ + } \ + (void)0; }) + +#endif /* _LINUX_UNALIGNED_GENERIC_H */ diff --git a/include/compat/linux/unaligned/le_byteshift.h b/include/compat/linux/unaligned/le_byteshift.h new file mode 100644 index 00000000..be376fb7 --- /dev/null +++ b/include/compat/linux/unaligned/le_byteshift.h @@ -0,0 +1,70 @@ +#ifndef _LINUX_UNALIGNED_LE_BYTESHIFT_H +#define _LINUX_UNALIGNED_LE_BYTESHIFT_H + +#include + +static inline u16 __get_unaligned_le16(const u8 *p) +{ + return p[0] | p[1] << 8; +} + +static inline u32 __get_unaligned_le32(const u8 *p) +{ + return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; +} + +static inline u64 __get_unaligned_le64(const u8 *p) +{ + return (u64)__get_unaligned_le32(p + 4) << 32 | + __get_unaligned_le32(p); +} + +static inline void __put_unaligned_le16(u16 val, u8 *p) +{ + *p++ = val; + *p++ = val >> 8; +} + +static inline void __put_unaligned_le32(u32 val, u8 *p) +{ + __put_unaligned_le16(val >> 16, p + 2); + __put_unaligned_le16(val, p); +} + +static inline void __put_unaligned_le64(u64 val, u8 *p) +{ + __put_unaligned_le32(val >> 32, p + 4); + __put_unaligned_le32(val, p); +} + +static inline u16 get_unaligned_le16(const void *p) +{ + return __get_unaligned_le16((const u8 *)p); +} + +static inline u32 get_unaligned_le32(const void *p) +{ + return __get_unaligned_le32((const u8 *)p); +} + +static inline u64 get_unaligned_le64(const void *p) +{ + return __get_unaligned_le64((const u8 *)p); +} + +static inline void put_unaligned_le16(u16 val, void *p) +{ + __put_unaligned_le16(val, p); +} + +static inline void put_unaligned_le32(u32 val, void *p) +{ + __put_unaligned_le32(val, p); +} + +static inline void put_unaligned_le64(u64 val, void *p) +{ + __put_unaligned_le64(val, p); +} + +#endif /* _LINUX_UNALIGNED_LE_BYTESHIFT_H */ diff --git a/include/compat/linux/unaligned/le_memmove.h b/include/compat/linux/unaligned/le_memmove.h new file mode 100644 index 00000000..269849be --- /dev/null +++ b/include/compat/linux/unaligned/le_memmove.h @@ -0,0 +1,36 @@ +#ifndef _LINUX_UNALIGNED_LE_MEMMOVE_H +#define _LINUX_UNALIGNED_LE_MEMMOVE_H + +#include + +static inline u16 get_unaligned_le16(const void *p) +{ + return __get_unaligned_memmove16((const u8 *)p); +} + +static inline u32 get_unaligned_le32(const void *p) +{ + return __get_unaligned_memmove32((const u8 *)p); +} + +static inline u64 get_unaligned_le64(const void *p) +{ + return __get_unaligned_memmove64((const u8 *)p); +} + +static inline void put_unaligned_le16(u16 val, void *p) +{ + __put_unaligned_memmove16(val, p); +} + +static inline void put_unaligned_le32(u32 val, void *p) +{ + __put_unaligned_memmove32(val, p); +} + +static inline void put_unaligned_le64(u64 val, void *p) +{ + __put_unaligned_memmove64(val, p); +} + +#endif /* _LINUX_UNALIGNED_LE_MEMMOVE_H */ diff --git a/include/compat/linux/unaligned/le_struct.h b/include/compat/linux/unaligned/le_struct.h new file mode 100644 index 00000000..088c4572 --- /dev/null +++ b/include/compat/linux/unaligned/le_struct.h @@ -0,0 +1,36 @@ +#ifndef _LINUX_UNALIGNED_LE_STRUCT_H +#define _LINUX_UNALIGNED_LE_STRUCT_H + +#include + +static inline u16 get_unaligned_le16(const void *p) +{ + return __get_unaligned_cpu16((const u8 *)p); +} + +static inline u32 get_unaligned_le32(const void *p) +{ + return __get_unaligned_cpu32((const u8 *)p); +} + +static inline u64 get_unaligned_le64(const void *p) +{ + return __get_unaligned_cpu64((const u8 *)p); +} + +static inline void put_unaligned_le16(u16 val, void *p) +{ + __put_unaligned_cpu16(val, p); +} + +static inline void put_unaligned_le32(u32 val, void *p) +{ + __put_unaligned_cpu32(val, p); +} + +static inline void put_unaligned_le64(u64 val, void *p) +{ + __put_unaligned_cpu64(val, p); +} + +#endif /* _LINUX_UNALIGNED_LE_STRUCT_H */ diff --git a/include/compat/linux/unaligned/memmove.h b/include/compat/linux/unaligned/memmove.h new file mode 100644 index 00000000..eeb5a779 --- /dev/null +++ b/include/compat/linux/unaligned/memmove.h @@ -0,0 +1,45 @@ +#ifndef _LINUX_UNALIGNED_MEMMOVE_H +#define _LINUX_UNALIGNED_MEMMOVE_H + +#include +#include + +/* Use memmove here, so gcc does not insert a __builtin_memcpy. */ + +static inline u16 __get_unaligned_memmove16(const void *p) +{ + u16 tmp; + memmove(&tmp, p, 2); + return tmp; +} + +static inline u32 __get_unaligned_memmove32(const void *p) +{ + u32 tmp; + memmove(&tmp, p, 4); + return tmp; +} + +static inline u64 __get_unaligned_memmove64(const void *p) +{ + u64 tmp; + memmove(&tmp, p, 8); + return tmp; +} + +static inline void __put_unaligned_memmove16(u16 val, void *p) +{ + memmove(p, &val, 2); +} + +static inline void __put_unaligned_memmove32(u32 val, void *p) +{ + memmove(p, &val, 4); +} + +static inline void __put_unaligned_memmove64(u64 val, void *p) +{ + memmove(p, &val, 8); +} + +#endif /* _LINUX_UNALIGNED_MEMMOVE_H */ diff --git a/include/compat/linux/unaligned/packed_struct.h b/include/compat/linux/unaligned/packed_struct.h new file mode 100644 index 00000000..2498bb9f --- /dev/null +++ b/include/compat/linux/unaligned/packed_struct.h @@ -0,0 +1,46 @@ +#ifndef _LINUX_UNALIGNED_PACKED_STRUCT_H +#define _LINUX_UNALIGNED_PACKED_STRUCT_H + +#include + +struct __una_u16 { u16 x __attribute__((packed)); }; +struct __una_u32 { u32 x __attribute__((packed)); }; +struct __una_u64 { u64 x __attribute__((packed)); }; + +static inline u16 __get_unaligned_cpu16(const void *p) +{ + const struct __una_u16 *ptr = (const struct __una_u16 *)p; + return ptr->x; +} + +static inline u32 __get_unaligned_cpu32(const void *p) +{ + const struct __una_u32 *ptr = (const struct __una_u32 *)p; + return ptr->x; +} + +static inline u64 __get_unaligned_cpu64(const void *p) +{ + const struct __una_u64 *ptr = (const struct __una_u64 *)p; + return ptr->x; +} + +static inline void __put_unaligned_cpu16(u16 val, void *p) +{ + struct __una_u16 *ptr = (struct __una_u16 *)p; + ptr->x = val; +} + +static inline void __put_unaligned_cpu32(u32 val, void *p) +{ + struct __una_u32 *ptr = (struct __una_u32 *)p; + ptr->x = val; +} + +static inline void __put_unaligned_cpu64(u64 val, void *p) +{ + struct __una_u64 *ptr = (struct __una_u64 *)p; + ptr->x = val; +} + +#endif /* _LINUX_UNALIGNED_PACKED_STRUCT_H */ diff --git a/include/compat/linux/usb/rndis_host.h b/include/compat/linux/usb/rndis_host.h new file mode 100644 index 00000000..88fceb71 --- /dev/null +++ b/include/compat/linux/usb/rndis_host.h @@ -0,0 +1,272 @@ +/* + * Host Side support for RNDIS Networking Links + * Copyright (C) 2005 by David Brownell + * + * 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_USB_RNDIS_HOST_H +#define __LINUX_USB_RNDIS_HOST_H + +/* + * CONTROL uses CDC "encapsulated commands" with funky notifications. + * - control-out: SEND_ENCAPSULATED + * - interrupt-in: RESPONSE_AVAILABLE + * - control-in: GET_ENCAPSULATED + * + * We'll try to ignore the RESPONSE_AVAILABLE notifications. + * + * REVISIT some RNDIS implementations seem to have curious issues still + * to be resolved. + */ +struct rndis_msg_hdr { + __le32 msg_type; /* RNDIS_MSG_* */ + __le32 msg_len; + /* followed by data that varies between messages */ + __le32 request_id; + __le32 status; + /* ... and more */ +} __attribute__ ((packed)); + +/* MS-Windows uses this strange size, but RNDIS spec says 1024 minimum */ +#define CONTROL_BUFFER_SIZE 1025 + +/* RNDIS defines an (absurdly huge) 10 second control timeout, + * but ActiveSync seems to use a more usual 5 second timeout + * (which matches the USB 2.0 spec). + */ +#define RNDIS_CONTROL_TIMEOUT_MS (5 * 1000) + +#define RNDIS_MSG_COMPLETION cpu_to_le32(0x80000000) + +/* codes for "msg_type" field of rndis messages; + * only the data channel uses packet messages (maybe batched); + * everything else goes on the control channel. + */ +#define RNDIS_MSG_PACKET cpu_to_le32(0x00000001) /* 1-N packets */ +#define RNDIS_MSG_INIT cpu_to_le32(0x00000002) +#define RNDIS_MSG_INIT_C (RNDIS_MSG_INIT|RNDIS_MSG_COMPLETION) +#define RNDIS_MSG_HALT cpu_to_le32(0x00000003) +#define RNDIS_MSG_QUERY cpu_to_le32(0x00000004) +#define RNDIS_MSG_QUERY_C (RNDIS_MSG_QUERY|RNDIS_MSG_COMPLETION) +#define RNDIS_MSG_SET cpu_to_le32(0x00000005) +#define RNDIS_MSG_SET_C (RNDIS_MSG_SET|RNDIS_MSG_COMPLETION) +#define RNDIS_MSG_RESET cpu_to_le32(0x00000006) +#define RNDIS_MSG_RESET_C (RNDIS_MSG_RESET|RNDIS_MSG_COMPLETION) +#define RNDIS_MSG_INDICATE cpu_to_le32(0x00000007) +#define RNDIS_MSG_KEEPALIVE cpu_to_le32(0x00000008) +#define RNDIS_MSG_KEEPALIVE_C (RNDIS_MSG_KEEPALIVE|RNDIS_MSG_COMPLETION) + +/* codes for "status" field of completion messages */ +#define RNDIS_STATUS_SUCCESS cpu_to_le32(0x00000000) +#define RNDIS_STATUS_FAILURE cpu_to_le32(0xc0000001) +#define RNDIS_STATUS_INVALID_DATA cpu_to_le32(0xc0010015) +#define RNDIS_STATUS_NOT_SUPPORTED cpu_to_le32(0xc00000bb) +#define RNDIS_STATUS_MEDIA_CONNECT cpu_to_le32(0x4001000b) +#define RNDIS_STATUS_MEDIA_DISCONNECT cpu_to_le32(0x4001000c) +#define RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION cpu_to_le32(0x40010012) + +/* codes for OID_GEN_PHYSICAL_MEDIUM */ +#define RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED cpu_to_le32(0x00000000) +#define RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN cpu_to_le32(0x00000001) +#define RNDIS_PHYSICAL_MEDIUM_CABLE_MODEM cpu_to_le32(0x00000002) +#define RNDIS_PHYSICAL_MEDIUM_PHONE_LINE cpu_to_le32(0x00000003) +#define RNDIS_PHYSICAL_MEDIUM_POWER_LINE cpu_to_le32(0x00000004) +#define RNDIS_PHYSICAL_MEDIUM_DSL cpu_to_le32(0x00000005) +#define RNDIS_PHYSICAL_MEDIUM_FIBRE_CHANNEL cpu_to_le32(0x00000006) +#define RNDIS_PHYSICAL_MEDIUM_1394 cpu_to_le32(0x00000007) +#define RNDIS_PHYSICAL_MEDIUM_WIRELESS_WAN cpu_to_le32(0x00000008) +#define RNDIS_PHYSICAL_MEDIUM_MAX cpu_to_le32(0x00000009) + +struct rndis_data_hdr { + __le32 msg_type; /* RNDIS_MSG_PACKET */ + __le32 msg_len; /* rndis_data_hdr + data_len + pad */ + __le32 data_offset; /* 36 -- right after header */ + __le32 data_len; /* ... real packet size */ + + __le32 oob_data_offset; /* zero */ + __le32 oob_data_len; /* zero */ + __le32 num_oob; /* zero */ + __le32 packet_data_offset; /* zero */ + + __le32 packet_data_len; /* zero */ + __le32 vc_handle; /* zero */ + __le32 reserved; /* zero */ +} __attribute__ ((packed)); + +struct rndis_init { /* OUT */ + /* header and: */ + __le32 msg_type; /* RNDIS_MSG_INIT */ + __le32 msg_len; /* 24 */ + __le32 request_id; + __le32 major_version; /* of rndis (1.0) */ + __le32 minor_version; + __le32 max_transfer_size; +} __attribute__ ((packed)); + +struct rndis_init_c { /* IN */ + /* header and: */ + __le32 msg_type; /* RNDIS_MSG_INIT_C */ + __le32 msg_len; + __le32 request_id; + __le32 status; + __le32 major_version; /* of rndis (1.0) */ + __le32 minor_version; + __le32 device_flags; + __le32 medium; /* zero == 802.3 */ + __le32 max_packets_per_message; + __le32 max_transfer_size; + __le32 packet_alignment; /* max 7; (1< + * Copyright (C) 2003-2005 David Hollis + * + * 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_USB_USBNET_H +#define __LINUX_USB_USBNET_H + +/* interface from usbnet core to each USB networking link we handle */ +struct usbnet { + /* housekeeping */ + struct usb_device *udev; + struct usb_interface *intf; + struct driver_info *driver_info; + const char *driver_name; + void *driver_priv; + wait_queue_head_t *wait; + struct mutex phy_mutex; + unsigned char suspend_count; + + /* i/o info: pipes etc */ + unsigned in, out; + struct usb_host_endpoint *status; + unsigned maxpacket; + struct timer_list delay; + + /* protocol/interface state */ + struct net_device *net; + int msg_enable; + unsigned long data[5]; + u32 xid; + u32 hard_mtu; /* count any extra framing */ + size_t rx_urb_size; /* size for rx urbs */ + struct mii_if_info mii; + + /* various kinds of pending driver work */ + struct sk_buff_head rxq; + struct sk_buff_head txq; + struct sk_buff_head done; + struct sk_buff_head rxq_pause; + struct urb *interrupt; + struct usb_anchor deferred; + struct tasklet_struct bh; + + struct work_struct kevent; + unsigned long flags; +# define EVENT_TX_HALT 0 +# define EVENT_RX_HALT 1 +# define EVENT_RX_MEMORY 2 +# define EVENT_STS_SPLIT 3 +# define EVENT_LINK_RESET 4 +# define EVENT_RX_PAUSED 5 +# define EVENT_DEV_WAKING 6 +# define EVENT_DEV_ASLEEP 7 +# define EVENT_DEV_OPEN 8 +}; + +static inline struct usb_driver *driver_of(struct usb_interface *intf) +{ + return to_usb_driver(intf->dev.driver); +} + +/* interface from the device/framing level "minidriver" to core */ +struct driver_info { + char *description; + + int flags; +/* framing is CDC Ethernet, not writing ZLPs (hw issues), or optionally: */ +#define FLAG_FRAMING_NC 0x0001 /* guard against device dropouts */ +#define FLAG_FRAMING_GL 0x0002 /* genelink batches packets */ +#define FLAG_FRAMING_Z 0x0004 /* zaurus adds a trailer */ +#define FLAG_FRAMING_RN 0x0008 /* RNDIS batches, plus huge header */ + +#define FLAG_NO_SETINT 0x0010 /* device can't set_interface() */ +#define FLAG_ETHER 0x0020 /* maybe use "eth%d" names */ + +#define FLAG_FRAMING_AX 0x0040 /* AX88772/178 packets */ +#define FLAG_WLAN 0x0080 /* use "wlan%d" names */ +#define FLAG_AVOID_UNLINK_URBS 0x0100 /* don't unlink urbs at usbnet_stop() */ +#define FLAG_SEND_ZLP 0x0200 /* hw requires ZLPs are sent */ +#define FLAG_WWAN 0x0400 /* use "wwan%d" names */ + +#define FLAG_LINK_INTR 0x0800 /* updates link (carrier) status */ + +#define FLAG_POINTTOPOINT 0x1000 /* possibly use "usb%d" names */ + +/* + * Indicates to usbnet, that USB driver accumulates multiple IP packets. + * Affects statistic (counters) and short packet handling. + */ +#define FLAG_MULTI_PACKET 0x2000 +#define FLAG_RX_ASSEMBLE 0x4000 /* rx packets may span >1 frames */ + + /* init device ... can sleep, or cause probe() failure */ + int (*bind)(struct usbnet *, struct usb_interface *); + + /* cleanup device ... can sleep, but can't fail */ + void (*unbind)(struct usbnet *, struct usb_interface *); + + /* reset device ... can sleep */ + int (*reset)(struct usbnet *); + + /* stop device ... can sleep */ + int (*stop)(struct usbnet *); + + /* see if peer is connected ... can sleep */ + int (*check_connect)(struct usbnet *); + + /* (dis)activate runtime power management */ + int (*manage_power)(struct usbnet *, int); + + /* for status polling */ + void (*status)(struct usbnet *, struct urb *); + + /* link reset handling, called from defer_kevent */ + int (*link_reset)(struct usbnet *); + + /* fixup rx packet (strip framing) */ + int (*rx_fixup)(struct usbnet *dev, struct sk_buff *skb); + + /* fixup tx packet (add framing) */ + struct sk_buff *(*tx_fixup)(struct usbnet *dev, + struct sk_buff *skb, gfp_t flags); + + /* early initialization code, can sleep. This is for minidrivers + * having 'subminidrivers' that need to do extra initialization + * right after minidriver have initialized hardware. */ + int (*early_init)(struct usbnet *dev); + + /* called by minidriver when receiving indication */ + void (*indication)(struct usbnet *dev, void *ind, int indlen); + + /* for new devices, use the descriptor-reading code instead */ + int in; /* rx endpoint */ + int out; /* tx endpoint */ + + unsigned long data; /* Misc driver specific data */ +}; + +/* Minidrivers are just drivers using the "usbnet" core as a powerful + * network-specific subroutine library ... that happens to do pretty + * much everything except custom framing and chip-specific stuff. + */ +extern int usbnet_probe(struct usb_interface *, const struct usb_device_id *); +extern int usbnet_suspend(struct usb_interface *, pm_message_t); +extern int usbnet_resume(struct usb_interface *); +extern void usbnet_disconnect(struct usb_interface *); + + +/* Drivers that reuse some of the standard USB CDC infrastructure + * (notably, using multiple interfaces according to the CDC + * union descriptor) get some helper code. + */ +struct cdc_state { + struct usb_cdc_header_desc *header; + struct usb_cdc_union_desc *u; + struct usb_cdc_ether_desc *ether; + struct usb_interface *control; + struct usb_interface *data; +}; + +extern int usbnet_generic_cdc_bind(struct usbnet *, struct usb_interface *); +extern int usbnet_cdc_bind(struct usbnet *, struct usb_interface *); +extern void usbnet_cdc_unbind(struct usbnet *, struct usb_interface *); +extern void usbnet_cdc_status(struct usbnet *, struct urb *); + +/* CDC and RNDIS support the same host-chosen packet filters for IN transfers */ +#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ + |USB_CDC_PACKET_TYPE_ALL_MULTICAST \ + |USB_CDC_PACKET_TYPE_PROMISCUOUS \ + |USB_CDC_PACKET_TYPE_DIRECTED) + + +/* we record the state for each of our queued skbs */ +enum skb_state { + illegal = 0, + tx_start, tx_done, + rx_start, rx_done, rx_cleanup +}; + +struct skb_data { /* skb->cb is one of these */ + struct urb *urb; + struct usbnet *dev; + enum skb_state state; + size_t length; +}; + +extern int usbnet_open(struct net_device *net); +extern int usbnet_stop(struct net_device *net); +extern netdev_tx_t usbnet_start_xmit(struct sk_buff *skb, + struct net_device *net); +extern void usbnet_tx_timeout(struct net_device *net); +extern int usbnet_change_mtu(struct net_device *net, int new_mtu); + +extern int usbnet_get_endpoints(struct usbnet *, struct usb_interface *); +extern int usbnet_get_ethernet_addr(struct usbnet *, int); +extern void usbnet_defer_kevent(struct usbnet *, int); +extern void usbnet_skb_return(struct usbnet *, struct sk_buff *); +extern void usbnet_unlink_rx_urbs(struct usbnet *); + +extern void usbnet_pause_rx(struct usbnet *); +extern void usbnet_resume_rx(struct usbnet *); +extern void usbnet_purge_paused_rxq(struct usbnet *); + +extern int usbnet_get_settings(struct net_device *net, + struct ethtool_cmd *cmd); +extern int usbnet_set_settings(struct net_device *net, + struct ethtool_cmd *cmd); +extern u32 usbnet_get_link(struct net_device *net); +extern u32 usbnet_get_msglevel(struct net_device *); +extern void usbnet_set_msglevel(struct net_device *, u32); +extern void usbnet_get_drvinfo(struct net_device *, struct ethtool_drvinfo *); +extern int usbnet_nway_reset(struct net_device *net); + +#endif /* __LINUX_USB_USBNET_H */ diff --git a/include/compat/linux/wireless.h b/include/compat/linux/wireless.h new file mode 100644 index 00000000..4395b28b --- /dev/null +++ b/include/compat/linux/wireless.h @@ -0,0 +1,1162 @@ +/* + * This file define a set of standard wireless extensions + * + * Version : 22 16.3.07 + * + * Authors : Jean Tourrilhes - HPL - + * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. + */ + +#ifndef _LINUX_WIRELESS_H +#define _LINUX_WIRELESS_H + +/************************** DOCUMENTATION **************************/ +/* + * Initial APIs (1996 -> onward) : + * ----------------------------- + * Basically, the wireless extensions are for now a set of standard ioctl + * call + /proc/net/wireless + * + * The entry /proc/net/wireless give statistics and information on the + * driver. + * This is better than having each driver having its entry because + * its centralised and we may remove the driver module safely. + * + * Ioctl are used to configure the driver and issue commands. This is + * better than command line options of insmod because we may want to + * change dynamically (while the driver is running) some parameters. + * + * The ioctl mechanimsm are copied from standard devices ioctl. + * We have the list of command plus a structure descibing the + * data exchanged... + * Note that to add these ioctl, I was obliged to modify : + * # net/core/dev.c (two place + add include) + * # net/ipv4/af_inet.c (one place + add include) + * + * /proc/net/wireless is a copy of /proc/net/dev. + * We have a structure for data passed from the driver to /proc/net/wireless + * Too add this, I've modified : + * # net/core/dev.c (two other places) + * # include/linux/netdevice.h (one place) + * # include/linux/proc_fs.h (one place) + * + * New driver API (2002 -> onward) : + * ------------------------------- + * This file is only concerned with the user space API and common definitions. + * The new driver API is defined and documented in : + * # include/net/iw_handler.h + * + * Note as well that /proc/net/wireless implementation has now moved in : + * # net/core/wireless.c + * + * Wireless Events (2002 -> onward) : + * -------------------------------- + * Events are defined at the end of this file, and implemented in : + * # net/core/wireless.c + * + * Other comments : + * -------------- + * Do not add here things that are redundant with other mechanisms + * (drivers init, ifconfig, /proc/net/dev, ...) and with are not + * wireless specific. + * + * These wireless extensions are not magic : each driver has to provide + * support for them... + * + * IMPORTANT NOTE : As everything in the kernel, this is very much a + * work in progress. Contact me if you have ideas of improvements... + */ + +/***************************** INCLUDES *****************************/ + +#include /* for __u* and __s* typedefs */ +#include /* for "struct sockaddr" et al */ +#include /* for IFNAMSIZ and co... */ + +/***************************** VERSION *****************************/ +/* + * This constant is used to know the availability of the wireless + * extensions and to know which version of wireless extensions it is + * (there is some stuff that will be added in the future...) + * I just plan to increment with each new version. + */ +#define WIRELESS_EXT 22 + +/* + * Changes : + * + * V2 to V3 + * -------- + * Alan Cox start some incompatibles changes. I've integrated a bit more. + * - Encryption renamed to Encode to avoid US regulation problems + * - Frequency changed from float to struct to avoid problems on old 386 + * + * V3 to V4 + * -------- + * - Add sensitivity + * + * V4 to V5 + * -------- + * - Missing encoding definitions in range + * - Access points stuff + * + * V5 to V6 + * -------- + * - 802.11 support (ESSID ioctls) + * + * V6 to V7 + * -------- + * - define IW_ESSID_MAX_SIZE and IW_MAX_AP + * + * V7 to V8 + * -------- + * - Changed my e-mail address + * - More 802.11 support (nickname, rate, rts, frag) + * - List index in frequencies + * + * V8 to V9 + * -------- + * - Support for 'mode of operation' (ad-hoc, managed...) + * - Support for unicast and multicast power saving + * - Change encoding to support larger tokens (>64 bits) + * - Updated iw_params (disable, flags) and use it for NWID + * - Extracted iw_point from iwreq for clarity + * + * V9 to V10 + * --------- + * - Add PM capability to range structure + * - Add PM modifier : MAX/MIN/RELATIVE + * - Add encoding option : IW_ENCODE_NOKEY + * - Add TxPower ioctls (work like TxRate) + * + * V10 to V11 + * ---------- + * - Add WE version in range (help backward/forward compatibility) + * - Add retry ioctls (work like PM) + * + * V11 to V12 + * ---------- + * - Add SIOCSIWSTATS to get /proc/net/wireless programatically + * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space + * - Add new statistics (frag, retry, beacon) + * - Add average quality (for user space calibration) + * + * V12 to V13 + * ---------- + * - Document creation of new driver API. + * - Extract union iwreq_data from struct iwreq (for new driver API). + * - Rename SIOCSIWNAME as SIOCSIWCOMMIT + * + * V13 to V14 + * ---------- + * - Wireless Events support : define struct iw_event + * - Define additional specific event numbers + * - Add "addr" and "param" fields in union iwreq_data + * - AP scanning stuff (SIOCSIWSCAN and friends) + * + * V14 to V15 + * ---------- + * - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg + * - Make struct iw_freq signed (both m & e), add explicit padding + * - Add IWEVCUSTOM for driver specific event/scanning token + * - Add IW_MAX_GET_SPY for driver returning a lot of addresses + * - Add IW_TXPOW_RANGE for range of Tx Powers + * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points + * - Add IW_MODE_MONITOR for passive monitor + * + * V15 to V16 + * ---------- + * - Increase the number of bitrates in iw_range to 32 (for 802.11g) + * - Increase the number of frequencies in iw_range to 32 (for 802.11b+a) + * - Reshuffle struct iw_range for increases, add filler + * - Increase IW_MAX_AP to 64 for driver returning a lot of addresses + * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support + * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy" + * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index + * + * V16 to V17 + * ---------- + * - Add flags to frequency -> auto/fixed + * - Document (struct iw_quality *)->updated, add new flags (INVALID) + * - Wireless Event capability in struct iw_range + * - Add support for relative TxPower (yick !) + * + * V17 to V18 (From Jouni Malinen ) + * ---------- + * - Add support for WPA/WPA2 + * - Add extended encoding configuration (SIOCSIWENCODEEXT and + * SIOCGIWENCODEEXT) + * - Add SIOCSIWGENIE/SIOCGIWGENIE + * - Add SIOCSIWMLME + * - Add SIOCSIWPMKSA + * - Add struct iw_range bit field for supported encoding capabilities + * - Add optional scan request parameters for SIOCSIWSCAN + * - Add SIOCSIWAUTH/SIOCGIWAUTH for setting authentication and WPA + * related parameters (extensible up to 4096 parameter values) + * - Add wireless events: IWEVGENIE, IWEVMICHAELMICFAILURE, + * IWEVASSOCREQIE, IWEVASSOCRESPIE, IWEVPMKIDCAND + * + * V18 to V19 + * ---------- + * - Remove (struct iw_point *)->pointer from events and streams + * - Remove header includes to help user space + * - Increase IW_ENCODING_TOKEN_MAX from 32 to 64 + * - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros + * - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM + * - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros + * + * V19 to V20 + * ---------- + * - RtNetlink requests support (SET/GET) + * + * V20 to V21 + * ---------- + * - Remove (struct net_device *)->get_wireless_stats() + * - Change length in ESSID and NICK to strlen() instead of strlen()+1 + * - Add IW_RETRY_SHORT/IW_RETRY_LONG retry modifiers + * - Power/Retry relative values no longer * 100000 + * - Add explicit flag to tell stats are in 802.11k RCPI : IW_QUAL_RCPI + * + * V21 to V22 + * ---------- + * - Prevent leaking of kernel space in stream on 64 bits. + */ + +/**************************** CONSTANTS ****************************/ + +/* -------------------------- IOCTL LIST -------------------------- */ + +/* Wireless Identification */ +#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */ +#define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ +/* SIOCGIWNAME is used to verify the presence of Wireless Extensions. + * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"... + * Don't put the name of your driver there, it's useless. */ + +/* Basic operations */ +#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */ +#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */ +#define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */ +#define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */ +#define SIOCSIWMODE 0x8B06 /* set operation mode */ +#define SIOCGIWMODE 0x8B07 /* get operation mode */ +#define SIOCSIWSENS 0x8B08 /* set sensitivity (dBm) */ +#define SIOCGIWSENS 0x8B09 /* get sensitivity (dBm) */ + +/* Informative stuff */ +#define SIOCSIWRANGE 0x8B0A /* Unused */ +#define SIOCGIWRANGE 0x8B0B /* Get range of parameters */ +#define SIOCSIWPRIV 0x8B0C /* Unused */ +#define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */ +#define SIOCSIWSTATS 0x8B0E /* Unused */ +#define SIOCGIWSTATS 0x8B0F /* Get /proc/net/wireless stats */ +/* SIOCGIWSTATS is strictly used between user space and the kernel, and + * is never passed to the driver (i.e. the driver will never see it). */ + +/* Spy support (statistics per MAC address - used for Mobile IP support) */ +#define SIOCSIWSPY 0x8B10 /* set spy addresses */ +#define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */ +#define SIOCSIWTHRSPY 0x8B12 /* set spy threshold (spy event) */ +#define SIOCGIWTHRSPY 0x8B13 /* get spy threshold */ + +/* Access Point manipulation */ +#define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ +#define SIOCGIWAP 0x8B15 /* get access point MAC addresses */ +#define SIOCGIWAPLIST 0x8B17 /* Deprecated in favor of scanning */ +#define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */ +#define SIOCGIWSCAN 0x8B19 /* get scanning results */ + +/* 802.11 specific support */ +#define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */ +#define SIOCGIWESSID 0x8B1B /* get ESSID */ +#define SIOCSIWNICKN 0x8B1C /* set node name/nickname */ +#define SIOCGIWNICKN 0x8B1D /* get node name/nickname */ +/* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit + * within the 'iwreq' structure, so we need to use the 'data' member to + * point to a string in user space, like it is done for RANGE... */ + +/* Other parameters useful in 802.11 and some other devices */ +#define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */ +#define SIOCGIWRATE 0x8B21 /* get default bit rate (bps) */ +#define SIOCSIWRTS 0x8B22 /* set RTS/CTS threshold (bytes) */ +#define SIOCGIWRTS 0x8B23 /* get RTS/CTS threshold (bytes) */ +#define SIOCSIWFRAG 0x8B24 /* set fragmentation thr (bytes) */ +#define SIOCGIWFRAG 0x8B25 /* get fragmentation thr (bytes) */ +#define SIOCSIWTXPOW 0x8B26 /* set transmit power (dBm) */ +#define SIOCGIWTXPOW 0x8B27 /* get transmit power (dBm) */ +#define SIOCSIWRETRY 0x8B28 /* set retry limits and lifetime */ +#define SIOCGIWRETRY 0x8B29 /* get retry limits and lifetime */ + +/* Encoding stuff (scrambling, hardware security, WEP...) */ +#define SIOCSIWENCODE 0x8B2A /* set encoding token & mode */ +#define SIOCGIWENCODE 0x8B2B /* get encoding token & mode */ +/* Power saving stuff (power management, unicast and multicast) */ +#define SIOCSIWPOWER 0x8B2C /* set Power Management settings */ +#define SIOCGIWPOWER 0x8B2D /* get Power Management settings */ + +/* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM). + * This ioctl uses struct iw_point and data buffer that includes IE id and len + * fields. More than one IE may be included in the request. Setting the generic + * IE to empty buffer (len=0) removes the generic IE from the driver. Drivers + * are allowed to generate their own WPA/RSN IEs, but in these cases, drivers + * are required to report the used IE as a wireless event, e.g., when + * associating with an AP. */ +#define SIOCSIWGENIE 0x8B30 /* set generic IE */ +#define SIOCGIWGENIE 0x8B31 /* get generic IE */ + +/* WPA : IEEE 802.11 MLME requests */ +#define SIOCSIWMLME 0x8B16 /* request MLME operation; uses + * struct iw_mlme */ +/* WPA : Authentication mode parameters */ +#define SIOCSIWAUTH 0x8B32 /* set authentication mode params */ +#define SIOCGIWAUTH 0x8B33 /* get authentication mode params */ + +/* WPA : Extended version of encoding configuration */ +#define SIOCSIWENCODEEXT 0x8B34 /* set encoding token & mode */ +#define SIOCGIWENCODEEXT 0x8B35 /* get encoding token & mode */ + +/* WPA2 : PMKSA cache management */ +#define SIOCSIWPMKSA 0x8B36 /* PMKSA cache operation */ + +/* -------------------- DEV PRIVATE IOCTL LIST -------------------- */ + +/* These 32 ioctl are wireless device private, for 16 commands. + * Each driver is free to use them for whatever purpose it chooses, + * however the driver *must* export the description of those ioctls + * with SIOCGIWPRIV and *must* use arguments as defined below. + * If you don't follow those rules, DaveM is going to hate you (reason : + * it make mixed 32/64bit operation impossible). + */ +#define SIOCIWFIRSTPRIV 0x8BE0 +#define SIOCIWLASTPRIV 0x8BFF +/* Previously, we were using SIOCDEVPRIVATE, but we now have our + * separate range because of collisions with other tools such as + * 'mii-tool'. + * We now have 32 commands, so a bit more space ;-). + * Also, all 'even' commands are only usable by root and don't return the + * content of ifr/iwr to user (but you are not obliged to use the set/get + * convention, just use every other two command). More details in iwpriv.c. + * And I repeat : you are not forced to use them with iwpriv, but you + * must be compliant with it. + */ + +/* ------------------------- IOCTL STUFF ------------------------- */ + +/* The first and the last (range) */ +#define SIOCIWFIRST 0x8B00 +#define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */ +#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) +#define IW_HANDLER(id, func) \ + [IW_IOCTL_IDX(id)] = func + +/* Odd : get (world access), even : set (root access) */ +#define IW_IS_SET(cmd) (!((cmd) & 0x1)) +#define IW_IS_GET(cmd) ((cmd) & 0x1) + +/* ----------------------- WIRELESS EVENTS ----------------------- */ +/* Those are *NOT* ioctls, do not issue request on them !!! */ +/* Most events use the same identifier as ioctl requests */ + +#define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */ +#define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */ +#define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */ +#define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */ +#define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */ +#define IWEVGENIE 0x8C05 /* Generic IE (WPA, RSN, WMM, ..) + * (scan results); This includes id and + * length fields. One IWEVGENIE may + * contain more than one IE. Scan + * results may contain one or more + * IWEVGENIE events. */ +#define IWEVMICHAELMICFAILURE 0x8C06 /* Michael MIC failure + * (struct iw_michaelmicfailure) + */ +#define IWEVASSOCREQIE 0x8C07 /* IEs used in (Re)Association Request. + * The data includes id and length + * fields and may contain more than one + * IE. This event is required in + * Managed mode if the driver + * generates its own WPA/RSN IE. This + * should be sent just before + * IWEVREGISTERED event for the + * association. */ +#define IWEVASSOCRESPIE 0x8C08 /* IEs used in (Re)Association + * Response. The data includes id and + * length fields and may contain more + * than one IE. This may be sent + * between IWEVASSOCREQIE and + * IWEVREGISTERED events for the + * association. */ +#define IWEVPMKIDCAND 0x8C09 /* PMKID candidate for RSN + * pre-authentication + * (struct iw_pmkid_cand) */ + +#define IWEVFIRST 0x8C00 +#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST) + +/* ------------------------- PRIVATE INFO ------------------------- */ +/* + * The following is used with SIOCGIWPRIV. It allow a driver to define + * the interface (name, type of data) for its private ioctl. + * Privates ioctl are SIOCIWFIRSTPRIV -> SIOCIWLASTPRIV + */ + +#define IW_PRIV_TYPE_MASK 0x7000 /* Type of arguments */ +#define IW_PRIV_TYPE_NONE 0x0000 +#define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */ +#define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */ +#define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */ +#define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */ +#define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */ + +#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */ + +#define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */ + +/* + * Note : if the number of args is fixed and the size < 16 octets, + * instead of passing a pointer we will put args in the iwreq struct... + */ + +/* ----------------------- OTHER CONSTANTS ----------------------- */ + +/* Maximum frequencies in the range struct */ +#define IW_MAX_FREQUENCIES 32 +/* Note : if you have something like 80 frequencies, + * don't increase this constant and don't fill the frequency list. + * The user will be able to set by channel anyway... */ + +/* Maximum bit rates in the range struct */ +#define IW_MAX_BITRATES 32 + +/* Maximum tx powers in the range struct */ +#define IW_MAX_TXPOWER 8 +/* Note : if you more than 8 TXPowers, just set the max and min or + * a few of them in the struct iw_range. */ + +/* Maximum of address that you may set with SPY */ +#define IW_MAX_SPY 8 + +/* Maximum of address that you may get in the + list of access points in range */ +#define IW_MAX_AP 64 + +/* Maximum size of the ESSID and NICKN strings */ +#define IW_ESSID_MAX_SIZE 32 + +/* Modes of operation */ +#define IW_MODE_AUTO 0 /* Let the driver decides */ +#define IW_MODE_ADHOC 1 /* Single cell network */ +#define IW_MODE_INFRA 2 /* Multi cell network, roaming, ... */ +#define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */ +#define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */ +#define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ +#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ +#define IW_MODE_MESH 7 /* Mesh (IEEE 802.11s) network */ + +/* Statistics flags (bitmask in updated) */ +#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */ +#define IW_QUAL_LEVEL_UPDATED 0x02 +#define IW_QUAL_NOISE_UPDATED 0x04 +#define IW_QUAL_ALL_UPDATED 0x07 +#define IW_QUAL_DBM 0x08 /* Level + Noise are dBm */ +#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */ +#define IW_QUAL_LEVEL_INVALID 0x20 +#define IW_QUAL_NOISE_INVALID 0x40 +#define IW_QUAL_RCPI 0x80 /* Level + Noise are 802.11k RCPI */ +#define IW_QUAL_ALL_INVALID 0x70 + +/* Frequency flags */ +#define IW_FREQ_AUTO 0x00 /* Let the driver decides */ +#define IW_FREQ_FIXED 0x01 /* Force a specific value */ + +/* Maximum number of size of encoding token available + * they are listed in the range structure */ +#define IW_MAX_ENCODING_SIZES 8 + +/* Maximum size of the encoding token in bytes */ +#define IW_ENCODING_TOKEN_MAX 64 /* 512 bits (for now) */ + +/* Flags for encoding (along with the token) */ +#define IW_ENCODE_INDEX 0x00FF /* Token index (if needed) */ +#define IW_ENCODE_FLAGS 0xFF00 /* Flags defined below */ +#define IW_ENCODE_MODE 0xF000 /* Modes defined below */ +#define IW_ENCODE_DISABLED 0x8000 /* Encoding disabled */ +#define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */ +#define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */ +#define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */ +#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ +#define IW_ENCODE_TEMP 0x0400 /* Temporary key */ + +/* Power management flags available (along with the value, if any) */ +#define IW_POWER_ON 0x0000 /* No details... */ +#define IW_POWER_TYPE 0xF000 /* Type of parameter */ +#define IW_POWER_PERIOD 0x1000 /* Value is a period/duration of */ +#define IW_POWER_TIMEOUT 0x2000 /* Value is a timeout (to go asleep) */ +#define IW_POWER_MODE 0x0F00 /* Power Management mode */ +#define IW_POWER_UNICAST_R 0x0100 /* Receive only unicast messages */ +#define IW_POWER_MULTICAST_R 0x0200 /* Receive only multicast messages */ +#define IW_POWER_ALL_R 0x0300 /* Receive all messages though PM */ +#define IW_POWER_FORCE_S 0x0400 /* Force PM procedure for sending unicast */ +#define IW_POWER_REPEATER 0x0800 /* Repeat broadcast messages in PM period */ +#define IW_POWER_MODIFIER 0x000F /* Modify a parameter */ +#define IW_POWER_MIN 0x0001 /* Value is a minimum */ +#define IW_POWER_MAX 0x0002 /* Value is a maximum */ +#define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ + +/* Transmit Power flags available */ +#define IW_TXPOW_TYPE 0x00FF /* Type of value */ +#define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ +#define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ +#define IW_TXPOW_RELATIVE 0x0002 /* Value is in arbitrary units */ +#define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ + +/* Retry limits and lifetime flags available */ +#define IW_RETRY_ON 0x0000 /* No details... */ +#define IW_RETRY_TYPE 0xF000 /* Type of parameter */ +#define IW_RETRY_LIMIT 0x1000 /* Maximum number of retries*/ +#define IW_RETRY_LIFETIME 0x2000 /* Maximum duration of retries in us */ +#define IW_RETRY_MODIFIER 0x00FF /* Modify a parameter */ +#define IW_RETRY_MIN 0x0001 /* Value is a minimum */ +#define IW_RETRY_MAX 0x0002 /* Value is a maximum */ +#define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ +#define IW_RETRY_SHORT 0x0010 /* Value is for short packets */ +#define IW_RETRY_LONG 0x0020 /* Value is for long packets */ + +/* Scanning request flags */ +#define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */ +#define IW_SCAN_ALL_ESSID 0x0001 /* Scan all ESSIDs */ +#define IW_SCAN_THIS_ESSID 0x0002 /* Scan only this ESSID */ +#define IW_SCAN_ALL_FREQ 0x0004 /* Scan all Frequencies */ +#define IW_SCAN_THIS_FREQ 0x0008 /* Scan only this Frequency */ +#define IW_SCAN_ALL_MODE 0x0010 /* Scan all Modes */ +#define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */ +#define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */ +#define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */ +/* struct iw_scan_req scan_type */ +#define IW_SCAN_TYPE_ACTIVE 0 +#define IW_SCAN_TYPE_PASSIVE 1 +/* Maximum size of returned data */ +#define IW_SCAN_MAX_DATA 4096 /* In bytes */ + +/* Scan capability flags - in (struct iw_range *)->scan_capa */ +#define IW_SCAN_CAPA_NONE 0x00 +#define IW_SCAN_CAPA_ESSID 0x01 +#define IW_SCAN_CAPA_BSSID 0x02 +#define IW_SCAN_CAPA_CHANNEL 0x04 +#define IW_SCAN_CAPA_MODE 0x08 +#define IW_SCAN_CAPA_RATE 0x10 +#define IW_SCAN_CAPA_TYPE 0x20 +#define IW_SCAN_CAPA_TIME 0x40 + +/* Max number of char in custom event - use multiple of them if needed */ +#define IW_CUSTOM_MAX 256 /* In bytes */ + +/* Generic information element */ +#define IW_GENERIC_IE_MAX 1024 + +/* MLME requests (SIOCSIWMLME / struct iw_mlme) */ +#define IW_MLME_DEAUTH 0 +#define IW_MLME_DISASSOC 1 +#define IW_MLME_AUTH 2 +#define IW_MLME_ASSOC 3 + +/* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */ +#define IW_AUTH_INDEX 0x0FFF +#define IW_AUTH_FLAGS 0xF000 +/* SIOCSIWAUTH/SIOCGIWAUTH parameters (0 .. 4095) + * (IW_AUTH_INDEX mask in struct iw_param flags; this is the index of the + * parameter that is being set/get to; value will be read/written to + * struct iw_param value field) */ +#define IW_AUTH_WPA_VERSION 0 +#define IW_AUTH_CIPHER_PAIRWISE 1 +#define IW_AUTH_CIPHER_GROUP 2 +#define IW_AUTH_KEY_MGMT 3 +#define IW_AUTH_TKIP_COUNTERMEASURES 4 +#define IW_AUTH_DROP_UNENCRYPTED 5 +#define IW_AUTH_80211_AUTH_ALG 6 +#define IW_AUTH_WPA_ENABLED 7 +#define IW_AUTH_RX_UNENCRYPTED_EAPOL 8 +#define IW_AUTH_ROAMING_CONTROL 9 +#define IW_AUTH_PRIVACY_INVOKED 10 +#define IW_AUTH_CIPHER_GROUP_MGMT 11 +#define IW_AUTH_MFP 12 + +/* IW_AUTH_WPA_VERSION values (bit field) */ +#define IW_AUTH_WPA_VERSION_DISABLED 0x00000001 +#define IW_AUTH_WPA_VERSION_WPA 0x00000002 +#define IW_AUTH_WPA_VERSION_WPA2 0x00000004 + +/* IW_AUTH_PAIRWISE_CIPHER, IW_AUTH_GROUP_CIPHER, and IW_AUTH_CIPHER_GROUP_MGMT + * values (bit field) */ +#define IW_AUTH_CIPHER_NONE 0x00000001 +#define IW_AUTH_CIPHER_WEP40 0x00000002 +#define IW_AUTH_CIPHER_TKIP 0x00000004 +#define IW_AUTH_CIPHER_CCMP 0x00000008 +#define IW_AUTH_CIPHER_WEP104 0x00000010 +#define IW_AUTH_CIPHER_AES_CMAC 0x00000020 + +/* IW_AUTH_KEY_MGMT values (bit field) */ +#define IW_AUTH_KEY_MGMT_802_1X 1 +#define IW_AUTH_KEY_MGMT_PSK 2 + +/* IW_AUTH_80211_AUTH_ALG values (bit field) */ +#define IW_AUTH_ALG_OPEN_SYSTEM 0x00000001 +#define IW_AUTH_ALG_SHARED_KEY 0x00000002 +#define IW_AUTH_ALG_LEAP 0x00000004 + +/* IW_AUTH_ROAMING_CONTROL values */ +#define IW_AUTH_ROAMING_ENABLE 0 /* driver/firmware based roaming */ +#define IW_AUTH_ROAMING_DISABLE 1 /* user space program used for roaming + * control */ + +/* IW_AUTH_MFP (management frame protection) values */ +#define IW_AUTH_MFP_DISABLED 0 /* MFP disabled */ +#define IW_AUTH_MFP_OPTIONAL 1 /* MFP optional */ +#define IW_AUTH_MFP_REQUIRED 2 /* MFP required */ + +/* SIOCSIWENCODEEXT definitions */ +#define IW_ENCODE_SEQ_MAX_SIZE 8 +/* struct iw_encode_ext ->alg */ +#define IW_ENCODE_ALG_NONE 0 +#define IW_ENCODE_ALG_WEP 1 +#define IW_ENCODE_ALG_TKIP 2 +#define IW_ENCODE_ALG_CCMP 3 +#define IW_ENCODE_ALG_PMK 4 +#define IW_ENCODE_ALG_AES_CMAC 5 +/* struct iw_encode_ext ->ext_flags */ +#define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001 +#define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002 +#define IW_ENCODE_EXT_GROUP_KEY 0x00000004 +#define IW_ENCODE_EXT_SET_TX_KEY 0x00000008 + +/* IWEVMICHAELMICFAILURE : struct iw_michaelmicfailure ->flags */ +#define IW_MICFAILURE_KEY_ID 0x00000003 /* Key ID 0..3 */ +#define IW_MICFAILURE_GROUP 0x00000004 +#define IW_MICFAILURE_PAIRWISE 0x00000008 +#define IW_MICFAILURE_STAKEY 0x00000010 +#define IW_MICFAILURE_COUNT 0x00000060 /* 1 or 2 (0 = count not supported) + */ + +/* Bit field values for enc_capa in struct iw_range */ +#define IW_ENC_CAPA_WPA 0x00000001 +#define IW_ENC_CAPA_WPA2 0x00000002 +#define IW_ENC_CAPA_CIPHER_TKIP 0x00000004 +#define IW_ENC_CAPA_CIPHER_CCMP 0x00000008 +#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010 + +/* Event capability macros - in (struct iw_range *)->event_capa + * Because we have more than 32 possible events, we use an array of + * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ +#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ + (cmd - SIOCIWFIRSTPRIV + 0x60) : \ + (cmd - SIOCIWFIRST)) +#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) +#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) +/* Event capability constants - event autogenerated by the kernel + * This list is valid for most 802.11 devices, customise as needed... */ +#define IW_EVENT_CAPA_K_0 (IW_EVENT_CAPA_MASK(0x8B04) | \ + IW_EVENT_CAPA_MASK(0x8B06) | \ + IW_EVENT_CAPA_MASK(0x8B1A)) +#define IW_EVENT_CAPA_K_1 (IW_EVENT_CAPA_MASK(0x8B2A)) +/* "Easy" macro to set events in iw_range (less efficient) */ +#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd)) +#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; } + + +/****************************** TYPES ******************************/ + +/* --------------------------- SUBTYPES --------------------------- */ +/* + * Generic format for most parameters that fit in an int + */ +struct iw_param +{ + __s32 value; /* The value of the parameter itself */ + __u8 fixed; /* Hardware should not use auto select */ + __u8 disabled; /* Disable the feature */ + __u16 flags; /* Various specifc flags (if any) */ +}; + +/* + * For all data larger than 16 octets, we need to use a + * pointer to memory allocated in user space. + */ +struct iw_point +{ + void __user *pointer; /* Pointer to the data (in user space) */ + __u16 length; /* number of fields or size in bytes */ + __u16 flags; /* Optional params */ +}; + +#ifdef __KERNEL__ +#ifdef CONFIG_COMPAT + +#include + +struct compat_iw_point { + compat_caddr_t pointer; + __u16 length; + __u16 flags; +}; +#endif +#endif + +/* + * A frequency + * For numbers lower than 10^9, we encode the number in 'm' and + * set 'e' to 0 + * For number greater than 10^9, we divide it by the lowest power + * of 10 to get 'm' lower than 10^9, with 'm'= f / (10^'e')... + * The power of 10 is in 'e', the result of the division is in 'm'. + */ +struct iw_freq +{ + __s32 m; /* Mantissa */ + __s16 e; /* Exponent */ + __u8 i; /* List index (when in range struct) */ + __u8 flags; /* Flags (fixed/auto) */ +}; + +/* + * Quality of the link + */ +struct iw_quality +{ + __u8 qual; /* link quality (%retries, SNR, + %missed beacons or better...) */ + __u8 level; /* signal level (dBm) */ + __u8 noise; /* noise level (dBm) */ + __u8 updated; /* Flags to know if updated */ +}; + +/* + * Packet discarded in the wireless adapter due to + * "wireless" specific problems... + * Note : the list of counter and statistics in net_device_stats + * is already pretty exhaustive, and you should use that first. + * This is only additional stats... + */ +struct iw_discarded +{ + __u32 nwid; /* Rx : Wrong nwid/essid */ + __u32 code; /* Rx : Unable to code/decode (WEP) */ + __u32 fragment; /* Rx : Can't perform MAC reassembly */ + __u32 retries; /* Tx : Max MAC retries num reached */ + __u32 misc; /* Others cases */ +}; + +/* + * Packet/Time period missed in the wireless adapter due to + * "wireless" specific problems... + */ +struct iw_missed +{ + __u32 beacon; /* Missed beacons/superframe */ +}; + +/* + * Quality range (for spy threshold) + */ +struct iw_thrspy +{ + struct sockaddr addr; /* Source address (hw/mac) */ + struct iw_quality qual; /* Quality of the link */ + struct iw_quality low; /* Low threshold */ + struct iw_quality high; /* High threshold */ +}; + +/* + * Optional data for scan request + * + * Note: these optional parameters are controlling parameters for the + * scanning behavior, these do not apply to getting scan results + * (SIOCGIWSCAN). Drivers are expected to keep a local BSS table and + * provide a merged results with all BSSes even if the previous scan + * request limited scanning to a subset, e.g., by specifying an SSID. + * Especially, scan results are required to include an entry for the + * current BSS if the driver is in Managed mode and associated with an AP. + */ +struct iw_scan_req +{ + __u8 scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */ + __u8 essid_len; + __u8 num_channels; /* num entries in channel_list; + * 0 = scan all allowed channels */ + __u8 flags; /* reserved as padding; use zero, this may + * be used in the future for adding flags + * to request different scan behavior */ + struct sockaddr bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or + * individual address of a specific BSS */ + + /* + * Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using + * the current ESSID. This allows scan requests for specific ESSID + * without having to change the current ESSID and potentially breaking + * the current association. + */ + __u8 essid[IW_ESSID_MAX_SIZE]; + + /* + * Optional parameters for changing the default scanning behavior. + * These are based on the MLME-SCAN.request from IEEE Std 802.11. + * TU is 1.024 ms. If these are set to 0, driver is expected to use + * reasonable default values. min_channel_time defines the time that + * will be used to wait for the first reply on each channel. If no + * replies are received, next channel will be scanned after this. If + * replies are received, total time waited on the channel is defined by + * max_channel_time. + */ + __u32 min_channel_time; /* in TU */ + __u32 max_channel_time; /* in TU */ + + struct iw_freq channel_list[IW_MAX_FREQUENCIES]; +}; + +/* ------------------------- WPA SUPPORT ------------------------- */ + +/* + * Extended data structure for get/set encoding (this is used with + * SIOCSIWENCODEEXT/SIOCGIWENCODEEXT. struct iw_point and IW_ENCODE_* + * flags are used in the same way as with SIOCSIWENCODE/SIOCGIWENCODE and + * only the data contents changes (key data -> this structure, including + * key data). + * + * If the new key is the first group key, it will be set as the default + * TX key. Otherwise, default TX key index is only changed if + * IW_ENCODE_EXT_SET_TX_KEY flag is set. + * + * Key will be changed with SIOCSIWENCODEEXT in all cases except for + * special "change TX key index" operation which is indicated by setting + * key_len = 0 and ext_flags |= IW_ENCODE_EXT_SET_TX_KEY. + * + * tx_seq/rx_seq are only used when respective + * IW_ENCODE_EXT_{TX,RX}_SEQ_VALID flag is set in ext_flags. Normal + * TKIP/CCMP operation is to set RX seq with SIOCSIWENCODEEXT and start + * TX seq from zero whenever key is changed. SIOCGIWENCODEEXT is normally + * used only by an Authenticator (AP or an IBSS station) to get the + * current TX sequence number. Using TX_SEQ_VALID for SIOCSIWENCODEEXT and + * RX_SEQ_VALID for SIOCGIWENCODEEXT are optional, but can be useful for + * debugging/testing. + */ +struct iw_encode_ext +{ + __u32 ext_flags; /* IW_ENCODE_EXT_* */ + __u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ + __u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ + struct sockaddr addr; /* ff:ff:ff:ff:ff:ff for broadcast/multicast + * (group) keys or unicast address for + * individual keys */ + __u16 alg; /* IW_ENCODE_ALG_* */ + __u16 key_len; + __u8 key[0]; +}; + +/* SIOCSIWMLME data */ +struct iw_mlme +{ + __u16 cmd; /* IW_MLME_* */ + __u16 reason_code; + struct sockaddr addr; +}; + +/* SIOCSIWPMKSA data */ +#define IW_PMKSA_ADD 1 +#define IW_PMKSA_REMOVE 2 +#define IW_PMKSA_FLUSH 3 + +#define IW_PMKID_LEN 16 + +struct iw_pmksa +{ + __u32 cmd; /* IW_PMKSA_* */ + struct sockaddr bssid; + __u8 pmkid[IW_PMKID_LEN]; +}; + +/* IWEVMICHAELMICFAILURE data */ +struct iw_michaelmicfailure +{ + __u32 flags; + struct sockaddr src_addr; + __u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ +}; + +/* IWEVPMKIDCAND data */ +#define IW_PMKID_CAND_PREAUTH 0x00000001 /* RNS pre-authentication enabled */ +struct iw_pmkid_cand +{ + __u32 flags; /* IW_PMKID_CAND_* */ + __u32 index; /* the smaller the index, the higher the + * priority */ + struct sockaddr bssid; +}; + +/* ------------------------ WIRELESS STATS ------------------------ */ +/* + * Wireless statistics (used for /proc/net/wireless) + */ +struct iw_statistics +{ + __u16 status; /* Status + * - device dependent for now */ + + struct iw_quality qual; /* Quality of the link + * (instant/mean/max) */ + struct iw_discarded discard; /* Packet discarded counts */ + struct iw_missed miss; /* Packet missed counts */ +}; + +/* ------------------------ IOCTL REQUEST ------------------------ */ +/* + * This structure defines the payload of an ioctl, and is used + * below. + * + * Note that this structure should fit on the memory footprint + * of iwreq (which is the same as ifreq), which mean a max size of + * 16 octets = 128 bits. Warning, pointers might be 64 bits wide... + * You should check this when increasing the structures defined + * above in this file... + */ +union iwreq_data +{ + /* Config - generic */ + char name[IFNAMSIZ]; + /* Name : used to verify the presence of wireless extensions. + * Name of the protocol/provider... */ + + struct iw_point essid; /* Extended network name */ + struct iw_param nwid; /* network id (or domain - the cell) */ + struct iw_freq freq; /* frequency or channel : + * 0-1000 = channel + * > 1000 = frequency in Hz */ + + struct iw_param sens; /* signal level threshold */ + struct iw_param bitrate; /* default bit rate */ + struct iw_param txpower; /* default transmit power */ + struct iw_param rts; /* RTS threshold threshold */ + struct iw_param frag; /* Fragmentation threshold */ + __u32 mode; /* Operation mode */ + struct iw_param retry; /* Retry limits & lifetime */ + + struct iw_point encoding; /* Encoding stuff : tokens */ + struct iw_param power; /* PM duration/timeout */ + struct iw_quality qual; /* Quality part of statistics */ + + struct sockaddr ap_addr; /* Access point address */ + struct sockaddr addr; /* Destination address (hw/mac) */ + + struct iw_param param; /* Other small parameters */ + struct iw_point data; /* Other large parameters */ +}; + +/* + * The structure to exchange data for ioctl. + * This structure is the same as 'struct ifreq', but (re)defined for + * convenience... + * Do I need to remind you about structure size (32 octets) ? + */ +struct iwreq +{ + union + { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ + } ifr_ifrn; + + /* Data part (defined just above) */ + union iwreq_data u; +}; + +/* -------------------------- IOCTL DATA -------------------------- */ +/* + * For those ioctl which want to exchange mode data that what could + * fit in the above structure... + */ + +/* + * Range of parameters + */ + +struct iw_range +{ + /* Informative stuff (to choose between different interface) */ + __u32 throughput; /* To give an idea... */ + /* In theory this value should be the maximum benchmarked + * TCP/IP throughput, because with most of these devices the + * bit rate is meaningless (overhead an co) to estimate how + * fast the connection will go and pick the fastest one. + * I suggest people to play with Netperf or any benchmark... + */ + + /* NWID (or domain id) */ + __u32 min_nwid; /* Minimal NWID we are able to set */ + __u32 max_nwid; /* Maximal NWID we are able to set */ + + /* Old Frequency (backward compat - moved lower ) */ + __u16 old_num_channels; + __u8 old_num_frequency; + + /* Scan capabilities */ + __u8 scan_capa; /* IW_SCAN_CAPA_* bit field */ + + /* Wireless event capability bitmasks */ + __u32 event_capa[6]; + + /* signal level threshold range */ + __s32 sensitivity; + + /* Quality of link & SNR stuff */ + /* Quality range (link, level, noise) + * If the quality is absolute, it will be in the range [0 ; max_qual], + * if the quality is dBm, it will be in the range [max_qual ; 0]. + * Don't forget that we use 8 bit arithmetics... */ + struct iw_quality max_qual; /* Quality of the link */ + /* This should contain the average/typical values of the quality + * indicator. This should be the threshold between a "good" and + * a "bad" link (example : monitor going from green to orange). + * Currently, user space apps like quality monitors don't have any + * way to calibrate the measurement. With this, they can split + * the range between 0 and max_qual in different quality level + * (using a geometric subdivision centered on the average). + * I expect that people doing the user space apps will feedback + * us on which value we need to put in each driver... */ + struct iw_quality avg_qual; /* Quality of the link */ + + /* Rates */ + __u8 num_bitrates; /* Number of entries in the list */ + __s32 bitrate[IW_MAX_BITRATES]; /* list, in bps */ + + /* RTS threshold */ + __s32 min_rts; /* Minimal RTS threshold */ + __s32 max_rts; /* Maximal RTS threshold */ + + /* Frag threshold */ + __s32 min_frag; /* Minimal frag threshold */ + __s32 max_frag; /* Maximal frag threshold */ + + /* Power Management duration & timeout */ + __s32 min_pmp; /* Minimal PM period */ + __s32 max_pmp; /* Maximal PM period */ + __s32 min_pmt; /* Minimal PM timeout */ + __s32 max_pmt; /* Maximal PM timeout */ + __u16 pmp_flags; /* How to decode max/min PM period */ + __u16 pmt_flags; /* How to decode max/min PM timeout */ + __u16 pm_capa; /* What PM options are supported */ + + /* Encoder stuff */ + __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */ + __u8 num_encoding_sizes; /* Number of entry in the list */ + __u8 max_encoding_tokens; /* Max number of tokens */ + /* For drivers that need a "login/passwd" form */ + __u8 encoding_login_index; /* token index for login token */ + + /* Transmit power */ + __u16 txpower_capa; /* What options are supported */ + __u8 num_txpower; /* Number of entries in the list */ + __s32 txpower[IW_MAX_TXPOWER]; /* list, in bps */ + + /* Wireless Extension version info */ + __u8 we_version_compiled; /* Must be WIRELESS_EXT */ + __u8 we_version_source; /* Last update of source */ + + /* Retry limits and lifetime */ + __u16 retry_capa; /* What retry options are supported */ + __u16 retry_flags; /* How to decode max/min retry limit */ + __u16 r_time_flags; /* How to decode max/min retry life */ + __s32 min_retry; /* Minimal number of retries */ + __s32 max_retry; /* Maximal number of retries */ + __s32 min_r_time; /* Minimal retry lifetime */ + __s32 max_r_time; /* Maximal retry lifetime */ + + /* Frequency */ + __u16 num_channels; /* Number of channels [0; num - 1] */ + __u8 num_frequency; /* Number of entry in the list */ + struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */ + /* Note : this frequency list doesn't need to fit channel numbers, + * because each entry contain its channel index */ + + __u32 enc_capa; /* IW_ENC_CAPA_* bit field */ +}; + +/* + * Private ioctl interface information + */ + +struct iw_priv_args +{ + __u32 cmd; /* Number of the ioctl to issue */ + __u16 set_args; /* Type and number of args */ + __u16 get_args; /* Type and number of args */ + char name[IFNAMSIZ]; /* Name of the extension */ +}; + +/* ----------------------- WIRELESS EVENTS ----------------------- */ +/* + * Wireless events are carried through the rtnetlink socket to user + * space. They are encapsulated in the IFLA_WIRELESS field of + * a RTM_NEWLINK message. + */ + +/* + * A Wireless Event. Contains basically the same data as the ioctl... + */ +struct iw_event +{ + __u16 len; /* Real length of this stuff */ + __u16 cmd; /* Wireless IOCTL */ + union iwreq_data u; /* IOCTL fixed payload */ +}; + +/* Size of the Event prefix (including padding and alignement junk) */ +#define IW_EV_LCP_LEN (sizeof(struct iw_event) - sizeof(union iwreq_data)) +/* Size of the various events */ +#define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ) +#define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32)) +#define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq)) +#define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param)) +#define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr)) +#define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality)) + +/* iw_point events are special. First, the payload (extra data) come at + * the end of the event, so they are bigger than IW_EV_POINT_LEN. Second, + * we omit the pointer, so start at an offset. */ +#define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - \ + (char *) NULL) +#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \ + IW_EV_POINT_OFF) + +#ifdef __KERNEL__ +#ifdef CONFIG_COMPAT +struct __compat_iw_event { + __u16 len; /* Real length of this stuff */ + __u16 cmd; /* Wireless IOCTL */ + compat_caddr_t pointer; +}; +#define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer) +#define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length) + +/* Size of the various events for compat */ +#define IW_EV_COMPAT_CHAR_LEN (IW_EV_COMPAT_LCP_LEN + IFNAMSIZ) +#define IW_EV_COMPAT_UINT_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(__u32)) +#define IW_EV_COMPAT_FREQ_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_freq)) +#define IW_EV_COMPAT_PARAM_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_param)) +#define IW_EV_COMPAT_ADDR_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct sockaddr)) +#define IW_EV_COMPAT_QUAL_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_quality)) +#define IW_EV_COMPAT_POINT_LEN \ + (IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \ + IW_EV_COMPAT_POINT_OFF) +#endif +#endif + +/* Size of the Event prefix when packed in stream */ +#define IW_EV_LCP_PK_LEN (4) +/* Size of the various events when packed in stream */ +#define IW_EV_CHAR_PK_LEN (IW_EV_LCP_PK_LEN + IFNAMSIZ) +#define IW_EV_UINT_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(__u32)) +#define IW_EV_FREQ_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_freq)) +#define IW_EV_PARAM_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_param)) +#define IW_EV_ADDR_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct sockaddr)) +#define IW_EV_QUAL_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_quality)) +#define IW_EV_POINT_PK_LEN (IW_EV_LCP_PK_LEN + 4) + +#endif /* _LINUX_WIRELESS_H */ diff --git a/include/compat/linux/wl12xx.h b/include/compat/linux/wl12xx.h new file mode 100644 index 00000000..4b697395 --- /dev/null +++ b/include/compat/linux/wl12xx.h @@ -0,0 +1,78 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Luciano Coelho + * + * 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 distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef _LINUX_WL12XX_H +#define _LINUX_WL12XX_H + +/* Reference clock values */ +enum { + WL12XX_REFCLOCK_19 = 0, /* 19.2 MHz */ + WL12XX_REFCLOCK_26 = 1, /* 26 MHz */ + WL12XX_REFCLOCK_38 = 2, /* 38.4 MHz */ + WL12XX_REFCLOCK_52 = 3, /* 52 MHz */ + WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */ + WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */ +}; + +/* TCXO clock values */ +enum { + WL12XX_TCXOCLOCK_19_2 = 0, /* 19.2MHz */ + WL12XX_TCXOCLOCK_26 = 1, /* 26 MHz */ + WL12XX_TCXOCLOCK_38_4 = 2, /* 38.4MHz */ + WL12XX_TCXOCLOCK_52 = 3, /* 52 MHz */ + WL12XX_TCXOCLOCK_16_368 = 4, /* 16.368 MHz */ + WL12XX_TCXOCLOCK_32_736 = 5, /* 32.736 MHz */ + WL12XX_TCXOCLOCK_16_8 = 6, /* 16.8 MHz */ + WL12XX_TCXOCLOCK_33_6 = 7, /* 33.6 MHz */ +}; + +struct wl12xx_platform_data { + void (*set_power)(bool enable); + /* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */ + int irq; + bool use_eeprom; + int board_ref_clock; + int board_tcxo_clock; + unsigned long platform_quirks; +}; + +/* Platform does not support level trigger interrupts */ +#define WL12XX_PLATFORM_QUIRK_EDGE_IRQ BIT(0) + +#ifdef CONFIG_WL12XX_PLATFORM_DATA + +int wl12xx_set_platform_data(const struct wl12xx_platform_data *data); + +#else + +static inline +int wl12xx_set_platform_data(const struct wl12xx_platform_data *data) +{ + return -ENOSYS; +} + +#endif + +const struct wl12xx_platform_data *wl12xx_get_platform_data(void); + +#endif diff --git a/include/compat/net/bluetooth/bluetooth.h b/include/compat/net/bluetooth/bluetooth.h new file mode 100644 index 00000000..71ab25bf --- /dev/null +++ b/include/compat/net/bluetooth/bluetooth.h @@ -0,0 +1,276 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2000-2001 Qualcomm Incorporated + + Written 2000,2001 by Maxim Krasnyansky + + 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; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +#ifndef __BLUETOOTH_H +#define __BLUETOOTH_H + +#include +#include +#include +#include +#include + +#ifndef AF_BLUETOOTH +#define AF_BLUETOOTH 31 +#define PF_BLUETOOTH AF_BLUETOOTH +#endif + +/* Reserv for core and drivers use */ +#define BT_SKB_RESERVE 8 + +#define BTPROTO_L2CAP 0 +#define BTPROTO_HCI 1 +#define BTPROTO_SCO 2 +#define BTPROTO_RFCOMM 3 +#define BTPROTO_BNEP 4 +#define BTPROTO_CMTP 5 +#define BTPROTO_HIDP 6 +#define BTPROTO_AVDTP 7 + +#define SOL_HCI 0 +#define SOL_L2CAP 6 +#define SOL_SCO 17 +#define SOL_RFCOMM 18 + +#define BT_SECURITY 4 +struct bt_security { + __u8 level; + __u8 key_size; +}; +#define BT_SECURITY_SDP 0 +#define BT_SECURITY_LOW 1 +#define BT_SECURITY_MEDIUM 2 +#define BT_SECURITY_HIGH 3 + +#define BT_DEFER_SETUP 7 + +#define BT_FLUSHABLE 8 + +#define BT_FLUSHABLE_OFF 0 +#define BT_FLUSHABLE_ON 1 + +#define BT_POWER 9 +struct bt_power { + __u8 force_active; +}; +#define BT_POWER_FORCE_ACTIVE_OFF 0 +#define BT_POWER_FORCE_ACTIVE_ON 1 + +#define BT_CHANNEL_POLICY 10 + +/* BR/EDR only (default policy) + * AMP controllers cannot be used. + * Channel move requests from the remote device are denied. + * If the L2CAP channel is currently using AMP, move the channel to BR/EDR. + */ +#define BT_CHANNEL_POLICY_BREDR_ONLY 0 + +/* BR/EDR Preferred + * Allow use of AMP controllers. + * If the L2CAP channel is currently on AMP, move it to BR/EDR. + * Channel move requests from the remote device are allowed. + */ +#define BT_CHANNEL_POLICY_BREDR_PREFERRED 1 + +/* AMP Preferred + * Allow use of AMP controllers + * If the L2CAP channel is currently on BR/EDR and AMP controller + * resources are available, initiate a channel move to AMP. + * Channel move requests from the remote device are allowed. + * If the L2CAP socket has not been connected yet, try to create + * and configure the channel directly on an AMP controller rather + * than BR/EDR. + */ +#define BT_CHANNEL_POLICY_AMP_PREFERRED 2 + +__printf(2, 3) +int bt_printk(const char *level, const char *fmt, ...); + +#define BT_INFO(fmt, arg...) bt_printk(KERN_INFO, pr_fmt(fmt), ##arg) +#define BT_ERR(fmt, arg...) bt_printk(KERN_ERR, pr_fmt(fmt), ##arg) +#define BT_DBG(fmt, arg...) pr_debug(fmt "\n", ##arg) + +/* Connection and socket states */ +enum { + BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */ + BT_OPEN, + BT_BOUND, + BT_LISTEN, + BT_CONNECT, + BT_CONNECT2, + BT_CONFIG, + BT_DISCONN, + BT_CLOSED +}; + +/* BD Address */ +typedef struct { + __u8 b[6]; +} __packed bdaddr_t; + +#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}}) +#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}}) + +/* Copy, swap, convert BD Address */ +static inline int bacmp(bdaddr_t *ba1, bdaddr_t *ba2) +{ + return memcmp(ba1, ba2, sizeof(bdaddr_t)); +} +static inline void bacpy(bdaddr_t *dst, bdaddr_t *src) +{ + memcpy(dst, src, sizeof(bdaddr_t)); +} + +void baswap(bdaddr_t *dst, bdaddr_t *src); +char *batostr(bdaddr_t *ba); +bdaddr_t *strtoba(char *str); + +/* Common socket structures and functions */ + +#define bt_sk(__sk) ((struct bt_sock *) __sk) + +struct bt_sock { + struct sock sk; + bdaddr_t src; + bdaddr_t dst; + struct list_head accept_q; + struct sock *parent; + u32 defer_setup; +}; + +struct bt_sock_list { + struct hlist_head head; + rwlock_t lock; +}; + +int bt_sock_register(int proto, const struct net_proto_family *ops); +int bt_sock_unregister(int proto); +void bt_sock_link(struct bt_sock_list *l, struct sock *s); +void bt_sock_unlink(struct bt_sock_list *l, struct sock *s); +int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len, int flags); +int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len, int flags); +uint bt_sock_poll(struct file * file, struct socket *sock, poll_table *wait); +int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); +int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo); + +void bt_accept_enqueue(struct sock *parent, struct sock *sk); +void bt_accept_unlink(struct sock *sk); +struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock); + +/* Skb helpers */ +struct bt_skb_cb { + __u8 pkt_type; + __u8 incoming; + __u16 expect; + __u16 tx_seq; + __u8 retries; + __u8 sar; + unsigned short channel; + __u8 force_active; +}; +#define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb)) + +static inline struct sk_buff *bt_skb_alloc(unsigned int len, gfp_t how) +{ + struct sk_buff *skb; + + if ((skb = alloc_skb(len + BT_SKB_RESERVE, how))) { + skb_reserve(skb, BT_SKB_RESERVE); + bt_cb(skb)->incoming = 0; + } + return skb; +} + +static inline struct sk_buff *bt_skb_send_alloc(struct sock *sk, + unsigned long len, int nb, int *err) +{ + struct sk_buff *skb; + + release_sock(sk); + if ((skb = sock_alloc_send_skb(sk, len + BT_SKB_RESERVE, nb, err))) { + skb_reserve(skb, BT_SKB_RESERVE); + bt_cb(skb)->incoming = 0; + } + lock_sock(sk); + + if (!skb && *err) + return NULL; + + *err = sock_error(sk); + if (*err) + goto out; + + if (sk->sk_shutdown) { + *err = -ECONNRESET; + goto out; + } + + return skb; + +out: + kfree_skb(skb); + return NULL; +} + +int bt_to_errno(__u16 code); + +extern int hci_sock_init(void); +extern void hci_sock_cleanup(void); + +extern int bt_sysfs_init(void); +extern void bt_sysfs_cleanup(void); + +extern struct dentry *bt_debugfs; + +#ifdef CONFIG_COMPAT_BT_L2CAP +int l2cap_init(void); +void l2cap_exit(void); +#else +static inline int l2cap_init(void) +{ + return 0; +} + +static inline void l2cap_exit(void) +{ +} +#endif + +#ifdef CONFIG_COMPAT_BT_SCO +int sco_init(void); +void sco_exit(void); +#else +static inline int sco_init(void) +{ + return 0; +} + +static inline void sco_exit(void) +{ +} +#endif + +#endif /* __BLUETOOTH_H */ diff --git a/include/compat/net/bluetooth/hci.h b/include/compat/net/bluetooth/hci.h new file mode 100644 index 00000000..139ce2aa --- /dev/null +++ b/include/compat/net/bluetooth/hci.h @@ -0,0 +1,1334 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2000-2001 Qualcomm Incorporated + + Written 2000,2001 by Maxim Krasnyansky + + 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; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +#ifndef __HCI_H +#define __HCI_H + +#define HCI_MAX_ACL_SIZE 1024 +#define HCI_MAX_SCO_SIZE 255 +#define HCI_MAX_EVENT_SIZE 260 +#define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4) + +/* HCI dev events */ +#define HCI_DEV_REG 1 +#define HCI_DEV_UNREG 2 +#define HCI_DEV_UP 3 +#define HCI_DEV_DOWN 4 +#define HCI_DEV_SUSPEND 5 +#define HCI_DEV_RESUME 6 + +/* HCI notify events */ +#define HCI_NOTIFY_CONN_ADD 1 +#define HCI_NOTIFY_CONN_DEL 2 +#define HCI_NOTIFY_VOICE_SETTING 3 + +/* HCI bus types */ +#define HCI_VIRTUAL 0 +#define HCI_USB 1 +#define HCI_PCCARD 2 +#define HCI_UART 3 +#define HCI_RS232 4 +#define HCI_PCI 5 +#define HCI_SDIO 6 + +/* HCI controller types */ +#define HCI_BREDR 0x00 +#define HCI_AMP 0x01 + +/* HCI device quirks */ +enum { + HCI_QUIRK_NO_RESET, + HCI_QUIRK_RAW_DEVICE, + HCI_QUIRK_FIXUP_BUFFER_SIZE +}; + +/* HCI device flags */ +enum { + HCI_UP, + HCI_INIT, + HCI_RUNNING, + + HCI_PSCAN, + HCI_ISCAN, + HCI_AUTH, + HCI_ENCRYPT, + HCI_INQUIRY, + + HCI_RAW, + + HCI_SETUP, + HCI_AUTO_OFF, + HCI_MGMT, + HCI_PAIRABLE, + HCI_SERVICE_CACHE, + HCI_LINK_KEYS, + HCI_DEBUG_KEYS, + + HCI_RESET, +}; + +/* HCI ioctl defines */ +#define HCIDEVUP _IOW('H', 201, int) +#define HCIDEVDOWN _IOW('H', 202, int) +#define HCIDEVRESET _IOW('H', 203, int) +#define HCIDEVRESTAT _IOW('H', 204, int) + +#define HCIGETDEVLIST _IOR('H', 210, int) +#define HCIGETDEVINFO _IOR('H', 211, int) +#define HCIGETCONNLIST _IOR('H', 212, int) +#define HCIGETCONNINFO _IOR('H', 213, int) +#define HCIGETAUTHINFO _IOR('H', 215, int) + +#define HCISETRAW _IOW('H', 220, int) +#define HCISETSCAN _IOW('H', 221, int) +#define HCISETAUTH _IOW('H', 222, int) +#define HCISETENCRYPT _IOW('H', 223, int) +#define HCISETPTYPE _IOW('H', 224, int) +#define HCISETLINKPOL _IOW('H', 225, int) +#define HCISETLINKMODE _IOW('H', 226, int) +#define HCISETACLMTU _IOW('H', 227, int) +#define HCISETSCOMTU _IOW('H', 228, int) + +#define HCIBLOCKADDR _IOW('H', 230, int) +#define HCIUNBLOCKADDR _IOW('H', 231, int) + +#define HCIINQUIRY _IOR('H', 240, int) + +/* HCI timeouts */ +#define HCI_CONNECT_TIMEOUT (40000) /* 40 seconds */ +#define HCI_DISCONN_TIMEOUT (2000) /* 2 seconds */ +#define HCI_PAIRING_TIMEOUT (60000) /* 60 seconds */ +#define HCI_IDLE_TIMEOUT (6000) /* 6 seconds */ +#define HCI_INIT_TIMEOUT (10000) /* 10 seconds */ +#define HCI_CMD_TIMEOUT (1000) /* 1 seconds */ + +/* HCI data types */ +#define HCI_COMMAND_PKT 0x01 +#define HCI_ACLDATA_PKT 0x02 +#define HCI_SCODATA_PKT 0x03 +#define HCI_EVENT_PKT 0x04 +#define HCI_VENDOR_PKT 0xff + +/* HCI packet types */ +#define HCI_DM1 0x0008 +#define HCI_DM3 0x0400 +#define HCI_DM5 0x4000 +#define HCI_DH1 0x0010 +#define HCI_DH3 0x0800 +#define HCI_DH5 0x8000 + +#define HCI_HV1 0x0020 +#define HCI_HV2 0x0040 +#define HCI_HV3 0x0080 + +#define SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3) +#define ACL_PTYPE_MASK (~SCO_PTYPE_MASK) + +/* eSCO packet types */ +#define ESCO_HV1 0x0001 +#define ESCO_HV2 0x0002 +#define ESCO_HV3 0x0004 +#define ESCO_EV3 0x0008 +#define ESCO_EV4 0x0010 +#define ESCO_EV5 0x0020 +#define ESCO_2EV3 0x0040 +#define ESCO_3EV3 0x0080 +#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) + +/* ACL flags */ +#define ACL_START_NO_FLUSH 0x00 +#define ACL_CONT 0x01 +#define ACL_START 0x02 +#define ACL_ACTIVE_BCAST 0x04 +#define ACL_PICO_BCAST 0x08 + +/* Baseband links */ +#define SCO_LINK 0x00 +#define ACL_LINK 0x01 +#define ESCO_LINK 0x02 +/* Low Energy links do not have defined link type. Use invented one */ +#define LE_LINK 0x80 + +/* LMP features */ +#define LMP_3SLOT 0x01 +#define LMP_5SLOT 0x02 +#define LMP_ENCRYPT 0x04 +#define LMP_SOFFSET 0x08 +#define LMP_TACCURACY 0x10 +#define LMP_RSWITCH 0x20 +#define LMP_HOLD 0x40 +#define LMP_SNIFF 0x80 + +#define LMP_PARK 0x01 +#define LMP_RSSI 0x02 +#define LMP_QUALITY 0x04 +#define LMP_SCO 0x08 +#define LMP_HV2 0x10 +#define LMP_HV3 0x20 +#define LMP_ULAW 0x40 +#define LMP_ALAW 0x80 + +#define LMP_CVSD 0x01 +#define LMP_PSCHEME 0x02 +#define LMP_PCONTROL 0x04 + +#define LMP_RSSI_INQ 0x40 +#define LMP_ESCO 0x80 + +#define LMP_EV4 0x01 +#define LMP_EV5 0x02 +#define LMP_LE 0x40 + +#define LMP_SNIFF_SUBR 0x02 +#define LMP_PAUSE_ENC 0x04 +#define LMP_EDR_ESCO_2M 0x20 +#define LMP_EDR_ESCO_3M 0x40 +#define LMP_EDR_3S_ESCO 0x80 + +#define LMP_EXT_INQ 0x01 +#define LMP_SIMUL_LE_BR 0x02 +#define LMP_SIMPLE_PAIR 0x08 +#define LMP_NO_FLUSH 0x40 + +#define LMP_LSTO 0x01 +#define LMP_INQ_TX_PWR 0x02 +#define LMP_EXTFEATURES 0x80 + +/* Extended LMP features */ +#define LMP_HOST_LE 0x02 + +/* Connection modes */ +#define HCI_CM_ACTIVE 0x0000 +#define HCI_CM_HOLD 0x0001 +#define HCI_CM_SNIFF 0x0002 +#define HCI_CM_PARK 0x0003 + +/* Link policies */ +#define HCI_LP_RSWITCH 0x0001 +#define HCI_LP_HOLD 0x0002 +#define HCI_LP_SNIFF 0x0004 +#define HCI_LP_PARK 0x0008 + +/* Link modes */ +#define HCI_LM_ACCEPT 0x8000 +#define HCI_LM_MASTER 0x0001 +#define HCI_LM_AUTH 0x0002 +#define HCI_LM_ENCRYPT 0x0004 +#define HCI_LM_TRUSTED 0x0008 +#define HCI_LM_RELIABLE 0x0010 +#define HCI_LM_SECURE 0x0020 + +/* Authentication types */ +#define HCI_AT_NO_BONDING 0x00 +#define HCI_AT_NO_BONDING_MITM 0x01 +#define HCI_AT_DEDICATED_BONDING 0x02 +#define HCI_AT_DEDICATED_BONDING_MITM 0x03 +#define HCI_AT_GENERAL_BONDING 0x04 +#define HCI_AT_GENERAL_BONDING_MITM 0x05 + +/* Link Key types */ +#define HCI_LK_COMBINATION 0x00 +#define HCI_LK_LOCAL_UNIT 0x01 +#define HCI_LK_REMOTE_UNIT 0x02 +#define HCI_LK_DEBUG_COMBINATION 0x03 +#define HCI_LK_UNAUTH_COMBINATION 0x04 +#define HCI_LK_AUTH_COMBINATION 0x05 +#define HCI_LK_CHANGED_COMBINATION 0x06 +/* The spec doesn't define types for SMP keys */ +#define HCI_LK_SMP_LTK 0x81 +#define HCI_LK_SMP_IRK 0x82 +#define HCI_LK_SMP_CSRK 0x83 + +/* ---- HCI Error Codes ---- */ +#define HCI_ERROR_AUTH_FAILURE 0x05 +#define HCI_ERROR_REJ_BAD_ADDR 0x0f +#define HCI_ERROR_REMOTE_USER_TERM 0x13 +#define HCI_ERROR_LOCAL_HOST_TERM 0x16 +#define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18 + +/* ----- HCI Commands ---- */ +#define HCI_OP_NOP 0x0000 + +#define HCI_OP_INQUIRY 0x0401 +struct hci_cp_inquiry { + __u8 lap[3]; + __u8 length; + __u8 num_rsp; +} __packed; + +#define HCI_OP_INQUIRY_CANCEL 0x0402 + +#define HCI_OP_EXIT_PERIODIC_INQ 0x0404 + +#define HCI_OP_CREATE_CONN 0x0405 +struct hci_cp_create_conn { + bdaddr_t bdaddr; + __le16 pkt_type; + __u8 pscan_rep_mode; + __u8 pscan_mode; + __le16 clock_offset; + __u8 role_switch; +} __packed; + +#define HCI_OP_DISCONNECT 0x0406 +struct hci_cp_disconnect { + __le16 handle; + __u8 reason; +} __packed; + +#define HCI_OP_ADD_SCO 0x0407 +struct hci_cp_add_sco { + __le16 handle; + __le16 pkt_type; +} __packed; + +#define HCI_OP_CREATE_CONN_CANCEL 0x0408 +struct hci_cp_create_conn_cancel { + bdaddr_t bdaddr; +} __packed; + +#define HCI_OP_ACCEPT_CONN_REQ 0x0409 +struct hci_cp_accept_conn_req { + bdaddr_t bdaddr; + __u8 role; +} __packed; + +#define HCI_OP_REJECT_CONN_REQ 0x040a +struct hci_cp_reject_conn_req { + bdaddr_t bdaddr; + __u8 reason; +} __packed; + +#define HCI_OP_LINK_KEY_REPLY 0x040b +struct hci_cp_link_key_reply { + bdaddr_t bdaddr; + __u8 link_key[16]; +} __packed; + +#define HCI_OP_LINK_KEY_NEG_REPLY 0x040c +struct hci_cp_link_key_neg_reply { + bdaddr_t bdaddr; +} __packed; + +#define HCI_OP_PIN_CODE_REPLY 0x040d +struct hci_cp_pin_code_reply { + bdaddr_t bdaddr; + __u8 pin_len; + __u8 pin_code[16]; +} __packed; +struct hci_rp_pin_code_reply { + __u8 status; + bdaddr_t bdaddr; +} __packed; + +#define HCI_OP_PIN_CODE_NEG_REPLY 0x040e +struct hci_cp_pin_code_neg_reply { + bdaddr_t bdaddr; +} __packed; +struct hci_rp_pin_code_neg_reply { + __u8 status; + bdaddr_t bdaddr; +} __packed; + +#define HCI_OP_CHANGE_CONN_PTYPE 0x040f +struct hci_cp_change_conn_ptype { + __le16 handle; + __le16 pkt_type; +} __packed; + +#define HCI_OP_AUTH_REQUESTED 0x0411 +struct hci_cp_auth_requested { + __le16 handle; +} __packed; + +#define HCI_OP_SET_CONN_ENCRYPT 0x0413 +struct hci_cp_set_conn_encrypt { + __le16 handle; + __u8 encrypt; +} __packed; + +#define HCI_OP_CHANGE_CONN_LINK_KEY 0x0415 +struct hci_cp_change_conn_link_key { + __le16 handle; +} __packed; + +#define HCI_OP_REMOTE_NAME_REQ 0x0419 +struct hci_cp_remote_name_req { + bdaddr_t bdaddr; + __u8 pscan_rep_mode; + __u8 pscan_mode; + __le16 clock_offset; +} __packed; + +#define HCI_OP_REMOTE_NAME_REQ_CANCEL 0x041a +struct hci_cp_remote_name_req_cancel { + bdaddr_t bdaddr; +} __packed; + +#define HCI_OP_READ_REMOTE_FEATURES 0x041b +struct hci_cp_read_remote_features { + __le16 handle; +} __packed; + +#define HCI_OP_READ_REMOTE_EXT_FEATURES 0x041c +struct hci_cp_read_remote_ext_features { + __le16 handle; + __u8 page; +} __packed; + +#define HCI_OP_READ_REMOTE_VERSION 0x041d +struct hci_cp_read_remote_version { + __le16 handle; +} __packed; + +#define HCI_OP_SETUP_SYNC_CONN 0x0428 +struct hci_cp_setup_sync_conn { + __le16 handle; + __le32 tx_bandwidth; + __le32 rx_bandwidth; + __le16 max_latency; + __le16 voice_setting; + __u8 retrans_effort; + __le16 pkt_type; +} __packed; + +#define HCI_OP_ACCEPT_SYNC_CONN_REQ 0x0429 +struct hci_cp_accept_sync_conn_req { + bdaddr_t bdaddr; + __le32 tx_bandwidth; + __le32 rx_bandwidth; + __le16 max_latency; + __le16 content_format; + __u8 retrans_effort; + __le16 pkt_type; +} __packed; + +#define HCI_OP_REJECT_SYNC_CONN_REQ 0x042a +struct hci_cp_reject_sync_conn_req { + bdaddr_t bdaddr; + __u8 reason; +} __packed; + +#define HCI_OP_IO_CAPABILITY_REPLY 0x042b +struct hci_cp_io_capability_reply { + bdaddr_t bdaddr; + __u8 capability; + __u8 oob_data; + __u8 authentication; +} __packed; + +#define HCI_OP_USER_CONFIRM_REPLY 0x042c +struct hci_cp_user_confirm_reply { + bdaddr_t bdaddr; +} __packed; +struct hci_rp_user_confirm_reply { + __u8 status; + bdaddr_t bdaddr; +} __packed; + +#define HCI_OP_USER_CONFIRM_NEG_REPLY 0x042d + +#define HCI_OP_REMOTE_OOB_DATA_REPLY 0x0430 +struct hci_cp_remote_oob_data_reply { + bdaddr_t bdaddr; + __u8 hash[16]; + __u8 randomizer[16]; +} __packed; + +#define HCI_OP_REMOTE_OOB_DATA_NEG_REPLY 0x0433 +struct hci_cp_remote_oob_data_neg_reply { + bdaddr_t bdaddr; +} __packed; + +#define HCI_OP_IO_CAPABILITY_NEG_REPLY 0x0434 +struct hci_cp_io_capability_neg_reply { + bdaddr_t bdaddr; + __u8 reason; +} __packed; + +#define HCI_OP_SNIFF_MODE 0x0803 +struct hci_cp_sniff_mode { + __le16 handle; + __le16 max_interval; + __le16 min_interval; + __le16 attempt; + __le16 timeout; +} __packed; + +#define HCI_OP_EXIT_SNIFF_MODE 0x0804 +struct hci_cp_exit_sniff_mode { + __le16 handle; +} __packed; + +#define HCI_OP_ROLE_DISCOVERY 0x0809 +struct hci_cp_role_discovery { + __le16 handle; +} __packed; +struct hci_rp_role_discovery { + __u8 status; + __le16 handle; + __u8 role; +} __packed; + +#define HCI_OP_SWITCH_ROLE 0x080b +struct hci_cp_switch_role { + bdaddr_t bdaddr; + __u8 role; +} __packed; + +#define HCI_OP_READ_LINK_POLICY 0x080c +struct hci_cp_read_link_policy { + __le16 handle; +} __packed; +struct hci_rp_read_link_policy { + __u8 status; + __le16 handle; + __le16 policy; +} __packed; + +#define HCI_OP_WRITE_LINK_POLICY 0x080d +struct hci_cp_write_link_policy { + __le16 handle; + __le16 policy; +} __packed; +struct hci_rp_write_link_policy { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_READ_DEF_LINK_POLICY 0x080e +struct hci_rp_read_def_link_policy { + __u8 status; + __le16 policy; +} __packed; + +#define HCI_OP_WRITE_DEF_LINK_POLICY 0x080f +struct hci_cp_write_def_link_policy { + __le16 policy; +} __packed; + +#define HCI_OP_SNIFF_SUBRATE 0x0811 +struct hci_cp_sniff_subrate { + __le16 handle; + __le16 max_latency; + __le16 min_remote_timeout; + __le16 min_local_timeout; +} __packed; + +#define HCI_OP_SET_EVENT_MASK 0x0c01 +struct hci_cp_set_event_mask { + __u8 mask[8]; +} __packed; + +#define HCI_OP_RESET 0x0c03 + +#define HCI_OP_SET_EVENT_FLT 0x0c05 +struct hci_cp_set_event_flt { + __u8 flt_type; + __u8 cond_type; + __u8 condition[0]; +} __packed; + +/* Filter types */ +#define HCI_FLT_CLEAR_ALL 0x00 +#define HCI_FLT_INQ_RESULT 0x01 +#define HCI_FLT_CONN_SETUP 0x02 + +/* CONN_SETUP Condition types */ +#define HCI_CONN_SETUP_ALLOW_ALL 0x00 +#define HCI_CONN_SETUP_ALLOW_CLASS 0x01 +#define HCI_CONN_SETUP_ALLOW_BDADDR 0x02 + +/* CONN_SETUP Conditions */ +#define HCI_CONN_SETUP_AUTO_OFF 0x01 +#define HCI_CONN_SETUP_AUTO_ON 0x02 + +#define HCI_OP_DELETE_STORED_LINK_KEY 0x0c12 +struct hci_cp_delete_stored_link_key { + bdaddr_t bdaddr; + __u8 delete_all; +} __packed; + +#define HCI_MAX_NAME_LENGTH 248 + +#define HCI_OP_WRITE_LOCAL_NAME 0x0c13 +struct hci_cp_write_local_name { + __u8 name[HCI_MAX_NAME_LENGTH]; +} __packed; + +#define HCI_OP_READ_LOCAL_NAME 0x0c14 +struct hci_rp_read_local_name { + __u8 status; + __u8 name[HCI_MAX_NAME_LENGTH]; +} __packed; + +#define HCI_OP_WRITE_CA_TIMEOUT 0x0c16 + +#define HCI_OP_WRITE_PG_TIMEOUT 0x0c18 + +#define HCI_OP_WRITE_SCAN_ENABLE 0x0c1a + #define SCAN_DISABLED 0x00 + #define SCAN_INQUIRY 0x01 + #define SCAN_PAGE 0x02 + +#define HCI_OP_READ_AUTH_ENABLE 0x0c1f + +#define HCI_OP_WRITE_AUTH_ENABLE 0x0c20 + #define AUTH_DISABLED 0x00 + #define AUTH_ENABLED 0x01 + +#define HCI_OP_READ_ENCRYPT_MODE 0x0c21 + +#define HCI_OP_WRITE_ENCRYPT_MODE 0x0c22 + #define ENCRYPT_DISABLED 0x00 + #define ENCRYPT_P2P 0x01 + #define ENCRYPT_BOTH 0x02 + +#define HCI_OP_READ_CLASS_OF_DEV 0x0c23 +struct hci_rp_read_class_of_dev { + __u8 status; + __u8 dev_class[3]; +} __packed; + +#define HCI_OP_WRITE_CLASS_OF_DEV 0x0c24 +struct hci_cp_write_class_of_dev { + __u8 dev_class[3]; +} __packed; + +#define HCI_OP_READ_VOICE_SETTING 0x0c25 +struct hci_rp_read_voice_setting { + __u8 status; + __le16 voice_setting; +} __packed; + +#define HCI_OP_WRITE_VOICE_SETTING 0x0c26 +struct hci_cp_write_voice_setting { + __le16 voice_setting; +} __packed; + +#define HCI_OP_HOST_BUFFER_SIZE 0x0c33 +struct hci_cp_host_buffer_size { + __le16 acl_mtu; + __u8 sco_mtu; + __le16 acl_max_pkt; + __le16 sco_max_pkt; +} __packed; + +#define HCI_OP_WRITE_INQUIRY_MODE 0x0c45 + +#define HCI_MAX_EIR_LENGTH 240 + +#define HCI_OP_WRITE_EIR 0x0c52 +struct hci_cp_write_eir { + uint8_t fec; + uint8_t data[HCI_MAX_EIR_LENGTH]; +} __packed; + +#define HCI_OP_READ_SSP_MODE 0x0c55 +struct hci_rp_read_ssp_mode { + __u8 status; + __u8 mode; +} __packed; + +#define HCI_OP_WRITE_SSP_MODE 0x0c56 +struct hci_cp_write_ssp_mode { + __u8 mode; +} __packed; + +#define HCI_OP_READ_LOCAL_OOB_DATA 0x0c57 +struct hci_rp_read_local_oob_data { + __u8 status; + __u8 hash[16]; + __u8 randomizer[16]; +} __packed; + +#define HCI_OP_READ_INQ_RSP_TX_POWER 0x0c58 + +#define HCI_OP_WRITE_LE_HOST_SUPPORTED 0x0c6d +struct hci_cp_write_le_host_supported { + __u8 le; + __u8 simul; +} __packed; + +#define HCI_OP_READ_LOCAL_VERSION 0x1001 +struct hci_rp_read_local_version { + __u8 status; + __u8 hci_ver; + __le16 hci_rev; + __u8 lmp_ver; + __le16 manufacturer; + __le16 lmp_subver; +} __packed; + +#define HCI_OP_READ_LOCAL_COMMANDS 0x1002 +struct hci_rp_read_local_commands { + __u8 status; + __u8 commands[64]; +} __packed; + +#define HCI_OP_READ_LOCAL_FEATURES 0x1003 +struct hci_rp_read_local_features { + __u8 status; + __u8 features[8]; +} __packed; + +#define HCI_OP_READ_LOCAL_EXT_FEATURES 0x1004 +struct hci_cp_read_local_ext_features { + __u8 page; +} __packed; +struct hci_rp_read_local_ext_features { + __u8 status; + __u8 page; + __u8 max_page; + __u8 features[8]; +} __packed; + +#define HCI_OP_READ_BUFFER_SIZE 0x1005 +struct hci_rp_read_buffer_size { + __u8 status; + __le16 acl_mtu; + __u8 sco_mtu; + __le16 acl_max_pkt; + __le16 sco_max_pkt; +} __packed; + +#define HCI_OP_READ_BD_ADDR 0x1009 +struct hci_rp_read_bd_addr { + __u8 status; + bdaddr_t bdaddr; +} __packed; + +#define HCI_OP_WRITE_PAGE_SCAN_ACTIVITY 0x0c1c +struct hci_cp_write_page_scan_activity { + __le16 interval; + __le16 window; +} __packed; + +#define HCI_OP_WRITE_PAGE_SCAN_TYPE 0x0c47 + #define PAGE_SCAN_TYPE_STANDARD 0x00 + #define PAGE_SCAN_TYPE_INTERLACED 0x01 + +#define HCI_OP_READ_LOCAL_AMP_INFO 0x1409 +struct hci_rp_read_local_amp_info { + __u8 status; + __u8 amp_status; + __le32 total_bw; + __le32 max_bw; + __le32 min_latency; + __le32 max_pdu; + __u8 amp_type; + __le16 pal_cap; + __le16 max_assoc_size; + __le32 max_flush_to; + __le32 be_flush_to; +} __packed; + +#define HCI_OP_LE_SET_EVENT_MASK 0x2001 +struct hci_cp_le_set_event_mask { + __u8 mask[8]; +} __packed; + +#define HCI_OP_LE_READ_BUFFER_SIZE 0x2002 +struct hci_rp_le_read_buffer_size { + __u8 status; + __le16 le_mtu; + __u8 le_max_pkt; +} __packed; + +#define HCI_OP_LE_SET_SCAN_ENABLE 0x200c +struct hci_cp_le_set_scan_enable { + __u8 enable; + __u8 filter_dup; +} __packed; + +#define HCI_OP_LE_CREATE_CONN 0x200d +struct hci_cp_le_create_conn { + __le16 scan_interval; + __le16 scan_window; + __u8 filter_policy; + __u8 peer_addr_type; + bdaddr_t peer_addr; + __u8 own_address_type; + __le16 conn_interval_min; + __le16 conn_interval_max; + __le16 conn_latency; + __le16 supervision_timeout; + __le16 min_ce_len; + __le16 max_ce_len; +} __packed; + +#define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e + +#define HCI_OP_LE_CONN_UPDATE 0x2013 +struct hci_cp_le_conn_update { + __le16 handle; + __le16 conn_interval_min; + __le16 conn_interval_max; + __le16 conn_latency; + __le16 supervision_timeout; + __le16 min_ce_len; + __le16 max_ce_len; +} __packed; + +#define HCI_OP_LE_START_ENC 0x2019 +struct hci_cp_le_start_enc { + __le16 handle; + __u8 rand[8]; + __le16 ediv; + __u8 ltk[16]; +} __packed; + +#define HCI_OP_LE_LTK_REPLY 0x201a +struct hci_cp_le_ltk_reply { + __le16 handle; + __u8 ltk[16]; +} __packed; +struct hci_rp_le_ltk_reply { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_LE_LTK_NEG_REPLY 0x201b +struct hci_cp_le_ltk_neg_reply { + __le16 handle; +} __packed; +struct hci_rp_le_ltk_neg_reply { + __u8 status; + __le16 handle; +} __packed; + +/* ---- HCI Events ---- */ +#define HCI_EV_INQUIRY_COMPLETE 0x01 + +#define HCI_EV_INQUIRY_RESULT 0x02 +struct inquiry_info { + bdaddr_t bdaddr; + __u8 pscan_rep_mode; + __u8 pscan_period_mode; + __u8 pscan_mode; + __u8 dev_class[3]; + __le16 clock_offset; +} __packed; + +#define HCI_EV_CONN_COMPLETE 0x03 +struct hci_ev_conn_complete { + __u8 status; + __le16 handle; + bdaddr_t bdaddr; + __u8 link_type; + __u8 encr_mode; +} __packed; + +#define HCI_EV_CONN_REQUEST 0x04 +struct hci_ev_conn_request { + bdaddr_t bdaddr; + __u8 dev_class[3]; + __u8 link_type; +} __packed; + +#define HCI_EV_DISCONN_COMPLETE 0x05 +struct hci_ev_disconn_complete { + __u8 status; + __le16 handle; + __u8 reason; +} __packed; + +#define HCI_EV_AUTH_COMPLETE 0x06 +struct hci_ev_auth_complete { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_EV_REMOTE_NAME 0x07 +struct hci_ev_remote_name { + __u8 status; + bdaddr_t bdaddr; + __u8 name[HCI_MAX_NAME_LENGTH]; +} __packed; + +#define HCI_EV_ENCRYPT_CHANGE 0x08 +struct hci_ev_encrypt_change { + __u8 status; + __le16 handle; + __u8 encrypt; +} __packed; + +#define HCI_EV_CHANGE_LINK_KEY_COMPLETE 0x09 +struct hci_ev_change_link_key_complete { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_EV_REMOTE_FEATURES 0x0b +struct hci_ev_remote_features { + __u8 status; + __le16 handle; + __u8 features[8]; +} __packed; + +#define HCI_EV_REMOTE_VERSION 0x0c +struct hci_ev_remote_version { + __u8 status; + __le16 handle; + __u8 lmp_ver; + __le16 manufacturer; + __le16 lmp_subver; +} __packed; + +#define HCI_EV_QOS_SETUP_COMPLETE 0x0d +struct hci_qos { + __u8 service_type; + __u32 token_rate; + __u32 peak_bandwidth; + __u32 latency; + __u32 delay_variation; +} __packed; +struct hci_ev_qos_setup_complete { + __u8 status; + __le16 handle; + struct hci_qos qos; +} __packed; + +#define HCI_EV_CMD_COMPLETE 0x0e +struct hci_ev_cmd_complete { + __u8 ncmd; + __le16 opcode; +} __packed; + +#define HCI_EV_CMD_STATUS 0x0f +struct hci_ev_cmd_status { + __u8 status; + __u8 ncmd; + __le16 opcode; +} __packed; + +#define HCI_EV_ROLE_CHANGE 0x12 +struct hci_ev_role_change { + __u8 status; + bdaddr_t bdaddr; + __u8 role; +} __packed; + +#define HCI_EV_NUM_COMP_PKTS 0x13 +struct hci_ev_num_comp_pkts { + __u8 num_hndl; + /* variable length part */ +} __packed; + +#define HCI_EV_MODE_CHANGE 0x14 +struct hci_ev_mode_change { + __u8 status; + __le16 handle; + __u8 mode; + __le16 interval; +} __packed; + +#define HCI_EV_PIN_CODE_REQ 0x16 +struct hci_ev_pin_code_req { + bdaddr_t bdaddr; +} __packed; + +#define HCI_EV_LINK_KEY_REQ 0x17 +struct hci_ev_link_key_req { + bdaddr_t bdaddr; +} __packed; + +#define HCI_EV_LINK_KEY_NOTIFY 0x18 +struct hci_ev_link_key_notify { + bdaddr_t bdaddr; + __u8 link_key[16]; + __u8 key_type; +} __packed; + +#define HCI_EV_CLOCK_OFFSET 0x1c +struct hci_ev_clock_offset { + __u8 status; + __le16 handle; + __le16 clock_offset; +} __packed; + +#define HCI_EV_PKT_TYPE_CHANGE 0x1d +struct hci_ev_pkt_type_change { + __u8 status; + __le16 handle; + __le16 pkt_type; +} __packed; + +#define HCI_EV_PSCAN_REP_MODE 0x20 +struct hci_ev_pscan_rep_mode { + bdaddr_t bdaddr; + __u8 pscan_rep_mode; +} __packed; + +#define HCI_EV_INQUIRY_RESULT_WITH_RSSI 0x22 +struct inquiry_info_with_rssi { + bdaddr_t bdaddr; + __u8 pscan_rep_mode; + __u8 pscan_period_mode; + __u8 dev_class[3]; + __le16 clock_offset; + __s8 rssi; +} __packed; +struct inquiry_info_with_rssi_and_pscan_mode { + bdaddr_t bdaddr; + __u8 pscan_rep_mode; + __u8 pscan_period_mode; + __u8 pscan_mode; + __u8 dev_class[3]; + __le16 clock_offset; + __s8 rssi; +} __packed; + +#define HCI_EV_REMOTE_EXT_FEATURES 0x23 +struct hci_ev_remote_ext_features { + __u8 status; + __le16 handle; + __u8 page; + __u8 max_page; + __u8 features[8]; +} __packed; + +#define HCI_EV_SYNC_CONN_COMPLETE 0x2c +struct hci_ev_sync_conn_complete { + __u8 status; + __le16 handle; + bdaddr_t bdaddr; + __u8 link_type; + __u8 tx_interval; + __u8 retrans_window; + __le16 rx_pkt_len; + __le16 tx_pkt_len; + __u8 air_mode; +} __packed; + +#define HCI_EV_SYNC_CONN_CHANGED 0x2d +struct hci_ev_sync_conn_changed { + __u8 status; + __le16 handle; + __u8 tx_interval; + __u8 retrans_window; + __le16 rx_pkt_len; + __le16 tx_pkt_len; +} __packed; + +#define HCI_EV_SNIFF_SUBRATE 0x2e +struct hci_ev_sniff_subrate { + __u8 status; + __le16 handle; + __le16 max_tx_latency; + __le16 max_rx_latency; + __le16 max_remote_timeout; + __le16 max_local_timeout; +} __packed; + +#define HCI_EV_EXTENDED_INQUIRY_RESULT 0x2f +struct extended_inquiry_info { + bdaddr_t bdaddr; + __u8 pscan_rep_mode; + __u8 pscan_period_mode; + __u8 dev_class[3]; + __le16 clock_offset; + __s8 rssi; + __u8 data[240]; +} __packed; + +#define HCI_EV_IO_CAPA_REQUEST 0x31 +struct hci_ev_io_capa_request { + bdaddr_t bdaddr; +} __packed; + +#define HCI_EV_IO_CAPA_REPLY 0x32 +struct hci_ev_io_capa_reply { + bdaddr_t bdaddr; + __u8 capability; + __u8 oob_data; + __u8 authentication; +} __packed; + +#define HCI_EV_USER_CONFIRM_REQUEST 0x33 +struct hci_ev_user_confirm_req { + bdaddr_t bdaddr; + __le32 passkey; +} __packed; + +#define HCI_EV_REMOTE_OOB_DATA_REQUEST 0x35 +struct hci_ev_remote_oob_data_request { + bdaddr_t bdaddr; +} __packed; + +#define HCI_EV_SIMPLE_PAIR_COMPLETE 0x36 +struct hci_ev_simple_pair_complete { + __u8 status; + bdaddr_t bdaddr; +} __packed; + +#define HCI_EV_REMOTE_HOST_FEATURES 0x3d +struct hci_ev_remote_host_features { + bdaddr_t bdaddr; + __u8 features[8]; +} __packed; + +#define HCI_EV_LE_META 0x3e +struct hci_ev_le_meta { + __u8 subevent; +} __packed; + +/* Low energy meta events */ +#define HCI_EV_LE_CONN_COMPLETE 0x01 +struct hci_ev_le_conn_complete { + __u8 status; + __le16 handle; + __u8 role; + __u8 bdaddr_type; + bdaddr_t bdaddr; + __le16 interval; + __le16 latency; + __le16 supervision_timeout; + __u8 clk_accurancy; +} __packed; + +#define HCI_EV_LE_LTK_REQ 0x05 +struct hci_ev_le_ltk_req { + __le16 handle; + __u8 random[8]; + __le16 ediv; +} __packed; + +/* Advertising report event types */ +#define ADV_IND 0x00 +#define ADV_DIRECT_IND 0x01 +#define ADV_SCAN_IND 0x02 +#define ADV_NONCONN_IND 0x03 +#define ADV_SCAN_RSP 0x04 + +#define ADDR_LE_DEV_PUBLIC 0x00 +#define ADDR_LE_DEV_RANDOM 0x01 + +#define HCI_EV_LE_ADVERTISING_REPORT 0x02 +struct hci_ev_le_advertising_info { + __u8 evt_type; + __u8 bdaddr_type; + bdaddr_t bdaddr; + __u8 length; + __u8 data[0]; +} __packed; + +/* Internal events generated by Bluetooth stack */ +#define HCI_EV_STACK_INTERNAL 0xfd +struct hci_ev_stack_internal { + __u16 type; + __u8 data[0]; +} __packed; + +#define HCI_EV_SI_DEVICE 0x01 +struct hci_ev_si_device { + __u16 event; + __u16 dev_id; +} __packed; + +#define HCI_EV_SI_SECURITY 0x02 +struct hci_ev_si_security { + __u16 event; + __u16 proto; + __u16 subproto; + __u8 incoming; +} __packed; + +/* ---- HCI Packet structures ---- */ +#define HCI_COMMAND_HDR_SIZE 3 +#define HCI_EVENT_HDR_SIZE 2 +#define HCI_ACL_HDR_SIZE 4 +#define HCI_SCO_HDR_SIZE 3 + +struct hci_command_hdr { + __le16 opcode; /* OCF & OGF */ + __u8 plen; +} __packed; + +struct hci_event_hdr { + __u8 evt; + __u8 plen; +} __packed; + +struct hci_acl_hdr { + __le16 handle; /* Handle & Flags(PB, BC) */ + __le16 dlen; +} __packed; + +struct hci_sco_hdr { + __le16 handle; + __u8 dlen; +} __packed; + +#include +static inline struct hci_event_hdr *hci_event_hdr(const struct sk_buff *skb) +{ + return (struct hci_event_hdr *) skb->data; +} + +static inline struct hci_acl_hdr *hci_acl_hdr(const struct sk_buff *skb) +{ + return (struct hci_acl_hdr *) skb->data; +} + +static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb) +{ + return (struct hci_sco_hdr *) skb->data; +} + +/* Command opcode pack/unpack */ +#define hci_opcode_pack(ogf, ocf) (__u16) ((ocf & 0x03ff)|(ogf << 10)) +#define hci_opcode_ogf(op) (op >> 10) +#define hci_opcode_ocf(op) (op & 0x03ff) + +/* ACL handle and flags pack/unpack */ +#define hci_handle_pack(h, f) (__u16) ((h & 0x0fff)|(f << 12)) +#define hci_handle(h) (h & 0x0fff) +#define hci_flags(h) (h >> 12) + +/* ---- HCI Sockets ---- */ + +/* Socket options */ +#define HCI_DATA_DIR 1 +#define HCI_FILTER 2 +#define HCI_TIME_STAMP 3 + +/* CMSG flags */ +#define HCI_CMSG_DIR 0x0001 +#define HCI_CMSG_TSTAMP 0x0002 + +struct sockaddr_hci { + sa_family_t hci_family; + unsigned short hci_dev; + unsigned short hci_channel; +}; +#define HCI_DEV_NONE 0xffff + +#define HCI_CHANNEL_RAW 0 +#define HCI_CHANNEL_CONTROL 1 + +struct hci_filter { + unsigned long type_mask; + unsigned long event_mask[2]; + __le16 opcode; +}; + +struct hci_ufilter { + __u32 type_mask; + __u32 event_mask[2]; + __le16 opcode; +}; + +#define HCI_FLT_TYPE_BITS 31 +#define HCI_FLT_EVENT_BITS 63 +#define HCI_FLT_OGF_BITS 63 +#define HCI_FLT_OCF_BITS 127 + +/* ---- HCI Ioctl requests structures ---- */ +struct hci_dev_stats { + __u32 err_rx; + __u32 err_tx; + __u32 cmd_tx; + __u32 evt_rx; + __u32 acl_tx; + __u32 acl_rx; + __u32 sco_tx; + __u32 sco_rx; + __u32 byte_rx; + __u32 byte_tx; +}; + +struct hci_dev_info { + __u16 dev_id; + char name[8]; + + bdaddr_t bdaddr; + + __u32 flags; + __u8 type; + + __u8 features[8]; + + __u32 pkt_type; + __u32 link_policy; + __u32 link_mode; + + __u16 acl_mtu; + __u16 acl_pkts; + __u16 sco_mtu; + __u16 sco_pkts; + + struct hci_dev_stats stat; +}; + +struct hci_conn_info { + __u16 handle; + bdaddr_t bdaddr; + __u8 type; + __u8 out; + __u16 state; + __u32 link_mode; +}; + +struct hci_dev_req { + __u16 dev_id; + __u32 dev_opt; +}; + +struct hci_dev_list_req { + __u16 dev_num; + struct hci_dev_req dev_req[0]; /* hci_dev_req structures */ +}; + +struct hci_conn_list_req { + __u16 dev_id; + __u16 conn_num; + struct hci_conn_info conn_info[0]; +}; + +struct hci_conn_info_req { + bdaddr_t bdaddr; + __u8 type; + struct hci_conn_info conn_info[0]; +}; + +struct hci_auth_info_req { + bdaddr_t bdaddr; + __u8 type; +}; + +struct hci_inquiry_req { + __u16 dev_id; + __u16 flags; + __u8 lap[3]; + __u8 length; + __u8 num_rsp; +}; +#define IREQ_CACHE_FLUSH 0x0001 + +#endif /* __HCI_H */ diff --git a/include/compat/net/bluetooth/hci_core.h b/include/compat/net/bluetooth/hci_core.h new file mode 100644 index 00000000..f333e768 --- /dev/null +++ b/include/compat/net/bluetooth/hci_core.h @@ -0,0 +1,988 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. + + Written 2000,2001 by Maxim Krasnyansky + + 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; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +#ifndef __HCI_CORE_H +#define __HCI_CORE_H + +#include +#include + +/* HCI upper protocols */ +#define HCI_PROTO_L2CAP 0 +#define HCI_PROTO_SCO 1 + +/* HCI priority */ +#define HCI_PRIO_MAX 7 + +/* HCI Core structures */ +struct inquiry_data { + bdaddr_t bdaddr; + __u8 pscan_rep_mode; + __u8 pscan_period_mode; + __u8 pscan_mode; + __u8 dev_class[3]; + __le16 clock_offset; + __s8 rssi; + __u8 ssp_mode; +}; + +struct inquiry_entry { + struct inquiry_entry *next; + __u32 timestamp; + struct inquiry_data data; +}; + +struct inquiry_cache { + spinlock_t lock; + __u32 timestamp; + struct inquiry_entry *list; +}; + +struct hci_conn_hash { + struct list_head list; + spinlock_t lock; + unsigned int acl_num; + unsigned int sco_num; + unsigned int le_num; +}; + +struct hci_chan_hash { + struct list_head list; + spinlock_t lock; + unsigned int num; +}; + +struct bdaddr_list { + struct list_head list; + bdaddr_t bdaddr; +}; + +struct bt_uuid { + struct list_head list; + u8 uuid[16]; + u8 svc_hint; +}; + +struct key_master_id { + __le16 ediv; + u8 rand[8]; +} __packed; + +struct link_key_data { + bdaddr_t bdaddr; + u8 type; + u8 val[16]; + u8 pin_len; + u8 dlen; + u8 data[0]; +} __packed; + +struct link_key { + struct list_head list; + bdaddr_t bdaddr; + u8 type; + u8 val[16]; + u8 pin_len; + u8 dlen; + u8 data[0]; +}; + +struct oob_data { + struct list_head list; + bdaddr_t bdaddr; + u8 hash[16]; + u8 randomizer[16]; +}; + +struct adv_entry { + struct list_head list; + bdaddr_t bdaddr; + u8 bdaddr_type; +}; + +#define NUM_REASSEMBLY 4 +struct hci_dev { + struct list_head list; + spinlock_t lock; + atomic_t refcnt; + + char name[8]; + unsigned long flags; + __u16 id; + __u8 bus; + __u8 dev_type; + bdaddr_t bdaddr; + __u8 dev_name[HCI_MAX_NAME_LENGTH]; + __u8 eir[HCI_MAX_EIR_LENGTH]; + __u8 dev_class[3]; + __u8 major_class; + __u8 minor_class; + __u8 features[8]; + __u8 extfeatures[8]; + __u8 commands[64]; + __u8 ssp_mode; + __u8 hci_ver; + __u16 hci_rev; + __u8 lmp_ver; + __u16 manufacturer; + __le16 lmp_subver; + __u16 voice_setting; + __u8 io_capability; + + __u16 pkt_type; + __u16 esco_type; + __u16 link_policy; + __u16 link_mode; + + __u32 idle_timeout; + __u16 sniff_min_interval; + __u16 sniff_max_interval; + + __u8 amp_status; + __u32 amp_total_bw; + __u32 amp_max_bw; + __u32 amp_min_latency; + __u32 amp_max_pdu; + __u8 amp_type; + __u16 amp_pal_cap; + __u16 amp_assoc_size; + __u32 amp_max_flush_to; + __u32 amp_be_flush_to; + + unsigned int auto_accept_delay; + + unsigned long quirks; + + atomic_t cmd_cnt; + unsigned int acl_cnt; + unsigned int sco_cnt; + unsigned int le_cnt; + + unsigned int acl_mtu; + unsigned int sco_mtu; + unsigned int le_mtu; + unsigned int acl_pkts; + unsigned int sco_pkts; + unsigned int le_pkts; + + unsigned long acl_last_tx; + unsigned long sco_last_tx; + unsigned long le_last_tx; + + struct workqueue_struct *workqueue; + + struct work_struct power_on; + struct delayed_work power_off; + + __u16 discov_timeout; + struct delayed_work discov_off; + + struct timer_list cmd_timer; + struct tasklet_struct cmd_task; + struct tasklet_struct rx_task; + struct tasklet_struct tx_task; + + struct sk_buff_head rx_q; + struct sk_buff_head raw_q; + struct sk_buff_head cmd_q; + + struct sk_buff *sent_cmd; + struct sk_buff *reassembly[NUM_REASSEMBLY]; + + struct mutex req_lock; + wait_queue_head_t req_wait_q; + __u32 req_status; + __u32 req_result; + + __u16 init_last_cmd; + + struct list_head mgmt_pending; + + struct inquiry_cache inq_cache; + struct hci_conn_hash conn_hash; + struct list_head blacklist; + + struct list_head uuids; + + struct list_head link_keys; + + struct list_head remote_oob_data; + + struct list_head adv_entries; + struct timer_list adv_timer; + + struct hci_dev_stats stat; + + struct sk_buff_head driver_init; + + void *driver_data; + void *core_data; + + atomic_t promisc; + + struct dentry *debugfs; + + struct device *parent; + struct device dev; + + struct rfkill *rfkill; + + struct module *owner; + + int (*open)(struct hci_dev *hdev); + int (*close)(struct hci_dev *hdev); + int (*flush)(struct hci_dev *hdev); + int (*send)(struct sk_buff *skb); + void (*destruct)(struct hci_dev *hdev); + void (*notify)(struct hci_dev *hdev, unsigned int evt); + int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg); +}; + +struct hci_conn { + struct list_head list; + + atomic_t refcnt; + + bdaddr_t dst; + __u8 dst_type; + __u16 handle; + __u16 state; + __u8 mode; + __u8 type; + __u8 out; + __u8 attempt; + __u8 dev_class[3]; + __u8 features[8]; + __u8 ssp_mode; + __u16 interval; + __u16 pkt_type; + __u16 link_policy; + __u32 link_mode; + __u8 key_type; + __u8 auth_type; + __u8 sec_level; + __u8 pending_sec_level; + __u8 pin_length; + __u8 enc_key_size; + __u8 io_capability; + __u8 power_save; + __u16 disc_timeout; + unsigned long pend; + + __u8 remote_cap; + __u8 remote_oob; + __u8 remote_auth; + + unsigned int sent; + + struct sk_buff_head data_q; + struct hci_chan_hash chan_hash; + + struct timer_list disc_timer; + struct timer_list idle_timer; + struct timer_list auto_accept_timer; + + struct work_struct work_add; + struct work_struct work_del; + + struct device dev; + atomic_t devref; + + struct hci_dev *hdev; + void *l2cap_data; + void *sco_data; + + struct hci_conn *link; + + void (*connect_cfm_cb) (struct hci_conn *conn, u8 status); + void (*security_cfm_cb) (struct hci_conn *conn, u8 status); + void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason); +}; + +struct hci_chan { + struct list_head list; + + struct hci_conn *conn; + struct sk_buff_head data_q; + unsigned int sent; +}; + +extern struct hci_proto *hci_proto[]; +extern struct list_head hci_dev_list; +extern struct list_head hci_cb_list; +extern rwlock_t hci_dev_list_lock; +extern rwlock_t hci_cb_list_lock; + +/* ----- Inquiry cache ----- */ +#define INQUIRY_CACHE_AGE_MAX (HZ*30) /* 30 seconds */ +#define INQUIRY_ENTRY_AGE_MAX (HZ*60) /* 60 seconds */ + +#define inquiry_cache_lock(c) spin_lock(&c->lock) +#define inquiry_cache_unlock(c) spin_unlock(&c->lock) +#define inquiry_cache_lock_bh(c) spin_lock_bh(&c->lock) +#define inquiry_cache_unlock_bh(c) spin_unlock_bh(&c->lock) + +static inline void inquiry_cache_init(struct hci_dev *hdev) +{ + struct inquiry_cache *c = &hdev->inq_cache; + spin_lock_init(&c->lock); + c->list = NULL; +} + +static inline int inquiry_cache_empty(struct hci_dev *hdev) +{ + struct inquiry_cache *c = &hdev->inq_cache; + return c->list == NULL; +} + +static inline long inquiry_cache_age(struct hci_dev *hdev) +{ + struct inquiry_cache *c = &hdev->inq_cache; + return jiffies - c->timestamp; +} + +static inline long inquiry_entry_age(struct inquiry_entry *e) +{ + return jiffies - e->timestamp; +} + +struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, + bdaddr_t *bdaddr); +void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data); + +/* ----- HCI Connections ----- */ +enum { + HCI_CONN_AUTH_PEND, + HCI_CONN_REAUTH_PEND, + HCI_CONN_ENCRYPT_PEND, + HCI_CONN_RSWITCH_PEND, + HCI_CONN_MODE_CHANGE_PEND, + HCI_CONN_SCO_SETUP_PEND, + HCI_CONN_LE_SMP_PEND, +}; + +static inline void hci_conn_hash_init(struct hci_dev *hdev) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + INIT_LIST_HEAD(&h->list); + spin_lock_init(&h->lock); + h->acl_num = 0; + h->sco_num = 0; +} + +static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + list_add(&c->list, &h->list); + switch (c->type) { + case ACL_LINK: + h->acl_num++; + break; + case LE_LINK: + h->le_num++; + break; + case SCO_LINK: + case ESCO_LINK: + h->sco_num++; + break; + } +} + +static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + list_del(&c->list); + switch (c->type) { + case ACL_LINK: + h->acl_num--; + break; + case LE_LINK: + h->le_num--; + break; + case SCO_LINK: + case ESCO_LINK: + h->sco_num--; + break; + } +} + +static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + switch (type) { + case ACL_LINK: + return h->acl_num; + case LE_LINK: + return h->le_num; + case SCO_LINK: + case ESCO_LINK: + return h->sco_num; + default: + return 0; + } +} + +static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev, + __u16 handle) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct list_head *p; + struct hci_conn *c; + + list_for_each(p, &h->list) { + c = list_entry(p, struct hci_conn, list); + if (c->handle == handle) + return c; + } + return NULL; +} + +static inline struct hci_conn *hci_conn_hash_lookup_ba(struct hci_dev *hdev, + __u8 type, bdaddr_t *ba) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct list_head *p; + struct hci_conn *c; + + list_for_each(p, &h->list) { + c = list_entry(p, struct hci_conn, list); + if (c->type == type && !bacmp(&c->dst, ba)) + return c; + } + return NULL; +} + +static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev, + __u8 type, __u16 state) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct list_head *p; + struct hci_conn *c; + + list_for_each(p, &h->list) { + c = list_entry(p, struct hci_conn, list); + if (c->type == type && c->state == state) + return c; + } + return NULL; +} + +static inline void hci_chan_hash_init(struct hci_conn *c) +{ + struct hci_chan_hash *h = &c->chan_hash; + INIT_LIST_HEAD(&h->list); + spin_lock_init(&h->lock); + h->num = 0; +} + +static inline void hci_chan_hash_add(struct hci_conn *c, struct hci_chan *chan) +{ + struct hci_chan_hash *h = &c->chan_hash; + list_add(&chan->list, &h->list); + h->num++; +} + +static inline void hci_chan_hash_del(struct hci_conn *c, struct hci_chan *chan) +{ + struct hci_chan_hash *h = &c->chan_hash; + list_del(&chan->list); + h->num--; +} + +void hci_acl_connect(struct hci_conn *conn); +void hci_acl_disconn(struct hci_conn *conn, __u8 reason); +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); +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); + +struct hci_chan *hci_chan_create(struct hci_conn *conn); +int hci_chan_del(struct hci_chan *chan); +void hci_chan_hash_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); +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); +int hci_conn_change_link_key(struct hci_conn *conn); +int hci_conn_switch_role(struct hci_conn *conn, __u8 role); + +void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active); +void hci_conn_enter_sniff_mode(struct hci_conn *conn); + +void hci_conn_hold_device(struct hci_conn *conn); +void hci_conn_put_device(struct hci_conn *conn); + +static inline void hci_conn_hold(struct hci_conn *conn) +{ + atomic_inc(&conn->refcnt); + del_timer(&conn->disc_timer); +} + +static inline void hci_conn_put(struct hci_conn *conn) +{ + if (atomic_dec_and_test(&conn->refcnt)) { + unsigned long timeo; + if (conn->type == ACL_LINK || conn->type == LE_LINK) { + del_timer(&conn->idle_timer); + if (conn->state == BT_CONNECTED) { + timeo = msecs_to_jiffies(conn->disc_timeout); + if (!conn->out) + timeo *= 2; + } else { + timeo = msecs_to_jiffies(10); + } + } else { + timeo = msecs_to_jiffies(10); + } + mod_timer(&conn->disc_timer, jiffies + timeo); + } +} + +/* ----- HCI Devices ----- */ +static inline void __hci_dev_put(struct hci_dev *d) +{ + if (atomic_dec_and_test(&d->refcnt)) + d->destruct(d); +} + +/* + * hci_dev_put and hci_dev_hold are macros to avoid dragging all the + * overhead of all the modular infrastructure into this header. + */ +#define hci_dev_put(d) \ +do { \ + __hci_dev_put(d); \ + module_put(d->owner); \ +} while (0) + +static inline struct hci_dev *__hci_dev_hold(struct hci_dev *d) +{ + atomic_inc(&d->refcnt); + return d; +} + +#define hci_dev_hold(d) \ +({ \ + try_module_get(d->owner) ? __hci_dev_hold(d) : NULL; \ +}) + +#define hci_dev_lock(d) spin_lock(&d->lock) +#define hci_dev_unlock(d) spin_unlock(&d->lock) +#define hci_dev_lock_bh(d) spin_lock_bh(&d->lock) +#define hci_dev_unlock_bh(d) spin_unlock_bh(&d->lock) + +struct hci_dev *hci_dev_get(int index); +struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst); + +struct hci_dev *hci_alloc_dev(void); +void hci_free_dev(struct hci_dev *hdev); +int hci_register_dev(struct hci_dev *hdev); +void hci_unregister_dev(struct hci_dev *hdev); +int hci_suspend_dev(struct hci_dev *hdev); +int hci_resume_dev(struct hci_dev *hdev); +int hci_dev_open(__u16 dev); +int hci_dev_close(__u16 dev); +int hci_dev_reset(__u16 dev); +int hci_dev_reset_stat(__u16 dev); +int hci_dev_cmd(unsigned int cmd, void __user *arg); +int hci_get_dev_list(void __user *arg); +int hci_get_dev_info(void __user *arg); +int hci_get_conn_list(void __user *arg); +int hci_get_conn_info(struct hci_dev *hdev, void __user *arg); +int hci_get_auth_info(struct hci_dev *hdev, void __user *arg); +int hci_inquiry(void __user *arg); + +struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); +int hci_blacklist_clear(struct hci_dev *hdev); +int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr); +int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr); + +int hci_uuids_clear(struct hci_dev *hdev); + +int hci_link_keys_clear(struct hci_dev *hdev); +struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); +int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, + bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len); +struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]); +struct link_key *hci_find_link_key_type(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 type); +int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, + u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16]); +int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); + +int hci_remote_oob_data_clear(struct hci_dev *hdev); +struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev, + bdaddr_t *bdaddr); +int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, + u8 *randomizer); +int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); + +#define ADV_CLEAR_TIMEOUT (3*60*HZ) /* Three minutes */ +int hci_adv_entries_clear(struct hci_dev *hdev); +struct adv_entry *hci_find_adv_entry(struct hci_dev *hdev, bdaddr_t *bdaddr); +int hci_add_adv_entry(struct hci_dev *hdev, + struct hci_ev_le_advertising_info *ev); + +void hci_del_off_timer(struct hci_dev *hdev); + +void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); + +int hci_recv_frame(struct sk_buff *skb); +int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count); +int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count); + +void hci_init_sysfs(struct hci_dev *hdev); +int hci_add_sysfs(struct hci_dev *hdev); +void hci_del_sysfs(struct hci_dev *hdev); +void hci_conn_init_sysfs(struct hci_conn *conn); +void hci_conn_add_sysfs(struct hci_conn *conn); +void hci_conn_del_sysfs(struct hci_conn *conn); + +#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->parent = (pdev)) + +/* ----- LMP capabilities ----- */ +#define lmp_rswitch_capable(dev) ((dev)->features[0] & LMP_RSWITCH) +#define lmp_encrypt_capable(dev) ((dev)->features[0] & LMP_ENCRYPT) +#define lmp_sniff_capable(dev) ((dev)->features[0] & LMP_SNIFF) +#define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR) +#define lmp_esco_capable(dev) ((dev)->features[3] & LMP_ESCO) +#define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR) +#define lmp_no_flush_capable(dev) ((dev)->features[6] & LMP_NO_FLUSH) +#define lmp_le_capable(dev) ((dev)->features[4] & LMP_LE) + +/* ----- Extended LMP capabilities ----- */ +#define lmp_host_le_capable(dev) ((dev)->extfeatures[0] & LMP_HOST_LE) + +/* ----- HCI protocols ----- */ +struct hci_proto { + char *name; + unsigned int id; + unsigned long flags; + + void *priv; + + int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, + __u8 type); + int (*connect_cfm) (struct hci_conn *conn, __u8 status); + int (*disconn_ind) (struct hci_conn *conn); + int (*disconn_cfm) (struct hci_conn *conn, __u8 reason); + int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb, + __u16 flags); + int (*recv_scodata) (struct hci_conn *conn, struct sk_buff *skb); + int (*security_cfm) (struct hci_conn *conn, __u8 status, + __u8 encrypt); +}; + +static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, + __u8 type) +{ + register struct hci_proto *hp; + int mask = 0; + + hp = hci_proto[HCI_PROTO_L2CAP]; + if (hp && hp->connect_ind) + mask |= hp->connect_ind(hdev, bdaddr, type); + + hp = hci_proto[HCI_PROTO_SCO]; + if (hp && hp->connect_ind) + mask |= hp->connect_ind(hdev, bdaddr, type); + + return mask; +} + +static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status) +{ + register struct hci_proto *hp; + + hp = hci_proto[HCI_PROTO_L2CAP]; + if (hp && hp->connect_cfm) + hp->connect_cfm(conn, status); + + hp = hci_proto[HCI_PROTO_SCO]; + if (hp && hp->connect_cfm) + hp->connect_cfm(conn, status); + + if (conn->connect_cfm_cb) + conn->connect_cfm_cb(conn, status); +} + +static inline int hci_proto_disconn_ind(struct hci_conn *conn) +{ + register struct hci_proto *hp; + int reason = HCI_ERROR_REMOTE_USER_TERM; + + hp = hci_proto[HCI_PROTO_L2CAP]; + if (hp && hp->disconn_ind) + reason = hp->disconn_ind(conn); + + hp = hci_proto[HCI_PROTO_SCO]; + if (hp && hp->disconn_ind) + reason = hp->disconn_ind(conn); + + return reason; +} + +static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason) +{ + register struct hci_proto *hp; + + hp = hci_proto[HCI_PROTO_L2CAP]; + if (hp && hp->disconn_cfm) + hp->disconn_cfm(conn, reason); + + hp = hci_proto[HCI_PROTO_SCO]; + if (hp && hp->disconn_cfm) + hp->disconn_cfm(conn, reason); + + if (conn->disconn_cfm_cb) + conn->disconn_cfm_cb(conn, reason); +} + +static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) +{ + register struct hci_proto *hp; + __u8 encrypt; + + if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) + return; + + encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; + + hp = hci_proto[HCI_PROTO_L2CAP]; + if (hp && hp->security_cfm) + hp->security_cfm(conn, status, encrypt); + + hp = hci_proto[HCI_PROTO_SCO]; + if (hp && hp->security_cfm) + hp->security_cfm(conn, status, encrypt); + + if (conn->security_cfm_cb) + conn->security_cfm_cb(conn, status); +} + +static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, + __u8 encrypt) +{ + register struct hci_proto *hp; + + hp = hci_proto[HCI_PROTO_L2CAP]; + if (hp && hp->security_cfm) + hp->security_cfm(conn, status, encrypt); + + hp = hci_proto[HCI_PROTO_SCO]; + if (hp && hp->security_cfm) + hp->security_cfm(conn, status, encrypt); + + if (conn->security_cfm_cb) + conn->security_cfm_cb(conn, status); +} + +int hci_register_proto(struct hci_proto *hproto); +int hci_unregister_proto(struct hci_proto *hproto); + +/* ----- HCI callbacks ----- */ +struct hci_cb { + struct list_head list; + + char *name; + + void (*security_cfm) (struct hci_conn *conn, __u8 status, + __u8 encrypt); + void (*key_change_cfm) (struct hci_conn *conn, __u8 status); + void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role); +}; + +static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) +{ + struct list_head *p; + __u8 encrypt; + + hci_proto_auth_cfm(conn, status); + + if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) + return; + + encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; + + read_lock_bh(&hci_cb_list_lock); + list_for_each(p, &hci_cb_list) { + struct hci_cb *cb = list_entry(p, struct hci_cb, list); + if (cb->security_cfm) + cb->security_cfm(conn, status, encrypt); + } + read_unlock_bh(&hci_cb_list_lock); +} + +static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, + __u8 encrypt) +{ + struct list_head *p; + + if (conn->sec_level == BT_SECURITY_SDP) + conn->sec_level = BT_SECURITY_LOW; + + if (conn->pending_sec_level > conn->sec_level) + conn->sec_level = conn->pending_sec_level; + + hci_proto_encrypt_cfm(conn, status, encrypt); + + read_lock_bh(&hci_cb_list_lock); + list_for_each(p, &hci_cb_list) { + struct hci_cb *cb = list_entry(p, struct hci_cb, list); + if (cb->security_cfm) + cb->security_cfm(conn, status, encrypt); + } + read_unlock_bh(&hci_cb_list_lock); +} + +static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status) +{ + struct list_head *p; + + read_lock_bh(&hci_cb_list_lock); + list_for_each(p, &hci_cb_list) { + struct hci_cb *cb = list_entry(p, struct hci_cb, list); + if (cb->key_change_cfm) + cb->key_change_cfm(conn, status); + } + read_unlock_bh(&hci_cb_list_lock); +} + +static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, + __u8 role) +{ + struct list_head *p; + + read_lock_bh(&hci_cb_list_lock); + list_for_each(p, &hci_cb_list) { + struct hci_cb *cb = list_entry(p, struct hci_cb, list); + if (cb->role_switch_cfm) + cb->role_switch_cfm(conn, status, role); + } + read_unlock_bh(&hci_cb_list_lock); +} + +int hci_register_cb(struct hci_cb *hcb); +int hci_unregister_cb(struct hci_cb *hcb); + +int hci_register_notifier(struct notifier_block *nb); +int hci_unregister_notifier(struct notifier_block *nb); + +int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param); +void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags); +void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb); + +void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); + +void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data); + +/* ----- HCI Sockets ----- */ +void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb, + struct sock *skip_sk); + +/* Management interface */ +int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); +int mgmt_index_added(struct hci_dev *hdev); +int mgmt_index_removed(struct hci_dev *hdev); +int mgmt_powered(struct hci_dev *hdev, u8 powered); +int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); +int mgmt_connectable(struct hci_dev *hdev, u8 connectable); +int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); +int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, + u8 persistent); +int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); +int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); +int mgmt_disconnect_failed(struct hci_dev *hdev); +int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, + u8 status); +int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure); +int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status); +int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status); +int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, + __le32 value, u8 confirm_hint); +int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status); +int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 status); +int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); +int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); +int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, + u8 *randomizer, u8 status); +int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, + u8 *dev_class, s8 rssi, u8 *eir); +int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name); +int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status); +int mgmt_discovering(struct hci_dev *hdev, u8 discovering); +int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr); +int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr); + +/* HCI info for socket */ +#define hci_pi(sk) ((struct hci_pinfo *) sk) + +struct hci_pinfo { + struct bt_sock bt; + struct hci_dev *hdev; + struct hci_filter filter; + __u32 cmsg_mask; + unsigned short channel; +}; + +/* HCI security filter */ +#define HCI_SFLT_MAX_OGF 5 + +struct hci_sec_filter { + __u32 type_mask; + __u32 event_mask[2]; + __u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4]; +}; + +/* ----- HCI requests ----- */ +#define HCI_REQ_DONE 0 +#define HCI_REQ_PEND 1 +#define HCI_REQ_CANCELED 2 + +#define hci_req_lock(d) mutex_lock(&d->req_lock) +#define hci_req_unlock(d) mutex_unlock(&d->req_lock) + +void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result); + +void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, + u16 latency, u16 to_multiplier); +void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], + __u8 ltk[16]); +void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]); +void hci_le_ltk_neg_reply(struct hci_conn *conn); + +int hci_do_inquiry(struct hci_dev *hdev, u8 length); +int hci_cancel_inquiry(struct hci_dev *hdev); + +#endif /* __HCI_CORE_H */ diff --git a/include/compat/net/bluetooth/l2cap.h b/include/compat/net/bluetooth/l2cap.h new file mode 100644 index 00000000..875021ad --- /dev/null +++ b/include/compat/net/bluetooth/l2cap.h @@ -0,0 +1,814 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2000-2001 Qualcomm Incorporated + Copyright (C) 2009-2010 Gustavo F. Padovan + Copyright (C) 2010 Google Inc. + + Written 2000,2001 by Maxim Krasnyansky + + 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; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +#ifndef __L2CAP_H +#define __L2CAP_H + +#include + +/* L2CAP defaults */ +#define L2CAP_DEFAULT_MTU 672 +#define L2CAP_DEFAULT_MIN_MTU 48 +#define L2CAP_DEFAULT_FLUSH_TO 0xffff +#define L2CAP_DEFAULT_TX_WINDOW 63 +#define L2CAP_DEFAULT_EXT_WINDOW 0x3FFF +#define L2CAP_DEFAULT_MAX_TX 3 +#define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */ +#define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */ +#define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */ +#define L2CAP_DEFAULT_ACK_TO 200 +#define L2CAP_LE_DEFAULT_MTU 23 +#define L2CAP_DEFAULT_MAX_SDU_SIZE 0xFFFF +#define L2CAP_DEFAULT_SDU_ITIME 0xFFFFFFFF +#define L2CAP_DEFAULT_ACC_LAT 0xFFFFFFFF + +#define L2CAP_DISC_TIMEOUT (100) +#define L2CAP_DISC_REJ_TIMEOUT (5000) /* 5 seconds */ +#define L2CAP_ENC_TIMEOUT (5000) /* 5 seconds */ +#define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */ +#define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */ + +/* L2CAP socket address */ +struct sockaddr_l2 { + sa_family_t l2_family; + __le16 l2_psm; + bdaddr_t l2_bdaddr; + __le16 l2_cid; +}; + +/* L2CAP socket options */ +#define L2CAP_OPTIONS 0x01 +struct l2cap_options { + __u16 omtu; + __u16 imtu; + __u16 flush_to; + __u8 mode; + __u8 fcs; + __u8 max_tx; + __u16 txwin_size; +}; + +#define L2CAP_CONNINFO 0x02 +struct l2cap_conninfo { + __u16 hci_handle; + __u8 dev_class[3]; +}; + +#define L2CAP_LM 0x03 +#define L2CAP_LM_MASTER 0x0001 +#define L2CAP_LM_AUTH 0x0002 +#define L2CAP_LM_ENCRYPT 0x0004 +#define L2CAP_LM_TRUSTED 0x0008 +#define L2CAP_LM_RELIABLE 0x0010 +#define L2CAP_LM_SECURE 0x0020 + +/* L2CAP command codes */ +#define L2CAP_COMMAND_REJ 0x01 +#define L2CAP_CONN_REQ 0x02 +#define L2CAP_CONN_RSP 0x03 +#define L2CAP_CONF_REQ 0x04 +#define L2CAP_CONF_RSP 0x05 +#define L2CAP_DISCONN_REQ 0x06 +#define L2CAP_DISCONN_RSP 0x07 +#define L2CAP_ECHO_REQ 0x08 +#define L2CAP_ECHO_RSP 0x09 +#define L2CAP_INFO_REQ 0x0a +#define L2CAP_INFO_RSP 0x0b +#define L2CAP_CREATE_CHAN_REQ 0x0c +#define L2CAP_CREATE_CHAN_RSP 0x0d +#define L2CAP_MOVE_CHAN_REQ 0x0e +#define L2CAP_MOVE_CHAN_RSP 0x0f +#define L2CAP_MOVE_CHAN_CFM 0x10 +#define L2CAP_MOVE_CHAN_CFM_RSP 0x11 +#define L2CAP_CONN_PARAM_UPDATE_REQ 0x12 +#define L2CAP_CONN_PARAM_UPDATE_RSP 0x13 + +/* L2CAP extended feature mask */ +#define L2CAP_FEAT_FLOWCTL 0x00000001 +#define L2CAP_FEAT_RETRANS 0x00000002 +#define L2CAP_FEAT_BIDIR_QOS 0x00000004 +#define L2CAP_FEAT_ERTM 0x00000008 +#define L2CAP_FEAT_STREAMING 0x00000010 +#define L2CAP_FEAT_FCS 0x00000020 +#define L2CAP_FEAT_EXT_FLOW 0x00000040 +#define L2CAP_FEAT_FIXED_CHAN 0x00000080 +#define L2CAP_FEAT_EXT_WINDOW 0x00000100 +#define L2CAP_FEAT_UCD 0x00000200 + +/* L2CAP checksum option */ +#define L2CAP_FCS_NONE 0x00 +#define L2CAP_FCS_CRC16 0x01 + +/* L2CAP fixed channels */ +#define L2CAP_FC_L2CAP 0x02 +#define L2CAP_FC_A2MP 0x08 + +/* L2CAP Control Field bit masks */ +#define L2CAP_CTRL_SAR 0xC000 +#define L2CAP_CTRL_REQSEQ 0x3F00 +#define L2CAP_CTRL_TXSEQ 0x007E +#define L2CAP_CTRL_SUPERVISE 0x000C + +#define L2CAP_CTRL_RETRANS 0x0080 +#define L2CAP_CTRL_FINAL 0x0080 +#define L2CAP_CTRL_POLL 0x0010 +#define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */ + +#define L2CAP_CTRL_TXSEQ_SHIFT 1 +#define L2CAP_CTRL_SUPER_SHIFT 2 +#define L2CAP_CTRL_REQSEQ_SHIFT 8 +#define L2CAP_CTRL_SAR_SHIFT 14 + +/* L2CAP Extended Control Field bit mask */ +#define L2CAP_EXT_CTRL_TXSEQ 0xFFFC0000 +#define L2CAP_EXT_CTRL_SAR 0x00030000 +#define L2CAP_EXT_CTRL_SUPERVISE 0x00030000 +#define L2CAP_EXT_CTRL_REQSEQ 0x0000FFFC + +#define L2CAP_EXT_CTRL_POLL 0x00040000 +#define L2CAP_EXT_CTRL_FINAL 0x00000002 +#define L2CAP_EXT_CTRL_FRAME_TYPE 0x00000001 /* I- or S-Frame */ + +#define L2CAP_EXT_CTRL_REQSEQ_SHIFT 2 +#define L2CAP_EXT_CTRL_SAR_SHIFT 16 +#define L2CAP_EXT_CTRL_SUPER_SHIFT 16 +#define L2CAP_EXT_CTRL_TXSEQ_SHIFT 18 + +/* L2CAP Supervisory Function */ +#define L2CAP_SUPER_RR 0x00 +#define L2CAP_SUPER_REJ 0x01 +#define L2CAP_SUPER_RNR 0x02 +#define L2CAP_SUPER_SREJ 0x03 + +/* L2CAP Segmentation and Reassembly */ +#define L2CAP_SAR_UNSEGMENTED 0x00 +#define L2CAP_SAR_START 0x01 +#define L2CAP_SAR_END 0x02 +#define L2CAP_SAR_CONTINUE 0x03 + +/* L2CAP Command rej. reasons */ +#define L2CAP_REJ_NOT_UNDERSTOOD 0x0000 +#define L2CAP_REJ_MTU_EXCEEDED 0x0001 +#define L2CAP_REJ_INVALID_CID 0x0002 + +/* L2CAP structures */ +struct l2cap_hdr { + __le16 len; + __le16 cid; +} __packed; +#define L2CAP_HDR_SIZE 4 +#define L2CAP_ENH_HDR_SIZE 6 +#define L2CAP_EXT_HDR_SIZE 8 + +#define L2CAP_FCS_SIZE 2 +#define L2CAP_SDULEN_SIZE 2 +#define L2CAP_PSMLEN_SIZE 2 + +struct l2cap_cmd_hdr { + __u8 code; + __u8 ident; + __le16 len; +} __packed; +#define L2CAP_CMD_HDR_SIZE 4 + +struct l2cap_cmd_rej_unk { + __le16 reason; +} __packed; + +struct l2cap_cmd_rej_mtu { + __le16 reason; + __le16 max_mtu; +} __packed; + +struct l2cap_cmd_rej_cid { + __le16 reason; + __le16 scid; + __le16 dcid; +} __packed; + +struct l2cap_conn_req { + __le16 psm; + __le16 scid; +} __packed; + +struct l2cap_conn_rsp { + __le16 dcid; + __le16 scid; + __le16 result; + __le16 status; +} __packed; + +/* channel indentifier */ +#define L2CAP_CID_SIGNALING 0x0001 +#define L2CAP_CID_CONN_LESS 0x0002 +#define L2CAP_CID_LE_DATA 0x0004 +#define L2CAP_CID_LE_SIGNALING 0x0005 +#define L2CAP_CID_SMP 0x0006 +#define L2CAP_CID_DYN_START 0x0040 +#define L2CAP_CID_DYN_END 0xffff + +/* connect/create channel results */ +#define L2CAP_CR_SUCCESS 0x0000 +#define L2CAP_CR_PEND 0x0001 +#define L2CAP_CR_BAD_PSM 0x0002 +#define L2CAP_CR_SEC_BLOCK 0x0003 +#define L2CAP_CR_NO_MEM 0x0004 +#define L2CAP_CR_BAD_AMP 0x0005 + +/* connect/create channel status */ +#define L2CAP_CS_NO_INFO 0x0000 +#define L2CAP_CS_AUTHEN_PEND 0x0001 +#define L2CAP_CS_AUTHOR_PEND 0x0002 + +struct l2cap_conf_req { + __le16 dcid; + __le16 flags; + __u8 data[0]; +} __packed; + +struct l2cap_conf_rsp { + __le16 scid; + __le16 flags; + __le16 result; + __u8 data[0]; +} __packed; + +#define L2CAP_CONF_SUCCESS 0x0000 +#define L2CAP_CONF_UNACCEPT 0x0001 +#define L2CAP_CONF_REJECT 0x0002 +#define L2CAP_CONF_UNKNOWN 0x0003 +#define L2CAP_CONF_PENDING 0x0004 +#define L2CAP_CONF_EFS_REJECT 0x0005 + +struct l2cap_conf_opt { + __u8 type; + __u8 len; + __u8 val[0]; +} __packed; +#define L2CAP_CONF_OPT_SIZE 2 + +#define L2CAP_CONF_HINT 0x80 +#define L2CAP_CONF_MASK 0x7f + +#define L2CAP_CONF_MTU 0x01 +#define L2CAP_CONF_FLUSH_TO 0x02 +#define L2CAP_CONF_QOS 0x03 +#define L2CAP_CONF_RFC 0x04 +#define L2CAP_CONF_FCS 0x05 +#define L2CAP_CONF_EFS 0x06 +#define L2CAP_CONF_EWS 0x07 + +#define L2CAP_CONF_MAX_SIZE 22 + +struct l2cap_conf_rfc { + __u8 mode; + __u8 txwin_size; + __u8 max_transmit; + __le16 retrans_timeout; + __le16 monitor_timeout; + __le16 max_pdu_size; +} __packed; + +#define L2CAP_MODE_BASIC 0x00 +#define L2CAP_MODE_RETRANS 0x01 +#define L2CAP_MODE_FLOWCTL 0x02 +#define L2CAP_MODE_ERTM 0x03 +#define L2CAP_MODE_STREAMING 0x04 + +struct l2cap_conf_efs { + __u8 id; + __u8 stype; + __le16 msdu; + __le32 sdu_itime; + __le32 acc_lat; + __le32 flush_to; +} __packed; + +#define L2CAP_SERV_NOTRAFIC 0x00 +#define L2CAP_SERV_BESTEFFORT 0x01 +#define L2CAP_SERV_GUARANTEED 0x02 + +#define L2CAP_BESTEFFORT_ID 0x01 + +struct l2cap_disconn_req { + __le16 dcid; + __le16 scid; +} __packed; + +struct l2cap_disconn_rsp { + __le16 dcid; + __le16 scid; +} __packed; + +struct l2cap_info_req { + __le16 type; +} __packed; + +struct l2cap_info_rsp { + __le16 type; + __le16 result; + __u8 data[0]; +} __packed; + +struct l2cap_create_chan_req { + __le16 psm; + __le16 scid; + __u8 amp_id; +} __packed; + +struct l2cap_create_chan_rsp { + __le16 dcid; + __le16 scid; + __le16 result; + __le16 status; +} __packed; + +struct l2cap_move_chan_req { + __le16 icid; + __u8 dest_amp_id; +} __packed; + +struct l2cap_move_chan_rsp { + __le16 icid; + __le16 result; +} __packed; + +#define L2CAP_MR_SUCCESS 0x0000 +#define L2CAP_MR_PEND 0x0001 +#define L2CAP_MR_BAD_ID 0x0002 +#define L2CAP_MR_SAME_ID 0x0003 +#define L2CAP_MR_NOT_SUPP 0x0004 +#define L2CAP_MR_COLLISION 0x0005 +#define L2CAP_MR_NOT_ALLOWED 0x0006 + +struct l2cap_move_chan_cfm { + __le16 icid; + __le16 result; +} __packed; + +#define L2CAP_MC_CONFIRMED 0x0000 +#define L2CAP_MC_UNCONFIRMED 0x0001 + +struct l2cap_move_chan_cfm_rsp { + __le16 icid; +} __packed; + +/* info type */ +#define L2CAP_IT_CL_MTU 0x0001 +#define L2CAP_IT_FEAT_MASK 0x0002 +#define L2CAP_IT_FIXED_CHAN 0x0003 + +/* info result */ +#define L2CAP_IR_SUCCESS 0x0000 +#define L2CAP_IR_NOTSUPP 0x0001 + +struct l2cap_conn_param_update_req { + __le16 min; + __le16 max; + __le16 latency; + __le16 to_multiplier; +} __packed; + +struct l2cap_conn_param_update_rsp { + __le16 result; +} __packed; + +/* Connection Parameters result */ +#define L2CAP_CONN_PARAM_ACCEPTED 0x0000 +#define L2CAP_CONN_PARAM_REJECTED 0x0001 + +/* ----- L2CAP channels and connections ----- */ +struct srej_list { + __u16 tx_seq; + struct list_head list; +}; + +struct l2cap_chan { + struct sock *sk; + + struct l2cap_conn *conn; + + __u8 state; + + atomic_t refcnt; + + __le16 psm; + __u16 dcid; + __u16 scid; + + __u16 imtu; + __u16 omtu; + __u16 flush_to; + __u8 mode; + __u8 chan_type; + __u8 chan_policy; + + __le16 sport; + + __u8 sec_level; + + __u8 ident; + + __u8 conf_req[64]; + __u8 conf_len; + __u8 num_conf_req; + __u8 num_conf_rsp; + + __u8 fcs; + + __u16 tx_win; + __u16 tx_win_max; + __u8 max_tx; + __u16 retrans_timeout; + __u16 monitor_timeout; + __u16 mps; + + unsigned long conf_state; + unsigned long conn_state; + unsigned long flags; + + __u16 next_tx_seq; + __u16 expected_ack_seq; + __u16 expected_tx_seq; + __u16 buffer_seq; + __u16 buffer_seq_srej; + __u16 srej_save_reqseq; + __u16 frames_sent; + __u16 unacked_frames; + __u8 retry_count; + __u8 num_acked; + __u16 sdu_len; + struct sk_buff *sdu; + struct sk_buff *sdu_last_frag; + + __u16 remote_tx_win; + __u8 remote_max_tx; + __u16 remote_mps; + + __u8 local_id; + __u8 local_stype; + __u16 local_msdu; + __u32 local_sdu_itime; + __u32 local_acc_lat; + __u32 local_flush_to; + + __u8 remote_id; + __u8 remote_stype; + __u16 remote_msdu; + __u32 remote_sdu_itime; + __u32 remote_acc_lat; + __u32 remote_flush_to; + + struct timer_list chan_timer; + struct timer_list retrans_timer; + struct timer_list monitor_timer; + struct timer_list ack_timer; + struct sk_buff *tx_send_head; + struct sk_buff_head tx_q; + struct sk_buff_head srej_q; + struct list_head srej_l; + + struct list_head list; + struct list_head global_l; + + void *data; + struct l2cap_ops *ops; +}; + +struct l2cap_ops { + char *name; + + struct l2cap_chan *(*new_connection) (void *data); + int (*recv) (void *data, struct sk_buff *skb); + void (*close) (void *data); + void (*state_change) (void *data, int state); +}; + +struct l2cap_conn { + struct hci_conn *hcon; + struct hci_chan *hchan; + + bdaddr_t *dst; + bdaddr_t *src; + + unsigned int mtu; + + __u32 feat_mask; + + __u8 info_state; + __u8 info_ident; + + struct timer_list info_timer; + + spinlock_t lock; + + struct sk_buff *rx_skb; + __u32 rx_len; + __u8 tx_ident; + + __u8 disc_reason; + + struct timer_list security_timer; + struct smp_chan *smp_chan; + + struct list_head chan_l; + rwlock_t chan_lock; +}; + +#define L2CAP_INFO_CL_MTU_REQ_SENT 0x01 +#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x04 +#define L2CAP_INFO_FEAT_MASK_REQ_DONE 0x08 + +#define L2CAP_CHAN_RAW 1 +#define L2CAP_CHAN_CONN_LESS 2 +#define L2CAP_CHAN_CONN_ORIENTED 3 + +/* ----- L2CAP socket info ----- */ +#define l2cap_pi(sk) ((struct l2cap_pinfo *) sk) + +struct l2cap_pinfo { + struct bt_sock bt; + struct l2cap_chan *chan; + struct sk_buff *rx_busy_skb; +}; + +enum { + CONF_REQ_SENT, + CONF_INPUT_DONE, + CONF_OUTPUT_DONE, + CONF_MTU_DONE, + CONF_MODE_DONE, + CONF_CONNECT_PEND, + CONF_NO_FCS_RECV, + CONF_STATE2_DEVICE, + CONF_EWS_RECV, + CONF_LOC_CONF_PEND, + CONF_REM_CONF_PEND, +}; + +#define L2CAP_CONF_MAX_CONF_REQ 2 +#define L2CAP_CONF_MAX_CONF_RSP 2 + +enum { + CONN_SREJ_SENT, + CONN_WAIT_F, + CONN_SREJ_ACT, + CONN_SEND_PBIT, + CONN_REMOTE_BUSY, + CONN_LOCAL_BUSY, + CONN_REJ_ACT, + CONN_SEND_FBIT, + CONN_RNR_SENT, +}; + +/* Definitions for flags in l2cap_chan */ +enum { + FLAG_ROLE_SWITCH, + FLAG_FORCE_ACTIVE, + FLAG_FORCE_RELIABLE, + FLAG_FLUSHABLE, + FLAG_EXT_CTRL, + FLAG_EFS_ENABLE, +}; + +#define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) +#define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer) +#define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \ + L2CAP_DEFAULT_RETRANS_TO); +#define __clear_retrans_timer(c) l2cap_clear_timer(c, &c->retrans_timer) +#define __set_monitor_timer(c) l2cap_set_timer(c, &c->monitor_timer, \ + L2CAP_DEFAULT_MONITOR_TO); +#define __clear_monitor_timer(c) l2cap_clear_timer(c, &c->monitor_timer) +#define __set_ack_timer(c) l2cap_set_timer(c, &chan->ack_timer, \ + L2CAP_DEFAULT_ACK_TO); +#define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer) + +static inline int __seq_offset(struct l2cap_chan *chan, __u16 seq1, __u16 seq2) +{ + int offset; + + offset = (seq1 - seq2) % (chan->tx_win_max + 1); + if (offset < 0) + offset += (chan->tx_win_max + 1); + + return offset; +} + +static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq) +{ + return (seq + 1) % (chan->tx_win_max + 1); +} + +static inline int l2cap_tx_window_full(struct l2cap_chan *ch) +{ + int sub; + + sub = (ch->next_tx_seq - ch->expected_ack_seq) % 64; + + if (sub < 0) + sub += 64; + + return sub == ch->remote_tx_win; +} + +static inline __u16 __get_reqseq(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (ctrl & L2CAP_EXT_CTRL_REQSEQ) >> + L2CAP_EXT_CTRL_REQSEQ_SHIFT; + else + return (ctrl & L2CAP_CTRL_REQSEQ) >> L2CAP_CTRL_REQSEQ_SHIFT; +} + +static inline __u32 __set_reqseq(struct l2cap_chan *chan, __u32 reqseq) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (reqseq << L2CAP_EXT_CTRL_REQSEQ_SHIFT) & + L2CAP_EXT_CTRL_REQSEQ; + else + return (reqseq << L2CAP_CTRL_REQSEQ_SHIFT) & L2CAP_CTRL_REQSEQ; +} + +static inline __u16 __get_txseq(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (ctrl & L2CAP_EXT_CTRL_TXSEQ) >> + L2CAP_EXT_CTRL_TXSEQ_SHIFT; + else + return (ctrl & L2CAP_CTRL_TXSEQ) >> L2CAP_CTRL_TXSEQ_SHIFT; +} + +static inline __u32 __set_txseq(struct l2cap_chan *chan, __u32 txseq) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (txseq << L2CAP_EXT_CTRL_TXSEQ_SHIFT) & + L2CAP_EXT_CTRL_TXSEQ; + else + return (txseq << L2CAP_CTRL_TXSEQ_SHIFT) & L2CAP_CTRL_TXSEQ; +} + +static inline bool __is_sframe(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return ctrl & L2CAP_EXT_CTRL_FRAME_TYPE; + else + return ctrl & L2CAP_CTRL_FRAME_TYPE; +} + +static inline __u32 __set_sframe(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_CTRL_FRAME_TYPE; + else + return L2CAP_CTRL_FRAME_TYPE; +} + +static inline __u8 __get_ctrl_sar(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (ctrl & L2CAP_EXT_CTRL_SAR) >> L2CAP_EXT_CTRL_SAR_SHIFT; + else + return (ctrl & L2CAP_CTRL_SAR) >> L2CAP_CTRL_SAR_SHIFT; +} + +static inline __u32 __set_ctrl_sar(struct l2cap_chan *chan, __u32 sar) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (sar << L2CAP_EXT_CTRL_SAR_SHIFT) & L2CAP_EXT_CTRL_SAR; + else + return (sar << L2CAP_CTRL_SAR_SHIFT) & L2CAP_CTRL_SAR; +} + +static inline bool __is_sar_start(struct l2cap_chan *chan, __u32 ctrl) +{ + return __get_ctrl_sar(chan, ctrl) == L2CAP_SAR_START; +} + +static inline __u32 __get_sar_mask(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_CTRL_SAR; + else + return L2CAP_CTRL_SAR; +} + +static inline __u8 __get_ctrl_super(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (ctrl & L2CAP_EXT_CTRL_SUPERVISE) >> + L2CAP_EXT_CTRL_SUPER_SHIFT; + else + return (ctrl & L2CAP_CTRL_SUPERVISE) >> L2CAP_CTRL_SUPER_SHIFT; +} + +static inline __u32 __set_ctrl_super(struct l2cap_chan *chan, __u32 super) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (super << L2CAP_EXT_CTRL_SUPER_SHIFT) & + L2CAP_EXT_CTRL_SUPERVISE; + else + return (super << L2CAP_CTRL_SUPER_SHIFT) & + L2CAP_CTRL_SUPERVISE; +} + +static inline __u32 __set_ctrl_final(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_CTRL_FINAL; + else + return L2CAP_CTRL_FINAL; +} + +static inline bool __is_ctrl_final(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return ctrl & L2CAP_EXT_CTRL_FINAL; + else + return ctrl & L2CAP_CTRL_FINAL; +} + +static inline __u32 __set_ctrl_poll(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_CTRL_POLL; + else + return L2CAP_CTRL_POLL; +} + +static inline bool __is_ctrl_poll(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return ctrl & L2CAP_EXT_CTRL_POLL; + else + return ctrl & L2CAP_CTRL_POLL; +} + +static inline __u32 __get_control(struct l2cap_chan *chan, void *p) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return get_unaligned_le32(p); + else + return get_unaligned_le16(p); +} + +static inline void __put_control(struct l2cap_chan *chan, __u32 control, + void *p) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return put_unaligned_le32(control, p); + else + return put_unaligned_le16(control, p); +} + +static inline __u8 __ctrl_size(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_HDR_SIZE - L2CAP_HDR_SIZE; + else + return L2CAP_ENH_HDR_SIZE - L2CAP_HDR_SIZE; +} + +extern int disable_ertm; +extern int enable_hs; + +int l2cap_init_sockets(void); +void l2cap_cleanup_sockets(void); + +void __l2cap_connect_rsp_defer(struct l2cap_chan *chan); +int __l2cap_wait_ack(struct sock *sk); + +int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm); +int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); + +struct l2cap_chan *l2cap_chan_create(struct sock *sk); +void l2cap_chan_close(struct l2cap_chan *chan, int reason); +void l2cap_chan_destroy(struct l2cap_chan *chan); +int l2cap_chan_connect(struct l2cap_chan *chan); +int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, + u32 priority); +void l2cap_chan_busy(struct l2cap_chan *chan, int busy); + +#endif /* __L2CAP_H */ diff --git a/include/compat/net/bluetooth/mgmt.h b/include/compat/net/bluetooth/mgmt.h new file mode 100644 index 00000000..3e320c9c --- /dev/null +++ b/include/compat/net/bluetooth/mgmt.h @@ -0,0 +1,324 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + + Copyright (C) 2010 Nokia 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; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +#define MGMT_INDEX_NONE 0xFFFF + +struct mgmt_hdr { + __le16 opcode; + __le16 index; + __le16 len; +} __packed; + +#define MGMT_OP_READ_VERSION 0x0001 +struct mgmt_rp_read_version { + __u8 version; + __le16 revision; +} __packed; + +#define MGMT_OP_READ_INDEX_LIST 0x0003 +struct mgmt_rp_read_index_list { + __le16 num_controllers; + __le16 index[0]; +} __packed; + +/* Reserve one extra byte for names in management messages so that they + * are always guaranteed to be nul-terminated */ +#define MGMT_MAX_NAME_LENGTH (HCI_MAX_NAME_LENGTH + 1) + +#define MGMT_OP_READ_INFO 0x0004 +struct mgmt_rp_read_info { + __u8 type; + __u8 powered; + __u8 connectable; + __u8 discoverable; + __u8 pairable; + __u8 sec_mode; + bdaddr_t bdaddr; + __u8 dev_class[3]; + __u8 features[8]; + __u16 manufacturer; + __u8 hci_ver; + __u16 hci_rev; + __u8 name[MGMT_MAX_NAME_LENGTH]; +} __packed; + +struct mgmt_mode { + __u8 val; +} __packed; + +#define MGMT_OP_SET_POWERED 0x0005 + +#define MGMT_OP_SET_DISCOVERABLE 0x0006 +struct mgmt_cp_set_discoverable { + __u8 val; + __u16 timeout; +} __packed; + +#define MGMT_OP_SET_CONNECTABLE 0x0007 + +#define MGMT_OP_SET_PAIRABLE 0x0008 + +#define MGMT_OP_ADD_UUID 0x0009 +struct mgmt_cp_add_uuid { + __u8 uuid[16]; + __u8 svc_hint; +} __packed; + +#define MGMT_OP_REMOVE_UUID 0x000A +struct mgmt_cp_remove_uuid { + __u8 uuid[16]; +} __packed; + +#define MGMT_OP_SET_DEV_CLASS 0x000B +struct mgmt_cp_set_dev_class { + __u8 major; + __u8 minor; +} __packed; + +#define MGMT_OP_SET_SERVICE_CACHE 0x000C +struct mgmt_cp_set_service_cache { + __u8 enable; +} __packed; + +struct mgmt_link_key_info { + bdaddr_t bdaddr; + u8 type; + u8 val[16]; + u8 pin_len; +} __packed; + +#define MGMT_OP_LOAD_LINK_KEYS 0x000D +struct mgmt_cp_load_link_keys { + __u8 debug_keys; + __le16 key_count; + struct mgmt_link_key_info keys[0]; +} __packed; + +#define MGMT_OP_REMOVE_KEYS 0x000E +struct mgmt_cp_remove_keys { + bdaddr_t bdaddr; + __u8 disconnect; +} __packed; + +#define MGMT_OP_DISCONNECT 0x000F +struct mgmt_cp_disconnect { + bdaddr_t bdaddr; +} __packed; +struct mgmt_rp_disconnect { + bdaddr_t bdaddr; +} __packed; + +#define MGMT_ADDR_BREDR 0x00 +#define MGMT_ADDR_LE 0x01 +#define MGMT_ADDR_BREDR_LE 0x02 +#define MGMT_ADDR_INVALID 0xff + +struct mgmt_addr_info { + bdaddr_t bdaddr; + __u8 type; +} __packed; + +#define MGMT_OP_GET_CONNECTIONS 0x0010 +struct mgmt_rp_get_connections { + __le16 conn_count; + struct mgmt_addr_info addr[0]; +} __packed; + +#define MGMT_OP_PIN_CODE_REPLY 0x0011 +struct mgmt_cp_pin_code_reply { + bdaddr_t bdaddr; + __u8 pin_len; + __u8 pin_code[16]; +} __packed; +struct mgmt_rp_pin_code_reply { + bdaddr_t bdaddr; + uint8_t status; +} __packed; + +#define MGMT_OP_PIN_CODE_NEG_REPLY 0x0012 +struct mgmt_cp_pin_code_neg_reply { + bdaddr_t bdaddr; +} __packed; + +#define MGMT_OP_SET_IO_CAPABILITY 0x0013 +struct mgmt_cp_set_io_capability { + __u8 io_capability; +} __packed; + +#define MGMT_OP_PAIR_DEVICE 0x0014 +struct mgmt_cp_pair_device { + bdaddr_t bdaddr; + __u8 io_cap; +} __packed; +struct mgmt_rp_pair_device { + bdaddr_t bdaddr; + __u8 status; +} __packed; + +#define MGMT_OP_USER_CONFIRM_REPLY 0x0015 +struct mgmt_cp_user_confirm_reply { + bdaddr_t bdaddr; +} __packed; +struct mgmt_rp_user_confirm_reply { + bdaddr_t bdaddr; + __u8 status; +} __packed; + +#define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x0016 + +#define MGMT_OP_SET_LOCAL_NAME 0x0017 +struct mgmt_cp_set_local_name { + __u8 name[MGMT_MAX_NAME_LENGTH]; +} __packed; + +#define MGMT_OP_READ_LOCAL_OOB_DATA 0x0018 +struct mgmt_rp_read_local_oob_data { + __u8 hash[16]; + __u8 randomizer[16]; +} __packed; + +#define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0019 +struct mgmt_cp_add_remote_oob_data { + bdaddr_t bdaddr; + __u8 hash[16]; + __u8 randomizer[16]; +} __packed; + +#define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x001A +struct mgmt_cp_remove_remote_oob_data { + bdaddr_t bdaddr; +} __packed; + +#define MGMT_OP_START_DISCOVERY 0x001B + +#define MGMT_OP_STOP_DISCOVERY 0x001C + +#define MGMT_OP_BLOCK_DEVICE 0x001D +struct mgmt_cp_block_device { + bdaddr_t bdaddr; +} __packed; + +#define MGMT_OP_UNBLOCK_DEVICE 0x001E +struct mgmt_cp_unblock_device { + bdaddr_t bdaddr; +} __packed; + +#define MGMT_OP_SET_FAST_CONNECTABLE 0x001F +struct mgmt_cp_set_fast_connectable { + __u8 enable; +} __packed; + +#define MGMT_EV_CMD_COMPLETE 0x0001 +struct mgmt_ev_cmd_complete { + __le16 opcode; + __u8 data[0]; +} __packed; + +#define MGMT_EV_CMD_STATUS 0x0002 +struct mgmt_ev_cmd_status { + __u8 status; + __le16 opcode; +} __packed; + +#define MGMT_EV_CONTROLLER_ERROR 0x0003 +struct mgmt_ev_controller_error { + __u8 error_code; +} __packed; + +#define MGMT_EV_INDEX_ADDED 0x0004 + +#define MGMT_EV_INDEX_REMOVED 0x0005 + +#define MGMT_EV_POWERED 0x0006 + +#define MGMT_EV_DISCOVERABLE 0x0007 + +#define MGMT_EV_CONNECTABLE 0x0008 + +#define MGMT_EV_PAIRABLE 0x0009 + +#define MGMT_EV_NEW_LINK_KEY 0x000A +struct mgmt_ev_new_link_key { + __u8 store_hint; + struct mgmt_link_key_info key; +} __packed; + +#define MGMT_EV_CONNECTED 0x000B + +#define MGMT_EV_DISCONNECTED 0x000C + +#define MGMT_EV_CONNECT_FAILED 0x000D +struct mgmt_ev_connect_failed { + struct mgmt_addr_info addr; + __u8 status; +} __packed; + +#define MGMT_EV_PIN_CODE_REQUEST 0x000E +struct mgmt_ev_pin_code_request { + bdaddr_t bdaddr; + __u8 secure; +} __packed; + +#define MGMT_EV_USER_CONFIRM_REQUEST 0x000F +struct mgmt_ev_user_confirm_request { + bdaddr_t bdaddr; + __u8 confirm_hint; + __le32 value; +} __packed; + +#define MGMT_EV_AUTH_FAILED 0x0010 +struct mgmt_ev_auth_failed { + bdaddr_t bdaddr; + __u8 status; +} __packed; + +#define MGMT_EV_LOCAL_NAME_CHANGED 0x0011 +struct mgmt_ev_local_name_changed { + __u8 name[MGMT_MAX_NAME_LENGTH]; +} __packed; + +#define MGMT_EV_DEVICE_FOUND 0x0012 +struct mgmt_ev_device_found { + struct mgmt_addr_info addr; + __u8 dev_class[3]; + __s8 rssi; + __u8 eir[HCI_MAX_EIR_LENGTH]; +} __packed; + +#define MGMT_EV_REMOTE_NAME 0x0013 +struct mgmt_ev_remote_name { + bdaddr_t bdaddr; + __u8 name[MGMT_MAX_NAME_LENGTH]; +} __packed; + +#define MGMT_EV_DISCOVERING 0x0014 + +#define MGMT_EV_DEVICE_BLOCKED 0x0015 +struct mgmt_ev_device_blocked { + bdaddr_t bdaddr; +} __packed; + +#define MGMT_EV_DEVICE_UNBLOCKED 0x0016 +struct mgmt_ev_device_unblocked { + bdaddr_t bdaddr; +} __packed; diff --git a/include/compat/net/bluetooth/rfcomm.h b/include/compat/net/bluetooth/rfcomm.h new file mode 100644 index 00000000..e2e3ecad --- /dev/null +++ b/include/compat/net/bluetooth/rfcomm.h @@ -0,0 +1,372 @@ +/* + RFCOMM implementation for Linux Bluetooth stack (BlueZ) + Copyright (C) 2002 Maxim Krasnyansky + Copyright (C) 2002 Marcel Holtmann + + 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; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +#ifndef __RFCOMM_H +#define __RFCOMM_H + +#define RFCOMM_PSM 3 + +#define RFCOMM_CONN_TIMEOUT (HZ * 30) +#define RFCOMM_DISC_TIMEOUT (HZ * 20) +#define RFCOMM_AUTH_TIMEOUT (HZ * 25) +#define RFCOMM_IDLE_TIMEOUT (HZ * 2) + +#define RFCOMM_DEFAULT_MTU 127 +#define RFCOMM_DEFAULT_CREDITS 7 + +#define RFCOMM_MAX_L2CAP_MTU 1013 +#define RFCOMM_MAX_CREDITS 40 + +#define RFCOMM_SKB_HEAD_RESERVE 8 +#define RFCOMM_SKB_TAIL_RESERVE 2 +#define RFCOMM_SKB_RESERVE (RFCOMM_SKB_HEAD_RESERVE + RFCOMM_SKB_TAIL_RESERVE) + +#define RFCOMM_SABM 0x2f +#define RFCOMM_DISC 0x43 +#define RFCOMM_UA 0x63 +#define RFCOMM_DM 0x0f +#define RFCOMM_UIH 0xef + +#define RFCOMM_TEST 0x08 +#define RFCOMM_FCON 0x28 +#define RFCOMM_FCOFF 0x18 +#define RFCOMM_MSC 0x38 +#define RFCOMM_RPN 0x24 +#define RFCOMM_RLS 0x14 +#define RFCOMM_PN 0x20 +#define RFCOMM_NSC 0x04 + +#define RFCOMM_V24_FC 0x02 +#define RFCOMM_V24_RTC 0x04 +#define RFCOMM_V24_RTR 0x08 +#define RFCOMM_V24_IC 0x40 +#define RFCOMM_V24_DV 0x80 + +#define RFCOMM_RPN_BR_2400 0x0 +#define RFCOMM_RPN_BR_4800 0x1 +#define RFCOMM_RPN_BR_7200 0x2 +#define RFCOMM_RPN_BR_9600 0x3 +#define RFCOMM_RPN_BR_19200 0x4 +#define RFCOMM_RPN_BR_38400 0x5 +#define RFCOMM_RPN_BR_57600 0x6 +#define RFCOMM_RPN_BR_115200 0x7 +#define RFCOMM_RPN_BR_230400 0x8 + +#define RFCOMM_RPN_DATA_5 0x0 +#define RFCOMM_RPN_DATA_6 0x1 +#define RFCOMM_RPN_DATA_7 0x2 +#define RFCOMM_RPN_DATA_8 0x3 + +#define RFCOMM_RPN_STOP_1 0 +#define RFCOMM_RPN_STOP_15 1 + +#define RFCOMM_RPN_PARITY_NONE 0x0 +#define RFCOMM_RPN_PARITY_ODD 0x1 +#define RFCOMM_RPN_PARITY_EVEN 0x3 +#define RFCOMM_RPN_PARITY_MARK 0x5 +#define RFCOMM_RPN_PARITY_SPACE 0x7 + +#define RFCOMM_RPN_FLOW_NONE 0x00 + +#define RFCOMM_RPN_XON_CHAR 0x11 +#define RFCOMM_RPN_XOFF_CHAR 0x13 + +#define RFCOMM_RPN_PM_BITRATE 0x0001 +#define RFCOMM_RPN_PM_DATA 0x0002 +#define RFCOMM_RPN_PM_STOP 0x0004 +#define RFCOMM_RPN_PM_PARITY 0x0008 +#define RFCOMM_RPN_PM_PARITY_TYPE 0x0010 +#define RFCOMM_RPN_PM_XON 0x0020 +#define RFCOMM_RPN_PM_XOFF 0x0040 +#define RFCOMM_RPN_PM_FLOW 0x3F00 + +#define RFCOMM_RPN_PM_ALL 0x3F7F + +struct rfcomm_hdr { + u8 addr; + u8 ctrl; + u8 len; /* Actual size can be 2 bytes */ +} __packed; + +struct rfcomm_cmd { + u8 addr; + u8 ctrl; + u8 len; + u8 fcs; +} __packed; + +struct rfcomm_mcc { + u8 type; + u8 len; +} __packed; + +struct rfcomm_pn { + u8 dlci; + u8 flow_ctrl; + u8 priority; + u8 ack_timer; + __le16 mtu; + u8 max_retrans; + u8 credits; +} __packed; + +struct rfcomm_rpn { + u8 dlci; + u8 bit_rate; + u8 line_settings; + u8 flow_ctrl; + u8 xon_char; + u8 xoff_char; + __le16 param_mask; +} __packed; + +struct rfcomm_rls { + u8 dlci; + u8 status; +} __packed; + +struct rfcomm_msc { + u8 dlci; + u8 v24_sig; +} __packed; + +/* ---- Core structures, flags etc ---- */ + +struct rfcomm_session { + struct list_head list; + struct socket *sock; + struct timer_list timer; + unsigned long state; + unsigned long flags; + atomic_t refcnt; + int initiator; + + /* Default DLC parameters */ + int cfc; + uint mtu; + + struct list_head dlcs; +}; + +struct rfcomm_dlc { + struct list_head list; + struct rfcomm_session *session; + struct sk_buff_head tx_queue; + struct timer_list timer; + + spinlock_t lock; + unsigned long state; + unsigned long flags; + atomic_t refcnt; + u8 dlci; + u8 addr; + u8 priority; + u8 v24_sig; + u8 remote_v24_sig; + u8 mscex; + u8 out; + u8 sec_level; + u8 role_switch; + u32 defer_setup; + + uint mtu; + uint cfc; + uint rx_credits; + uint tx_credits; + + void *owner; + + void (*data_ready)(struct rfcomm_dlc *d, struct sk_buff *skb); + void (*state_change)(struct rfcomm_dlc *d, int err); + void (*modem_status)(struct rfcomm_dlc *d, u8 v24_sig); +}; + +/* DLC and session flags */ +#define RFCOMM_RX_THROTTLED 0 +#define RFCOMM_TX_THROTTLED 1 +#define RFCOMM_TIMED_OUT 2 +#define RFCOMM_MSC_PENDING 3 +#define RFCOMM_SEC_PENDING 4 +#define RFCOMM_AUTH_PENDING 5 +#define RFCOMM_AUTH_ACCEPT 6 +#define RFCOMM_AUTH_REJECT 7 +#define RFCOMM_DEFER_SETUP 8 +#define RFCOMM_ENC_DROP 9 + +/* Scheduling flags and events */ +#define RFCOMM_SCHED_WAKEUP 31 + +/* MSC exchange flags */ +#define RFCOMM_MSCEX_TX 1 +#define RFCOMM_MSCEX_RX 2 +#define RFCOMM_MSCEX_OK (RFCOMM_MSCEX_TX + RFCOMM_MSCEX_RX) + +/* CFC states */ +#define RFCOMM_CFC_UNKNOWN -1 +#define RFCOMM_CFC_DISABLED 0 +#define RFCOMM_CFC_ENABLED RFCOMM_MAX_CREDITS + +/* ---- RFCOMM SEND RPN ---- */ +int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, + u8 bit_rate, u8 data_bits, u8 stop_bits, + u8 parity, u8 flow_ctrl_settings, + u8 xon_char, u8 xoff_char, u16 param_mask); + +/* ---- RFCOMM DLCs (channels) ---- */ +struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio); +void rfcomm_dlc_free(struct rfcomm_dlc *d); +int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, + u8 channel); +int rfcomm_dlc_close(struct rfcomm_dlc *d, int reason); +int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb); +int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig); +int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig); +void rfcomm_dlc_accept(struct rfcomm_dlc *d); + +#define rfcomm_dlc_lock(d) spin_lock(&d->lock) +#define rfcomm_dlc_unlock(d) spin_unlock(&d->lock) + +static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d) +{ + atomic_inc(&d->refcnt); +} + +static inline void rfcomm_dlc_put(struct rfcomm_dlc *d) +{ + if (atomic_dec_and_test(&d->refcnt)) + rfcomm_dlc_free(d); +} + +extern void __rfcomm_dlc_throttle(struct rfcomm_dlc *d); +extern void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d); + +static inline void rfcomm_dlc_throttle(struct rfcomm_dlc *d) +{ + if (!test_and_set_bit(RFCOMM_RX_THROTTLED, &d->flags)) + __rfcomm_dlc_throttle(d); +} + +static inline void rfcomm_dlc_unthrottle(struct rfcomm_dlc *d) +{ + if (test_and_clear_bit(RFCOMM_RX_THROTTLED, &d->flags)) + __rfcomm_dlc_unthrottle(d); +} + +/* ---- RFCOMM sessions ---- */ +void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, + bdaddr_t *dst); + +static inline void rfcomm_session_hold(struct rfcomm_session *s) +{ + atomic_inc(&s->refcnt); +} + +/* ---- RFCOMM sockets ---- */ +struct sockaddr_rc { + sa_family_t rc_family; + bdaddr_t rc_bdaddr; + u8 rc_channel; +}; + +#define RFCOMM_CONNINFO 0x02 +struct rfcomm_conninfo { + __u16 hci_handle; + __u8 dev_class[3]; +}; + +#define RFCOMM_LM 0x03 +#define RFCOMM_LM_MASTER 0x0001 +#define RFCOMM_LM_AUTH 0x0002 +#define RFCOMM_LM_ENCRYPT 0x0004 +#define RFCOMM_LM_TRUSTED 0x0008 +#define RFCOMM_LM_RELIABLE 0x0010 +#define RFCOMM_LM_SECURE 0x0020 + +#define rfcomm_pi(sk) ((struct rfcomm_pinfo *) sk) + +struct rfcomm_pinfo { + struct bt_sock bt; + struct rfcomm_dlc *dlc; + u8 channel; + u8 sec_level; + u8 role_switch; +}; + +int rfcomm_init_sockets(void); +void rfcomm_cleanup_sockets(void); + +int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, + struct rfcomm_dlc **d); + +/* ---- RFCOMM TTY ---- */ +#define RFCOMM_MAX_DEV 256 + +#define RFCOMMCREATEDEV _IOW('R', 200, int) +#define RFCOMMRELEASEDEV _IOW('R', 201, int) +#define RFCOMMGETDEVLIST _IOR('R', 210, int) +#define RFCOMMGETDEVINFO _IOR('R', 211, int) +#define RFCOMMSTEALDLC _IOW('R', 220, int) + +#define RFCOMM_REUSE_DLC 0 +#define RFCOMM_RELEASE_ONHUP 1 +#define RFCOMM_HANGUP_NOW 2 +#define RFCOMM_TTY_ATTACHED 3 +#define RFCOMM_TTY_RELEASED 4 + +struct rfcomm_dev_req { + s16 dev_id; + u32 flags; + bdaddr_t src; + bdaddr_t dst; + u8 channel; +}; + +struct rfcomm_dev_info { + s16 id; + u32 flags; + u16 state; + bdaddr_t src; + bdaddr_t dst; + u8 channel; +}; + +struct rfcomm_dev_list_req { + u16 dev_num; + struct rfcomm_dev_info dev_info[0]; +}; + +int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, void __user *arg); + +#ifdef CONFIG_BT_RFCOMM_TTY +int rfcomm_init_ttys(void); +void rfcomm_cleanup_ttys(void); +#else +static inline int rfcomm_init_ttys(void) +{ + return 0; +} +static inline void rfcomm_cleanup_ttys(void) +{ +} +#endif +#endif /* __RFCOMM_H */ diff --git a/include/compat/net/bluetooth/smp.h b/include/compat/net/bluetooth/smp.h new file mode 100644 index 00000000..15b97d54 --- /dev/null +++ b/include/compat/net/bluetooth/smp.h @@ -0,0 +1,140 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + + 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; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +#ifndef __SMP_H +#define __SMP_H + +struct smp_command_hdr { + __u8 code; +} __packed; + +#define SMP_CMD_PAIRING_REQ 0x01 +#define SMP_CMD_PAIRING_RSP 0x02 +struct smp_cmd_pairing { + __u8 io_capability; + __u8 oob_flag; + __u8 auth_req; + __u8 max_key_size; + __u8 init_key_dist; + __u8 resp_key_dist; +} __packed; + +#define SMP_IO_DISPLAY_ONLY 0x00 +#define SMP_IO_DISPLAY_YESNO 0x01 +#define SMP_IO_KEYBOARD_ONLY 0x02 +#define SMP_IO_NO_INPUT_OUTPUT 0x03 +#define SMP_IO_KEYBOARD_DISPLAY 0x04 + +#define SMP_OOB_NOT_PRESENT 0x00 +#define SMP_OOB_PRESENT 0x01 + +#define SMP_DIST_ENC_KEY 0x01 +#define SMP_DIST_ID_KEY 0x02 +#define SMP_DIST_SIGN 0x04 + +#define SMP_AUTH_NONE 0x00 +#define SMP_AUTH_BONDING 0x01 +#define SMP_AUTH_MITM 0x04 + +#define SMP_CMD_PAIRING_CONFIRM 0x03 +struct smp_cmd_pairing_confirm { + __u8 confirm_val[16]; +} __packed; + +#define SMP_CMD_PAIRING_RANDOM 0x04 +struct smp_cmd_pairing_random { + __u8 rand_val[16]; +} __packed; + +#define SMP_CMD_PAIRING_FAIL 0x05 +struct smp_cmd_pairing_fail { + __u8 reason; +} __packed; + +#define SMP_CMD_ENCRYPT_INFO 0x06 +struct smp_cmd_encrypt_info { + __u8 ltk[16]; +} __packed; + +#define SMP_CMD_MASTER_IDENT 0x07 +struct smp_cmd_master_ident { + __u16 ediv; + __u8 rand[8]; +} __packed; + +#define SMP_CMD_IDENT_INFO 0x08 +struct smp_cmd_ident_info { + __u8 irk[16]; +} __packed; + +#define SMP_CMD_IDENT_ADDR_INFO 0x09 +struct smp_cmd_ident_addr_info { + __u8 addr_type; + bdaddr_t bdaddr; +} __packed; + +#define SMP_CMD_SIGN_INFO 0x0a +struct smp_cmd_sign_info { + __u8 csrk[16]; +} __packed; + +#define SMP_CMD_SECURITY_REQ 0x0b +struct smp_cmd_security_req { + __u8 auth_req; +} __packed; + +#define SMP_PASSKEY_ENTRY_FAILED 0x01 +#define SMP_OOB_NOT_AVAIL 0x02 +#define SMP_AUTH_REQUIREMENTS 0x03 +#define SMP_CONFIRM_FAILED 0x04 +#define SMP_PAIRING_NOTSUPP 0x05 +#define SMP_ENC_KEY_SIZE 0x06 +#define SMP_CMD_NOTSUPP 0x07 +#define SMP_UNSPECIFIED 0x08 +#define SMP_REPEATED_ATTEMPTS 0x09 + +#define SMP_MIN_ENC_KEY_SIZE 7 +#define SMP_MAX_ENC_KEY_SIZE 16 + +struct smp_chan { + struct l2cap_conn *conn; + u8 preq[7]; /* SMP Pairing Request */ + u8 prsp[7]; /* SMP Pairing Response */ + u8 prnd[16]; /* SMP Pairing Random (local) */ + u8 rrnd[16]; /* SMP Pairing Random (remote) */ + u8 pcnf[16]; /* SMP Pairing Confirm */ + u8 tk[16]; /* SMP Temporary Key */ + u8 smp_key_size; + struct crypto_blkcipher *tfm; + struct work_struct confirm; + struct work_struct random; + +}; + +/* SMP Commands */ +int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level); +int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb); +int smp_distribute_keys(struct l2cap_conn *conn, __u8 force); + +void smp_chan_destroy(struct l2cap_conn *conn); + +#endif /* __SMP_H */ diff --git a/include/compat/net/cfg80211-wext.h b/include/compat/net/cfg80211-wext.h new file mode 100644 index 00000000..25baddc4 --- /dev/null +++ b/include/compat/net/cfg80211-wext.h @@ -0,0 +1,55 @@ +#ifndef __NET_CFG80211_WEXT_H +#define __NET_CFG80211_WEXT_H +/* + * 802.11 device and configuration interface -- wext handlers + * + * Copyright 2006-2010 Johannes Berg + * + * 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 + +/* + * Temporary wext handlers & helper functions + * + * These are used only by drivers that aren't yet fully + * converted to cfg80211. + */ +int cfg80211_wext_giwname(struct net_device *dev, + struct iw_request_info *info, + char *name, char *extra); +int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, + u32 *mode, char *extra); +int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info, + u32 *mode, char *extra); +int cfg80211_wext_siwscan(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); +int cfg80211_wext_giwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra); +int cfg80211_wext_giwrange(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra); +int cfg80211_wext_siwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra); +int cfg80211_wext_giwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra); +int cfg80211_wext_siwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *frag, char *extra); +int cfg80211_wext_giwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *frag, char *extra); +int cfg80211_wext_giwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *retry, char *extra); + +#endif /* __NET_CFG80211_WEXT_H */ diff --git a/include/compat/net/cfg80211.h b/include/compat/net/cfg80211.h new file mode 100644 index 00000000..17376bd7 --- /dev/null +++ b/include/compat/net/cfg80211.h @@ -0,0 +1,3479 @@ +#ifndef __NET_CFG80211_H +#define __NET_CFG80211_H +/* + * 802.11 device and configuration interface + * + * Copyright 2006-2010 Johannes Berg + * + * 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 + +/** + * DOC: Introduction + * + * cfg80211 is the configuration API for 802.11 devices in Linux. It bridges + * userspace and drivers, and offers some utility functionality associated + * with 802.11. cfg80211 must, directly or indirectly via mac80211, be used + * by all modern wireless drivers in Linux, so that they offer a consistent + * API through nl80211. For backward compatibility, cfg80211 also offers + * wireless extensions to userspace, but hides them from drivers completely. + * + * Additionally, cfg80211 contains code to help enforce regulatory spectrum + * use restrictions. + */ + + +/** + * DOC: Device registration + * + * In order for a driver to use cfg80211, it must register the hardware device + * with cfg80211. This happens through a number of hardware capability structs + * described below. + * + * The fundamental structure for each device is the 'wiphy', of which each + * instance describes a physical wireless device connected to the system. Each + * such wiphy can have zero, one, or many virtual interfaces associated with + * it, which need to be identified as such by pointing the network interface's + * @ieee80211_ptr pointer to a &struct wireless_dev which further describes + * the wireless part of the interface, normally this struct is embedded in the + * network interface's private data area. Drivers can optionally allow creating + * or destroying virtual interfaces on the fly, but without at least one or the + * ability to create some the wireless device isn't useful. + * + * Each wiphy structure contains device capability information, and also has + * a pointer to the various operations the driver offers. The definitions and + * structures here describe these capabilities in detail. + */ + +/* + * wireless hardware capability structures + */ + +/** + * enum ieee80211_band - supported frequency bands + * + * The bands are assigned this way because the supported + * bitrates differ in these bands. + * + * @IEEE80211_BAND_2GHZ: 2.4GHz ISM band + * @IEEE80211_BAND_5GHZ: around 5GHz band (4.9-5.7) + * @IEEE80211_NUM_BANDS: number of defined bands + */ +enum ieee80211_band { + IEEE80211_BAND_2GHZ = NL80211_BAND_2GHZ, + IEEE80211_BAND_5GHZ = NL80211_BAND_5GHZ, + + /* keep last */ + IEEE80211_NUM_BANDS +}; + +/** + * enum ieee80211_channel_flags - channel flags + * + * Channel flags set by the regulatory control code. + * + * @IEEE80211_CHAN_DISABLED: This channel is disabled. + * @IEEE80211_CHAN_PASSIVE_SCAN: Only passive scanning is permitted + * on this channel. + * @IEEE80211_CHAN_NO_IBSS: IBSS is not allowed on this channel. + * @IEEE80211_CHAN_RADAR: Radar detection is required on this channel. + * @IEEE80211_CHAN_NO_HT40PLUS: extension channel above this channel + * is not permitted. + * @IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel + * is not permitted. + */ +enum ieee80211_channel_flags { + IEEE80211_CHAN_DISABLED = 1<<0, + IEEE80211_CHAN_PASSIVE_SCAN = 1<<1, + IEEE80211_CHAN_NO_IBSS = 1<<2, + IEEE80211_CHAN_RADAR = 1<<3, + IEEE80211_CHAN_NO_HT40PLUS = 1<<4, + IEEE80211_CHAN_NO_HT40MINUS = 1<<5, +}; + +#define IEEE80211_CHAN_NO_HT40 \ + (IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS) + +/** + * struct ieee80211_channel - channel definition + * + * This structure describes a single channel for use + * with cfg80211. + * + * @center_freq: center frequency in MHz + * @hw_value: hardware-specific value for the channel + * @flags: channel flags from &enum ieee80211_channel_flags. + * @orig_flags: channel flags at registration time, used by regulatory + * code to support devices with additional restrictions + * @band: band this channel belongs to. + * @max_antenna_gain: maximum antenna gain in dBi + * @max_power: maximum transmission power (in dBm) + * @beacon_found: helper to regulatory code to indicate when a beacon + * has been found on this channel. Use regulatory_hint_found_beacon() + * to enable this, this is useful only on 5 GHz band. + * @orig_mag: internal use + * @orig_mpwr: internal use + */ +struct ieee80211_channel { + enum ieee80211_band band; + u16 center_freq; + u16 hw_value; + u32 flags; + int max_antenna_gain; + int max_power; + bool beacon_found; + u32 orig_flags; + int orig_mag, orig_mpwr; +}; + +/** + * enum ieee80211_rate_flags - rate flags + * + * Hardware/specification flags for rates. These are structured + * in a way that allows using the same bitrate structure for + * different bands/PHY modes. + * + * @IEEE80211_RATE_SHORT_PREAMBLE: Hardware can send with short + * preamble on this bitrate; only relevant in 2.4GHz band and + * with CCK rates. + * @IEEE80211_RATE_MANDATORY_A: This bitrate is a mandatory rate + * when used with 802.11a (on the 5 GHz band); filled by the + * core code when registering the wiphy. + * @IEEE80211_RATE_MANDATORY_B: This bitrate is a mandatory rate + * when used with 802.11b (on the 2.4 GHz band); filled by the + * core code when registering the wiphy. + * @IEEE80211_RATE_MANDATORY_G: This bitrate is a mandatory rate + * when used with 802.11g (on the 2.4 GHz band); filled by the + * core code when registering the wiphy. + * @IEEE80211_RATE_ERP_G: This is an ERP rate in 802.11g mode. + */ +enum ieee80211_rate_flags { + IEEE80211_RATE_SHORT_PREAMBLE = 1<<0, + IEEE80211_RATE_MANDATORY_A = 1<<1, + IEEE80211_RATE_MANDATORY_B = 1<<2, + IEEE80211_RATE_MANDATORY_G = 1<<3, + IEEE80211_RATE_ERP_G = 1<<4, +}; + +/** + * struct ieee80211_rate - bitrate definition + * + * This structure describes a bitrate that an 802.11 PHY can + * operate with. The two values @hw_value and @hw_value_short + * are only for driver use when pointers to this structure are + * passed around. + * + * @flags: rate-specific flags + * @bitrate: bitrate in units of 100 Kbps + * @hw_value: driver/hardware value for this rate + * @hw_value_short: driver/hardware value for this rate when + * short preamble is used + */ +struct ieee80211_rate { + u32 flags; + u16 bitrate; + u16 hw_value, hw_value_short; +}; + +/** + * struct ieee80211_sta_ht_cap - STA's HT capabilities + * + * This structure describes most essential parameters needed + * to describe 802.11n HT capabilities for an STA. + * + * @ht_supported: is HT supported by the STA + * @cap: HT capabilities map as described in 802.11n spec + * @ampdu_factor: Maximum A-MPDU length factor + * @ampdu_density: Minimum A-MPDU spacing + * @mcs: Supported MCS rates + */ +struct ieee80211_sta_ht_cap { + u16 cap; /* use IEEE80211_HT_CAP_ */ + bool ht_supported; + u8 ampdu_factor; + u8 ampdu_density; + struct ieee80211_mcs_info mcs; +}; + +/** + * struct ieee80211_supported_band - frequency band definition + * + * This structure describes a frequency band a wiphy + * is able to operate in. + * + * @channels: Array of channels the hardware can operate in + * in this band. + * @band: the band this structure represents + * @n_channels: Number of channels in @channels + * @bitrates: Array of bitrates the hardware can operate with + * in this band. Must be sorted to give a valid "supported + * rates" IE, i.e. CCK rates first, then OFDM. + * @n_bitrates: Number of bitrates in @bitrates + * @ht_cap: HT capabilities in this band + */ +struct ieee80211_supported_band { + struct ieee80211_channel *channels; + struct ieee80211_rate *bitrates; + enum ieee80211_band band; + int n_channels; + int n_bitrates; + struct ieee80211_sta_ht_cap ht_cap; +}; + +/* + * Wireless hardware/device configuration structures and methods + */ + +/** + * DOC: Actions and configuration + * + * Each wireless device and each virtual interface offer a set of configuration + * operations and other actions that are invoked by userspace. Each of these + * actions is described in the operations structure, and the parameters these + * operations use are described separately. + * + * Additionally, some operations are asynchronous and expect to get status + * information via some functions that drivers need to call. + * + * Scanning and BSS list handling with its associated functionality is described + * in a separate chapter. + */ + +/** + * struct vif_params - describes virtual interface parameters + * @use_4addr: use 4-address frames + */ +struct vif_params { + int use_4addr; +}; + +/** + * struct key_params - key information + * + * Information about a key + * + * @key: key material + * @key_len: length of key material + * @cipher: cipher suite selector + * @seq: sequence counter (IV/PN) for TKIP and CCMP keys, only used + * with the get_key() callback, must be in little endian, + * length given by @seq_len. + * @seq_len: length of @seq. + */ +struct key_params { + u8 *key; + u8 *seq; + int key_len; + int seq_len; + u32 cipher; +}; + +/** + * enum survey_info_flags - survey information flags + * + * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in + * @SURVEY_INFO_IN_USE: channel is currently being used + * @SURVEY_INFO_CHANNEL_TIME: channel active time (in ms) was filled in + * @SURVEY_INFO_CHANNEL_TIME_BUSY: channel busy time was filled in + * @SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: extension channel busy time was filled in + * @SURVEY_INFO_CHANNEL_TIME_RX: channel receive time was filled in + * @SURVEY_INFO_CHANNEL_TIME_TX: channel transmit time was filled in + * + * Used by the driver to indicate which info in &struct survey_info + * it has filled in during the get_survey(). + */ +enum survey_info_flags { + SURVEY_INFO_NOISE_DBM = 1<<0, + SURVEY_INFO_IN_USE = 1<<1, + SURVEY_INFO_CHANNEL_TIME = 1<<2, + SURVEY_INFO_CHANNEL_TIME_BUSY = 1<<3, + SURVEY_INFO_CHANNEL_TIME_EXT_BUSY = 1<<4, + SURVEY_INFO_CHANNEL_TIME_RX = 1<<5, + SURVEY_INFO_CHANNEL_TIME_TX = 1<<6, +}; + +/** + * struct survey_info - channel survey response + * + * @channel: the channel this survey record reports, mandatory + * @filled: bitflag of flags from &enum survey_info_flags + * @noise: channel noise in dBm. This and all following fields are + * optional + * @channel_time: amount of time in ms the radio spent on the channel + * @channel_time_busy: amount of time the primary channel was sensed busy + * @channel_time_ext_busy: amount of time the extension channel was sensed busy + * @channel_time_rx: amount of time the radio spent receiving data + * @channel_time_tx: amount of time the radio spent transmitting data + * + * Used by dump_survey() to report back per-channel survey information. + * + * This structure can later be expanded with things like + * channel duty cycle etc. + */ +struct survey_info { + struct ieee80211_channel *channel; + u64 channel_time; + u64 channel_time_busy; + u64 channel_time_ext_busy; + u64 channel_time_rx; + u64 channel_time_tx; + u32 filled; + s8 noise; +}; + +/** + * struct cfg80211_crypto_settings - Crypto settings + * @wpa_versions: indicates which, if any, WPA versions are enabled + * (from enum nl80211_wpa_versions) + * @cipher_group: group key cipher suite (or 0 if unset) + * @n_ciphers_pairwise: number of AP supported unicast ciphers + * @ciphers_pairwise: unicast key cipher suites + * @n_akm_suites: number of AKM suites + * @akm_suites: AKM suites + * @control_port: Whether user space controls IEEE 802.1X port, i.e., + * sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is + * required to assume that the port is unauthorized until authorized by + * user space. Otherwise, port is marked authorized by default. + * @control_port_ethertype: the control port protocol that should be + * allowed through even on unauthorized ports + * @control_port_no_encrypt: TRUE to prevent encryption of control port + * protocol frames. + */ +struct cfg80211_crypto_settings { + u32 wpa_versions; + u32 cipher_group; + int n_ciphers_pairwise; + u32 ciphers_pairwise[NL80211_MAX_NR_CIPHER_SUITES]; + int n_akm_suites; + u32 akm_suites[NL80211_MAX_NR_AKM_SUITES]; + bool control_port; + __be16 control_port_ethertype; + bool control_port_no_encrypt; +}; + +/** + * struct beacon_parameters - beacon parameters + * + * Used to configure the beacon for an interface. + * + * @head: head portion of beacon (before TIM IE) + * or %NULL if not changed + * @tail: tail portion of beacon (after TIM IE) + * or %NULL if not changed + * @interval: beacon interval or zero if not changed + * @dtim_period: DTIM period or zero if not changed + * @head_len: length of @head + * @tail_len: length of @tail + * @ssid: SSID to be used in the BSS (note: may be %NULL if not provided from + * user space) + * @ssid_len: length of @ssid + * @hidden_ssid: whether to hide the SSID in Beacon/Probe Response frames + * @crypto: crypto settings + * @privacy: the BSS uses privacy + * @acl_mac: Enable or disable mac address based acl in driver, + * valid only for the drivers which have this support implemented. + * @auth_type: Authentication type (algorithm) + * @beacon_ies: extra information element(s) to add into Beacon frames or %NULL + * @beacon_ies_len: length of beacon_ies in octets + * @proberesp_ies: extra information element(s) to add into Probe Response + * frames or %NULL + * @proberesp_ies_len: length of proberesp_ies in octets + * @assocresp_ies: extra information element(s) to add into (Re)Association + * Response frames or %NULL + * @assocresp_ies_len: length of assocresp_ies in octets + * @probe_resp_len: length of probe response template (@probe_resp) + * @probe_resp: probe response template (AP mode only) + */ +struct beacon_parameters { + u8 *head, *tail; + int interval, dtim_period; + int head_len, tail_len; + const u8 *ssid; + size_t ssid_len; + enum nl80211_hidden_ssid hidden_ssid; + struct cfg80211_crypto_settings crypto; + bool privacy; + bool acl_mac; + enum nl80211_auth_type auth_type; + const u8 *beacon_ies; + size_t beacon_ies_len; + const u8 *proberesp_ies; + size_t proberesp_ies_len; + const u8 *assocresp_ies; + size_t assocresp_ies_len; + int probe_resp_len; + u8 *probe_resp; + enum nl80211_sta_capab_req_options sta_cap_req; + u8 auto_channel_select; +}; + +/** + * enum plink_action - actions to perform in mesh peers + * + * @PLINK_ACTION_INVALID: action 0 is reserved + * @PLINK_ACTION_OPEN: start mesh peer link establishment + * @PLINK_ACTION_BLOCK: block traffic from this mesh peer + */ +enum plink_actions { + PLINK_ACTION_INVALID, + PLINK_ACTION_OPEN, + PLINK_ACTION_BLOCK, +}; + +/** + * enum station_parameters_apply_mask - station parameter values to apply + * @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp) + * + * Not all station parameters have in-band "no change" signalling, + * for those that don't these flags will are used. + */ +enum station_parameters_apply_mask { + STATION_PARAM_APPLY_UAPSD = BIT(0), +}; + +/** + * struct station_parameters - station parameters + * + * Used to change and create a new station. + * + * @vlan: vlan interface station should belong to + * @supported_rates: supported rates in IEEE 802.11 format + * (or NULL for no change) + * @supported_rates_len: number of supported rates + * @sta_flags_mask: station flags that changed + * (bitmask of BIT(NL80211_STA_FLAG_...)) + * @sta_flags_set: station flags values + * (bitmask of BIT(NL80211_STA_FLAG_...)) + * @listen_interval: listen interval or -1 for no change + * @aid: AID or zero for no change + * @plink_action: plink action to take + * @plink_state: set the peer link state for a station + * @ht_capa: HT capabilities of station + * @uapsd_queues: bitmap of queues configured for uapsd. same format + * as the AC bitmap in the QoS info field + * @max_sp: max Service Period. same format as the MAX_SP in the + * QoS info field (but already shifted down) + * @sta_modify_mask: bitmap indicating which parameters changed + * (for those that don't have a natural "no change" value), + * see &enum station_parameters_apply_mask + */ +struct station_parameters { + u8 *supported_rates; + struct net_device *vlan; + u32 sta_flags_mask, sta_flags_set; + u32 sta_modify_mask; + int listen_interval; + u16 aid; + u8 supported_rates_len; + u8 plink_action; + u8 plink_state; + struct ieee80211_ht_cap *ht_capa; + u8 uapsd_queues; + u8 max_sp; +}; + +/** + * enum station_info_flags - station information flags + * + * Used by the driver to indicate which info in &struct station_info + * it has filled in during get_station() or dump_station(). + * + * @STATION_INFO_INACTIVE_TIME: @inactive_time filled + * @STATION_INFO_RX_BYTES: @rx_bytes filled + * @STATION_INFO_TX_BYTES: @tx_bytes filled + * @STATION_INFO_LLID: @llid filled + * @STATION_INFO_PLID: @plid filled + * @STATION_INFO_PLINK_STATE: @plink_state filled + * @STATION_INFO_SIGNAL: @signal filled + * @STATION_INFO_TX_BITRATE: @txrate fields are filled + * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs) + * @STATION_INFO_RX_PACKETS: @rx_packets filled + * @STATION_INFO_TX_PACKETS: @tx_packets filled + * @STATION_INFO_TX_RETRIES: @tx_retries filled + * @STATION_INFO_TX_FAILED: @tx_failed filled + * @STATION_INFO_RX_DROP_MISC: @rx_dropped_misc filled + * @STATION_INFO_SIGNAL_AVG: @signal_avg filled + * @STATION_INFO_RX_BITRATE: @rxrate fields are filled + * @STATION_INFO_BSS_PARAM: @bss_param filled + * @STATION_INFO_CONNECTED_TIME: @connected_time filled + * @STATION_INFO_ASSOC_REQ_IES: @assoc_req_ies filled + * @STATION_INFO_STA_FLAGS: @sta_flags filled + */ +enum station_info_flags { + STATION_INFO_INACTIVE_TIME = 1<<0, + STATION_INFO_RX_BYTES = 1<<1, + STATION_INFO_TX_BYTES = 1<<2, + STATION_INFO_LLID = 1<<3, + STATION_INFO_PLID = 1<<4, + STATION_INFO_PLINK_STATE = 1<<5, + STATION_INFO_SIGNAL = 1<<6, + STATION_INFO_TX_BITRATE = 1<<7, + STATION_INFO_RX_PACKETS = 1<<8, + STATION_INFO_TX_PACKETS = 1<<9, + STATION_INFO_TX_RETRIES = 1<<10, + STATION_INFO_TX_FAILED = 1<<11, + STATION_INFO_RX_DROP_MISC = 1<<12, + STATION_INFO_SIGNAL_AVG = 1<<13, + STATION_INFO_RX_BITRATE = 1<<14, + STATION_INFO_BSS_PARAM = 1<<15, + STATION_INFO_CONNECTED_TIME = 1<<16, + STATION_INFO_ASSOC_REQ_IES = 1<<17, + STATION_INFO_STA_FLAGS = 1<<18 +}; + +/** + * enum station_info_rate_flags - bitrate info flags + * + * Used by the driver to indicate the specific rate transmission + * type for 802.11n transmissions. + * + * @RATE_INFO_FLAGS_MCS: @tx_bitrate_mcs filled + * @RATE_INFO_FLAGS_40_MHZ_WIDTH: 40 Mhz width transmission + * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval + */ +enum rate_info_flags { + RATE_INFO_FLAGS_MCS = 1<<0, + RATE_INFO_FLAGS_40_MHZ_WIDTH = 1<<1, + RATE_INFO_FLAGS_SHORT_GI = 1<<2, +}; + +/** + * struct rate_info - bitrate information + * + * Information about a receiving or transmitting bitrate + * + * @flags: bitflag of flags from &enum rate_info_flags + * @mcs: mcs index if struct describes a 802.11n bitrate + * @legacy: bitrate in 100kbit/s for 802.11abg + */ +struct rate_info { + u8 flags; + u8 mcs; + u16 legacy; +}; + +/** + * enum station_info_rate_flags - bitrate info flags + * + * Used by the driver to indicate the specific rate transmission + * type for 802.11n transmissions. + * + * @BSS_PARAM_FLAGS_CTS_PROT: whether CTS protection is enabled + * @BSS_PARAM_FLAGS_SHORT_PREAMBLE: whether short preamble is enabled + * @BSS_PARAM_FLAGS_SHORT_SLOT_TIME: whether short slot time is enabled + */ +enum bss_param_flags { + BSS_PARAM_FLAGS_CTS_PROT = 1<<0, + BSS_PARAM_FLAGS_SHORT_PREAMBLE = 1<<1, + BSS_PARAM_FLAGS_SHORT_SLOT_TIME = 1<<2, +}; + +/** + * struct sta_bss_parameters - BSS parameters for the attached station + * + * Information about the currently associated BSS + * + * @flags: bitflag of flags from &enum bss_param_flags + * @dtim_period: DTIM period for the BSS + * @beacon_interval: beacon interval + */ +struct sta_bss_parameters { + u8 flags; + u8 dtim_period; + u16 beacon_interval; +}; + +/** + * struct station_info - station information + * + * Station information filled by driver for get_station() and dump_station. + * + * @filled: bitflag of flags from &enum station_info_flags + * @connected_time: time(in secs) since a station is last connected + * @inactive_time: time since last station activity (tx/rx) in milliseconds + * @rx_bytes: bytes received from this station + * @tx_bytes: bytes transmitted to this station + * @llid: mesh local link id + * @plid: mesh peer link id + * @plink_state: mesh peer link state + * @signal: signal strength of last received packet in dBm + * @signal_avg: signal strength average in dBm + * @txrate: current unicast bitrate from this station + * @rxrate: current unicast bitrate to this station + * @rx_packets: packets received from this station + * @tx_packets: packets transmitted to this station + * @tx_retries: cumulative retry counts + * @tx_failed: number of failed transmissions (retries exceeded, no ACK) + * @rx_dropped_misc: Dropped for un-specified reason. + * @bss_param: current BSS parameters + * @generation: generation number for nl80211 dumps. + * This number should increase every time the list of stations + * changes, i.e. when a station is added or removed, so that + * userspace can tell whether it got a consistent snapshot. + * @assoc_req_ies: IEs from (Re)Association Request. + * This is used only when in AP mode with drivers that do not use + * user space MLME/SME implementation. The information is provided for + * the cfg80211_new_sta() calls to notify user space of the IEs. + * @assoc_req_ies_len: Length of assoc_req_ies buffer in octets. + * @sta_flags: station flags mask & values + */ +struct station_info { + u32 filled; + u32 connected_time; + u32 inactive_time; + u32 rx_bytes; + u32 tx_bytes; + u16 llid; + u16 plid; + u8 plink_state; + s8 signal; + s8 signal_avg; + struct rate_info txrate; + struct rate_info rxrate; + u32 rx_packets; + u32 tx_packets; + u32 tx_retries; + u32 tx_failed; + u32 rx_dropped_misc; + struct sta_bss_parameters bss_param; + struct nl80211_sta_flag_update sta_flags; + + int generation; + + const u8 *assoc_req_ies; + size_t assoc_req_ies_len; + + /* + * Note: Add a new enum station_info_flags value for each new field and + * use it to check which fields are initialized. + */ +}; + +/** + * enum monitor_flags - monitor flags + * + * Monitor interface configuration flags. Note that these must be the bits + * according to the nl80211 flags. + * + * @MONITOR_FLAG_FCSFAIL: pass frames with bad FCS + * @MONITOR_FLAG_PLCPFAIL: pass frames with bad PLCP + * @MONITOR_FLAG_CONTROL: pass control frames + * @MONITOR_FLAG_OTHER_BSS: disable BSSID filtering + * @MONITOR_FLAG_COOK_FRAMES: report frames after processing + */ +enum monitor_flags { + MONITOR_FLAG_FCSFAIL = 1<bss_priv_size bytes + */ +struct cfg80211_bss { + struct ieee80211_channel *channel; + + u8 bssid[ETH_ALEN]; + u64 tsf; + u16 beacon_interval; + u16 capability; + u8 *information_elements; + size_t len_information_elements; + u8 *beacon_ies; + size_t len_beacon_ies; + u8 *proberesp_ies; + size_t len_proberesp_ies; + + s32 signal; + + void (*free_priv)(struct cfg80211_bss *bss); + u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); +}; + +/** + * ieee80211_bss_get_ie - find IE with given ID + * @bss: the bss to search + * @ie: the IE ID + * Returns %NULL if not found. + */ +const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie); + + +/** + * struct cfg80211_auth_request - Authentication request data + * + * This structure provides information needed to complete IEEE 802.11 + * authentication. + * + * @bss: The BSS to authenticate with. + * @auth_type: Authentication type (algorithm) + * @ie: Extra IEs to add to Authentication frame or %NULL + * @ie_len: Length of ie buffer in octets + * @key_len: length of WEP key for shared key authentication + * @key_idx: index of WEP key for shared key authentication + * @key: WEP key for shared key authentication + * @local_state_change: This is a request for a local state only, i.e., no + * Authentication frame is to be transmitted and authentication state is + * to be changed without having to wait for a response from the peer STA + * (AP). + */ +struct cfg80211_auth_request { + struct cfg80211_bss *bss; + const u8 *ie; + size_t ie_len; + enum nl80211_auth_type auth_type; + const u8 *key; + u8 key_len, key_idx; + bool local_state_change; +}; + +/** + * struct cfg80211_assoc_request - (Re)Association request data + * + * This structure provides information needed to complete IEEE 802.11 + * (re)association. + * @bss: The BSS to associate with. + * @ie: Extra IEs to add to (Re)Association Request frame or %NULL + * @ie_len: Length of ie buffer in octets + * @use_mfp: Use management frame protection (IEEE 802.11w) in this association + * @crypto: crypto settings + * @prev_bssid: previous BSSID, if not %NULL use reassociate frame + */ +struct cfg80211_assoc_request { + struct cfg80211_bss *bss; + const u8 *ie, *prev_bssid; + size_t ie_len; + struct cfg80211_crypto_settings crypto; + bool use_mfp; +}; + +/** + * struct cfg80211_deauth_request - Deauthentication request data + * + * This structure provides information needed to complete IEEE 802.11 + * deauthentication. + * + * @bss: the BSS to deauthenticate from + * @ie: Extra IEs to add to Deauthentication frame or %NULL + * @ie_len: Length of ie buffer in octets + * @reason_code: The reason code for the deauthentication + * @local_state_change: This is a request for a local state only, i.e., no + * Deauthentication frame is to be transmitted. + */ +struct cfg80211_deauth_request { + struct cfg80211_bss *bss; + const u8 *ie; + size_t ie_len; + u16 reason_code; + bool local_state_change; +}; + +/** + * struct cfg80211_disassoc_request - Disassociation request data + * + * This structure provides information needed to complete IEEE 802.11 + * disassocation. + * + * @bss: the BSS to disassociate from + * @ie: Extra IEs to add to Disassociation frame or %NULL + * @ie_len: Length of ie buffer in octets + * @reason_code: The reason code for the disassociation + * @local_state_change: This is a request for a local state only, i.e., no + * Disassociation frame is to be transmitted. + */ +struct cfg80211_disassoc_request { + struct cfg80211_bss *bss; + const u8 *ie; + size_t ie_len; + u16 reason_code; + bool local_state_change; +}; + +/** + * struct cfg80211_ibss_params - IBSS parameters + * + * This structure defines the IBSS parameters for the join_ibss() + * method. + * + * @ssid: The SSID, will always be non-null. + * @ssid_len: The length of the SSID, will always be non-zero. + * @bssid: Fixed BSSID requested, maybe be %NULL, if set do not + * search for IBSSs with a different BSSID. + * @channel: The channel to use if no IBSS can be found to join. + * @channel_fixed: The channel should be fixed -- do not search for + * IBSSs to join on other channels. + * @ie: information element(s) to include in the beacon + * @ie_len: length of that + * @beacon_interval: beacon interval to use + * @privacy: this is a protected network, keys will be configured + * after joining + * @basic_rates: bitmap of basic rates to use when creating the IBSS + * @mcast_rate: per-band multicast rate index + 1 (0: disabled) + */ +struct cfg80211_ibss_params { + u8 *ssid; + u8 *bssid; + struct ieee80211_channel *channel; + u8 *ie; + u8 ssid_len, ie_len; + u16 beacon_interval; + u32 basic_rates; + bool channel_fixed; + bool privacy; + int mcast_rate[IEEE80211_NUM_BANDS]; +}; + +/** + * struct cfg80211_connect_params - Connection parameters + * + * This structure provides information needed to complete IEEE 802.11 + * authentication and association. + * + * @channel: The channel to use or %NULL if not specified (auto-select based + * on scan results) + * @bssid: The AP BSSID or %NULL if not specified (auto-select based on scan + * results) + * @ssid: SSID + * @ssid_len: Length of ssid in octets + * @auth_type: Authentication type (algorithm) + * @ie: IEs for association request + * @ie_len: Length of assoc_ie in octets + * @privacy: indicates whether privacy-enabled APs should be used + * @crypto: crypto settings + * @key_len: length of WEP key for shared key authentication + * @key_idx: index of WEP key for shared key authentication + * @key: WEP key for shared key authentication + * @bg_scan_period: Background scan period in seconds + * or -1 to indicate that default value is to be used. + */ +struct cfg80211_connect_params { + struct ieee80211_channel *channel; + u8 *bssid; + u8 *ssid; + size_t ssid_len; + enum nl80211_auth_type auth_type; + u8 *ie; + size_t ie_len; + bool privacy; + struct cfg80211_crypto_settings crypto; + const u8 *key; + u8 key_len, key_idx; + int bg_scan_period; +}; + +/** + * enum wiphy_params_flags - set_wiphy_params bitfield values + * @WIPHY_PARAM_RETRY_SHORT: wiphy->retry_short has changed + * @WIPHY_PARAM_RETRY_LONG: wiphy->retry_long has changed + * @WIPHY_PARAM_FRAG_THRESHOLD: wiphy->frag_threshold has changed + * @WIPHY_PARAM_RTS_THRESHOLD: wiphy->rts_threshold has changed + * @WIPHY_PARAM_COVERAGE_CLASS: coverage class changed + */ +enum wiphy_params_flags { + WIPHY_PARAM_RETRY_SHORT = 1 << 0, + WIPHY_PARAM_RETRY_LONG = 1 << 1, + WIPHY_PARAM_FRAG_THRESHOLD = 1 << 2, + WIPHY_PARAM_RTS_THRESHOLD = 1 << 3, + WIPHY_PARAM_COVERAGE_CLASS = 1 << 4, +}; + +/* + * cfg80211_bitrate_mask - masks for bitrate control + */ +struct cfg80211_bitrate_mask { + struct { + u32 legacy; + u8 mcs[IEEE80211_HT_MCS_MASK_LEN]; + } control[IEEE80211_NUM_BANDS]; +}; +/** + * struct cfg80211_pmksa - PMK Security Association + * + * This structure is passed to the set/del_pmksa() method for PMKSA + * caching. + * + * @bssid: The AP's BSSID. + * @pmkid: The PMK material itself. + */ +struct cfg80211_pmksa { + u8 *bssid; + u8 *pmkid; +}; + +/** + * struct cfg80211_wowlan_trig_pkt_pattern - packet pattern + * @mask: bitmask where to match pattern and where to ignore bytes, + * one bit per byte, in same format as nl80211 + * @pattern: bytes to match where bitmask is 1 + * @pattern_len: length of pattern (in bytes) + * + * Internal note: @mask and @pattern are allocated in one chunk of + * memory, free @mask only! + */ +struct cfg80211_wowlan_trig_pkt_pattern { + u8 *mask, *pattern; + int pattern_len; +}; + +/** + * struct cfg80211_wowlan - Wake on Wireless-LAN support info + * + * This structure defines the enabled WoWLAN triggers for the device. + * @any: wake up on any activity -- special trigger if device continues + * operating as normal during suspend + * @disconnect: wake up if getting disconnected + * @magic_pkt: wake up on receiving magic packet + * @patterns: wake up on receiving packet matching a pattern + * @n_patterns: number of patterns + * @gtk_rekey_failure: wake up on GTK rekey failure + * @eap_identity_req: wake up on EAP identity request packet + * @four_way_handshake: wake up on 4-way handshake + * @rfkill_release: wake up when rfkill is released + */ +struct cfg80211_wowlan { + bool any, disconnect, magic_pkt, gtk_rekey_failure, + eap_identity_req, four_way_handshake, + rfkill_release; + struct cfg80211_wowlan_trig_pkt_pattern *patterns; + int n_patterns; +}; + +/** + * struct cfg80211_gtk_rekey_data - rekey data + * @kek: key encryption key + * @kck: key confirmation key + * @replay_ctr: replay counter + */ +struct cfg80211_gtk_rekey_data { + u8 kek[NL80211_KEK_LEN]; + u8 kck[NL80211_KCK_LEN]; + u8 replay_ctr[NL80211_REPLAY_CTR_LEN]; +}; + +struct mac_address { + u8 addr[ETH_ALEN]; +}; + +/** + * struct cfg80211_acl_params - Access control parameters + * @mac_addrs: List of mac addresses of stations to be used for acl + * @acl_policy: Access control policy to be applied on the station's + * entry specified by mac_addr + */ +struct cfg80211_acl_params { + enum nl80211_acl_policy_attr acl_policy; + int n_acl_entries; + + /* Keep it last */ + struct mac_address mac_addrs[0]; +}; + +/** + * struct cfg80211_ops - backend description for wireless configuration + * + * This struct is registered by fullmac card drivers and/or wireless stacks + * in order to handle configuration requests on their interfaces. + * + * All callbacks except where otherwise noted should return 0 + * on success or a negative error code. + * + * All operations are currently invoked under rtnl for consistency with the + * wireless extensions but this is subject to reevaluation as soon as this + * code is used more widely and we have a first user without wext. + * + * @suspend: wiphy device needs to be suspended. The variable @wow will + * be %NULL or contain the enabled Wake-on-Wireless triggers that are + * configured for the device. + * @resume: wiphy device needs to be resumed + * + * @add_virtual_intf: create a new virtual interface with the given name, + * must set the struct wireless_dev's iftype. Beware: You must create + * the new netdev in the wiphy's network namespace! Returns the netdev, + * or an ERR_PTR. + * + * @del_virtual_intf: remove the virtual interface determined by ifindex. + * + * @change_virtual_intf: change type/configuration of virtual interface, + * keep the struct wireless_dev's iftype updated. + * + * @add_key: add a key with the given parameters. @mac_addr will be %NULL + * when adding a group key. + * + * @get_key: get information about the key with the given parameters. + * @mac_addr will be %NULL when requesting information for a group + * key. All pointers given to the @callback function need not be valid + * after it returns. This function should return an error if it is + * not possible to retrieve the key, -ENOENT if it doesn't exist. + * + * @del_key: remove a key given the @mac_addr (%NULL for a group key) + * and @key_index, return -ENOENT if the key doesn't exist. + * + * @set_default_key: set the default key on an interface + * + * @set_default_mgmt_key: set the default management frame key on an interface + * + * @set_rekey_data: give the data necessary for GTK rekeying to the driver + * + * @add_beacon: Add a beacon with given parameters, @head, @interval + * and @dtim_period will be valid, @tail is optional. + * @set_beacon: Change the beacon parameters for an access point mode + * interface. This should reject the call when no beacon has been + * configured. + * @del_beacon: Remove beacon configuration and stop sending the beacon. + * + * @add_station: Add a new station. + * @del_station: Remove a station; @mac may be NULL to remove all stations. + * @change_station: Modify a given station. + * @get_station: get station information for the station identified by @mac + * @dump_station: dump station callback -- resume dump at index @idx + * + * @add_mpath: add a fixed mesh path + * @del_mpath: delete a given mesh path + * @change_mpath: change a given mesh path + * @get_mpath: get a mesh path for the given parameters + * @dump_mpath: dump mesh path callback -- resume dump at index @idx + * @join_mesh: join the mesh network with the specified parameters + * @leave_mesh: leave the current mesh network + * + * @get_mesh_config: Get the current mesh configuration + * + * @update_mesh_config: Update mesh parameters on a running mesh. + * The mask is a bitfield which tells us which parameters to + * set, and which to leave alone. + * + * @change_bss: Modify parameters for a given BSS. + * + * @set_txq_params: Set TX queue parameters + * + * @set_channel: Set channel for a given wireless interface. Some devices + * may support multi-channel operation (by channel hopping) so cfg80211 + * doesn't verify much. Note, however, that the passed netdev may be + * %NULL as well if the user requested changing the channel for the + * device itself, or for a monitor interface. + * @get_channel: Get the current operating channel, should return %NULL if + * there's no single defined operating channel if for example the + * device implements channel hopping for multi-channel virtual interfaces. + * + * @scan: Request to do a scan. If returning zero, the scan request is given + * the driver, and will be valid until passed to cfg80211_scan_done(). + * For scan results, call cfg80211_inform_bss(); you can call this outside + * the scan/scan_done bracket too. + * + * @auth: Request to authenticate with the specified peer + * @assoc: Request to (re)associate with the specified peer + * @deauth: Request to deauthenticate from the specified peer + * @disassoc: Request to disassociate from the specified peer + * + * @connect: Connect to the ESS with the specified parameters. When connected, + * call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS. + * If the connection fails for some reason, call cfg80211_connect_result() + * with the status from the AP. + * @disconnect: Disconnect from the BSS/ESS. + * + * @join_ibss: Join the specified IBSS (or create if necessary). Once done, call + * cfg80211_ibss_joined(), also call that function when changing BSSID due + * to a merge. + * @leave_ibss: Leave the IBSS. + * + * @set_wiphy_params: Notify that wiphy parameters have changed; + * @changed bitfield (see &enum wiphy_params_flags) describes which values + * have changed. The actual parameter values are available in + * struct wiphy. If returning an error, no value should be changed. + * + * @set_tx_power: set the transmit power according to the parameters + * @get_tx_power: store the current TX power into the dbm variable; + * return 0 if successful + * + * @set_wds_peer: set the WDS peer for a WDS interface + * + * @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting + * functions to adjust rfkill hw state + * + * @dump_survey: get site survey information. + * + * @remain_on_channel: Request the driver to remain awake on the specified + * channel for the specified duration to complete an off-channel + * operation (e.g., public action frame exchange). When the driver is + * ready on the requested channel, it must indicate this with an event + * notification by calling cfg80211_ready_on_channel(). + * @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation. + * This allows the operation to be terminated prior to timeout based on + * the duration value. + * @mgmt_tx: Transmit a management frame. + * @mgmt_tx_cancel_wait: Cancel the wait time from transmitting a management + * frame on another channel + * + * @testmode_cmd: run a test mode command + * @testmode_dump: Implement a test mode dump. The cb->args[2] and up may be + * used by the function, but 0 and 1 must not be touched. Additionally, + * return error codes other than -ENOBUFS and -ENOENT will terminate the + * dump and return to userspace with an error, so be careful. If any data + * was passed in from userspace then the data/len arguments will be present + * and point to the data contained in %NL80211_ATTR_TESTDATA. + * + * @set_bitrate_mask: set the bitrate mask configuration + * + * @set_pmksa: Cache a PMKID for a BSSID. This is mostly useful for fullmac + * devices running firmwares capable of generating the (re) association + * RSN IE. It allows for faster roaming between WPA2 BSSIDs. + * @del_pmksa: Delete a cached PMKID. + * @flush_pmksa: Flush all cached PMKIDs. + * @set_power_mgmt: Configure WLAN power management. A timeout value of -1 + * allows the driver to adjust the dynamic ps timeout value. + * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold. + * @set_cqm_txe_config: Configure connection quality monitor TX error + * thresholds. + * @sched_scan_start: Tell the driver to start a scheduled scan. + * @sched_scan_stop: Tell the driver to stop an ongoing scheduled + * scan. The driver_initiated flag specifies whether the driver + * itself has informed that the scan has stopped. + * + * @mgmt_frame_register: Notify driver that a management frame type was + * registered. Note that this callback may not sleep, and cannot run + * concurrently with itself. + * + * @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device. + * Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may + * reject TX/RX mask combinations they cannot support by returning -EINVAL + * (also see nl80211.h @NL80211_ATTR_WIPHY_ANTENNA_TX). + * + * @get_antenna: Get current antenna configuration from device (tx_ant, rx_ant). + * + * @set_ringparam: Set tx and rx ring sizes. + * + * @get_ringparam: Get tx and rx ring current and maximum sizes. + * + * @tdls_mgmt: Transmit a TDLS management frame. + * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). + * + * @probe_client: probe an associated client, must return a cookie that it + * later passes to cfg80211_probe_status(). + * + * @notify_btcoex: Send BT coex WMI command. + * + * @set_mac_acl: Set stations' mac address to driver's access control list in + * AP and P2P GO mode. Parameters are mac address of list of stations, + * number of entries and acl policy to be used with this list. If there + * is already a list in driver for this acl policy, this new list may + * replace the existing one. When no entry is passed in the list of mac + * address, driver has to clear it's acl list for that acl policy. Drivers + * which advertises the support for mac address based access control can + * implement this callback. + * + */ + +struct cfg80211_ops { + int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); + int (*resume)(struct wiphy *wiphy); + + struct net_device * (*add_virtual_intf)(struct wiphy *wiphy, + char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params); + int (*del_virtual_intf)(struct wiphy *wiphy, struct net_device *dev); + int (*change_virtual_intf)(struct wiphy *wiphy, + struct net_device *dev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params); + + int (*add_key)(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, bool pairwise, const u8 *mac_addr, + struct key_params *params); + int (*get_key)(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, bool pairwise, const u8 *mac_addr, + void *cookie, + void (*callback)(void *cookie, struct key_params*)); + int (*del_key)(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, bool pairwise, const u8 *mac_addr); + int (*set_default_key)(struct wiphy *wiphy, + struct net_device *netdev, + u8 key_index, bool unicast, bool multicast); + int (*set_default_mgmt_key)(struct wiphy *wiphy, + struct net_device *netdev, + u8 key_index); + + int (*add_beacon)(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *info); + int (*set_beacon)(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *info); + int (*del_beacon)(struct wiphy *wiphy, struct net_device *dev); + + + int (*add_station)(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_parameters *params); + int (*del_station)(struct wiphy *wiphy, struct net_device *dev, + u8 *mac); + int (*change_station)(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_parameters *params); + int (*get_station)(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_info *sinfo); + int (*dump_station)(struct wiphy *wiphy, struct net_device *dev, + int idx, u8 *mac, struct station_info *sinfo); + + int (*add_mpath)(struct wiphy *wiphy, struct net_device *dev, + u8 *dst, u8 *next_hop); + int (*del_mpath)(struct wiphy *wiphy, struct net_device *dev, + u8 *dst); + int (*change_mpath)(struct wiphy *wiphy, struct net_device *dev, + u8 *dst, u8 *next_hop); + int (*get_mpath)(struct wiphy *wiphy, struct net_device *dev, + u8 *dst, u8 *next_hop, + struct mpath_info *pinfo); + int (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev, + int idx, u8 *dst, u8 *next_hop, + struct mpath_info *pinfo); + int (*get_mesh_config)(struct wiphy *wiphy, + struct net_device *dev, + struct mesh_config *conf); + int (*update_mesh_config)(struct wiphy *wiphy, + struct net_device *dev, u32 mask, + const struct mesh_config *nconf); + int (*join_mesh)(struct wiphy *wiphy, struct net_device *dev, + const struct mesh_config *conf, + const struct mesh_setup *setup); + int (*leave_mesh)(struct wiphy *wiphy, struct net_device *dev); + + int (*change_bss)(struct wiphy *wiphy, struct net_device *dev, + struct bss_parameters *params); + + int (*set_txq_params)(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_txq_params *params); + + int (*set_channel)(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type); + + int (*scan)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_scan_request *request); + + int (*auth)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_auth_request *req); + int (*assoc)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_assoc_request *req); + int (*deauth)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_deauth_request *req, + void *cookie); + int (*disassoc)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_disassoc_request *req, + void *cookie); + + int (*connect)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme); + int (*disconnect)(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code); + + int (*join_ibss)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params); + int (*leave_ibss)(struct wiphy *wiphy, struct net_device *dev); + + int (*set_wiphy_params)(struct wiphy *wiphy, u32 changed); + + int (*set_tx_power)(struct wiphy *wiphy, + enum nl80211_tx_power_setting type, int mbm); + int (*get_tx_power)(struct wiphy *wiphy, int *dbm); + + int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev, + const u8 *addr); + + void (*rfkill_poll)(struct wiphy *wiphy); + +#ifdef CONFIG_NL80211_TESTMODE + int (*testmode_cmd)(struct wiphy *wiphy, void *data, int len); + int (*testmode_dump)(struct wiphy *wiphy, struct sk_buff *skb, + struct netlink_callback *cb, + void *data, int len); +#endif + + int (*set_bitrate_mask)(struct wiphy *wiphy, + struct net_device *dev, + const u8 *peer, + const struct cfg80211_bitrate_mask *mask); + + int (*dump_survey)(struct wiphy *wiphy, struct net_device *netdev, + int idx, struct survey_info *info); + + int (*set_pmksa)(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_pmksa *pmksa); + int (*del_pmksa)(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_pmksa *pmksa); + int (*flush_pmksa)(struct wiphy *wiphy, struct net_device *netdev); + + int (*remain_on_channel)(struct wiphy *wiphy, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int duration, + u64 *cookie); + int (*cancel_remain_on_channel)(struct wiphy *wiphy, + struct net_device *dev, + u64 cookie); + + int (*mgmt_tx)(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, + const u8 *buf, size_t len, bool no_cck, + bool dont_wait_for_ack, u64 *cookie); + int (*mgmt_tx_cancel_wait)(struct wiphy *wiphy, + struct net_device *dev, + u64 cookie); + + int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev, + bool enabled, int timeout); + + int (*set_cqm_rssi_config)(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst); + + int (*set_cqm_txe_config)(struct wiphy *wiphy, + struct net_device *dev, + u32 rate, u32 pkts, u32 intvl); + + void (*mgmt_frame_register)(struct wiphy *wiphy, + struct net_device *dev, + u16 frame_type, bool reg); + + int (*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant); + int (*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant); + + int (*set_ringparam)(struct wiphy *wiphy, u32 tx, u32 rx); + void (*get_ringparam)(struct wiphy *wiphy, + u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max); + + int (*sched_scan_start)(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *request); + int (*sched_scan_stop)(struct wiphy *wiphy, struct net_device *dev); + + int (*set_rekey_data)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_gtk_rekey_data *data); + + int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, size_t len); + int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, enum nl80211_tdls_operation oper); + + int (*probe_client)(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u64 *cookie); + + struct ieee80211_channel *(*get_channel)(struct wiphy *wiphy); + + int (*notify_btcoex)(struct wiphy *wiphy, + u8 *buf, int len); + + int (*set_mac_acl)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_acl_params *params); + + int (*set_wow_mode)(struct wiphy *wiphy, + struct cfg80211_wowlan *wow); + + int (*clr_wow_mode)(struct wiphy *wiphy); +}; + +/* + * wireless hardware and networking interfaces structures + * and registration/helper functions + */ + +/** + * enum wiphy_flags - wiphy capability flags + * + * @WIPHY_FLAG_CUSTOM_REGULATORY: tells us the driver for this device + * has its own custom regulatory domain and cannot identify the + * ISO / IEC 3166 alpha2 it belongs to. When this is enabled + * we will disregard the first regulatory hint (when the + * initiator is %REGDOM_SET_BY_CORE). + * @WIPHY_FLAG_STRICT_REGULATORY: tells us the driver for this device will + * ignore regulatory domain settings until it gets its own regulatory + * domain via its regulatory_hint() unless the regulatory hint is + * from a country IE. After its gets its own regulatory domain it will + * only allow further regulatory domain settings to further enhance + * compliance. For example if channel 13 and 14 are disabled by this + * regulatory domain no user regulatory domain can enable these channels + * at a later time. This can be used for devices which do not have + * calibration information guaranteed for frequencies or settings + * outside of its regulatory domain. + * @WIPHY_FLAG_DISABLE_BEACON_HINTS: enable this if your driver needs to ensure + * that passive scan flags and beaconing flags may not be lifted by + * cfg80211 due to regulatory beacon hints. For more information on beacon + * hints read the documenation for regulatory_hint_found_beacon() + * @WIPHY_FLAG_NETNS_OK: if not set, do not allow changing the netns of this + * wiphy at all + * @WIPHY_FLAG_ENFORCE_COMBINATIONS: Set this flag to enforce interface + * combinations for this device. This flag is used for backward + * compatibility only until all drivers advertise combinations and + * they will always be enforced. + * @WIPHY_FLAG_PS_ON_BY_DEFAULT: if set to true, powersave will be enabled + * by default -- this flag will be set depending on the kernel's default + * on wiphy_new(), but can be changed by the driver if it has a good + * reason to override the default + * @WIPHY_FLAG_4ADDR_AP: supports 4addr mode even on AP (with a single station + * on a VLAN interface) + * @WIPHY_FLAG_4ADDR_STATION: supports 4addr mode even as a station + * @WIPHY_FLAG_CONTROL_PORT_PROTOCOL: This device supports setting the + * control port protocol ethertype. The device also honours the + * control_port_no_encrypt flag. + * @WIPHY_FLAG_IBSS_RSN: The device supports IBSS RSN. + * @WIPHY_FLAG_MESH_AUTH: The device supports mesh authentication by routing + * auth frames to userspace. See @NL80211_MESH_SETUP_USERSPACE_AUTH. + * @WIPHY_FLAG_SUPPORTS_SCHED_SCAN: The device supports scheduled scans. + * @WIPHY_FLAG_SUPPORTS_FW_ROAM: The device supports roaming feature in the + * firmware. + * @WIPHY_FLAG_AP_UAPSD: The device supports uapsd on AP. + * @WIPHY_FLAG_SUPPORTS_TDLS: The device supports TDLS (802.11z) operation. + * @WIPHY_FLAG_TDLS_EXTERNAL_SETUP: The device does not handle TDLS (802.11z) + * link setup/discovery operations internally. Setup, discovery and + * teardown packets should be sent through the @NL80211_CMD_TDLS_MGMT + * command. When this flag is not set, @NL80211_CMD_TDLS_OPER should be + * used for asking the driver/firmware to perform a TDLS operation. + * @WIPHY_FLAG_HAVE_AP_SME: device integrates AP SME + * @WIPHY_FLAG_REPORTS_OBSS: the device will report beacons from other BSSes + * when there are virtual interfaces in AP mode by calling + * cfg80211_report_obss_beacon(). + * @WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD: When operating as an AP, the device + * responds to probe-requests in hardware. + */ +enum wiphy_flags { + WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), + WIPHY_FLAG_STRICT_REGULATORY = BIT(1), + WIPHY_FLAG_DISABLE_BEACON_HINTS = BIT(2), + WIPHY_FLAG_NETNS_OK = BIT(3), + WIPHY_FLAG_PS_ON_BY_DEFAULT = BIT(4), + WIPHY_FLAG_4ADDR_AP = BIT(5), + WIPHY_FLAG_4ADDR_STATION = BIT(6), + WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7), + WIPHY_FLAG_IBSS_RSN = BIT(8), + WIPHY_FLAG_MESH_AUTH = BIT(10), + WIPHY_FLAG_SUPPORTS_SCHED_SCAN = BIT(11), + WIPHY_FLAG_ENFORCE_COMBINATIONS = BIT(12), + WIPHY_FLAG_SUPPORTS_FW_ROAM = BIT(13), + WIPHY_FLAG_AP_UAPSD = BIT(14), + WIPHY_FLAG_SUPPORTS_TDLS = BIT(15), + WIPHY_FLAG_TDLS_EXTERNAL_SETUP = BIT(16), + WIPHY_FLAG_HAVE_AP_SME = BIT(17), + WIPHY_FLAG_REPORTS_OBSS = BIT(18), + WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD = BIT(19), + WIPHY_FLAG_SUPPORTS_ACS = BIT(20), +}; + +/** + * struct ieee80211_iface_limit - limit on certain interface types + * @max: maximum number of interfaces of these types + * @types: interface types (bits) + */ +struct ieee80211_iface_limit { + u16 max; + u16 types; +}; + +/** + * struct ieee80211_iface_combination - possible interface combination + * @limits: limits for the given interface types + * @n_limits: number of limitations + * @num_different_channels: can use up to this many different channels + * @max_interfaces: maximum number of interfaces in total allowed in this + * group + * @beacon_int_infra_match: In this combination, the beacon intervals + * between infrastructure and AP types must match. This is required + * only in special cases. + * + * These examples can be expressed as follows: + * + * Allow #STA <= 1, #AP <= 1, matching BI, channels = 1, 2 total: + * + * struct ieee80211_iface_limit limits1[] = { + * { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, + * { .max = 1, .types = BIT(NL80211_IFTYPE_AP}, }, + * }; + * struct ieee80211_iface_combination combination1 = { + * .limits = limits1, + * .n_limits = ARRAY_SIZE(limits1), + * .max_interfaces = 2, + * .beacon_int_infra_match = true, + * }; + * + * + * Allow #{AP, P2P-GO} <= 8, channels = 1, 8 total: + * + * struct ieee80211_iface_limit limits2[] = { + * { .max = 8, .types = BIT(NL80211_IFTYPE_AP) | + * BIT(NL80211_IFTYPE_P2P_GO), }, + * }; + * struct ieee80211_iface_combination combination2 = { + * .limits = limits2, + * .n_limits = ARRAY_SIZE(limits2), + * .max_interfaces = 8, + * .num_different_channels = 1, + * }; + * + * + * Allow #STA <= 1, #{P2P-client,P2P-GO} <= 3 on two channels, 4 total. + * This allows for an infrastructure connection and three P2P connections. + * + * struct ieee80211_iface_limit limits3[] = { + * { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, + * { .max = 3, .types = BIT(NL80211_IFTYPE_P2P_GO) | + * BIT(NL80211_IFTYPE_P2P_CLIENT), }, + * }; + * struct ieee80211_iface_combination combination3 = { + * .limits = limits3, + * .n_limits = ARRAY_SIZE(limits3), + * .max_interfaces = 4, + * .num_different_channels = 2, + * }; + */ +struct ieee80211_iface_combination { + const struct ieee80211_iface_limit *limits; + u32 num_different_channels; + u16 max_interfaces; + u8 n_limits; + bool beacon_int_infra_match; +}; + +struct ieee80211_txrx_stypes { + u16 tx, rx; +}; + +/** + * enum wiphy_wowlan_support_flags - WoWLAN support flags + * @WIPHY_WOWLAN_ANY: supports wakeup for the special "any" + * trigger that keeps the device operating as-is and + * wakes up the host on any activity, for example a + * received packet that passed filtering; note that the + * packet should be preserved in that case + * @WIPHY_WOWLAN_MAGIC_PKT: supports wakeup on magic packet + * (see nl80211.h) + * @WIPHY_WOWLAN_DISCONNECT: supports wakeup on disconnect + * @WIPHY_WOWLAN_SUPPORTS_GTK_REKEY: supports GTK rekeying while asleep + * @WIPHY_WOWLAN_GTK_REKEY_FAILURE: supports wakeup on GTK rekey failure + * @WIPHY_WOWLAN_EAP_IDENTITY_REQ: supports wakeup on EAP identity request + * @WIPHY_WOWLAN_4WAY_HANDSHAKE: supports wakeup on 4-way handshake failure + * @WIPHY_WOWLAN_RFKILL_RELEASE: supports wakeup on RF-kill release + */ +enum wiphy_wowlan_support_flags { + WIPHY_WOWLAN_ANY = BIT(0), + WIPHY_WOWLAN_MAGIC_PKT = BIT(1), + WIPHY_WOWLAN_DISCONNECT = BIT(2), + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY = BIT(3), + WIPHY_WOWLAN_GTK_REKEY_FAILURE = BIT(4), + WIPHY_WOWLAN_EAP_IDENTITY_REQ = BIT(5), + WIPHY_WOWLAN_4WAY_HANDSHAKE = BIT(6), + WIPHY_WOWLAN_RFKILL_RELEASE = BIT(7), +}; + +/** + * struct wiphy_wowlan_support - WoWLAN support data + * @flags: see &enum wiphy_wowlan_support_flags + * @n_patterns: number of supported wakeup patterns + * (see nl80211.h for the pattern definition) + * @pattern_max_len: maximum length of each pattern + * @pattern_min_len: minimum length of each pattern + */ +struct wiphy_wowlan_support { + u32 flags; + int n_patterns; + int pattern_max_len; + int pattern_min_len; +}; + +/** + * struct wiphy - wireless hardware description + * @reg_notifier: the driver's regulatory notification callback, + * note that if your driver uses wiphy_apply_custom_regulatory() + * the reg_notifier's request can be passed as NULL + * @regd: the driver's regulatory domain, if one was requested via + * the regulatory_hint() API. This can be used by the driver + * on the reg_notifier() if it chooses to ignore future + * regulatory domain changes caused by other drivers. + * @signal_type: signal type reported in &struct cfg80211_bss. + * @cipher_suites: supported cipher suites + * @n_cipher_suites: number of supported cipher suites + * @retry_short: Retry limit for short frames (dot11ShortRetryLimit) + * @retry_long: Retry limit for long frames (dot11LongRetryLimit) + * @frag_threshold: Fragmentation threshold (dot11FragmentationThreshold); + * -1 = fragmentation disabled, only odd values >= 256 used + * @rts_threshold: RTS threshold (dot11RTSThreshold); -1 = RTS/CTS disabled + * @_net: the network namespace this wiphy currently lives in + * @perm_addr: permanent MAC address of this device + * @addr_mask: If the device supports multiple MAC addresses by masking, + * set this to a mask with variable bits set to 1, e.g. if the last + * four bits are variable then set it to 00:...:00:0f. The actual + * variable bits shall be determined by the interfaces added, with + * interfaces not matching the mask being rejected to be brought up. + * @n_addresses: number of addresses in @addresses. + * @addresses: If the device has more than one address, set this pointer + * to a list of addresses (6 bytes each). The first one will be used + * by default for perm_addr. In this case, the mask should be set to + * all-zeroes. In this case it is assumed that the device can handle + * the same number of arbitrary MAC addresses. + * @registered: protects ->resume and ->suspend sysfs callbacks against + * unregister hardware + * @debugfsdir: debugfs directory used for this wiphy, will be renamed + * automatically on wiphy renames + * @dev: (virtual) struct device for this wiphy + * @registered: helps synchronize suspend/resume with wiphy unregister + * @wext: wireless extension handlers + * @priv: driver private data (sized according to wiphy_new() parameter) + * @interface_modes: bitmask of interfaces types valid for this wiphy, + * must be set by driver + * @iface_combinations: Valid interface combinations array, should not + * list single interface types. + * @n_iface_combinations: number of entries in @iface_combinations array. + * @software_iftypes: bitmask of software interface types, these are not + * subject to any restrictions since they are purely managed in SW. + * @flags: wiphy flags, see &enum wiphy_flags + * @features: features advertised to nl80211, see &enum nl80211_feature_flags. + * @bss_priv_size: each BSS struct has private data allocated with it, + * this variable determines its size + * @max_scan_ssids: maximum number of SSIDs the device can scan for in + * any given scan + * @max_sched_scan_ssids: maximum number of SSIDs the device can scan + * for in any given scheduled scan + * @max_match_sets: maximum number of match sets the device can handle + * when performing a scheduled scan, 0 if filtering is not + * supported. + * @max_scan_ie_len: maximum length of user-controlled IEs device can + * add to probe request frames transmitted during a scan, must not + * include fixed IEs like supported rates + * @max_sched_scan_ie_len: same as max_scan_ie_len, but for scheduled + * scans + * @coverage_class: current coverage class + * @fw_version: firmware version for ethtool reporting + * @hw_version: hardware version for ethtool reporting + * @max_num_pmkids: maximum number of PMKIDs supported by device + * @privid: a pointer that drivers can use to identify if an arbitrary + * wiphy is theirs, e.g. in global notifiers + * @bands: information about bands/channels supported by this device + * + * @mgmt_stypes: bitmasks of frame subtypes that can be subscribed to or + * transmitted through nl80211, points to an array indexed by interface + * type + * + * @available_antennas_tx: bitmap of antennas which are available to be + * configured as TX antennas. Antenna configuration commands will be + * rejected unless this or @available_antennas_rx is set. + * + * @available_antennas_rx: bitmap of antennas which are available to be + * configured as RX antennas. Antenna configuration commands will be + * rejected unless this or @available_antennas_tx is set. + * + * @max_remain_on_channel_duration: Maximum time a remain-on-channel operation + * may request, if implemented. + * + * @wowlan: WoWLAN support information + * + * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features. + * + * @max_acl_mac_addrs: Maximum number of mac addresses that the device + * supports for ACL. Driver having ACL based on MAC address support + * has to fill this. This limit is common for both (white and black) + * the acl policies. + */ +struct wiphy { + /* assign these fields before you register the wiphy */ + +#define WIPHY_COMPAT_PAD_SIZE 2048 + u8 padding[WIPHY_COMPAT_PAD_SIZE]; + + /* permanent MAC address(es) */ + u8 perm_addr[ETH_ALEN]; + u8 addr_mask[ETH_ALEN]; + + struct mac_address *addresses; + + const struct ieee80211_txrx_stypes *mgmt_stypes; + + const struct ieee80211_iface_combination *iface_combinations; + int n_iface_combinations; + u16 software_iftypes; + + u16 n_addresses; + + /* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */ + u16 interface_modes; + + u32 flags, features; + + u32 ap_sme_capa; + + enum cfg80211_signal_type signal_type; + + int bss_priv_size; + u8 max_scan_ssids; + u8 max_sched_scan_ssids; + u8 max_match_sets; + u16 max_scan_ie_len; + u16 max_sched_scan_ie_len; + + int n_cipher_suites; + const u32 *cipher_suites; + + u8 retry_short; + u8 retry_long; + u32 frag_threshold; + u32 rts_threshold; + u8 coverage_class; + + char fw_version[ETHTOOL_BUSINFO_LEN]; + u32 hw_version; + + struct wiphy_wowlan_support wowlan; + + u16 max_remain_on_channel_duration; + + u8 max_num_pmkids; + + u32 available_antennas_tx; + u32 available_antennas_rx; + + /* + * Bitmap of supported protocols for probe response offloading + * see &enum nl80211_probe_resp_offload_support_attr. Only valid + * when the wiphy flag @WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD is set. + */ + u32 probe_resp_offload; + + /* If multiple wiphys are registered and you're handed e.g. + * a regular netdev with assigned ieee80211_ptr, you won't + * know whether it points to a wiphy your driver has registered + * or not. Assign this to something global to your driver to + * help determine whether you own this wiphy or not. */ + const void *privid; + + struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS]; + + /* Lets us get back the wiphy on the callback */ + int (*reg_notifier)(struct wiphy *wiphy, + struct regulatory_request *request); + + /* fields below are read-only, assigned by cfg80211 */ + + const struct ieee80211_regdomain *regd; + + /* the item in /sys/class/ieee80211/ points to this, + * you need use set_wiphy_dev() (see below) */ + struct device dev; + + /* protects ->resume, ->suspend sysfs callbacks against unregister hw */ + bool registered; + + /* dir in debugfs: ieee80211/ */ + struct dentry *debugfsdir; + +#ifdef CONFIG_NET_NS + /* the network namespace this phy lives in currently */ + struct net *_net; +#endif + +#ifdef CONFIG_CFG80211_WEXT + const struct iw_handler_def *wext; +#endif + + /* + * Maximum number of mac address entries that device can + * support for access control based on mac address. + */ + u8 max_acl_mac_addrs; + + char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); +}; + +static inline struct net *wiphy_net(struct wiphy *wiphy) +{ + return read_pnet(&wiphy->_net); +} + +static inline void wiphy_net_set(struct wiphy *wiphy, struct net *net) +{ + write_pnet(&wiphy->_net, net); +} + +/** + * wiphy_priv - return priv from wiphy + * + * @wiphy: the wiphy whose priv pointer to return + */ +static inline void *wiphy_priv(struct wiphy *wiphy) +{ + BUG_ON(!wiphy); + return &wiphy->priv; +} + +/** + * priv_to_wiphy - return the wiphy containing the priv + * + * @priv: a pointer previously returned by wiphy_priv + */ +static inline struct wiphy *priv_to_wiphy(void *priv) +{ + BUG_ON(!priv); + return container_of(priv, struct wiphy, priv); +} + +/** + * set_wiphy_dev - set device pointer for wiphy + * + * @wiphy: The wiphy whose device to bind + * @dev: The device to parent it to + */ +static inline void set_wiphy_dev(struct wiphy *wiphy, struct device *dev) +{ + wiphy->dev.parent = dev; +} + +/** + * wiphy_dev - get wiphy dev pointer + * + * @wiphy: The wiphy whose device struct to look up + */ +static inline struct device *wiphy_dev(struct wiphy *wiphy) +{ + return wiphy->dev.parent; +} + +/** + * wiphy_name - get wiphy name + * + * @wiphy: The wiphy whose name to return + */ +static inline const char *wiphy_name(const struct wiphy *wiphy) +{ + return dev_name(&wiphy->dev); +} + +/** + * wiphy_new - create a new wiphy for use with cfg80211 + * + * @ops: The configuration operations for this device + * @sizeof_priv: The size of the private area to allocate + * + * Create a new wiphy and associate the given operations with it. + * @sizeof_priv bytes are allocated for private use. + * + * The returned pointer must be assigned to each netdev's + * ieee80211_ptr for proper operation. + */ +struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv); + +/** + * wiphy_register - register a wiphy with cfg80211 + * + * @wiphy: The wiphy to register. + * + * Returns a non-negative wiphy index or a negative error code. + */ +extern int wiphy_register(struct wiphy *wiphy); + +/** + * wiphy_unregister - deregister a wiphy from cfg80211 + * + * @wiphy: The wiphy to unregister. + * + * After this call, no more requests can be made with this priv + * pointer, but the call may sleep to wait for an outstanding + * request that is being handled. + */ +extern void wiphy_unregister(struct wiphy *wiphy); + +/** + * wiphy_free - free wiphy + * + * @wiphy: The wiphy to free + */ +extern void wiphy_free(struct wiphy *wiphy); + +/* internal structs */ +struct cfg80211_conn; +struct cfg80211_internal_bss; +struct cfg80211_cached_keys; + +#define MAX_AUTH_BSSES 4 + +/** + * struct wireless_dev - wireless per-netdev state + * + * This structure must be allocated by the driver/stack + * that uses the ieee80211_ptr field in struct net_device + * (this is intentional so it can be allocated along with + * the netdev.) + * + * @wiphy: pointer to hardware description + * @iftype: interface type + * @list: (private) Used to collect the interfaces + * @netdev: (private) Used to reference back to the netdev + * @current_bss: (private) Used by the internal configuration code + * @channel: (private) Used by the internal configuration code to track + * user-set AP, monitor and WDS channels for wireless extensions + * @bssid: (private) Used by the internal configuration code + * @ssid: (private) Used by the internal configuration code + * @ssid_len: (private) Used by the internal configuration code + * @mesh_id_len: (private) Used by the internal configuration code + * @mesh_id_up_len: (private) Used by the internal configuration code + * @wext: (private) Used by the internal wireless extensions compat code + * @use_4addr: indicates 4addr mode is used on this interface, must be + * set by driver (if supported) on add_interface BEFORE registering the + * netdev and may otherwise be used by driver read-only, will be update + * by cfg80211 on change_interface + * @mgmt_registrations: list of registrations for management frames + * @mgmt_registrations_lock: lock for the list + * @mtx: mutex used to lock data in this struct + * @cleanup_work: work struct used for cleanup that can't be done directly + * @beacon_interval: beacon interval used on this device for transmitting + * beacons, 0 when not valid + */ +struct wireless_dev { + struct wiphy *wiphy; + enum nl80211_iftype iftype; + + /* the remainder of this struct should be private to cfg80211 */ + struct list_head list; + struct net_device *netdev; + + struct list_head mgmt_registrations; + spinlock_t mgmt_registrations_lock; + + struct mutex mtx; + + struct work_struct cleanup_work; + + bool use_4addr; + + /* currently used for IBSS and SME - might be rearranged later */ + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 ssid_len, mesh_id_len, mesh_id_up_len; + enum { + CFG80211_SME_IDLE, + CFG80211_SME_CONNECTING, + CFG80211_SME_CONNECTED, + } sme_state; + struct cfg80211_conn *conn; + struct cfg80211_cached_keys *connect_keys; + + struct list_head event_list; + spinlock_t event_lock; + + struct cfg80211_internal_bss *authtry_bsses[MAX_AUTH_BSSES]; + struct cfg80211_internal_bss *auth_bsses[MAX_AUTH_BSSES]; + struct cfg80211_internal_bss *current_bss; /* associated / joined */ + struct ieee80211_channel *channel; + + bool ps; + int ps_timeout; + + int beacon_interval; + + u32 ap_unexpected_nlpid; + +#ifdef CONFIG_CFG80211_WEXT + /* wext data */ + struct { + struct cfg80211_ibss_params ibss; + struct cfg80211_connect_params connect; + struct cfg80211_cached_keys *keys; + u8 *ie; + size_t ie_len; + u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + s8 default_key, default_mgmt_key; + bool prev_bssid_valid; + } wext; +#endif +}; + +/** + * wdev_priv - return wiphy priv from wireless_dev + * + * @wdev: The wireless device whose wiphy's priv pointer to return + */ +static inline void *wdev_priv(struct wireless_dev *wdev) +{ + BUG_ON(!wdev); + return wiphy_priv(wdev->wiphy); +} + +/** + * DOC: Utility functions + * + * cfg80211 offers a number of utility functions that can be useful. + */ + +/** + * ieee80211_channel_to_frequency - convert channel number to frequency + * @chan: channel number + * @band: band, necessary due to channel number overlap + */ +extern int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band); + +/** + * ieee80211_frequency_to_channel - convert frequency to channel number + * @freq: center frequency + */ +extern int ieee80211_frequency_to_channel(int freq); + +/* + * Name indirection necessary because the ieee80211 code also has + * a function named "ieee80211_get_channel", so if you include + * cfg80211's header file you get cfg80211's version, if you try + * to include both header files you'll (rightfully!) get a symbol + * clash. + */ +extern struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy, + int freq); +/** + * ieee80211_get_channel - get channel struct from wiphy for specified frequency + * @wiphy: the struct wiphy to get the channel for + * @freq: the center frequency of the channel + */ +static inline struct ieee80211_channel * +ieee80211_get_channel(struct wiphy *wiphy, int freq) +{ + return __ieee80211_get_channel(wiphy, freq); +} + +/** + * ieee80211_get_response_rate - get basic rate for a given rate + * + * @sband: the band to look for rates in + * @basic_rates: bitmap of basic rates + * @bitrate: the bitrate for which to find the basic rate + * + * This function returns the basic rate corresponding to a given + * bitrate, that is the next lower bitrate contained in the basic + * rate map, which is, for this function, given as a bitmap of + * indices of rates in the band's bitrate table. + */ +struct ieee80211_rate * +ieee80211_get_response_rate(struct ieee80211_supported_band *sband, + u32 basic_rates, int bitrate); + +/* + * Radiotap parsing functions -- for controlled injection support + * + * Implemented in net/wireless/radiotap.c + * Documentation in Documentation/networking/radiotap-headers.txt + */ + +struct radiotap_align_size { + uint8_t align:4, size:4; +}; + +struct ieee80211_radiotap_namespace { + const struct radiotap_align_size *align_size; + int n_bits; + uint32_t oui; + uint8_t subns; +}; + +struct ieee80211_radiotap_vendor_namespaces { + const struct ieee80211_radiotap_namespace *ns; + int n_ns; +}; + +/** + * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args + * @this_arg_index: index of current arg, valid after each successful call + * to ieee80211_radiotap_iterator_next() + * @this_arg: pointer to current radiotap arg; it is valid after each + * call to ieee80211_radiotap_iterator_next() but also after + * ieee80211_radiotap_iterator_init() where it will point to + * the beginning of the actual data portion + * @this_arg_size: length of the current arg, for convenience + * @current_namespace: pointer to the current namespace definition + * (or internally %NULL if the current namespace is unknown) + * @is_radiotap_ns: indicates whether the current namespace is the default + * radiotap namespace or not + * + * @_rtheader: pointer to the radiotap header we are walking through + * @_max_length: length of radiotap header in cpu byte ordering + * @_arg_index: next argument index + * @_arg: next argument pointer + * @_next_bitmap: internal pointer to next present u32 + * @_bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present + * @_vns: vendor namespace definitions + * @_next_ns_data: beginning of the next namespace's data + * @_reset_on_ext: internal; reset the arg index to 0 when going to the + * next bitmap word + * + * Describes the radiotap parser state. Fields prefixed with an underscore + * must not be used by users of the parser, only by the parser internally. + */ + +struct ieee80211_radiotap_iterator { + struct ieee80211_radiotap_header *_rtheader; + const struct ieee80211_radiotap_vendor_namespaces *_vns; + const struct ieee80211_radiotap_namespace *current_namespace; + + unsigned char *_arg, *_next_ns_data; + __le32 *_next_bitmap; + + unsigned char *this_arg; + int this_arg_index; + int this_arg_size; + + int is_radiotap_ns; + + int _max_length; + int _arg_index; + uint32_t _bitmap_shifter; + int _reset_on_ext; +}; + +extern int ieee80211_radiotap_iterator_init( + struct ieee80211_radiotap_iterator *iterator, + struct ieee80211_radiotap_header *radiotap_header, + int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns); + +extern int ieee80211_radiotap_iterator_next( + struct ieee80211_radiotap_iterator *iterator); + + +extern const unsigned char rfc1042_header[6]; +extern const unsigned char bridge_tunnel_header[6]; + +/* Parsed Information Elements */ +struct ieee802_11_elems { + u8 *ie_start; + size_t total_len; + + /* pointers to IEs */ + u8 *ssid; + u8 *supp_rates; + u8 *fh_params; + u8 *ds_params; + u8 *cf_params; + struct ieee80211_tim_ie *tim; + u8 *ibss_params; + u8 *challenge; + u8 *wpa; + u8 *rsn; + u8 *erp_info; + u8 *ext_supp_rates; + u8 *wmm_info; + u8 *wmm_param; + struct ieee80211_ht_cap *ht_cap_elem; + struct ieee80211_ht_info *ht_info_elem; + struct ieee80211_meshconf_ie *mesh_config; + u8 *mesh_id; + u8 *peering; + u8 *preq; + u8 *prep; + u8 *perr; + struct ieee80211_rann_ie *rann; + u8 *ch_switch_elem; + u8 *country_elem; + u8 *pwr_constr_elem; + u8 *quiet_elem; /* first quite element */ + u8 *timeout_int; + + /* length of them, respectively */ + u8 ssid_len; + u8 supp_rates_len; + u8 fh_params_len; + u8 ds_params_len; + u8 cf_params_len; + u8 tim_len; + u8 ibss_params_len; + u8 challenge_len; + u8 wpa_len; + u8 rsn_len; + u8 erp_info_len; + u8 ext_supp_rates_len; + u8 wmm_info_len; + u8 wmm_param_len; + u8 mesh_id_len; + u8 peering_len; + u8 preq_len; + u8 prep_len; + u8 perr_len; + u8 ch_switch_elem_len; + u8 country_elem_len; + u8 pwr_constr_elem_len; + u8 quiet_elem_len; + u8 num_of_quiet_elem; /* can be more the one */ + u8 timeout_int_len; +}; + +/** + * ieee80211_get_hdrlen_from_skb - get header length from data + * + * Given an skb with a raw 802.11 header at the data pointer this function + * returns the 802.11 header length in bytes (not including encryption + * headers). If the data in the sk_buff is too short to contain a valid 802.11 + * header the function returns 0. + * + * @skb: the frame + */ +unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb); + +/** + * ieee80211_hdrlen - get header length in bytes from frame control + * @fc: frame control field in little-endian format + */ +unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc); + +/** + * DOC: Data path helpers + * + * In addition to generic utilities, cfg80211 also offers + * functions that help implement the data path for devices + * that do not do the 802.11/802.3 conversion on the device. + */ + +/** + * ieee80211_data_to_8023 - convert an 802.11 data frame to 802.3 + * @skb: the 802.11 data frame + * @addr: the device MAC address + * @iftype: the virtual interface type + */ +int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, + enum nl80211_iftype iftype); + +/** + * ieee80211_data_from_8023 - convert an 802.3 frame to 802.11 + * @skb: the 802.3 frame + * @addr: the device MAC address + * @iftype: the virtual interface type + * @bssid: the network bssid (used only for iftype STATION and ADHOC) + * @qos: build 802.11 QoS data frame + */ +int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, + enum nl80211_iftype iftype, u8 *bssid, bool qos); + +/** + * ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame + * + * Decode an IEEE 802.11n A-MSDU frame and convert it to a list of + * 802.3 frames. The @list will be empty if the decode fails. The + * @skb is consumed after the function returns. + * + * @skb: The input IEEE 802.11n A-MSDU frame. + * @list: The output list of 802.3 frames. It must be allocated and + * initialized by by the caller. + * @addr: The device MAC address. + * @iftype: The device interface type. + * @extra_headroom: The hardware extra headroom for SKBs in the @list. + * @has_80211_header: Set it true if SKB is with IEEE 802.11 header. + */ +void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, + const u8 *addr, enum nl80211_iftype iftype, + const unsigned int extra_headroom, + bool has_80211_header); + +/** + * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame + * @skb: the data frame + */ +unsigned int cfg80211_classify8021d(struct sk_buff *skb); + +/** + * cfg80211_find_ie - find information element in data + * + * @eid: element ID + * @ies: data consisting of IEs + * @len: length of data + * + * This function will return %NULL if the element ID could + * not be found or if the element is invalid (claims to be + * longer than the given data), or a pointer to the first byte + * of the requested element, that is the byte containing the + * element ID. There are no checks on the element length + * other than having to fit into the given data. + */ +const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len); + +/** + * cfg80211_find_vendor_ie - find vendor specific information element in data + * + * @oui: vendor OUI + * @oui_type: vendor-specific OUI type + * @ies: data consisting of IEs + * @len: length of data + * + * This function will return %NULL if the vendor specific element ID + * could not be found or if the element is invalid (claims to be + * longer than the given data), or a pointer to the first byte + * of the requested element, that is the byte containing the + * element ID. There are no checks on the element length + * other than having to fit into the given data. + */ +const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, + const u8 *ies, int len); + +/** + * DOC: Regulatory enforcement infrastructure + * + * TODO + */ + +/** + * regulatory_hint - driver hint to the wireless core a regulatory domain + * @wiphy: the wireless device giving the hint (used only for reporting + * conflicts) + * @alpha2: the ISO/IEC 3166 alpha2 the driver claims its regulatory domain + * should be in. If @rd is set this should be NULL. Note that if you + * set this to NULL you should still set rd->alpha2 to some accepted + * alpha2. + * + * Wireless drivers can use this function to hint to the wireless core + * what it believes should be the current regulatory domain by + * giving it an ISO/IEC 3166 alpha2 country code it knows its regulatory + * domain should be in or by providing a completely build regulatory domain. + * If the driver provides an ISO/IEC 3166 alpha2 userspace will be queried + * for a regulatory domain structure for the respective country. + * + * The wiphy must have been registered to cfg80211 prior to this call. + * For cfg80211 drivers this means you must first use wiphy_register(), + * for mac80211 drivers you must first use ieee80211_register_hw(). + * + * Drivers should check the return value, its possible you can get + * an -ENOMEM. + */ +extern int regulatory_hint(struct wiphy *wiphy, const char *alpha2); + +/** + * wiphy_apply_custom_regulatory - apply a custom driver regulatory domain + * @wiphy: the wireless device we want to process the regulatory domain on + * @regd: the custom regulatory domain to use for this wiphy + * + * Drivers can sometimes have custom regulatory domains which do not apply + * to a specific country. Drivers can use this to apply such custom regulatory + * domains. This routine must be called prior to wiphy registration. The + * custom regulatory domain will be trusted completely and as such previous + * default channel settings will be disregarded. If no rule is found for a + * channel on the regulatory domain the channel will be disabled. + */ +extern void wiphy_apply_custom_regulatory( + struct wiphy *wiphy, + const struct ieee80211_regdomain *regd); + +/** + * freq_reg_info - get regulatory information for the given frequency + * @wiphy: the wiphy for which we want to process this rule for + * @center_freq: Frequency in KHz for which we want regulatory information for + * @desired_bw_khz: the desired max bandwidth you want to use per + * channel. Note that this is still 20 MHz if you want to use HT40 + * as HT40 makes use of two channels for its 40 MHz width bandwidth. + * If set to 0 we'll assume you want the standard 20 MHz. + * @reg_rule: the regulatory rule which we have for this frequency + * + * Use this function to get the regulatory rule for a specific frequency on + * a given wireless device. If the device has a specific regulatory domain + * it wants to follow we respect that unless a country IE has been received + * and processed already. + * + * Returns 0 if it was able to find a valid regulatory rule which does + * apply to the given center_freq otherwise it returns non-zero. It will + * also return -ERANGE if we determine the given center_freq does not even have + * a regulatory rule for a frequency range in the center_freq's band. See + * freq_in_rule_band() for our current definition of a band -- this is purely + * subjective and right now its 802.11 specific. + */ +extern int freq_reg_info(struct wiphy *wiphy, + u32 center_freq, + u32 desired_bw_khz, + const struct ieee80211_reg_rule **reg_rule); + +/* + * callbacks for asynchronous cfg80211 methods, notification + * functions and BSS handling helpers + */ + +/** + * cfg80211_scan_done - notify that scan finished + * + * @request: the corresponding scan request + * @aborted: set to true if the scan was aborted for any reason, + * userspace will be notified of that + */ +void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted); + +/** + * cfg80211_sched_scan_results - notify that new scan results are available + * + * @wiphy: the wiphy which got scheduled scan results + */ +void cfg80211_sched_scan_results(struct wiphy *wiphy); + +/** + * cfg80211_sched_scan_stopped - notify that the scheduled scan has stopped + * + * @wiphy: the wiphy on which the scheduled scan stopped + * + * The driver can call this function to inform cfg80211 that the + * scheduled scan had to be stopped, for whatever reason. The driver + * is then called back via the sched_scan_stop operation when done. + */ +void cfg80211_sched_scan_stopped(struct wiphy *wiphy); + +/** + * cfg80211_inform_bss_frame - inform cfg80211 of a received BSS frame + * + * @wiphy: the wiphy reporting the BSS + * @channel: The channel the frame was received on + * @mgmt: the management frame (probe response or beacon) + * @len: length of the management frame + * @signal: the signal strength, type depends on the wiphy's signal_type + * @gfp: context flags + * + * This informs cfg80211 that BSS information was found and + * the BSS should be updated/added. + * + * NOTE: Returns a referenced struct, must be released with cfg80211_put_bss()! + */ +struct cfg80211_bss * __must_check +cfg80211_inform_bss_frame(struct wiphy *wiphy, + struct ieee80211_channel *channel, + struct ieee80211_mgmt *mgmt, size_t len, + s32 signal, gfp_t gfp); + +/** + * cfg80211_inform_bss - inform cfg80211 of a new BSS + * + * @wiphy: the wiphy reporting the BSS + * @channel: The channel the frame was received on + * @bssid: the BSSID of the BSS + * @timestamp: the TSF timestamp sent by the peer + * @capability: the capability field sent by the peer + * @beacon_interval: the beacon interval announced by the peer + * @ie: additional IEs sent by the peer + * @ielen: length of the additional IEs + * @signal: the signal strength, type depends on the wiphy's signal_type + * @gfp: context flags + * + * This informs cfg80211 that BSS information was found and + * the BSS should be updated/added. + * + * NOTE: Returns a referenced struct, must be released with cfg80211_put_bss()! + */ +struct cfg80211_bss * __must_check +cfg80211_inform_bss(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *bssid, + u64 timestamp, u16 capability, u16 beacon_interval, + const u8 *ie, size_t ielen, + s32 signal, gfp_t gfp); + +struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *bssid, + const u8 *ssid, size_t ssid_len, + u16 capa_mask, u16 capa_val); +static inline struct cfg80211_bss * +cfg80211_get_ibss(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *ssid, size_t ssid_len) +{ + return cfg80211_get_bss(wiphy, channel, NULL, ssid, ssid_len, + WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); +} + +struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *meshid, size_t meshidlen, + const u8 *meshcfg); +void cfg80211_put_bss(struct cfg80211_bss *bss); + +/** + * cfg80211_unlink_bss - unlink BSS from internal data structures + * @wiphy: the wiphy + * @bss: the bss to remove + * + * This function removes the given BSS from the internal data structures + * thereby making it no longer show up in scan results etc. Use this + * function when you detect a BSS is gone. Normally BSSes will also time + * out, so it is not necessary to use this function at all. + */ +void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss); + +void cfg80211_unlink_allbss(struct wiphy *wiphy); + +/** + * cfg80211_send_rx_auth - notification of processed authentication + * @dev: network device + * @buf: authentication frame (header + body) + * @len: length of the frame data + * + * This function is called whenever an authentication has been processed in + * station mode. The driver is required to call either this function or + * cfg80211_send_auth_timeout() to indicate the result of cfg80211_ops::auth() + * call. This function may sleep. + */ +void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len); + +/** + * cfg80211_send_auth_timeout - notification of timed out authentication + * @dev: network device + * @addr: The MAC address of the device with which the authentication timed out + * + * This function may sleep. + */ +void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr); + +/** + * __cfg80211_auth_canceled - notify cfg80211 that authentication was canceled + * @dev: network device + * @addr: The MAC address of the device with which the authentication timed out + * + * When a pending authentication had no action yet, the driver may decide + * to not send a deauth frame, but in that case must calls this function + * to tell cfg80211 about this decision. It is only valid to call this + * function within the deauth() callback. + */ +void __cfg80211_auth_canceled(struct net_device *dev, const u8 *addr); + +/** + * cfg80211_send_rx_assoc - notification of processed association + * @dev: network device + * @buf: (re)association response frame (header + body) + * @len: length of the frame data + * + * This function is called whenever a (re)association response has been + * processed in station mode. The driver is required to call either this + * function or cfg80211_send_assoc_timeout() to indicate the result of + * cfg80211_ops::assoc() call. This function may sleep. + */ +void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len); + +/** + * cfg80211_send_assoc_timeout - notification of timed out association + * @dev: network device + * @addr: The MAC address of the device with which the association timed out + * + * This function may sleep. + */ +void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr); + +/** + * cfg80211_send_deauth - notification of processed deauthentication + * @dev: network device + * @buf: deauthentication frame (header + body) + * @len: length of the frame data + * + * This function is called whenever deauthentication has been processed in + * station mode. This includes both received deauthentication frames and + * locally generated ones. This function may sleep. + */ +void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len); + +/** + * __cfg80211_send_deauth - notification of processed deauthentication + * @dev: network device + * @buf: deauthentication frame (header + body) + * @len: length of the frame data + * + * Like cfg80211_send_deauth(), but doesn't take the wdev lock. + */ +void __cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len); + +/** + * cfg80211_send_disassoc - notification of processed disassociation + * @dev: network device + * @buf: disassociation response frame (header + body) + * @len: length of the frame data + * + * This function is called whenever disassociation has been processed in + * station mode. This includes both received disassociation frames and locally + * generated ones. This function may sleep. + */ +void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len); + +/** + * __cfg80211_send_disassoc - notification of processed disassociation + * @dev: network device + * @buf: disassociation response frame (header + body) + * @len: length of the frame data + * + * Like cfg80211_send_disassoc(), but doesn't take the wdev lock. + */ +void __cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, + size_t len); + +/** + * cfg80211_send_unprot_deauth - notification of unprotected deauthentication + * @dev: network device + * @buf: deauthentication frame (header + body) + * @len: length of the frame data + * + * This function is called whenever a received Deauthentication frame has been + * dropped in station mode because of MFP being used but the Deauthentication + * frame was not protected. This function may sleep. + */ +void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf, + size_t len); + +/** + * cfg80211_send_unprot_disassoc - notification of unprotected disassociation + * @dev: network device + * @buf: disassociation frame (header + body) + * @len: length of the frame data + * + * This function is called whenever a received Disassociation frame has been + * dropped in station mode because of MFP being used but the Disassociation + * frame was not protected. This function may sleep. + */ +void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf, + size_t len); + +/** + * cfg80211_michael_mic_failure - notification of Michael MIC failure (TKIP) + * @dev: network device + * @addr: The source MAC address of the frame + * @key_type: The key type that the received frame used + * @key_id: Key identifier (0..3). Can be -1 if missing. + * @tsc: The TSC value of the frame that generated the MIC failure (6 octets) + * @gfp: allocation flags + * + * This function is called whenever the local MAC detects a MIC failure in a + * received frame. This matches with MLME-MICHAELMICFAILURE.indication() + * primitive. + */ +void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, + enum nl80211_key_type key_type, int key_id, + const u8 *tsc, gfp_t gfp); + +/** + * cfg80211_ibss_joined - notify cfg80211 that device joined an IBSS + * + * @dev: network device + * @bssid: the BSSID of the IBSS joined + * @gfp: allocation flags + * + * This function notifies cfg80211 that the device joined an IBSS or + * switched to a different BSSID. Before this function can be called, + * either a beacon has to have been received from the IBSS, or one of + * the cfg80211_inform_bss{,_frame} functions must have been called + * with the locally generated beacon -- this guarantees that there is + * always a scan result for this IBSS. cfg80211 will handle the rest. + */ +void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp); + +/** + * cfg80211_notify_new_candidate - notify cfg80211 of a new mesh peer candidate + * + * @dev: network device + * @macaddr: the MAC address of the new candidate + * @ie: information elements advertised by the peer candidate + * @ie_len: lenght of the information elements buffer + * @gfp: allocation flags + * + * This function notifies cfg80211 that the mesh peer candidate has been + * detected, most likely via a beacon or, less likely, via a probe response. + * cfg80211 then sends a notification to userspace. + */ +void cfg80211_notify_new_peer_candidate(struct net_device *dev, + const u8 *macaddr, const u8 *ie, u8 ie_len, gfp_t gfp); + +/** + * DOC: RFkill integration + * + * RFkill integration in cfg80211 is almost invisible to drivers, + * as cfg80211 automatically registers an rfkill instance for each + * wireless device it knows about. Soft kill is also translated + * into disconnecting and turning all interfaces off, drivers are + * expected to turn off the device when all interfaces are down. + * + * However, devices may have a hard RFkill line, in which case they + * also need to interact with the rfkill subsystem, via cfg80211. + * They can do this with a few helper functions documented here. + */ + +/** + * wiphy_rfkill_set_hw_state - notify cfg80211 about hw block state + * @wiphy: the wiphy + * @blocked: block status + */ +void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked); + +/** + * wiphy_rfkill_start_polling - start polling rfkill + * @wiphy: the wiphy + */ +void wiphy_rfkill_start_polling(struct wiphy *wiphy); + +/** + * wiphy_rfkill_stop_polling - stop polling rfkill + * @wiphy: the wiphy + */ +void wiphy_rfkill_stop_polling(struct wiphy *wiphy); + +#ifdef CONFIG_NL80211_TESTMODE +/** + * DOC: Test mode + * + * Test mode is a set of utility functions to allow drivers to + * interact with driver-specific tools to aid, for instance, + * factory programming. + * + * This chapter describes how drivers interact with it, for more + * information see the nl80211 book's chapter on it. + */ + +/** + * cfg80211_testmode_alloc_reply_skb - allocate testmode reply + * @wiphy: the wiphy + * @approxlen: an upper bound of the length of the data that will + * be put into the skb + * + * This function allocates and pre-fills an skb for a reply to + * the testmode command. Since it is intended for a reply, calling + * it outside of the @testmode_cmd operation is invalid. + * + * The returned skb (or %NULL if any errors happen) is pre-filled + * with the wiphy index and set up in a way that any data that is + * put into the skb (with skb_put(), nla_put() or similar) will end + * up being within the %NL80211_ATTR_TESTDATA attribute, so all that + * needs to be done with the skb is adding data for the corresponding + * userspace tool which can then read that data out of the testdata + * attribute. You must not modify the skb in any other way. + * + * When done, call cfg80211_testmode_reply() with the skb and return + * its error code as the result of the @testmode_cmd operation. + */ +struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, + int approxlen); + +/** + * cfg80211_testmode_reply - send the reply skb + * @skb: The skb, must have been allocated with + * cfg80211_testmode_alloc_reply_skb() + * + * Returns an error code or 0 on success, since calling this + * function will usually be the last thing before returning + * from the @testmode_cmd you should return the error code. + * Note that this function consumes the skb regardless of the + * return value. + */ +int cfg80211_testmode_reply(struct sk_buff *skb); + +/** + * cfg80211_testmode_alloc_event_skb - allocate testmode event + * @wiphy: the wiphy + * @approxlen: an upper bound of the length of the data that will + * be put into the skb + * @gfp: allocation flags + * + * This function allocates and pre-fills an skb for an event on the + * testmode multicast group. + * + * The returned skb (or %NULL if any errors happen) is set up in the + * same way as with cfg80211_testmode_alloc_reply_skb() but prepared + * for an event. As there, you should simply add data to it that will + * then end up in the %NL80211_ATTR_TESTDATA attribute. Again, you must + * not modify the skb in any other way. + * + * When done filling the skb, call cfg80211_testmode_event() with the + * skb to send the event. + */ +struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, + int approxlen, gfp_t gfp); + +/** + * cfg80211_testmode_event - send the event + * @skb: The skb, must have been allocated with + * cfg80211_testmode_alloc_event_skb() + * @gfp: allocation flags + * + * This function sends the given @skb, which must have been allocated + * by cfg80211_testmode_alloc_event_skb(), as an event. It always + * consumes it. + */ +void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp); + +#define CFG80211_TESTMODE_CMD(cmd) .testmode_cmd = (cmd), +#define CFG80211_TESTMODE_DUMP(cmd) .testmode_dump = (cmd), +#else +#define CFG80211_TESTMODE_CMD(cmd) +#define CFG80211_TESTMODE_DUMP(cmd) +#endif + +/** + * cfg80211_connect_result - notify cfg80211 of connection result + * + * @dev: network device + * @bssid: the BSSID of the AP + * @req_ie: association request IEs (maybe be %NULL) + * @req_ie_len: association request IEs length + * @resp_ie: association response IEs (may be %NULL) + * @resp_ie_len: assoc response IEs length + * @status: status code, 0 for successful connection, use + * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you + * the real status code for failures. + * @gfp: allocation flags + * + * It should be called by the underlying driver whenever connect() has + * succeeded. + */ +void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp); + +/** + * cfg80211_roamed - notify cfg80211 of roaming + * + * @dev: network device + * @channel: the channel of the new AP + * @bssid: the BSSID of the new AP + * @req_ie: association request IEs (maybe be %NULL) + * @req_ie_len: association request IEs length + * @resp_ie: association response IEs (may be %NULL) + * @resp_ie_len: assoc response IEs length + * @gfp: allocation flags + * + * It should be called by the underlying driver whenever it roamed + * from one AP to another while connected. + */ +void cfg80211_roamed(struct net_device *dev, + struct ieee80211_channel *channel, + const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); + +/** + * cfg80211_roamed_bss - notify cfg80211 of roaming + * + * @dev: network device + * @bss: entry of bss to which STA got roamed + * @req_ie: association request IEs (maybe be %NULL) + * @req_ie_len: association request IEs length + * @resp_ie: association response IEs (may be %NULL) + * @resp_ie_len: assoc response IEs length + * @gfp: allocation flags + * + * This is just a wrapper to notify cfg80211 of roaming event with driver + * passing bss to avoid a race in timeout of the bss entry. It should be + * called by the underlying driver whenever it roamed from one AP to another + * while connected. Drivers which have roaming implemented in firmware + * may use this function to avoid a race in bss entry timeout where the bss + * entry of the new AP is seen in the driver, but gets timed out by the time + * it is accessed in __cfg80211_roamed() due to delay in scheduling + * rdev->event_work. In case of any failures, the reference is released + * either in cfg80211_roamed_bss() or in __cfg80211_romed(), Otherwise, + * it will be released while diconneting from the current bss. + */ +void cfg80211_roamed_bss(struct net_device *dev, struct cfg80211_bss *bss, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); + +/** + * cfg80211_disconnected - notify cfg80211 that connection was dropped + * + * @dev: network device + * @ie: information elements of the deauth/disassoc frame (may be %NULL) + * @ie_len: length of IEs + * @reason: reason code for the disconnection, set it to 0 if unknown + * @gfp: allocation flags + * + * After it calls this function, the driver should enter an idle state + * and not try to connect to any AP any more. + */ +void cfg80211_disconnected(struct net_device *dev, u16 reason, + u8 *ie, size_t ie_len, gfp_t gfp); + +/** + * cfg80211_ready_on_channel - notification of remain_on_channel start + * @dev: network device + * @cookie: the request cookie + * @chan: The current channel (from remain_on_channel request) + * @channel_type: Channel type + * @duration: Duration in milliseconds that the driver intents to remain on the + * channel + * @gfp: allocation flags + */ +void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int duration, gfp_t gfp); + +/** + * cfg80211_remain_on_channel_expired - remain_on_channel duration expired + * @dev: network device + * @cookie: the request cookie + * @chan: The current channel (from remain_on_channel request) + * @channel_type: Channel type + * @gfp: allocation flags + */ +void cfg80211_remain_on_channel_expired(struct net_device *dev, + u64 cookie, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + gfp_t gfp); + + +/** + * cfg80211_new_sta - notify userspace about station + * + * @dev: the netdev + * @mac_addr: the station's address + * @sinfo: the station information + * @gfp: allocation flags + */ +void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, + struct station_info *sinfo, gfp_t gfp); + +/** + * cfg80211_del_sta - notify userspace about deletion of a station + * + * @dev: the netdev + * @mac_addr: the station's address + * @gfp: allocation flags + */ +void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp); + +/** + * cfg80211_conn_failed - connection request failed notification + * + * @dev: the netdev + * @mac_addr: the station's address + * @reason: the reason for connection failure + * @gfp: allocation flags + * + * Whenever a station tries to connect to an AP and if the station + * could not connect to the AP as the AP has rejected the connection + * for some reasons, this function is called. + * + * The reason for connection failure can be any of the value from + * nl80211_connect_failed_reason enum + */ +void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr, + enum nl80211_connect_failed_reason reason, + gfp_t gfp); + +/** + * cfg80211_rx_mgmt - notification of received, unprocessed management frame + * @dev: network device + * @freq: Frequency on which the frame was received in MHz + * @buf: Management frame (header + body) + * @len: length of the frame data + * @gfp: context flags + * + * Returns %true if a user space application has registered for this frame. + * For action frames, that makes it responsible for rejecting unrecognized + * action frames; %false otherwise, in which case for action frames the + * driver is responsible for rejecting the frame. + * + * This function is called whenever an Action frame is received for a station + * mode interface, but is not processed in kernel. + */ +bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, + size_t len, gfp_t gfp); + +/** + * cfg80211_mgmt_tx_status - notification of TX status for management frame + * @dev: network device + * @cookie: Cookie returned by cfg80211_ops::mgmt_tx() + * @buf: Management frame (header + body) + * @len: length of the frame data + * @ack: Whether frame was acknowledged + * @gfp: context flags + * + * This function is called whenever a management frame was requested to be + * transmitted with cfg80211_ops::mgmt_tx() to report the TX status of the + * transmission attempt. + */ +void cfg80211_mgmt_tx_status(struct net_device *dev, u64 cookie, + const u8 *buf, size_t len, bool ack, gfp_t gfp); + + +/** + * cfg80211_cqm_rssi_notify - connection quality monitoring rssi event + * @dev: network device + * @rssi_event: the triggered RSSI event + * @gfp: context flags + * + * This function is called when a configured connection quality monitoring + * rssi threshold reached event occurs. + */ +void cfg80211_cqm_rssi_notify(struct net_device *dev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp); + +/** + * cfg80211_cqm_pktloss_notify - notify userspace about packetloss to peer + * @dev: network device + * @peer: peer's MAC address + * @num_packets: how many packets were lost -- should be a fixed threshold + * but probably no less than maybe 50, or maybe a throughput dependent + * threshold (to account for temporary interference) + * @gfp: context flags + */ +void cfg80211_cqm_pktloss_notify(struct net_device *dev, + const u8 *peer, u32 num_packets, gfp_t gfp); + +/** + * cfg80211_cqm_txe_notify - TX error rate event + * @dev: network device + * @peer: peer's MAC address + * @num_packets: how many packets were lost + * @rate: % of packets which failed transmission + * @intvl: interval (in s) over which the TX failure threshold was breached. + * @gfp: context flags + * + * Notify userspace when configured % TX failures over number of packets in a + * given interval is exceeded. + */ +void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer, + u32 num_packets, u32 rate, u32 intvl, gfp_t gfp); + +/** + * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying + * @dev: network device + * @bssid: BSSID of AP (to avoid races) + * @replay_ctr: new replay counter + * @gfp: allocation flags + */ +void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, + const u8 *replay_ctr, gfp_t gfp); + +/** + * cfg80211_pmksa_candidate_notify - notify about PMKSA caching candidate + * @dev: network device + * @index: candidate index (the smaller the index, the higher the priority) + * @bssid: BSSID of AP + * @preauth: Whether AP advertises support for RSN pre-authentication + * @gfp: allocation flags + */ +void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, + const u8 *bssid, bool preauth, gfp_t gfp); + +/** + * cfg80211_rx_spurious_frame - inform userspace about a spurious frame + * @dev: The device the frame matched to + * @addr: the transmitter address + * @gfp: context flags + * + * This function is used in AP mode (only!) to inform userspace that + * a spurious class 3 frame was received, to be able to deauth the + * sender. + * Returns %true if the frame was passed to userspace (or this failed + * for a reason other than not having a subscription.) + */ +bool cfg80211_rx_spurious_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp); + +/** + * cfg80211_rx_unexpected_4addr_frame - inform about unexpected WDS frame + * @dev: The device the frame matched to + * @addr: the transmitter address + * @gfp: context flags + * + * This function is used in AP mode (only!) to inform userspace that + * an associated station sent a 4addr frame but that wasn't expected. + * It is allowed and desirable to send this event only once for each + * station to avoid event flooding. + * Returns %true if the frame was passed to userspace (or this failed + * for a reason other than not having a subscription.) + */ +bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp); + +/** + * cfg80211_probe_status - notify userspace about probe status + * @dev: the device the probe was sent on + * @addr: the address of the peer + * @cookie: the cookie filled in @probe_client previously + * @acked: indicates whether probe was acked or not + * @gfp: allocation flags + */ +void cfg80211_probe_status(struct net_device *dev, const u8 *addr, + u64 cookie, bool acked, gfp_t gfp); + +/** + * cfg80211_report_obss_beacon - report beacon from other APs + * @wiphy: The wiphy that received the beacon + * @frame: the frame + * @len: length of the frame + * @freq: frequency the frame was received on + * @gfp: allocation flags + * + * Use this function to report to userspace when a beacon was + * received. It is not useful to call this when there is no + * netdev that is in AP/GO mode. + */ +void cfg80211_report_obss_beacon(struct wiphy *wiphy, + const u8 *frame, size_t len, + int freq, gfp_t gfp); + +/* + * cfg80211_ch_switch_notify - update wdev channel and notify userspace + * @dev: the device which switched channels + * @freq: new channel frequency (in MHz) + * @type: channel type + * + * Acquires wdev_lock, so must only be called from sleepable driver context! + */ +void cfg80211_ch_switch_notify(struct net_device *dev, int freq, + enum nl80211_channel_type type); + +/* Logging, debugging and troubleshooting/diagnostic helpers. */ + +/* wiphy_printk helpers, similar to dev_printk */ + +#define wiphy_printk(level, wiphy, format, args...) \ + dev_printk(level, &(wiphy)->dev, format, ##args) +#define wiphy_emerg(wiphy, format, args...) \ + dev_emerg(&(wiphy)->dev, format, ##args) +#define wiphy_alert(wiphy, format, args...) \ + dev_alert(&(wiphy)->dev, format, ##args) +#define wiphy_crit(wiphy, format, args...) \ + dev_crit(&(wiphy)->dev, format, ##args) +#define wiphy_err(wiphy, format, args...) \ + dev_err(&(wiphy)->dev, format, ##args) +#define wiphy_warn(wiphy, format, args...) \ + dev_warn(&(wiphy)->dev, format, ##args) +#define wiphy_notice(wiphy, format, args...) \ + dev_notice(&(wiphy)->dev, format, ##args) +#define wiphy_info(wiphy, format, args...) \ + dev_info(&(wiphy)->dev, format, ##args) + +#define wiphy_debug(wiphy, format, args...) \ + wiphy_printk(KERN_DEBUG, wiphy, format, ##args) + +#define wiphy_dbg(wiphy, format, args...) \ + dev_dbg(&(wiphy)->dev, format, ##args) + +#if defined(VERBOSE_DEBUG) +#define wiphy_vdbg wiphy_dbg +#else +#define wiphy_vdbg(wiphy, format, args...) \ +({ \ + if (0) \ + wiphy_printk(KERN_DEBUG, wiphy, format, ##args); \ + 0; \ +}) +#endif + +/* + * wiphy_WARN() acts like wiphy_printk(), but with the key difference + * of using a WARN/WARN_ON to get the message out, including the + * file/line information and a backtrace. + */ +#define wiphy_WARN(wiphy, format, args...) \ + WARN(1, "wiphy: %s\n" format, wiphy_name(wiphy), ##args); + +#endif /* __NET_CFG80211_H */ diff --git a/include/compat/net/ieee80211_radiotap.h b/include/compat/net/ieee80211_radiotap.h new file mode 100644 index 00000000..71392545 --- /dev/null +++ b/include/compat/net/ieee80211_radiotap.h @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2003, 2004 David Young. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID + * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* + * Modifications to fit into the linux IEEE 802.11 stack, + * Mike Kershaw (dragorn@kismetwireless.net) + */ + +#ifndef IEEE80211RADIOTAP_H +#define IEEE80211RADIOTAP_H + +#include +#include +#include + +/* Base version of the radiotap packet header data */ +#define PKTHDR_RADIOTAP_VERSION 0 + +/* A generic radio capture format is desirable. There is one for + * Linux, but it is neither rigidly defined (there were not even + * units given for some fields) nor easily extensible. + * + * I suggest the following extensible radio capture format. It is + * based on a bitmap indicating which fields are present. + * + * I am trying to describe precisely what the application programmer + * should expect in the following, and for that reason I tell the + * units and origin of each measurement (where it applies), or else I + * use sufficiently weaselly language ("is a monotonically nondecreasing + * function of...") that I cannot set false expectations for lawyerly + * readers. + */ + +/* + * The radio capture header precedes the 802.11 header. + * All data in the header is little endian on all platforms. + */ +struct ieee80211_radiotap_header { + u8 it_version; /* Version 0. Only increases + * for drastic changes, + * introduction of compatible + * new fields does not count. + */ + u8 it_pad; + __le16 it_len; /* length of the whole + * header in bytes, including + * it_version, it_pad, + * it_len, and data fields. + */ + __le32 it_present; /* A bitmap telling which + * fields are present. Set bit 31 + * (0x80000000) to extend the + * bitmap by another 32 bits. + * Additional extensions are made + * by setting bit 31. + */ +} __packed; + +/* Name Data type Units + * ---- --------- ----- + * + * IEEE80211_RADIOTAP_TSFT __le64 microseconds + * + * Value in microseconds of the MAC's 64-bit 802.11 Time + * Synchronization Function timer when the first bit of the + * MPDU arrived at the MAC. For received frames, only. + * + * IEEE80211_RADIOTAP_CHANNEL 2 x __le16 MHz, bitmap + * + * Tx/Rx frequency in MHz, followed by flags (see below). + * + * IEEE80211_RADIOTAP_FHSS __le16 see below + * + * For frequency-hopping radios, the hop set (first byte) + * and pattern (second byte). + * + * IEEE80211_RADIOTAP_RATE u8 500kb/s + * + * Tx/Rx data rate + * + * IEEE80211_RADIOTAP_DBM_ANTSIGNAL s8 decibels from + * one milliwatt (dBm) + * + * RF signal power at the antenna, decibel difference from + * one milliwatt. + * + * IEEE80211_RADIOTAP_DBM_ANTNOISE s8 decibels from + * one milliwatt (dBm) + * + * RF noise power at the antenna, decibel difference from one + * milliwatt. + * + * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB) + * + * RF signal power at the antenna, decibel difference from an + * arbitrary, fixed reference. + * + * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB) + * + * RF noise power at the antenna, decibel difference from an + * arbitrary, fixed reference point. + * + * IEEE80211_RADIOTAP_LOCK_QUALITY __le16 unitless + * + * Quality of Barker code lock. Unitless. Monotonically + * nondecreasing with "better" lock strength. Called "Signal + * Quality" in datasheets. (Is there a standard way to measure + * this?) + * + * IEEE80211_RADIOTAP_TX_ATTENUATION __le16 unitless + * + * Transmit power expressed as unitless distance from max + * power set at factory calibration. 0 is max power. + * Monotonically nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DB_TX_ATTENUATION __le16 decibels (dB) + * + * Transmit power expressed as decibel distance from max power + * set at factory calibration. 0 is max power. Monotonically + * nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DBM_TX_POWER s8 decibels from + * one milliwatt (dBm) + * + * Transmit power expressed as dBm (decibels from a 1 milliwatt + * reference). This is the absolute power level measured at + * the antenna port. + * + * IEEE80211_RADIOTAP_FLAGS u8 bitmap + * + * Properties of transmitted and received frames. See flags + * defined below. + * + * IEEE80211_RADIOTAP_ANTENNA u8 antenna index + * + * Unitless indication of the Rx/Tx antenna for this packet. + * The first antenna is antenna 0. + * + * IEEE80211_RADIOTAP_RX_FLAGS __le16 bitmap + * + * Properties of received frames. See flags defined below. + * + * IEEE80211_RADIOTAP_TX_FLAGS __le16 bitmap + * + * Properties of transmitted frames. See flags defined below. + * + * IEEE80211_RADIOTAP_RTS_RETRIES u8 data + * + * Number of rts retries a transmitted frame used. + * + * IEEE80211_RADIOTAP_DATA_RETRIES u8 data + * + * Number of unicast retries a transmitted frame used. + * + * IEEE80211_RADIOTAP_MCS u8, u8, u8 unitless + * + * Contains a bitmap of known fields/flags, the flags, and + * the MCS index. + * + */ +enum ieee80211_radiotap_type { + IEEE80211_RADIOTAP_TSFT = 0, + IEEE80211_RADIOTAP_FLAGS = 1, + IEEE80211_RADIOTAP_RATE = 2, + IEEE80211_RADIOTAP_CHANNEL = 3, + IEEE80211_RADIOTAP_FHSS = 4, + IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, + IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, + IEEE80211_RADIOTAP_LOCK_QUALITY = 7, + IEEE80211_RADIOTAP_TX_ATTENUATION = 8, + IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, + IEEE80211_RADIOTAP_DBM_TX_POWER = 10, + IEEE80211_RADIOTAP_ANTENNA = 11, + IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, + IEEE80211_RADIOTAP_DB_ANTNOISE = 13, + IEEE80211_RADIOTAP_RX_FLAGS = 14, + IEEE80211_RADIOTAP_TX_FLAGS = 15, + IEEE80211_RADIOTAP_RTS_RETRIES = 16, + IEEE80211_RADIOTAP_DATA_RETRIES = 17, + + IEEE80211_RADIOTAP_MCS = 19, + + /* valid in every it_present bitmap, even vendor namespaces */ + IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, + IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30, + IEEE80211_RADIOTAP_EXT = 31 +}; + +/* Channel flags. */ +#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ + +/* For IEEE80211_RADIOTAP_FLAGS */ +#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received + * during CFP + */ +#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received + * with short + * preamble + */ +#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received + * with WEP encryption + */ +#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received + * with fragmentation + */ +#define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */ +#define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between + * 802.11 header and payload + * (to 32-bit boundary) + */ +#define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* bad FCS */ + +/* For IEEE80211_RADIOTAP_RX_FLAGS */ +#define IEEE80211_RADIOTAP_F_RX_BADPLCP 0x0002 /* frame has bad PLCP */ + +/* For IEEE80211_RADIOTAP_TX_FLAGS */ +#define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive + * retries */ +#define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ +#define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ +#define IEEE80211_RADIOTAP_F_TX_NOACK 0x0008 /* don't expect an ack */ + + +/* For IEEE80211_RADIOTAP_MCS */ +#define IEEE80211_RADIOTAP_MCS_HAVE_BW 0x01 +#define IEEE80211_RADIOTAP_MCS_HAVE_MCS 0x02 +#define IEEE80211_RADIOTAP_MCS_HAVE_GI 0x04 +#define IEEE80211_RADIOTAP_MCS_HAVE_FMT 0x08 +#define IEEE80211_RADIOTAP_MCS_HAVE_FEC 0x10 + +#define IEEE80211_RADIOTAP_MCS_BW_MASK 0x03 +#define IEEE80211_RADIOTAP_MCS_BW_20 0 +#define IEEE80211_RADIOTAP_MCS_BW_40 1 +#define IEEE80211_RADIOTAP_MCS_BW_20L 2 +#define IEEE80211_RADIOTAP_MCS_BW_20U 3 +#define IEEE80211_RADIOTAP_MCS_SGI 0x04 +#define IEEE80211_RADIOTAP_MCS_FMT_GF 0x08 +#define IEEE80211_RADIOTAP_MCS_FEC_LDPC 0x10 + + +/* helpers */ +static inline int ieee80211_get_radiotap_len(unsigned char *data) +{ + struct ieee80211_radiotap_header *hdr = + (struct ieee80211_radiotap_header *)data; + + return get_unaligned_le16(&hdr->it_len); +} + +#endif /* IEEE80211_RADIOTAP_H */ diff --git a/include/compat/net/lib80211.h b/include/compat/net/lib80211.h new file mode 100644 index 00000000..d178c26a --- /dev/null +++ b/include/compat/net/lib80211.h @@ -0,0 +1,124 @@ +/* + * lib80211.h -- common bits for IEEE802.11 wireless drivers + * + * Copyright (c) 2008, John W. Linville + * + * Some bits copied from old ieee80211 component, w/ original copyright + * notices below: + * + * Original code based on Host AP (software wireless LAN access point) driver + * for Intersil Prism2/2.5/3. + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen + * + * Adaption to a generic IEEE 802.11 stack by James Ketrenos + * + * + * Copyright (c) 2004, Intel Corporation + * + */ + +#ifndef LIB80211_H +#define LIB80211_H + +#include +#include +#include +#include +#include +#include +#include +/* print_ssid() is intended to be used in debug (and possibly error) + * messages. It should never be used for passing ssid to user space. */ +const char *print_ssid(char *buf, const char *ssid, u8 ssid_len); +#define DECLARE_SSID_BUF(var) char var[IEEE80211_MAX_SSID_LEN * 4 + 1] __maybe_unused + +#define NUM_WEP_KEYS 4 + +enum { + IEEE80211_CRYPTO_TKIP_COUNTERMEASURES = (1 << 0), +}; + +struct module; + +struct lib80211_crypto_ops { + const char *name; + struct list_head list; + + /* init new crypto context (e.g., allocate private data space, + * select IV, etc.); returns NULL on failure or pointer to allocated + * private data on success */ + void *(*init) (int keyidx); + + /* deinitialize crypto context and free allocated private data */ + void (*deinit) (void *priv); + + /* encrypt/decrypt return < 0 on error or >= 0 on success. The return + * value from decrypt_mpdu is passed as the keyidx value for + * decrypt_msdu. skb must have enough head and tail room for the + * encryption; if not, error will be returned; these functions are + * called for all MPDUs (i.e., fragments). + */ + int (*encrypt_mpdu) (struct sk_buff * skb, int hdr_len, void *priv); + int (*decrypt_mpdu) (struct sk_buff * skb, int hdr_len, void *priv); + + /* These functions are called for full MSDUs, i.e. full frames. + * These can be NULL if full MSDU operations are not needed. */ + int (*encrypt_msdu) (struct sk_buff * skb, int hdr_len, void *priv); + int (*decrypt_msdu) (struct sk_buff * skb, int keyidx, int hdr_len, + void *priv); + + int (*set_key) (void *key, int len, u8 * seq, void *priv); + int (*get_key) (void *key, int len, u8 * seq, void *priv); + + /* procfs handler for printing out key information and possible + * statistics */ + char *(*print_stats) (char *p, void *priv); + + /* Crypto specific flag get/set for configuration settings */ + unsigned long (*get_flags) (void *priv); + unsigned long (*set_flags) (unsigned long flags, void *priv); + + /* maximum number of bytes added by encryption; encrypt buf is + * allocated with extra_prefix_len bytes, copy of in_buf, and + * extra_postfix_len; encrypt need not use all this space, but + * the result must start at the beginning of the buffer and correct + * length must be returned */ + int extra_mpdu_prefix_len, extra_mpdu_postfix_len; + int extra_msdu_prefix_len, extra_msdu_postfix_len; + + struct module *owner; +}; + +struct lib80211_crypt_data { + struct list_head list; /* delayed deletion list */ + struct lib80211_crypto_ops *ops; + void *priv; + atomic_t refcnt; +}; + +struct lib80211_crypt_info { + char *name; + /* Most clients will already have a lock, + so just point to that. */ + spinlock_t *lock; + + struct lib80211_crypt_data *crypt[NUM_WEP_KEYS]; + int tx_keyidx; /* default TX key index (crypt[tx_keyidx]) */ + struct list_head crypt_deinit_list; + struct timer_list crypt_deinit_timer; + int crypt_quiesced; +}; + +int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name, + spinlock_t *lock); +void lib80211_crypt_info_free(struct lib80211_crypt_info *info); +int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops); +int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops); +struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name); +void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info, + struct lib80211_crypt_data **crypt); + +#endif /* LIB80211_H */ diff --git a/include/compat/net/mac80211.h b/include/compat/net/mac80211.h new file mode 100644 index 00000000..48953e20 --- /dev/null +++ b/include/compat/net/mac80211.h @@ -0,0 +1,3686 @@ +/* + * mac80211 <-> driver interface + * + * Copyright 2002-2005, Devicescape Software, Inc. + * Copyright 2006-2007 Jiri Benc + * Copyright 2007-2010 Johannes Berg + * + * 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 MAC80211_H +#define MAC80211_H + +#include +#include +#include +#include +#include +#include +#include + +/** + * DOC: Introduction + * + * mac80211 is the Linux stack for 802.11 hardware that implements + * only partial functionality in hard- or firmware. This document + * defines the interface between mac80211 and low-level hardware + * drivers. + */ + +/** + * DOC: Calling mac80211 from interrupts + * + * Only ieee80211_tx_status_irqsafe() and ieee80211_rx_irqsafe() can be + * called in hardware interrupt context. The low-level driver must not call any + * other functions in hardware interrupt context. If there is a need for such + * call, the low-level driver should first ACK the interrupt and perform the + * IEEE 802.11 code call after this, e.g. from a scheduled workqueue or even + * tasklet function. + * + * NOTE: If the driver opts to use the _irqsafe() functions, it may not also + * use the non-IRQ-safe functions! + */ + +/** + * DOC: Warning + * + * If you're reading this document and not the header file itself, it will + * be incomplete because not all documentation has been converted yet. + */ + +/** + * DOC: Frame format + * + * As a general rule, when frames are passed between mac80211 and the driver, + * they start with the IEEE 802.11 header and include the same octets that are + * sent over the air except for the FCS which should be calculated by the + * hardware. + * + * There are, however, various exceptions to this rule for advanced features: + * + * The first exception is for hardware encryption and decryption offload + * where the IV/ICV may or may not be generated in hardware. + * + * Secondly, when the hardware handles fragmentation, the frame handed to + * the driver from mac80211 is the MSDU, not the MPDU. + * + * Finally, for received frames, the driver is able to indicate that it has + * filled a radiotap header and put that in front of the frame; if it does + * not do so then mac80211 may add this under certain circumstances. + */ + +/** + * DOC: mac80211 workqueue + * + * mac80211 provides its own workqueue for drivers and internal mac80211 use. + * The workqueue is a single threaded workqueue and can only be accessed by + * helpers for sanity checking. Drivers must ensure all work added onto the + * mac80211 workqueue should be cancelled on the driver stop() callback. + * + * mac80211 will flushed the workqueue upon interface removal and during + * suspend. + * + * All work performed on the mac80211 workqueue must not acquire the RTNL lock. + * + */ + +/** + * enum ieee80211_max_queues - maximum number of queues + * + * @IEEE80211_MAX_QUEUES: Maximum number of regular device queues. + */ +enum ieee80211_max_queues { + IEEE80211_MAX_QUEUES = 4, +}; + +/** + * enum ieee80211_ac_numbers - AC numbers as used in mac80211 + * @IEEE80211_AC_VO: voice + * @IEEE80211_AC_VI: video + * @IEEE80211_AC_BE: best effort + * @IEEE80211_AC_BK: background + */ +enum ieee80211_ac_numbers { + IEEE80211_AC_VO = 0, + IEEE80211_AC_VI = 1, + IEEE80211_AC_BE = 2, + IEEE80211_AC_BK = 3, +}; +#define IEEE80211_NUM_ACS 4 + +/** + * struct ieee80211_tx_queue_params - transmit queue configuration + * + * The information provided in this structure is required for QoS + * transmit queue configuration. Cf. IEEE 802.11 7.3.2.29. + * + * @aifs: arbitration interframe space [0..255] + * @cw_min: minimum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @cw_max: maximum contention window [like @cw_min] + * @txop: maximum burst time in units of 32 usecs, 0 meaning disabled + * @uapsd: is U-APSD mode enabled for the queue + */ +struct ieee80211_tx_queue_params { + u16 txop; + u16 cw_min; + u16 cw_max; + u8 aifs; + bool uapsd; +}; + +struct ieee80211_low_level_stats { + unsigned int dot11ACKFailureCount; + unsigned int dot11RTSFailureCount; + unsigned int dot11FCSErrorCount; + unsigned int dot11RTSSuccessCount; +}; + +/** + * enum ieee80211_bss_change - BSS change notification flags + * + * These flags are used with the bss_info_changed() callback + * to indicate which BSS parameter changed. + * + * @BSS_CHANGED_ASSOC: association status changed (associated/disassociated), + * also implies a change in the AID. + * @BSS_CHANGED_ERP_CTS_PROT: CTS protection changed + * @BSS_CHANGED_ERP_PREAMBLE: preamble changed + * @BSS_CHANGED_ERP_SLOT: slot timing changed + * @BSS_CHANGED_HT: 802.11n parameters changed + * @BSS_CHANGED_BASIC_RATES: Basic rateset changed + * @BSS_CHANGED_BEACON_INT: Beacon interval changed + * @BSS_CHANGED_BSSID: BSSID changed, for whatever + * reason (IBSS and managed mode) + * @BSS_CHANGED_BEACON: Beacon data changed, retrieve + * new beacon (beaconing modes) + * @BSS_CHANGED_BEACON_ENABLED: Beaconing should be + * enabled/disabled (beaconing modes) + * @BSS_CHANGED_CQM: Connection quality monitor config changed + * @BSS_CHANGED_IBSS: IBSS join status changed + * @BSS_CHANGED_ARP_FILTER: Hardware ARP filter address list or state changed. + * @BSS_CHANGED_QOS: QoS for this association was enabled/disabled. Note + * that it is only ever disabled for station mode. + * @BSS_CHANGED_IDLE: Idle changed for this BSS/interface. + * @BSS_CHANGED_SSID: SSID changed for this BSS (AP mode) + * @BSS_CHANGED_AP_PROBE_RESP: Probe Response changed for this BSS (AP mode) + */ +enum ieee80211_bss_change { + BSS_CHANGED_ASSOC = 1<<0, + BSS_CHANGED_ERP_CTS_PROT = 1<<1, + BSS_CHANGED_ERP_PREAMBLE = 1<<2, + BSS_CHANGED_ERP_SLOT = 1<<3, + BSS_CHANGED_HT = 1<<4, + BSS_CHANGED_BASIC_RATES = 1<<5, + BSS_CHANGED_BEACON_INT = 1<<6, + BSS_CHANGED_BSSID = 1<<7, + BSS_CHANGED_BEACON = 1<<8, + BSS_CHANGED_BEACON_ENABLED = 1<<9, + BSS_CHANGED_CQM = 1<<10, + BSS_CHANGED_IBSS = 1<<11, + BSS_CHANGED_ARP_FILTER = 1<<12, + BSS_CHANGED_QOS = 1<<13, + BSS_CHANGED_IDLE = 1<<14, + BSS_CHANGED_SSID = 1<<15, + BSS_CHANGED_AP_PROBE_RESP = 1<<16, + + /* when adding here, make sure to change ieee80211_reconfig */ +}; + +/* + * The maximum number of IPv4 addresses listed for ARP filtering. If the number + * of addresses for an interface increase beyond this value, hardware ARP + * filtering will be disabled. + */ +#define IEEE80211_BSS_ARP_ADDR_LIST_LEN 4 + +/** + * enum ieee80211_rssi_event - RSSI threshold event + * An indicator for when RSSI goes below/above a certain threshold. + * @RSSI_EVENT_HIGH: AP's rssi crossed the high threshold set by the driver. + * @RSSI_EVENT_LOW: AP's rssi crossed the low threshold set by the driver. + */ +enum ieee80211_rssi_event { + RSSI_EVENT_HIGH, + RSSI_EVENT_LOW, +}; + +/** + * struct ieee80211_bss_conf - holds the BSS's changing parameters + * + * This structure keeps information about a BSS (and an association + * to that BSS) that can change during the lifetime of the BSS. + * + * @assoc: association status + * @ibss_joined: indicates whether this station is part of an IBSS + * or not + * @aid: association ID number, valid only when @assoc is true + * @use_cts_prot: use CTS protection + * @use_short_preamble: use 802.11b short preamble; + * if the hardware cannot handle this it must set the + * IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE hardware flag + * @use_short_slot: use short slot time (only relevant for ERP); + * if the hardware cannot handle this it must set the + * IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE hardware flag + * @dtim_period: num of beacons before the next DTIM, for beaconing, + * valid in station mode only while @assoc is true and if also + * requested by %IEEE80211_HW_NEED_DTIM_PERIOD (cf. also hw conf + * @ps_dtim_period) + * @timestamp: beacon timestamp + * @beacon_int: beacon interval + * @assoc_capability: capabilities taken from assoc resp + * @basic_rates: bitmap of basic rates, each bit stands for an + * index into the rate table configured by the driver in + * the current band. + * @mcast_rate: per-band multicast rate index + 1 (0: disabled) + * @bssid: The BSSID for this BSS + * @enable_beacon: whether beaconing should be enabled or not + * @channel_type: Channel type for this BSS -- the hardware might be + * configured for HT40+ while this BSS only uses no-HT, for + * example. + * @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info). + * This field is only valid when the channel type is one of the HT types. + * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value + * implies disabled + * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis + * @arp_addr_list: List of IPv4 addresses for hardware ARP filtering. The + * may filter ARP queries targeted for other addresses than listed here. + * The driver must allow ARP queries targeted for all address listed here + * to pass through. An empty list implies no ARP queries need to pass. + * @arp_addr_cnt: Number of addresses currently on the list. + * @arp_filter_enabled: Enable ARP filtering - if enabled, the hardware may + * filter ARP queries based on the @arp_addr_list, if disabled, the + * hardware must not perform any ARP filtering. Note, that the filter will + * be enabled also in promiscuous mode. + * @qos: This is a QoS-enabled BSS. + * @idle: This interface is idle. There's also a global idle flag in the + * hardware config which may be more appropriate depending on what + * your driver/device needs to do. + * @ssid: The SSID of the current vif. Only valid in AP-mode. + * @ssid_len: Length of SSID given in @ssid. + * @hidden_ssid: The SSID of the current vif is hidden. Only valid in AP-mode. + */ +struct ieee80211_bss_conf { + const u8 *bssid; + /* association related data */ + bool assoc, ibss_joined; + u16 aid; + /* erp related data */ + bool use_cts_prot; + bool use_short_preamble; + bool use_short_slot; + bool enable_beacon; + u8 dtim_period; + u16 beacon_int; + u16 assoc_capability; + u64 timestamp; + u32 basic_rates; + int mcast_rate[IEEE80211_NUM_BANDS]; + u16 ht_operation_mode; + s32 cqm_rssi_thold; + u32 cqm_rssi_hyst; + enum nl80211_channel_type channel_type; + __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN]; + u8 arp_addr_cnt; + bool arp_filter_enabled; + bool qos; + bool idle; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + size_t ssid_len; + bool hidden_ssid; +}; + +/** + * enum mac80211_tx_control_flags - flags to describe transmission information/status + * + * These flags are used with the @flags member of &ieee80211_tx_info. + * + * @IEEE80211_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame. + * @IEEE80211_TX_CTL_ASSIGN_SEQ: The driver has to assign a sequence + * number to this frame, taking care of not overwriting the fragment + * number and increasing the sequence number only when the + * IEEE80211_TX_CTL_FIRST_FRAGMENT flag is set. mac80211 will properly + * assign sequence numbers to QoS-data frames but cannot do so correctly + * for non-QoS-data and management frames because beacons need them from + * that counter as well and mac80211 cannot guarantee proper sequencing. + * If this flag is set, the driver should instruct the hardware to + * assign a sequence number to the frame or assign one itself. Cf. IEEE + * 802.11-2007 7.1.3.4.1 paragraph 3. This flag will always be set for + * beacons and always be clear for frames without a sequence number field. + * @IEEE80211_TX_CTL_NO_ACK: tell the low level not to wait for an ack + * @IEEE80211_TX_CTL_CLEAR_PS_FILT: clear powersave filter for destination + * station + * @IEEE80211_TX_CTL_FIRST_FRAGMENT: this is a first fragment of the frame + * @IEEE80211_TX_CTL_SEND_AFTER_DTIM: send this frame after DTIM beacon + * @IEEE80211_TX_CTL_AMPDU: this frame should be sent as part of an A-MPDU + * @IEEE80211_TX_CTL_INJECTED: Frame was injected, internal to mac80211. + * @IEEE80211_TX_STAT_TX_FILTERED: The frame was not transmitted + * because the destination STA was in powersave mode. Note that to + * avoid race conditions, the filter must be set by the hardware or + * firmware upon receiving a frame that indicates that the station + * went to sleep (must be done on device to filter frames already on + * the queue) and may only be unset after mac80211 gives the OK for + * that by setting the IEEE80211_TX_CTL_CLEAR_PS_FILT (see above), + * since only then is it guaranteed that no more frames are in the + * hardware queue. + * @IEEE80211_TX_STAT_ACK: Frame was acknowledged + * @IEEE80211_TX_STAT_AMPDU: The frame was aggregated, so status + * is for the whole aggregation. + * @IEEE80211_TX_STAT_AMPDU_NO_BACK: no block ack was returned, + * so consider using block ack request (BAR). + * @IEEE80211_TX_CTL_RATE_CTRL_PROBE: internal to mac80211, can be + * set by rate control algorithms to indicate probe rate, will + * be cleared for fragmented frames (except on the last fragment) + * @IEEE80211_TX_INTFL_NEED_TXPROCESSING: completely internal to mac80211, + * used to indicate that a pending frame requires TX processing before + * it can be sent out. + * @IEEE80211_TX_INTFL_RETRIED: completely internal to mac80211, + * used to indicate that a frame was already retried due to PS + * @IEEE80211_TX_INTFL_DONT_ENCRYPT: completely internal to mac80211, + * used to indicate frame should not be encrypted + * @IEEE80211_TX_CTL_POLL_RESPONSE: This frame is a response to a poll + * frame (PS-Poll or uAPSD) and should be sent although the station + * is in powersave mode. + * @IEEE80211_TX_CTL_MORE_FRAMES: More frames will be passed to the + * transmit function after the current frame, this can be used + * by drivers to kick the DMA queue only if unset or when the + * queue gets full. + * @IEEE80211_TX_INTFL_RETRANSMISSION: This frame is being retransmitted + * after TX status because the destination was asleep, it must not + * be modified again (no seqno assignment, crypto, etc.) + * @IEEE80211_TX_INTFL_NL80211_FRAME_TX: Frame was requested through nl80211 + * MLME command (internal to mac80211 to figure out whether to send TX + * status to user space) + * @IEEE80211_TX_CTL_LDPC: tells the driver to use LDPC for this frame + * @IEEE80211_TX_CTL_STBC: Enables Space-Time Block Coding (STBC) for this + * frame and selects the maximum number of streams that it can use. + * @IEEE80211_TX_CTL_TX_OFFCHAN: Marks this packet to be transmitted on + * the off-channel channel when a remain-on-channel offload is done + * in hardware -- normal packets still flow and are expected to be + * handled properly by the device. + * @IEEE80211_TX_INTFL_TKIP_MIC_FAILURE: Marks this packet to be used for TKIP + * testing. It will be sent out with incorrect Michael MIC key to allow + * TKIP countermeasures to be tested. + * @IEEE80211_TX_CTL_NO_CCK_RATE: This frame will be sent at non CCK rate. + * This flag is actually used for management frame especially for P2P + * frames not being sent at CCK rate in 2GHz band. + * @IEEE80211_TX_STATUS_EOSP: This packet marks the end of service period, + * when its status is reported the service period ends. For frames in + * an SP that mac80211 transmits, it is already set; for driver frames + * the driver may set this flag. It is also used to do the same for + * PS-Poll responses. + * @IEEE80211_TX_CTL_USE_MINRATE: This frame will be sent at lowest rate. + * This flag is used to send nullfunc frame at minimum rate when + * the nullfunc is used for connection monitoring purpose. + * @IEEE80211_TX_CTL_DONTFRAG: Don't fragment this packet even if it + * would be fragmented by size (this is optional, only used for + * monitor injection). + * + * Note: If you have to add new flags to the enumeration, then don't + * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. + */ +enum mac80211_tx_control_flags { + IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0), + IEEE80211_TX_CTL_ASSIGN_SEQ = BIT(1), + IEEE80211_TX_CTL_NO_ACK = BIT(2), + IEEE80211_TX_CTL_CLEAR_PS_FILT = BIT(3), + IEEE80211_TX_CTL_FIRST_FRAGMENT = BIT(4), + IEEE80211_TX_CTL_SEND_AFTER_DTIM = BIT(5), + IEEE80211_TX_CTL_AMPDU = BIT(6), + IEEE80211_TX_CTL_INJECTED = BIT(7), + IEEE80211_TX_STAT_TX_FILTERED = BIT(8), + IEEE80211_TX_STAT_ACK = BIT(9), + IEEE80211_TX_STAT_AMPDU = BIT(10), + IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(11), + IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12), + IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14), + IEEE80211_TX_INTFL_RETRIED = BIT(15), + IEEE80211_TX_INTFL_DONT_ENCRYPT = BIT(16), + IEEE80211_TX_CTL_POLL_RESPONSE = BIT(17), + IEEE80211_TX_CTL_MORE_FRAMES = BIT(18), + IEEE80211_TX_INTFL_RETRANSMISSION = BIT(19), + /* hole at 20, use later */ + IEEE80211_TX_INTFL_NL80211_FRAME_TX = BIT(21), + IEEE80211_TX_CTL_LDPC = BIT(22), + IEEE80211_TX_CTL_STBC = BIT(23) | BIT(24), + IEEE80211_TX_CTL_TX_OFFCHAN = BIT(25), + IEEE80211_TX_INTFL_TKIP_MIC_FAILURE = BIT(26), + IEEE80211_TX_CTL_NO_CCK_RATE = BIT(27), + IEEE80211_TX_STATUS_EOSP = BIT(28), + IEEE80211_TX_CTL_USE_MINRATE = BIT(29), + IEEE80211_TX_CTL_DONTFRAG = BIT(30), +}; + +#define IEEE80211_TX_CTL_STBC_SHIFT 23 + +/* + * This definition is used as a mask to clear all temporary flags, which are + * set by the tx handlers for each transmission attempt by the mac80211 stack. + */ +#define IEEE80211_TX_TEMPORARY_FLAGS (IEEE80211_TX_CTL_NO_ACK | \ + IEEE80211_TX_CTL_CLEAR_PS_FILT | IEEE80211_TX_CTL_FIRST_FRAGMENT | \ + IEEE80211_TX_CTL_SEND_AFTER_DTIM | IEEE80211_TX_CTL_AMPDU | \ + IEEE80211_TX_STAT_TX_FILTERED | IEEE80211_TX_STAT_ACK | \ + IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_STAT_AMPDU_NO_BACK | \ + IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_POLL_RESPONSE | \ + IEEE80211_TX_CTL_MORE_FRAMES | IEEE80211_TX_CTL_LDPC | \ + IEEE80211_TX_CTL_STBC | IEEE80211_TX_STATUS_EOSP) + +/** + * enum mac80211_rate_control_flags - per-rate flags set by the + * Rate Control algorithm. + * + * These flags are set by the Rate control algorithm for each rate during tx, + * in the @flags member of struct ieee80211_tx_rate. + * + * @IEEE80211_TX_RC_USE_RTS_CTS: Use RTS/CTS exchange for this rate. + * @IEEE80211_TX_RC_USE_CTS_PROTECT: CTS-to-self protection is required. + * This is set if the current BSS requires ERP protection. + * @IEEE80211_TX_RC_USE_SHORT_PREAMBLE: Use short preamble. + * @IEEE80211_TX_RC_MCS: HT rate. + * @IEEE80211_TX_RC_GREEN_FIELD: Indicates whether this rate should be used in + * Greenfield mode. + * @IEEE80211_TX_RC_40_MHZ_WIDTH: Indicates if the Channel Width should be 40 MHz. + * @IEEE80211_TX_RC_DUP_DATA: The frame should be transmitted on both of the + * adjacent 20 MHz channels, if the current channel type is + * NL80211_CHAN_HT40MINUS or NL80211_CHAN_HT40PLUS. + * @IEEE80211_TX_RC_SHORT_GI: Short Guard interval should be used for this rate. + */ +enum mac80211_rate_control_flags { + IEEE80211_TX_RC_USE_RTS_CTS = BIT(0), + IEEE80211_TX_RC_USE_CTS_PROTECT = BIT(1), + IEEE80211_TX_RC_USE_SHORT_PREAMBLE = BIT(2), + + /* rate index is an MCS rate number instead of an index */ + IEEE80211_TX_RC_MCS = BIT(3), + IEEE80211_TX_RC_GREEN_FIELD = BIT(4), + IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(5), + IEEE80211_TX_RC_DUP_DATA = BIT(6), + IEEE80211_TX_RC_SHORT_GI = BIT(7), +}; + + +/* there are 40 bytes if you don't need the rateset to be kept */ +#define IEEE80211_TX_INFO_DRIVER_DATA_SIZE 40 + +/* if you do need the rateset, then you have less space */ +#define IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE 24 + +/* maximum number of rate stages */ +#define IEEE80211_TX_MAX_RATES 5 + +/** + * struct ieee80211_tx_rate - rate selection/status + * + * @idx: rate index to attempt to send with + * @flags: rate control flags (&enum mac80211_rate_control_flags) + * @count: number of tries in this rate before going to the next rate + * + * A value of -1 for @idx indicates an invalid rate and, if used + * in an array of retry rates, that no more rates should be tried. + * + * When used for transmit status reporting, the driver should + * always report the rate along with the flags it used. + * + * &struct ieee80211_tx_info contains an array of these structs + * in the control information, and it will be filled by the rate + * control algorithm according to what should be sent. For example, + * if this array contains, in the format { , } the + * information + * { 3, 2 }, { 2, 2 }, { 1, 4 }, { -1, 0 }, { -1, 0 } + * then this means that the frame should be transmitted + * up to twice at rate 3, up to twice at rate 2, and up to four + * times at rate 1 if it doesn't get acknowledged. Say it gets + * acknowledged by the peer after the fifth attempt, the status + * information should then contain + * { 3, 2 }, { 2, 2 }, { 1, 1 }, { -1, 0 } ... + * since it was transmitted twice at rate 3, twice at rate 2 + * and once at rate 1 after which we received an acknowledgement. + */ +struct ieee80211_tx_rate { + s8 idx; + u8 count; + u8 flags; +} __packed; + +/** + * struct ieee80211_tx_info - skb transmit information + * + * This structure is placed in skb->cb for three uses: + * (1) mac80211 TX control - mac80211 tells the driver what to do + * (2) driver internal use (if applicable) + * (3) TX status information - driver tells mac80211 what happened + * + * The TX control's sta pointer is only valid during the ->tx call, + * it may be NULL. + * + * @flags: transmit info flags, defined above + * @band: the band to transmit on (use for checking for races) + * @antenna_sel_tx: antenna to use, 0 for automatic diversity + * @ack_frame_id: internal frame ID for TX status, used internally + * @control: union for control data + * @status: union for status data + * @driver_data: array of driver_data pointers + * @ampdu_ack_len: number of acked aggregated frames. + * relevant only if IEEE80211_TX_STAT_AMPDU was set. + * @ampdu_len: number of aggregated frames. + * relevant only if IEEE80211_TX_STAT_AMPDU was set. + * @ack_signal: signal strength of the ACK frame + */ +struct ieee80211_tx_info { + /* common information */ + u32 flags; + u8 band; + + u8 antenna_sel_tx; + + u16 ack_frame_id; + + union { + struct { + union { + /* rate control */ + struct { + struct ieee80211_tx_rate rates[ + IEEE80211_TX_MAX_RATES]; + s8 rts_cts_rate_idx; + }; + /* only needed before rate control */ + unsigned long jiffies; + }; + /* NB: vif can be NULL for injected frames */ + struct ieee80211_vif *vif; + struct ieee80211_key_conf *hw_key; + struct ieee80211_sta *sta; + } control; + struct { + struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES]; + u8 ampdu_ack_len; + int ack_signal; + u8 ampdu_len; + /* 15 bytes free */ + } status; + struct { + struct ieee80211_tx_rate driver_rates[ + IEEE80211_TX_MAX_RATES]; + void *rate_driver_data[ + IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE / sizeof(void *)]; + }; + void *driver_data[ + IEEE80211_TX_INFO_DRIVER_DATA_SIZE / sizeof(void *)]; + }; +}; + +/** + * struct ieee80211_sched_scan_ies - scheduled scan IEs + * + * This structure is used to pass the appropriate IEs to be used in scheduled + * scans for all bands. It contains both the IEs passed from the userspace + * and the ones generated by mac80211. + * + * @ie: array with the IEs for each supported band + * @len: array with the total length of the IEs for each band + */ +struct ieee80211_sched_scan_ies { + u8 *ie[IEEE80211_NUM_BANDS]; + size_t len[IEEE80211_NUM_BANDS]; +}; + +static inline struct ieee80211_tx_info *IEEE80211_SKB_CB(struct sk_buff *skb) +{ + return (struct ieee80211_tx_info *)skb->cb; +} + +static inline struct ieee80211_rx_status *IEEE80211_SKB_RXCB(struct sk_buff *skb) +{ + return (struct ieee80211_rx_status *)skb->cb; +} + +/** + * ieee80211_tx_info_clear_status - clear TX status + * + * @info: The &struct ieee80211_tx_info to be cleared. + * + * When the driver passes an skb back to mac80211, it must report + * a number of things in TX status. This function clears everything + * in the TX status but the rate control information (it does clear + * the count since you need to fill that in anyway). + * + * NOTE: You can only use this function if you do NOT use + * info->driver_data! Use info->rate_driver_data + * instead if you need only the less space that allows. + */ +static inline void +ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) +{ + int i; + + BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) != + offsetof(struct ieee80211_tx_info, control.rates)); + BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) != + offsetof(struct ieee80211_tx_info, driver_rates)); + BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) != 8); + /* clear the rate counts */ + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) + info->status.rates[i].count = 0; + + BUILD_BUG_ON( + offsetof(struct ieee80211_tx_info, status.ampdu_ack_len) != 23); + memset(&info->status.ampdu_ack_len, 0, + sizeof(struct ieee80211_tx_info) - + offsetof(struct ieee80211_tx_info, status.ampdu_ack_len)); +} + + +/** + * enum mac80211_rx_flags - receive flags + * + * These flags are used with the @flag member of &struct ieee80211_rx_status. + * @RX_FLAG_MMIC_ERROR: Michael MIC error was reported on this frame. + * Use together with %RX_FLAG_MMIC_STRIPPED. + * @RX_FLAG_DECRYPTED: This frame was decrypted in hardware. + * @RX_FLAG_MMIC_STRIPPED: the Michael MIC is stripped off this frame, + * verification has been done by the hardware. + * @RX_FLAG_IV_STRIPPED: The IV/ICV are stripped from this frame. + * If this flag is set, the stack cannot do any replay detection + * hence the driver or hardware will have to do that. + * @RX_FLAG_FAILED_FCS_CRC: Set this flag if the FCS check failed on + * the frame. + * @RX_FLAG_FAILED_PLCP_CRC: Set this flag if the PCLP check failed on + * the frame. + * @RX_FLAG_MACTIME_MPDU: The timestamp passed in the RX status (@mactime + * field) is valid and contains the time the first symbol of the MPDU + * was received. This is useful in monitor mode and for proper IBSS + * merging. + * @RX_FLAG_SHORTPRE: Short preamble was used for this frame + * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index + * @RX_FLAG_40MHZ: HT40 (40 MHz) was used + * @RX_FLAG_SHORT_GI: Short guard interval was used + */ +enum mac80211_rx_flags { + RX_FLAG_MMIC_ERROR = 1<<0, + RX_FLAG_DECRYPTED = 1<<1, + RX_FLAG_MMIC_STRIPPED = 1<<3, + RX_FLAG_IV_STRIPPED = 1<<4, + RX_FLAG_FAILED_FCS_CRC = 1<<5, + RX_FLAG_FAILED_PLCP_CRC = 1<<6, + RX_FLAG_MACTIME_MPDU = 1<<7, + RX_FLAG_SHORTPRE = 1<<8, + RX_FLAG_HT = 1<<9, + RX_FLAG_40MHZ = 1<<10, + RX_FLAG_SHORT_GI = 1<<11, +}; + +/** + * struct ieee80211_rx_status - receive status + * + * The low-level driver should provide this information (the subset + * supported by hardware) to the 802.11 code with each received + * frame, in the skb's control buffer (cb). + * + * @mactime: value in microseconds of the 64-bit Time Synchronization Function + * (TSF) timer when the first data symbol (MPDU) arrived at the hardware. + * @band: the active band when this frame was received + * @freq: frequency the radio was tuned to when receiving this frame, in MHz + * @signal: signal strength when receiving this frame, either in dBm, in dB or + * unspecified depending on the hardware capabilities flags + * @IEEE80211_HW_SIGNAL_* + * @antenna: antenna used + * @rate_idx: index of data rate into band's supported rates or MCS index if + * HT rates are use (RX_FLAG_HT) + * @flag: %RX_FLAG_* + * @rx_flags: internal RX flags for mac80211 + */ +struct ieee80211_rx_status { + u64 mactime; + enum ieee80211_band band; + int freq; + int signal; + int antenna; + int rate_idx; + int flag; + unsigned int rx_flags; +}; + +/** + * enum ieee80211_conf_flags - configuration flags + * + * Flags to define PHY configuration options + * + * @IEEE80211_CONF_MONITOR: there's a monitor interface present -- use this + * to determine for example whether to calculate timestamps for packets + * or not, do not use instead of filter flags! + * @IEEE80211_CONF_PS: Enable 802.11 power save mode (managed mode only). + * This is the power save mode defined by IEEE 802.11-2007 section 11.2, + * meaning that the hardware still wakes up for beacons, is able to + * transmit frames and receive the possible acknowledgment frames. + * Not to be confused with hardware specific wakeup/sleep states, + * driver is responsible for that. See the section "Powersave support" + * for more. + * @IEEE80211_CONF_IDLE: The device is running, but idle; if the flag is set + * the driver should be prepared to handle configuration requests but + * may turn the device off as much as possible. Typically, this flag will + * be set when an interface is set UP but not associated or scanning, but + * it can also be unset in that case when monitor interfaces are active. + * @IEEE80211_CONF_OFFCHANNEL: The device is currently not on its main + * operating channel. + */ +enum ieee80211_conf_flags { + IEEE80211_CONF_MONITOR = (1<<0), + IEEE80211_CONF_PS = (1<<1), + IEEE80211_CONF_IDLE = (1<<2), + IEEE80211_CONF_OFFCHANNEL = (1<<3), +}; + + +/** + * enum ieee80211_conf_changed - denotes which configuration changed + * + * @IEEE80211_CONF_CHANGE_LISTEN_INTERVAL: the listen interval changed + * @IEEE80211_CONF_CHANGE_MONITOR: the monitor flag changed + * @IEEE80211_CONF_CHANGE_PS: the PS flag or dynamic PS timeout changed + * @IEEE80211_CONF_CHANGE_POWER: the TX power changed + * @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed + * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed + * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed + * @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing powersave mode changed + */ +enum ieee80211_conf_changed { + IEEE80211_CONF_CHANGE_SMPS = BIT(1), + IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2), + IEEE80211_CONF_CHANGE_MONITOR = BIT(3), + IEEE80211_CONF_CHANGE_PS = BIT(4), + IEEE80211_CONF_CHANGE_POWER = BIT(5), + IEEE80211_CONF_CHANGE_CHANNEL = BIT(6), + IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(7), + IEEE80211_CONF_CHANGE_IDLE = BIT(8), +}; + +/** + * enum ieee80211_smps_mode - spatial multiplexing power save mode + * + * @IEEE80211_SMPS_AUTOMATIC: automatic + * @IEEE80211_SMPS_OFF: off + * @IEEE80211_SMPS_STATIC: static + * @IEEE80211_SMPS_DYNAMIC: dynamic + * @IEEE80211_SMPS_NUM_MODES: internal, don't use + */ +enum ieee80211_smps_mode { + IEEE80211_SMPS_AUTOMATIC, + IEEE80211_SMPS_OFF, + IEEE80211_SMPS_STATIC, + IEEE80211_SMPS_DYNAMIC, + + /* keep last */ + IEEE80211_SMPS_NUM_MODES, +}; + +/** + * struct ieee80211_conf - configuration of the device + * + * This struct indicates how the driver shall configure the hardware. + * + * @flags: configuration flags defined above + * + * @listen_interval: listen interval in units of beacon interval + * @max_sleep_period: the maximum number of beacon intervals to sleep for + * before checking the beacon for a TIM bit (managed mode only); this + * value will be only achievable between DTIM frames, the hardware + * needs to check for the multicast traffic bit in DTIM beacons. + * This variable is valid only when the CONF_PS flag is set. + * @ps_dtim_period: The DTIM period of the AP we're connected to, for use + * in power saving. Power saving will not be enabled until a beacon + * has been received and the DTIM period is known. + * @dynamic_ps_timeout: The dynamic powersave timeout (in ms), see the + * powersave documentation below. This variable is valid only when + * the CONF_PS flag is set. + * + * @power_level: requested transmit power (in dBm) + * + * @channel: the channel to tune to + * @channel_type: the channel (HT) type + * + * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame + * (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11, + * but actually means the number of transmissions not the number of retries + * @short_frame_max_tx_count: Maximum number of transmissions for a "short" + * frame, called "dot11ShortRetryLimit" in 802.11, but actually means the + * number of transmissions not the number of retries + * + * @smps_mode: spatial multiplexing powersave mode; note that + * %IEEE80211_SMPS_STATIC is used when the device is not + * configured for an HT channel + */ +struct ieee80211_conf { + u32 flags; + int power_level, dynamic_ps_timeout; + int max_sleep_period; + + u16 listen_interval; + u8 ps_dtim_period; + + u8 long_frame_max_tx_count, short_frame_max_tx_count; + + struct ieee80211_channel *channel; + enum nl80211_channel_type channel_type; + enum ieee80211_smps_mode smps_mode; +}; + +/** + * struct ieee80211_channel_switch - holds the channel switch data + * + * The information provided in this structure is required for channel switch + * operation. + * + * @timestamp: value in microseconds of the 64-bit Time Synchronization + * Function (TSF) timer when the frame containing the channel switch + * announcement was received. This is simply the rx.mactime parameter + * the driver passed into mac80211. + * @block_tx: Indicates whether transmission must be blocked before the + * scheduled channel switch, as indicated by the AP. + * @channel: the new channel to switch to + * @count: the number of TBTT's until the channel switch event + */ +struct ieee80211_channel_switch { + u64 timestamp; + bool block_tx; + struct ieee80211_channel *channel; + u8 count; +}; + +/** + * struct ieee80211_vif - per-interface data + * + * Data in this structure is continually present for driver + * use during the life of a virtual interface. + * + * @type: type of this virtual interface + * @bss_conf: BSS configuration for this interface, either our own + * or the BSS we're associated to + * @addr: address of this interface + * @p2p: indicates whether this AP or STA interface is a p2p + * interface, i.e. a GO or p2p-sta respectively + * @drv_priv: data area for driver use, will always be aligned to + * sizeof(void *). + */ +struct ieee80211_vif { + enum nl80211_iftype type; + struct ieee80211_bss_conf bss_conf; + u8 addr[ETH_ALEN]; + bool p2p; + /* must be last */ + u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); +}; + +static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) +{ +#ifdef CONFIG_MAC80211_MESH + return vif->type == NL80211_IFTYPE_MESH_POINT; +#endif + return false; +} + +/** + * enum ieee80211_key_flags - key flags + * + * These flags are used for communication about keys between the driver + * and mac80211, with the @flags parameter of &struct ieee80211_key_conf. + * + * @IEEE80211_KEY_FLAG_WMM_STA: Set by mac80211, this flag indicates + * that the STA this key will be used with could be using QoS. + * @IEEE80211_KEY_FLAG_GENERATE_IV: This flag should be set by the + * driver to indicate that it requires IV generation for this + * particular key. + * @IEEE80211_KEY_FLAG_GENERATE_MMIC: This flag should be set by + * the driver for a TKIP key if it requires Michael MIC + * generation in software. + * @IEEE80211_KEY_FLAG_PAIRWISE: Set by mac80211, this flag indicates + * that the key is pairwise rather then a shared key. + * @IEEE80211_KEY_FLAG_SW_MGMT: This flag should be set by the driver for a + * CCMP key if it requires CCMP encryption of management frames (MFP) to + * be done in software. + * @IEEE80211_KEY_FLAG_PUT_IV_SPACE: This flag should be set by the driver + * for a CCMP key if space should be prepared for the IV, but the IV + * itself should not be generated. Do not set together with + * @IEEE80211_KEY_FLAG_GENERATE_IV on the same key. + */ +enum ieee80211_key_flags { + IEEE80211_KEY_FLAG_WMM_STA = 1<<0, + IEEE80211_KEY_FLAG_GENERATE_IV = 1<<1, + IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2, + IEEE80211_KEY_FLAG_PAIRWISE = 1<<3, + IEEE80211_KEY_FLAG_SW_MGMT = 1<<4, + IEEE80211_KEY_FLAG_PUT_IV_SPACE = 1<<5, +}; + +/** + * struct ieee80211_key_conf - key information + * + * This key information is given by mac80211 to the driver by + * the set_key() callback in &struct ieee80211_ops. + * + * @hw_key_idx: To be set by the driver, this is the key index the driver + * wants to be given when a frame is transmitted and needs to be + * encrypted in hardware. + * @cipher: The key's cipher suite selector. + * @flags: key flags, see &enum ieee80211_key_flags. + * @keyidx: the key index (0-3) + * @keylen: key material length + * @key: key material. For ALG_TKIP the key is encoded as a 256-bit (32 byte) + * data block: + * - Temporal Encryption Key (128 bits) + * - Temporal Authenticator Tx MIC Key (64 bits) + * - Temporal Authenticator Rx MIC Key (64 bits) + * @icv_len: The ICV length for this key type + * @iv_len: The IV length for this key type + */ +struct ieee80211_key_conf { + u32 cipher; + u8 icv_len; + u8 iv_len; + u8 hw_key_idx; + u8 flags; + s8 keyidx; + u8 keylen; + u8 key[0]; +}; + +/** + * enum set_key_cmd - key command + * + * Used with the set_key() callback in &struct ieee80211_ops, this + * indicates whether a key is being removed or added. + * + * @SET_KEY: a key is set + * @DISABLE_KEY: a key must be disabled + */ +enum set_key_cmd { + SET_KEY, DISABLE_KEY, +}; + +/** + * struct ieee80211_sta - station table entry + * + * A station table entry represents a station we are possibly + * communicating with. Since stations are RCU-managed in + * mac80211, any ieee80211_sta pointer you get access to must + * either be protected by rcu_read_lock() explicitly or implicitly, + * or you must take good care to not use such a pointer after a + * call to your sta_remove callback that removed it. + * + * @addr: MAC address + * @aid: AID we assigned to the station if we're an AP + * @supp_rates: Bitmap of supported rates (per band) + * @ht_cap: HT capabilities of this STA; restricted to our own TX capabilities + * @wme: indicates whether the STA supports WME. Only valid during AP-mode. + * @drv_priv: data area for driver use, will always be aligned to + * sizeof(void *), size is determined in hw information. + * @uapsd_queues: bitmap of queues configured for uapsd. Only valid + * if wme is supported. + * @max_sp: max Service Period. Only valid if wme is supported. + */ +struct ieee80211_sta { + u32 supp_rates[IEEE80211_NUM_BANDS]; + u8 addr[ETH_ALEN]; + u16 aid; + struct ieee80211_sta_ht_cap ht_cap; + bool wme; + u8 uapsd_queues; + u8 max_sp; + + /* must be last */ + u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); +}; + +/** + * enum sta_notify_cmd - sta notify command + * + * Used with the sta_notify() callback in &struct ieee80211_ops, this + * indicates if an associated station made a power state transition. + * + * @STA_NOTIFY_SLEEP: a station is now sleeping + * @STA_NOTIFY_AWAKE: a sleeping station woke up + */ +enum sta_notify_cmd { + STA_NOTIFY_SLEEP, STA_NOTIFY_AWAKE, +}; + +/** + * enum ieee80211_hw_flags - hardware flags + * + * These flags are used to indicate hardware capabilities to + * the stack. Generally, flags here should have their meaning + * done in a way that the simplest hardware doesn't need setting + * any particular flags. There are some exceptions to this rule, + * however, so you are advised to review these flags carefully. + * + * @IEEE80211_HW_HAS_RATE_CONTROL: + * The hardware or firmware includes rate control, and cannot be + * controlled by the stack. As such, no rate control algorithm + * should be instantiated, and the TX rate reported to userspace + * will be taken from the TX status instead of the rate control + * algorithm. + * Note that this requires that the driver implement a number of + * callbacks so it has the correct information, it needs to have + * the @set_rts_threshold callback and must look at the BSS config + * @use_cts_prot for G/N protection, @use_short_slot for slot + * timing in 2.4 GHz and @use_short_preamble for preambles for + * CCK frames. + * + * @IEEE80211_HW_RX_INCLUDES_FCS: + * Indicates that received frames passed to the stack include + * the FCS at the end. + * + * @IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING: + * Some wireless LAN chipsets buffer broadcast/multicast frames + * for power saving stations in the hardware/firmware and others + * rely on the host system for such buffering. This option is used + * to configure the IEEE 802.11 upper layer to buffer broadcast and + * multicast frames when there are power saving stations so that + * the driver can fetch them with ieee80211_get_buffered_bc(). + * + * @IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE: + * Hardware is not capable of short slot operation on the 2.4 GHz band. + * + * @IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE: + * Hardware is not capable of receiving frames with short preamble on + * the 2.4 GHz band. + * + * @IEEE80211_HW_SIGNAL_UNSPEC: + * Hardware can provide signal values but we don't know its units. We + * expect values between 0 and @max_signal. + * If possible please provide dB or dBm instead. + * + * @IEEE80211_HW_SIGNAL_DBM: + * Hardware gives signal values in dBm, decibel difference from + * one milliwatt. This is the preferred method since it is standardized + * between different devices. @max_signal does not need to be set. + * + * @IEEE80211_HW_SPECTRUM_MGMT: + * Hardware supports spectrum management defined in 802.11h + * Measurement, Channel Switch, Quieting, TPC + * + * @IEEE80211_HW_AMPDU_AGGREGATION: + * Hardware supports 11n A-MPDU aggregation. + * + * @IEEE80211_HW_SUPPORTS_PS: + * Hardware has power save support (i.e. can go to sleep). + * + * @IEEE80211_HW_PS_NULLFUNC_STACK: + * Hardware requires nullfunc frame handling in stack, implies + * stack support for dynamic PS. + * + * @IEEE80211_HW_SUPPORTS_DYNAMIC_PS: + * Hardware has support for dynamic PS. + * + * @IEEE80211_HW_MFP_CAPABLE: + * Hardware supports management frame protection (MFP, IEEE 802.11w). + * + * @IEEE80211_HW_BEACON_FILTER: + * Hardware supports dropping of irrelevant beacon frames to + * avoid waking up cpu. + * + * @IEEE80211_HW_SUPPORTS_STATIC_SMPS: + * Hardware supports static spatial multiplexing powersave, + * ie. can turn off all but one chain even on HT connections + * that should be using more chains. + * + * @IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS: + * Hardware supports dynamic spatial multiplexing powersave, + * ie. can turn off all but one chain and then wake the rest + * up as required after, for example, rts/cts handshake. + * + * @IEEE80211_HW_SUPPORTS_UAPSD: + * Hardware supports Unscheduled Automatic Power Save Delivery + * (U-APSD) in managed mode. The mode is configured with + * conf_tx() operation. + * + * @IEEE80211_HW_REPORTS_TX_ACK_STATUS: + * Hardware can provide ack status reports of Tx frames to + * the stack. + * + * @IEEE80211_HW_CONNECTION_MONITOR: + * The hardware performs its own connection monitoring, including + * periodic keep-alives to the AP and probing the AP on beacon loss. + * When this flag is set, signaling beacon-loss will cause an immediate + * change to disassociated state. + * + * @IEEE80211_HW_SUPPORTS_CQM_RSSI: + * Hardware can do connection quality monitoring - i.e. it can monitor + * connection quality related parameters, such as the RSSI level and + * provide notifications if configured trigger levels are reached. + * + * @IEEE80211_HW_NEED_DTIM_PERIOD: + * This device needs to know the DTIM period for the BSS before + * associating. + * + * @IEEE80211_HW_SUPPORTS_PER_STA_GTK: The device's crypto engine supports + * per-station GTKs as used by IBSS RSN or during fast transition. If + * the device doesn't support per-station GTKs, but can be asked not + * to decrypt group addressed frames, then IBSS RSN support is still + * possible but software crypto will be used. Advertise the wiphy flag + * only in that case. + * + * @IEEE80211_HW_AP_LINK_PS: When operating in AP mode the device + * autonomously manages the PS status of connected stations. When + * this flag is set mac80211 will not trigger PS mode for connected + * stations based on the PM bit of incoming frames. + * Use ieee80211_start_ps()/ieee8021_end_ps() to manually configure + * the PS mode of connected stations. + * + * @IEEE80211_HW_TX_AMPDU_SETUP_IN_HW: The device handles TX A-MPDU session + * setup strictly in HW. mac80211 should not attempt to do this in + * software. + */ +enum ieee80211_hw_flags { + IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, + IEEE80211_HW_RX_INCLUDES_FCS = 1<<1, + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING = 1<<2, + IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE = 1<<3, + IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE = 1<<4, + IEEE80211_HW_SIGNAL_UNSPEC = 1<<5, + IEEE80211_HW_SIGNAL_DBM = 1<<6, + IEEE80211_HW_NEED_DTIM_PERIOD = 1<<7, + IEEE80211_HW_SPECTRUM_MGMT = 1<<8, + IEEE80211_HW_AMPDU_AGGREGATION = 1<<9, + IEEE80211_HW_SUPPORTS_PS = 1<<10, + IEEE80211_HW_PS_NULLFUNC_STACK = 1<<11, + IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12, + IEEE80211_HW_MFP_CAPABLE = 1<<13, + IEEE80211_HW_BEACON_FILTER = 1<<14, + IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15, + IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16, + IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, + IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18, + IEEE80211_HW_CONNECTION_MONITOR = 1<<19, + IEEE80211_HW_SUPPORTS_CQM_RSSI = 1<<20, + IEEE80211_HW_SUPPORTS_PER_STA_GTK = 1<<21, + IEEE80211_HW_AP_LINK_PS = 1<<22, + IEEE80211_HW_TX_AMPDU_SETUP_IN_HW = 1<<23, +}; + +/** + * struct ieee80211_hw - hardware information and state + * + * This structure contains the configuration and hardware + * information for an 802.11 PHY. + * + * @wiphy: This points to the &struct wiphy allocated for this + * 802.11 PHY. You must fill in the @perm_addr and @dev + * members of this structure using SET_IEEE80211_DEV() + * and SET_IEEE80211_PERM_ADDR(). Additionally, all supported + * bands (with channels, bitrates) are registered here. + * + * @conf: &struct ieee80211_conf, device configuration, don't use. + * + * @priv: pointer to private area that was allocated for driver use + * along with this structure. + * + * @flags: hardware flags, see &enum ieee80211_hw_flags. + * + * @extra_tx_headroom: headroom to reserve in each transmit skb + * for use by the driver (e.g. for transmit headers.) + * + * @channel_change_time: time (in microseconds) it takes to change channels. + * + * @max_signal: Maximum value for signal (rssi) in RX information, used + * only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB + * + * @max_listen_interval: max listen interval in units of beacon interval + * that HW supports + * + * @queues: number of available hardware transmit queues for + * data packets. WMM/QoS requires at least four, these + * queues need to have configurable access parameters. + * + * @rate_control_algorithm: rate control algorithm for this hardware. + * If unset (NULL), the default algorithm will be used. Must be + * set before calling ieee80211_register_hw(). + * + * @vif_data_size: size (in bytes) of the drv_priv data area + * within &struct ieee80211_vif. + * @sta_data_size: size (in bytes) of the drv_priv data area + * within &struct ieee80211_sta. + * + * @max_rates: maximum number of alternate rate retry stages the hw + * can handle. + * @max_report_rates: maximum number of alternate rate retry stages + * the hw can report back. + * @max_rate_tries: maximum number of tries for each stage + * + * @napi_weight: weight used for NAPI polling. You must specify an + * appropriate value here if a napi_poll operation is provided + * by your driver. + * + * @max_rx_aggregation_subframes: maximum buffer size (number of + * sub-frames) to be used for A-MPDU block ack receiver + * aggregation. + * This is only relevant if the device has restrictions on the + * number of subframes, if it relies on mac80211 to do reordering + * it shouldn't be set. + * + * @max_tx_aggregation_subframes: maximum number of subframes in an + * aggregate an HT driver will transmit, used by the peer as a + * hint to size its reorder buffer. + */ +struct ieee80211_hw { + struct ieee80211_conf conf; + struct wiphy *wiphy; + const char *rate_control_algorithm; + void *priv; + u32 flags; + unsigned int extra_tx_headroom; + int channel_change_time; + int vif_data_size; + int sta_data_size; + int napi_weight; + u16 queues; + u16 max_listen_interval; + s8 max_signal; + u8 max_rates; + u8 max_report_rates; + u8 max_rate_tries; + u8 max_rx_aggregation_subframes; + u8 max_tx_aggregation_subframes; +}; + +/** + * wiphy_to_ieee80211_hw - return a mac80211 driver hw struct from a wiphy + * + * @wiphy: the &struct wiphy which we want to query + * + * mac80211 drivers can use this to get to their respective + * &struct ieee80211_hw. Drivers wishing to get to their own private + * structure can then access it via hw->priv. Note that mac802111 drivers should + * not use wiphy_priv() to try to get their private driver structure as this + * is already used internally by mac80211. + */ +struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy); + +/** + * SET_IEEE80211_DEV - set device for 802.11 hardware + * + * @hw: the &struct ieee80211_hw to set the device for + * @dev: the &struct device of this 802.11 device + */ +static inline void SET_IEEE80211_DEV(struct ieee80211_hw *hw, struct device *dev) +{ + set_wiphy_dev(hw->wiphy, dev); +} + +/** + * SET_IEEE80211_PERM_ADDR - set the permanent MAC address for 802.11 hardware + * + * @hw: the &struct ieee80211_hw to set the MAC address for + * @addr: the address to set + */ +static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, u8 *addr) +{ + memcpy(hw->wiphy->perm_addr, addr, ETH_ALEN); +} + +static inline struct ieee80211_rate * +ieee80211_get_tx_rate(const struct ieee80211_hw *hw, + const struct ieee80211_tx_info *c) +{ + if (WARN_ON(c->control.rates[0].idx < 0)) + return NULL; + return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[0].idx]; +} + +static inline struct ieee80211_rate * +ieee80211_get_rts_cts_rate(const struct ieee80211_hw *hw, + const struct ieee80211_tx_info *c) +{ + if (c->control.rts_cts_rate_idx < 0) + return NULL; + return &hw->wiphy->bands[c->band]->bitrates[c->control.rts_cts_rate_idx]; +} + +static inline struct ieee80211_rate * +ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, + const struct ieee80211_tx_info *c, int idx) +{ + if (c->control.rates[idx + 1].idx < 0) + return NULL; + return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[idx + 1].idx]; +} + +/** + * ieee80211_free_txskb - free TX skb + * @hw: the hardware + * @skb: the skb + * + * Free a transmit skb. Use this funtion when some failure + * to transmit happened and thus status cannot be reported. + */ +void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); + +/** + * DOC: Hardware crypto acceleration + * + * mac80211 is capable of taking advantage of many hardware + * acceleration designs for encryption and decryption operations. + * + * The set_key() callback in the &struct ieee80211_ops for a given + * device is called to enable hardware acceleration of encryption and + * decryption. The callback takes a @sta parameter that will be NULL + * for default keys or keys used for transmission only, or point to + * the station information for the peer for individual keys. + * Multiple transmission keys with the same key index may be used when + * VLANs are configured for an access point. + * + * When transmitting, the TX control data will use the @hw_key_idx + * selected by the driver by modifying the &struct ieee80211_key_conf + * pointed to by the @key parameter to the set_key() function. + * + * The set_key() call for the %SET_KEY command should return 0 if + * the key is now in use, -%EOPNOTSUPP or -%ENOSPC if it couldn't be + * added; if you return 0 then hw_key_idx must be assigned to the + * hardware key index, you are free to use the full u8 range. + * + * When the cmd is %DISABLE_KEY then it must succeed. + * + * Note that it is permissible to not decrypt a frame even if a key + * for it has been uploaded to hardware, the stack will not make any + * decision based on whether a key has been uploaded or not but rather + * based on the receive flags. + * + * The &struct ieee80211_key_conf structure pointed to by the @key + * parameter is guaranteed to be valid until another call to set_key() + * removes it, but it can only be used as a cookie to differentiate + * keys. + * + * In TKIP some HW need to be provided a phase 1 key, for RX decryption + * acceleration (i.e. iwlwifi). Those drivers should provide update_tkip_key + * handler. + * The update_tkip_key() call updates the driver with the new phase 1 key. + * This happens every time the iv16 wraps around (every 65536 packets). The + * set_key() call will happen only once for each key (unless the AP did + * rekeying), it will not include a valid phase 1 key. The valid phase 1 key is + * provided by update_tkip_key only. The trigger that makes mac80211 call this + * handler is software decryption with wrap around of iv16. + */ + +/** + * DOC: Powersave support + * + * mac80211 has support for various powersave implementations. + * + * First, it can support hardware that handles all powersaving by itself, + * such hardware should simply set the %IEEE80211_HW_SUPPORTS_PS hardware + * flag. In that case, it will be told about the desired powersave mode + * with the %IEEE80211_CONF_PS flag depending on the association status. + * The hardware must take care of sending nullfunc frames when necessary, + * i.e. when entering and leaving powersave mode. The hardware is required + * to look at the AID in beacons and signal to the AP that it woke up when + * it finds traffic directed to it. + * + * %IEEE80211_CONF_PS flag enabled means that the powersave mode defined in + * IEEE 802.11-2007 section 11.2 is enabled. This is not to be confused + * with hardware wakeup and sleep states. Driver is responsible for waking + * up the hardware before issuing commands to the hardware and putting it + * back to sleep at appropriate times. + * + * When PS is enabled, hardware needs to wakeup for beacons and receive the + * buffered multicast/broadcast frames after the beacon. Also it must be + * possible to send frames and receive the acknowledment frame. + * + * Other hardware designs cannot send nullfunc frames by themselves and also + * need software support for parsing the TIM bitmap. This is also supported + * by mac80211 by combining the %IEEE80211_HW_SUPPORTS_PS and + * %IEEE80211_HW_PS_NULLFUNC_STACK flags. The hardware is of course still + * required to pass up beacons. The hardware is still required to handle + * waking up for multicast traffic; if it cannot the driver must handle that + * as best as it can, mac80211 is too slow to do that. + * + * Dynamic powersave is an extension to normal powersave in which the + * hardware stays awake for a user-specified period of time after sending a + * frame so that reply frames need not be buffered and therefore delayed to + * the next wakeup. It's compromise of getting good enough latency when + * there's data traffic and still saving significantly power in idle + * periods. + * + * Dynamic powersave is simply supported by mac80211 enabling and disabling + * PS based on traffic. Driver needs to only set %IEEE80211_HW_SUPPORTS_PS + * flag and mac80211 will handle everything automatically. Additionally, + * hardware having support for the dynamic PS feature may set the + * %IEEE80211_HW_SUPPORTS_DYNAMIC_PS flag to indicate that it can support + * dynamic PS mode itself. The driver needs to look at the + * @dynamic_ps_timeout hardware configuration value and use it that value + * whenever %IEEE80211_CONF_PS is set. In this case mac80211 will disable + * dynamic PS feature in stack and will just keep %IEEE80211_CONF_PS + * enabled whenever user has enabled powersave. + * + * Some hardware need to toggle a single shared antenna between WLAN and + * Bluetooth to facilitate co-existence. These types of hardware set + * limitations on the use of host controlled dynamic powersave whenever there + * is simultaneous WLAN and Bluetooth traffic. For these types of hardware, the + * driver may request temporarily going into full power save, in order to + * enable toggling the antenna between BT and WLAN. If the driver requests + * disabling dynamic powersave, the @dynamic_ps_timeout value will be + * temporarily set to zero until the driver re-enables dynamic powersave. + * + * Driver informs U-APSD client support by enabling + * %IEEE80211_HW_SUPPORTS_UAPSD flag. The mode is configured through the + * uapsd paramater in conf_tx() operation. Hardware needs to send the QoS + * Nullfunc frames and stay awake until the service period has ended. To + * utilize U-APSD, dynamic powersave is disabled for voip AC and all frames + * from that AC are transmitted with powersave enabled. + * + * Note: U-APSD client mode is not yet supported with + * %IEEE80211_HW_PS_NULLFUNC_STACK. + */ + +/** + * DOC: Beacon filter support + * + * Some hardware have beacon filter support to reduce host cpu wakeups + * which will reduce system power consumption. It usuallly works so that + * the firmware creates a checksum of the beacon but omits all constantly + * changing elements (TSF, TIM etc). Whenever the checksum changes the + * beacon is forwarded to the host, otherwise it will be just dropped. That + * way the host will only receive beacons where some relevant information + * (for example ERP protection or WMM settings) have changed. + * + * Beacon filter support is advertised with the %IEEE80211_HW_BEACON_FILTER + * hardware capability. The driver needs to enable beacon filter support + * whenever power save is enabled, that is %IEEE80211_CONF_PS is set. When + * power save is enabled, the stack will not check for beacon loss and the + * driver needs to notify about loss of beacons with ieee80211_beacon_loss(). + * + * The time (or number of beacons missed) until the firmware notifies the + * driver of a beacon loss event (which in turn causes the driver to call + * ieee80211_beacon_loss()) should be configurable and will be controlled + * by mac80211 and the roaming algorithm in the future. + * + * Since there may be constantly changing information elements that nothing + * in the software stack cares about, we will, in the future, have mac80211 + * tell the driver which information elements are interesting in the sense + * that we want to see changes in them. This will include + * - a list of information element IDs + * - a list of OUIs for the vendor information element + * + * Ideally, the hardware would filter out any beacons without changes in the + * requested elements, but if it cannot support that it may, at the expense + * of some efficiency, filter out only a subset. For example, if the device + * doesn't support checking for OUIs it should pass up all changes in all + * vendor information elements. + * + * Note that change, for the sake of simplification, also includes information + * elements appearing or disappearing from the beacon. + * + * Some hardware supports an "ignore list" instead, just make sure nothing + * that was requested is on the ignore list, and include commonly changing + * information element IDs in the ignore list, for example 11 (BSS load) and + * the various vendor-assigned IEs with unknown contents (128, 129, 133-136, + * 149, 150, 155, 156, 173, 176, 178, 179, 219); for forward compatibility + * it could also include some currently unused IDs. + * + * + * In addition to these capabilities, hardware should support notifying the + * host of changes in the beacon RSSI. This is relevant to implement roaming + * when no traffic is flowing (when traffic is flowing we see the RSSI of + * the received data packets). This can consist in notifying the host when + * the RSSI changes significantly or when it drops below or rises above + * configurable thresholds. In the future these thresholds will also be + * configured by mac80211 (which gets them from userspace) to implement + * them as the roaming algorithm requires. + * + * If the hardware cannot implement this, the driver should ask it to + * periodically pass beacon frames to the host so that software can do the + * signal strength threshold checking. + */ + +/** + * DOC: Spatial multiplexing power save + * + * SMPS (Spatial multiplexing power save) is a mechanism to conserve + * power in an 802.11n implementation. For details on the mechanism + * and rationale, please refer to 802.11 (as amended by 802.11n-2009) + * "11.2.3 SM power save". + * + * The mac80211 implementation is capable of sending action frames + * to update the AP about the station's SMPS mode, and will instruct + * the driver to enter the specific mode. It will also announce the + * requested SMPS mode during the association handshake. Hardware + * support for this feature is required, and can be indicated by + * hardware flags. + * + * The default mode will be "automatic", which nl80211/cfg80211 + * defines to be dynamic SMPS in (regular) powersave, and SMPS + * turned off otherwise. + * + * To support this feature, the driver must set the appropriate + * hardware support flags, and handle the SMPS flag to the config() + * operation. It will then with this mechanism be instructed to + * enter the requested SMPS mode while associated to an HT AP. + */ + +/** + * DOC: Frame filtering + * + * mac80211 requires to see many management frames for proper + * operation, and users may want to see many more frames when + * in monitor mode. However, for best CPU usage and power consumption, + * having as few frames as possible percolate through the stack is + * desirable. Hence, the hardware should filter as much as possible. + * + * To achieve this, mac80211 uses filter flags (see below) to tell + * the driver's configure_filter() function which frames should be + * passed to mac80211 and which should be filtered out. + * + * Before configure_filter() is invoked, the prepare_multicast() + * callback is invoked with the parameters @mc_count and @mc_list + * for the combined multicast address list of all virtual interfaces. + * It's use is optional, and it returns a u64 that is passed to + * configure_filter(). Additionally, configure_filter() has the + * arguments @changed_flags telling which flags were changed and + * @total_flags with the new flag states. + * + * If your device has no multicast address filters your driver will + * need to check both the %FIF_ALLMULTI flag and the @mc_count + * parameter to see whether multicast frames should be accepted + * or dropped. + * + * All unsupported flags in @total_flags must be cleared. + * Hardware does not support a flag if it is incapable of _passing_ + * the frame to the stack. Otherwise the driver must ignore + * the flag, but not clear it. + * You must _only_ clear the flag (announce no support for the + * flag to mac80211) if you are not able to pass the packet type + * to the stack (so the hardware always filters it). + * So for example, you should clear @FIF_CONTROL, if your hardware + * always filters control frames. If your hardware always passes + * control frames to the kernel and is incapable of filtering them, + * you do _not_ clear the @FIF_CONTROL flag. + * This rule applies to all other FIF flags as well. + */ + +/** + * DOC: AP support for powersaving clients + * + * In order to implement AP and P2P GO modes, mac80211 has support for + * client powersaving, both "legacy" PS (PS-Poll/null data) and uAPSD. + * There currently is no support for sAPSD. + * + * There is one assumption that mac80211 makes, namely that a client + * will not poll with PS-Poll and trigger with uAPSD at the same time. + * Both are supported, and both can be used by the same client, but + * they can't be used concurrently by the same client. This simplifies + * the driver code. + * + * The first thing to keep in mind is that there is a flag for complete + * driver implementation: %IEEE80211_HW_AP_LINK_PS. If this flag is set, + * mac80211 expects the driver to handle most of the state machine for + * powersaving clients and will ignore the PM bit in incoming frames. + * Drivers then use ieee80211_sta_ps_transition() to inform mac80211 of + * stations' powersave transitions. In this mode, mac80211 also doesn't + * handle PS-Poll/uAPSD. + * + * In the mode without %IEEE80211_HW_AP_LINK_PS, mac80211 will check the + * PM bit in incoming frames for client powersave transitions. When a + * station goes to sleep, we will stop transmitting to it. There is, + * however, a race condition: a station might go to sleep while there is + * data buffered on hardware queues. If the device has support for this + * it will reject frames, and the driver should give the frames back to + * mac80211 with the %IEEE80211_TX_STAT_TX_FILTERED flag set which will + * cause mac80211 to retry the frame when the station wakes up. The + * driver is also notified of powersave transitions by calling its + * @sta_notify callback. + * + * When the station is asleep, it has three choices: it can wake up, + * it can PS-Poll, or it can possibly start a uAPSD service period. + * Waking up is implemented by simply transmitting all buffered (and + * filtered) frames to the station. This is the easiest case. When + * the station sends a PS-Poll or a uAPSD trigger frame, mac80211 + * will inform the driver of this with the @allow_buffered_frames + * callback; this callback is optional. mac80211 will then transmit + * the frames as usual and set the %IEEE80211_TX_CTL_POLL_RESPONSE + * on each frame. The last frame in the service period (or the only + * response to a PS-Poll) also has %IEEE80211_TX_STATUS_EOSP set to + * indicate that it ends the service period; as this frame must have + * TX status report it also sets %IEEE80211_TX_CTL_REQ_TX_STATUS. + * When TX status is reported for this frame, the service period is + * marked has having ended and a new one can be started by the peer. + * + * Another race condition can happen on some devices like iwlwifi + * when there are frames queued for the station and it wakes up + * or polls; the frames that are already queued could end up being + * transmitted first instead, causing reordering and/or wrong + * processing of the EOSP. The cause is that allowing frames to be + * transmitted to a certain station is out-of-band communication to + * the device. To allow this problem to be solved, the driver can + * call ieee80211_sta_block_awake() if frames are buffered when it + * is notified that the station went to sleep. When all these frames + * have been filtered (see above), it must call the function again + * to indicate that the station is no longer blocked. + * + * If the driver buffers frames in the driver for aggregation in any + * way, it must use the ieee80211_sta_set_buffered() call when it is + * notified of the station going to sleep to inform mac80211 of any + * TIDs that have frames buffered. Note that when a station wakes up + * this information is reset (hence the requirement to call it when + * informed of the station going to sleep). Then, when a service + * period starts for any reason, @release_buffered_frames is called + * with the number of frames to be released and which TIDs they are + * to come from. In this case, the driver is responsible for setting + * the EOSP (for uAPSD) and MORE_DATA bits in the released frames, + * to help the @more_data paramter is passed to tell the driver if + * there is more data on other TIDs -- the TIDs to release frames + * from are ignored since mac80211 doesn't know how many frames the + * buffers for those TIDs contain. + * + * If the driver also implement GO mode, where absence periods may + * shorten service periods (or abort PS-Poll responses), it must + * filter those response frames except in the case of frames that + * are buffered in the driver -- those must remain buffered to avoid + * reordering. Because it is possible that no frames are released + * in this case, the driver must call ieee80211_sta_eosp_irqsafe() + * to indicate to mac80211 that the service period ended anyway. + * + * Finally, if frames from multiple TIDs are released from mac80211 + * but the driver might reorder them, it must clear & set the flags + * appropriately (only the last frame may have %IEEE80211_TX_STATUS_EOSP) + * and also take care of the EOSP and MORE_DATA bits in the frame. + * The driver may also use ieee80211_sta_eosp_irqsafe() in this case. + */ + +/** + * enum ieee80211_filter_flags - hardware filter flags + * + * These flags determine what the filter in hardware should be + * programmed to let through and what should not be passed to the + * stack. It is always safe to pass more frames than requested, + * but this has negative impact on power consumption. + * + * @FIF_PROMISC_IN_BSS: promiscuous mode within your BSS, + * think of the BSS as your network segment and then this corresponds + * to the regular ethernet device promiscuous mode. + * + * @FIF_ALLMULTI: pass all multicast frames, this is used if requested + * by the user or if the hardware is not capable of filtering by + * multicast address. + * + * @FIF_FCSFAIL: pass frames with failed FCS (but you need to set the + * %RX_FLAG_FAILED_FCS_CRC for them) + * + * @FIF_PLCPFAIL: pass frames with failed PLCP CRC (but you need to set + * the %RX_FLAG_FAILED_PLCP_CRC for them + * + * @FIF_BCN_PRBRESP_PROMISC: This flag is set during scanning to indicate + * to the hardware that it should not filter beacons or probe responses + * by BSSID. Filtering them can greatly reduce the amount of processing + * mac80211 needs to do and the amount of CPU wakeups, so you should + * honour this flag if possible. + * + * @FIF_CONTROL: pass control frames (except for PS Poll), if PROMISC_IN_BSS + * is not set then only those addressed to this station. + * + * @FIF_OTHER_BSS: pass frames destined to other BSSes + * + * @FIF_PSPOLL: pass PS Poll frames, if PROMISC_IN_BSS is not set then only + * those addressed to this station. + * + * @FIF_PROBE_REQ: pass probe request frames + */ +enum ieee80211_filter_flags { + FIF_PROMISC_IN_BSS = 1<<0, + FIF_ALLMULTI = 1<<1, + FIF_FCSFAIL = 1<<2, + FIF_PLCPFAIL = 1<<3, + FIF_BCN_PRBRESP_PROMISC = 1<<4, + FIF_CONTROL = 1<<5, + FIF_OTHER_BSS = 1<<6, + FIF_PSPOLL = 1<<7, + FIF_PROBE_REQ = 1<<8, +}; + +/** + * enum ieee80211_ampdu_mlme_action - A-MPDU actions + * + * These flags are used with the ampdu_action() callback in + * &struct ieee80211_ops to indicate which action is needed. + * + * Note that drivers MUST be able to deal with a TX aggregation + * session being stopped even before they OK'ed starting it by + * calling ieee80211_start_tx_ba_cb_irqsafe, because the peer + * might receive the addBA frame and send a delBA right away! + * + * @IEEE80211_AMPDU_RX_START: start Rx aggregation + * @IEEE80211_AMPDU_RX_STOP: stop Rx aggregation + * @IEEE80211_AMPDU_TX_START: start Tx aggregation + * @IEEE80211_AMPDU_TX_STOP: stop Tx aggregation + * @IEEE80211_AMPDU_TX_OPERATIONAL: TX aggregation has become operational + */ +enum ieee80211_ampdu_mlme_action { + IEEE80211_AMPDU_RX_START, + IEEE80211_AMPDU_RX_STOP, + IEEE80211_AMPDU_TX_START, + IEEE80211_AMPDU_TX_STOP, + IEEE80211_AMPDU_TX_OPERATIONAL, +}; + +/** + * enum ieee80211_tx_sync_type - TX sync type + * @IEEE80211_TX_SYNC_AUTH: sync TX for authentication + * (and possibly also before direct probe) + * @IEEE80211_TX_SYNC_ASSOC: sync TX for association + * @IEEE80211_TX_SYNC_ACTION: sync TX for action frame + * (not implemented yet) + */ +enum ieee80211_tx_sync_type { + IEEE80211_TX_SYNC_AUTH, + IEEE80211_TX_SYNC_ASSOC, + IEEE80211_TX_SYNC_ACTION, +}; + +/** + * enum ieee80211_frame_release_type - frame release reason + * @IEEE80211_FRAME_RELEASE_PSPOLL: frame released for PS-Poll + * @IEEE80211_FRAME_RELEASE_UAPSD: frame(s) released due to + * frame received on trigger-enabled AC + */ +enum ieee80211_frame_release_type { + IEEE80211_FRAME_RELEASE_PSPOLL, + IEEE80211_FRAME_RELEASE_UAPSD, +}; + +/** + * struct ieee80211_ops - callbacks from mac80211 to the driver + * + * This structure contains various callbacks that the driver may + * handle or, in some cases, must handle, for example to configure + * the hardware to a new channel or to transmit a frame. + * + * @tx: Handler that 802.11 module calls for each transmitted frame. + * skb contains the buffer starting from the IEEE 802.11 header. + * The low-level driver should send the frame out based on + * configuration in the TX control data. This handler should, + * preferably, never fail and stop queues appropriately, more + * importantly, however, it must never fail for A-MPDU-queues. + * This function should return NETDEV_TX_OK except in very + * limited cases. + * Must be implemented and atomic. + * + * @start: Called before the first netdevice attached to the hardware + * is enabled. This should turn on the hardware and must turn on + * frame reception (for possibly enabled monitor interfaces.) + * Returns negative error codes, these may be seen in userspace, + * or zero. + * When the device is started it should not have a MAC address + * to avoid acknowledging frames before a non-monitor device + * is added. + * Must be implemented and can sleep. + * + * @stop: Called after last netdevice attached to the hardware + * is disabled. This should turn off the hardware (at least + * it must turn off frame reception.) + * May be called right after add_interface if that rejects + * an interface. If you added any work onto the mac80211 workqueue + * you should ensure to cancel it on this callback. + * Must be implemented and can sleep. + * + * @suspend: Suspend the device; mac80211 itself will quiesce before and + * stop transmitting and doing any other configuration, and then + * ask the device to suspend. This is only invoked when WoWLAN is + * configured, otherwise the device is deconfigured completely and + * reconfigured at resume time. + * The driver may also impose special conditions under which it + * wants to use the "normal" suspend (deconfigure), say if it only + * supports WoWLAN when the device is associated. In this case, it + * must return 1 from this function. + * + * @resume: If WoWLAN was configured, this indicates that mac80211 is + * now resuming its operation, after this the device must be fully + * functional again. If this returns an error, the only way out is + * to also unregister the device. If it returns 1, then mac80211 + * will also go through the regular complete restart on resume. + * + * @add_interface: Called when a netdevice attached to the hardware is + * enabled. Because it is not called for monitor mode devices, @start + * and @stop must be implemented. + * The driver should perform any initialization it needs before + * the device can be enabled. The initial configuration for the + * interface is given in the conf parameter. + * The callback may refuse to add an interface by returning a + * negative error code (which will be seen in userspace.) + * Must be implemented and can sleep. + * + * @change_interface: Called when a netdevice changes type. This callback + * is optional, but only if it is supported can interface types be + * switched while the interface is UP. The callback may sleep. + * Note that while an interface is being switched, it will not be + * found by the interface iteration callbacks. + * + * @remove_interface: Notifies a driver that an interface is going down. + * The @stop callback is called after this if it is the last interface + * and no monitor interfaces are present. + * When all interfaces are removed, the MAC address in the hardware + * must be cleared so the device no longer acknowledges packets, + * the mac_addr member of the conf structure is, however, set to the + * MAC address of the device going away. + * Hence, this callback must be implemented. It can sleep. + * + * @config: Handler for configuration requests. IEEE 802.11 code calls this + * function to change hardware configuration, e.g., channel. + * This function should never fail but returns a negative error code + * if it does. The callback can sleep. + * + * @bss_info_changed: Handler for configuration requests related to BSS + * parameters that may vary during BSS's lifespan, and may affect low + * level driver (e.g. assoc/disassoc status, erp parameters). + * This function should not be used if no BSS has been set, unless + * for association indication. The @changed parameter indicates which + * of the bss parameters has changed when a call is made. The callback + * can sleep. + * + * @tx_sync: Called before a frame is sent to an AP/GO. In the GO case, the + * driver should sync with the GO's powersaving so the device doesn't + * transmit the frame while the GO is asleep. In the regular AP case + * it may be used by drivers for devices implementing other restrictions + * on talking to APs, e.g. due to regulatory enforcement or just HW + * restrictions. + * This function is called for every authentication, association and + * action frame separately since applications might attempt to auth + * with multiple APs before chosing one to associate to. If it returns + * an error, the corresponding authentication, association or frame + * transmission is aborted and reported as having failed. It is always + * called after tuning to the correct channel. + * The callback might be called multiple times before @finish_tx_sync + * (but @finish_tx_sync will be called once for each) but in practice + * this is unlikely to happen. It can also refuse in that case if the + * driver cannot handle that situation. + * This callback can sleep. + * @finish_tx_sync: Called as a counterpart to @tx_sync, unless that returned + * an error. This callback can sleep. + * + * @prepare_multicast: Prepare for multicast filter configuration. + * This callback is optional, and its return value is passed + * to configure_filter(). This callback must be atomic. + * + * @configure_filter: Configure the device's RX filter. + * See the section "Frame filtering" for more information. + * This callback must be implemented and can sleep. + * + * @set_tim: Set TIM bit. mac80211 calls this function when a TIM bit + * must be set or cleared for a given STA. Must be atomic. + * + * @set_key: See the section "Hardware crypto acceleration" + * This callback is only called between add_interface and + * remove_interface calls, i.e. while the given virtual interface + * is enabled. + * Returns a negative error code if the key can't be added. + * The callback can sleep. + * + * @update_tkip_key: See the section "Hardware crypto acceleration" + * This callback will be called in the context of Rx. Called for drivers + * which set IEEE80211_KEY_FLAG_TKIP_REQ_RX_P1_KEY. + * The callback must be atomic. + * + * @set_rekey_data: If the device supports GTK rekeying, for example while the + * host is suspended, it can assign this callback to retrieve the data + * necessary to do GTK rekeying, this is the KEK, KCK and replay counter. + * After rekeying was done it should (for example during resume) notify + * userspace of the new replay counter using ieee80211_gtk_rekey_notify(). + * + * @hw_scan: Ask the hardware to service the scan request, no need to start + * the scan state machine in stack. The scan must honour the channel + * configuration done by the regulatory agent in the wiphy's + * registered bands. The hardware (or the driver) needs to make sure + * that power save is disabled. + * The @req ie/ie_len members are rewritten by mac80211 to contain the + * entire IEs after the SSID, so that drivers need not look at these + * at all but just send them after the SSID -- mac80211 includes the + * (extended) supported rates and HT information (where applicable). + * When the scan finishes, ieee80211_scan_completed() must be called; + * note that it also must be called when the scan cannot finish due to + * any error unless this callback returned a negative error code. + * The callback can sleep. + * + * @cancel_hw_scan: Ask the low-level tp cancel the active hw scan. + * The driver should ask the hardware to cancel the scan (if possible), + * but the scan will be completed only after the driver will call + * ieee80211_scan_completed(). + * This callback is needed for wowlan, to prevent enqueueing a new + * scan_work after the low-level driver was already suspended. + * The callback can sleep. + * + * @sched_scan_start: Ask the hardware to start scanning repeatedly at + * specific intervals. The driver must call the + * ieee80211_sched_scan_results() function whenever it finds results. + * This process will continue until sched_scan_stop is called. + * + * @sched_scan_stop: Tell the hardware to stop an ongoing scheduled scan. + * + * @sw_scan_start: Notifier function that is called just before a software scan + * is started. Can be NULL, if the driver doesn't need this notification. + * The callback can sleep. + * + * @sw_scan_complete: Notifier function that is called just after a + * software scan finished. Can be NULL, if the driver doesn't need + * this notification. + * The callback can sleep. + * + * @get_stats: Return low-level statistics. + * Returns zero if statistics are available. + * The callback can sleep. + * + * @get_tkip_seq: If your device implements TKIP encryption in hardware this + * callback should be provided to read the TKIP transmit IVs (both IV32 + * and IV16) for the given key from hardware. + * The callback must be atomic. + * + * @set_frag_threshold: Configuration of fragmentation threshold. Assign this + * if the device does fragmentation by itself; if this callback is + * implemented then the stack will not do fragmentation. + * The callback can sleep. + * + * @set_rts_threshold: Configuration of RTS threshold (if device needs it) + * The callback can sleep. + * + * @sta_add: Notifies low level driver about addition of an associated station, + * AP, IBSS/WDS/mesh peer etc. This callback can sleep. + * + * @sta_remove: Notifies low level driver about removal of an associated + * station, AP, IBSS/WDS/mesh peer etc. This callback can sleep. + * + * @sta_notify: Notifies low level driver about power state transition of an + * associated station, AP, IBSS/WDS/mesh peer etc. For a VIF operating + * in AP mode, this callback will not be called when the flag + * %IEEE80211_HW_AP_LINK_PS is set. Must be atomic. + * + * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max), + * bursting) for a hardware TX queue. + * Returns a negative error code on failure. + * The callback can sleep. + * + * @get_tsf: Get the current TSF timer value from firmware/hardware. Currently, + * this is only used for IBSS mode BSSID merging and debugging. Is not a + * required function. + * The callback can sleep. + * + * @set_tsf: Set the TSF timer to the specified value in the firmware/hardware. + * Currently, this is only used for IBSS mode debugging. Is not a + * required function. + * The callback can sleep. + * + * @reset_tsf: Reset the TSF timer and allow firmware/hardware to synchronize + * with other STAs in the IBSS. This is only used in IBSS mode. This + * function is optional if the firmware/hardware takes full care of + * TSF synchronization. + * The callback can sleep. + * + * @tx_last_beacon: Determine whether the last IBSS beacon was sent by us. + * This is needed only for IBSS mode and the result of this function is + * used to determine whether to reply to Probe Requests. + * Returns non-zero if this device sent the last beacon. + * The callback can sleep. + * + * @ampdu_action: Perform a certain A-MPDU action + * The RA/TID combination determines the destination and TID we want + * the ampdu action to be performed for. The action is defined through + * ieee80211_ampdu_mlme_action. Starting sequence number (@ssn) + * is the first frame we expect to perform the action on. Notice + * that TX/RX_STOP can pass NULL for this parameter. + * The @buf_size parameter is only valid when the action is set to + * %IEEE80211_AMPDU_TX_OPERATIONAL and indicates the peer's reorder + * buffer size (number of subframes) for this session -- the driver + * may neither send aggregates containing more subframes than this + * nor send aggregates in a way that lost frames would exceed the + * buffer size. If just limiting the aggregate size, this would be + * possible with a buf_size of 8: + * - TX: 1.....7 + * - RX: 2....7 (lost frame #1) + * - TX: 8..1... + * which is invalid since #1 was now re-transmitted well past the + * buffer size of 8. Correct ways to retransmit #1 would be: + * - TX: 1 or 18 or 81 + * Even "189" would be wrong since 1 could be lost again. + * + * Returns a negative error code on failure. + * The callback can sleep. + * + * @get_survey: Return per-channel survey information + * + * @rfkill_poll: Poll rfkill hardware state. If you need this, you also + * need to set wiphy->rfkill_poll to %true before registration, + * and need to call wiphy_rfkill_set_hw_state() in the callback. + * The callback can sleep. + * + * @set_coverage_class: Set slot time for given coverage class as specified + * in IEEE 802.11-2007 section 17.3.8.6 and modify ACK timeout + * accordingly. This callback is not required and may sleep. + * + * @testmode_cmd: Implement a cfg80211 test mode command. + * The callback can sleep. + * @testmode_dump: Implement a cfg80211 test mode dump. The callback can sleep. + * + * @flush: Flush all pending frames from the hardware queue, making sure + * that the hardware queues are empty. If the parameter @drop is set + * to %true, pending frames may be dropped. The callback can sleep. + * + * @channel_switch: Drivers that need (or want) to offload the channel + * switch operation for CSAs received from the AP may implement this + * callback. They must then call ieee80211_chswitch_done() to indicate + * completion of the channel switch. + * + * @napi_poll: Poll Rx queue for incoming data frames. + * + * @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device. + * Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may + * reject TX/RX mask combinations they cannot support by returning -EINVAL + * (also see nl80211.h @NL80211_ATTR_WIPHY_ANTENNA_TX). + * + * @get_antenna: Get current antenna configuration from device (tx_ant, rx_ant). + * + * @remain_on_channel: Starts an off-channel period on the given channel, must + * call back to ieee80211_ready_on_channel() when on that channel. Note + * that normal channel traffic is not stopped as this is intended for hw + * offload. Frames to transmit on the off-channel channel are transmitted + * normally except for the %IEEE80211_TX_CTL_TX_OFFCHAN flag. When the + * duration (which will always be non-zero) expires, the driver must call + * ieee80211_remain_on_channel_expired(). This callback may sleep. + * @cancel_remain_on_channel: Requests that an ongoing off-channel period is + * aborted before it expires. This callback may sleep. + * + * @set_ringparam: Set tx and rx ring sizes. + * + * @get_ringparam: Get tx and rx ring current and maximum sizes. + * + * @tx_frames_pending: Check if there is any pending frame in the hardware + * queues before entering power save. + * + * @set_bitrate_mask: Set a mask of rates to be used for rate control selection + * when transmitting a frame. Currently only legacy rates are handled. + * The callback can sleep. + * @rssi_callback: Notify driver when the average RSSI goes above/below + * thresholds that were registered previously. The callback can sleep. + * + * @release_buffered_frames: Release buffered frames according to the given + * parameters. In the case where the driver buffers some frames for + * sleeping stations mac80211 will use this callback to tell the driver + * to release some frames, either for PS-poll or uAPSD. + * Note that if the @more_data paramter is %false the driver must check + * if there are more frames on the given TIDs, and if there are more than + * the frames being released then it must still set the more-data bit in + * the frame. If the @more_data parameter is %true, then of course the + * more-data bit must always be set. + * The @tids parameter tells the driver which TIDs to release frames + * from, for PS-poll it will always have only a single bit set. + * In the case this is used for a PS-poll initiated release, the + * @num_frames parameter will always be 1 so code can be shared. In + * this case the driver must also set %IEEE80211_TX_STATUS_EOSP flag + * on the TX status (and must report TX status) so that the PS-poll + * period is properly ended. This is used to avoid sending multiple + * responses for a retried PS-poll frame. + * In the case this is used for uAPSD, the @num_frames parameter may be + * bigger than one, but the driver may send fewer frames (it must send + * at least one, however). In this case it is also responsible for + * setting the EOSP flag in the QoS header of the frames. Also, when the + * service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP + * on the last frame in the SP. Alternatively, it may call the function + * ieee80211_sta_eosp_irqsafe() to inform mac80211 of the end of the SP. + * This callback must be atomic. + * @allow_buffered_frames: Prepare device to allow the given number of frames + * to go out to the given station. The frames will be sent by mac80211 + * via the usual TX path after this call. The TX information for frames + * released will also have the %IEEE80211_TX_CTL_POLL_RESPONSE flag set + * and the last one will also have %IEEE80211_TX_STATUS_EOSP set. In case + * frames from multiple TIDs are released and the driver might reorder + * them between the TIDs, it must set the %IEEE80211_TX_STATUS_EOSP flag + * on the last frame and clear it on all others and also handle the EOSP + * bit in the QoS header correctly. Alternatively, it can also call the + * ieee80211_sta_eosp_irqsafe() function. + * The @tids parameter is a bitmap and tells the driver which TIDs the + * frames will be on; it will at most have two bits set. + * This callback must be atomic. + */ +struct ieee80211_ops { + void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); + int (*start)(struct ieee80211_hw *hw); + void (*stop)(struct ieee80211_hw *hw); +#ifdef CONFIG_PM + int (*suspend)(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); + int (*resume)(struct ieee80211_hw *hw); +#endif + int (*add_interface)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + int (*change_interface)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, bool p2p); + void (*remove_interface)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + int (*config)(struct ieee80211_hw *hw, u32 changed); + void (*bss_info_changed)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed); + + int (*tx_sync)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + const u8 *bssid, enum ieee80211_tx_sync_type type); + void (*finish_tx_sync)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *bssid, + enum ieee80211_tx_sync_type type); + + u64 (*prepare_multicast)(struct ieee80211_hw *hw, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) + struct netdev_hw_addr_list *mc_list); +#else + int mc_count, struct dev_addr_list *mc_list); +#endif + void (*configure_filter)(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast); + int (*set_tim)(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + bool set); + int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); + void (*update_tkip_key)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *conf, + struct ieee80211_sta *sta, + u32 iv32, u16 *phase1key); + void (*set_rekey_data)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data); + int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct cfg80211_scan_request *req); + void (*cancel_hw_scan)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + int (*sched_scan_start)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies); + void (*sched_scan_stop)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + void (*sw_scan_start)(struct ieee80211_hw *hw); + void (*sw_scan_complete)(struct ieee80211_hw *hw); + int (*get_stats)(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats); + void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx, + u32 *iv32, u16 *iv16); + int (*set_frag_threshold)(struct ieee80211_hw *hw, u32 value); + int (*set_rts_threshold)(struct ieee80211_hw *hw, u32 value); + int (*sta_add)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + int (*sta_remove)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum sta_notify_cmd, struct ieee80211_sta *sta); + int (*conf_tx)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params); + u64 (*get_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + void (*set_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 tsf); + void (*reset_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + int (*tx_last_beacon)(struct ieee80211_hw *hw); + int (*ampdu_action)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size); + int (*get_survey)(struct ieee80211_hw *hw, int idx, + struct survey_info *survey); + void (*rfkill_poll)(struct ieee80211_hw *hw); + void (*set_coverage_class)(struct ieee80211_hw *hw, u8 coverage_class); +#ifdef CONFIG_NL80211_TESTMODE + int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len); + int (*testmode_dump)(struct ieee80211_hw *hw, struct sk_buff *skb, + struct netlink_callback *cb, + void *data, int len); +#endif + void (*flush)(struct ieee80211_hw *hw, bool drop); + void (*channel_switch)(struct ieee80211_hw *hw, + struct ieee80211_channel_switch *ch_switch); + int (*napi_poll)(struct ieee80211_hw *hw, int budget); + int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant); + int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant); + + int (*remain_on_channel)(struct ieee80211_hw *hw, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + int duration); + int (*cancel_remain_on_channel)(struct ieee80211_hw *hw); + int (*set_ringparam)(struct ieee80211_hw *hw, u32 tx, u32 rx); + void (*get_ringparam)(struct ieee80211_hw *hw, + u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max); + bool (*tx_frames_pending)(struct ieee80211_hw *hw); + int (*set_bitrate_mask)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + const struct cfg80211_bitrate_mask *mask); + void (*rssi_callback)(struct ieee80211_hw *hw, + enum ieee80211_rssi_event rssi_event); + + void (*allow_buffered_frames)(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data); + void (*release_buffered_frames)(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data); +}; + +/** + * ieee80211_alloc_hw - Allocate a new hardware device + * + * This must be called once for each hardware device. The returned pointer + * must be used to refer to this device when calling other functions. + * mac80211 allocates a private data area for the driver pointed to by + * @priv in &struct ieee80211_hw, the size of this area is given as + * @priv_data_len. + * + * @priv_data_len: length of private data + * @ops: callbacks for this device + */ +struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, + const struct ieee80211_ops *ops); + +/** + * ieee80211_register_hw - Register hardware device + * + * You must call this function before any other functions in + * mac80211. Note that before a hardware can be registered, you + * need to fill the contained wiphy's information. + * + * @hw: the device to register as returned by ieee80211_alloc_hw() + */ +int ieee80211_register_hw(struct ieee80211_hw *hw); + +/** + * struct ieee80211_tpt_blink - throughput blink description + * @throughput: throughput in Kbit/sec + * @blink_time: blink time in milliseconds + * (full cycle, ie. one off + one on period) + */ +struct ieee80211_tpt_blink { + int throughput; + int blink_time; +}; + +/** + * enum ieee80211_tpt_led_trigger_flags - throughput trigger flags + * @IEEE80211_TPT_LEDTRIG_FL_RADIO: enable blinking with radio + * @IEEE80211_TPT_LEDTRIG_FL_WORK: enable blinking when working + * @IEEE80211_TPT_LEDTRIG_FL_CONNECTED: enable blinking when at least one + * interface is connected in some way, including being an AP + */ +enum ieee80211_tpt_led_trigger_flags { + IEEE80211_TPT_LEDTRIG_FL_RADIO = BIT(0), + IEEE80211_TPT_LEDTRIG_FL_WORK = BIT(1), + IEEE80211_TPT_LEDTRIG_FL_CONNECTED = BIT(2), +}; + +#ifdef CONFIG_MAC80211_LEDS +extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw); +extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw); +extern char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw); +extern char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw); +extern char *__ieee80211_create_tpt_led_trigger( + struct ieee80211_hw *hw, unsigned int flags, + const struct ieee80211_tpt_blink *blink_table, + unsigned int blink_table_len); +#endif +/** + * ieee80211_get_tx_led_name - get name of TX LED + * + * mac80211 creates a transmit LED trigger for each wireless hardware + * that can be used to drive LEDs if your driver registers a LED device. + * This function returns the name (or %NULL if not configured for LEDs) + * of the trigger so you can automatically link the LED device. + * + * @hw: the hardware to get the LED trigger name for + */ +static inline char *ieee80211_get_tx_led_name(struct ieee80211_hw *hw) +{ +#ifdef CONFIG_MAC80211_LEDS + return __ieee80211_get_tx_led_name(hw); +#else + return NULL; +#endif +} + +/** + * ieee80211_get_rx_led_name - get name of RX LED + * + * mac80211 creates a receive LED trigger for each wireless hardware + * that can be used to drive LEDs if your driver registers a LED device. + * This function returns the name (or %NULL if not configured for LEDs) + * of the trigger so you can automatically link the LED device. + * + * @hw: the hardware to get the LED trigger name for + */ +static inline char *ieee80211_get_rx_led_name(struct ieee80211_hw *hw) +{ +#ifdef CONFIG_MAC80211_LEDS + return __ieee80211_get_rx_led_name(hw); +#else + return NULL; +#endif +} + +/** + * ieee80211_get_assoc_led_name - get name of association LED + * + * mac80211 creates a association LED trigger for each wireless hardware + * that can be used to drive LEDs if your driver registers a LED device. + * This function returns the name (or %NULL if not configured for LEDs) + * of the trigger so you can automatically link the LED device. + * + * @hw: the hardware to get the LED trigger name for + */ +static inline char *ieee80211_get_assoc_led_name(struct ieee80211_hw *hw) +{ +#ifdef CONFIG_MAC80211_LEDS + return __ieee80211_get_assoc_led_name(hw); +#else + return NULL; +#endif +} + +/** + * ieee80211_get_radio_led_name - get name of radio LED + * + * mac80211 creates a radio change LED trigger for each wireless hardware + * that can be used to drive LEDs if your driver registers a LED device. + * This function returns the name (or %NULL if not configured for LEDs) + * of the trigger so you can automatically link the LED device. + * + * @hw: the hardware to get the LED trigger name for + */ +static inline char *ieee80211_get_radio_led_name(struct ieee80211_hw *hw) +{ +#ifdef CONFIG_MAC80211_LEDS + return __ieee80211_get_radio_led_name(hw); +#else + return NULL; +#endif +} + +/** + * ieee80211_create_tpt_led_trigger - create throughput LED trigger + * @hw: the hardware to create the trigger for + * @flags: trigger flags, see &enum ieee80211_tpt_led_trigger_flags + * @blink_table: the blink table -- needs to be ordered by throughput + * @blink_table_len: size of the blink table + * + * This function returns %NULL (in case of error, or if no LED + * triggers are configured) or the name of the new trigger. + * This function must be called before ieee80211_register_hw(). + */ +static inline char * +ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, unsigned int flags, + const struct ieee80211_tpt_blink *blink_table, + unsigned int blink_table_len) +{ +#ifdef CONFIG_MAC80211_LEDS + return __ieee80211_create_tpt_led_trigger(hw, flags, blink_table, + blink_table_len); +#else + return NULL; +#endif +} + +/** + * ieee80211_unregister_hw - Unregister a hardware device + * + * This function instructs mac80211 to free allocated resources + * and unregister netdevices from the networking subsystem. + * + * @hw: the hardware to unregister + */ +void ieee80211_unregister_hw(struct ieee80211_hw *hw); + +/** + * ieee80211_free_hw - free hardware descriptor + * + * This function frees everything that was allocated, including the + * private data for the driver. You must call ieee80211_unregister_hw() + * before calling this function. + * + * @hw: the hardware to free + */ +void ieee80211_free_hw(struct ieee80211_hw *hw); + +/** + * ieee80211_restart_hw - restart hardware completely + * + * Call this function when the hardware was restarted for some reason + * (hardware error, ...) and the driver is unable to restore its state + * by itself. mac80211 assumes that at this point the driver/hardware + * is completely uninitialised and stopped, it starts the process by + * calling the ->start() operation. The driver will need to reset all + * internal state that it has prior to calling this function. + * + * @hw: the hardware to restart + */ +void ieee80211_restart_hw(struct ieee80211_hw *hw); + +/** ieee80211_napi_schedule - schedule NAPI poll + * + * Use this function to schedule NAPI polling on a device. + * + * @hw: the hardware to start polling + */ +void ieee80211_napi_schedule(struct ieee80211_hw *hw); + +/** ieee80211_napi_complete - complete NAPI polling + * + * Use this function to finish NAPI polling on a device. + * + * @hw: the hardware to stop polling + */ +void ieee80211_napi_complete(struct ieee80211_hw *hw); + +/** + * ieee80211_rx - receive frame + * + * Use this function to hand received frames to mac80211. The receive + * buffer in @skb must start with an IEEE 802.11 header. In case of a + * paged @skb is used, the driver is recommended to put the ieee80211 + * header of the frame on the linear part of the @skb to avoid memory + * allocation and/or memcpy by the stack. + * + * This function may not be called in IRQ context. Calls to this function + * for a single hardware must be synchronized against each other. Calls to + * this function, ieee80211_rx_ni() and ieee80211_rx_irqsafe() may not be + * mixed for a single hardware. + * + * In process context use instead ieee80211_rx_ni(). + * + * @hw: the hardware this frame came in on + * @skb: the buffer to receive, owned by mac80211 after this call + */ +void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb); + +/** + * ieee80211_rx_irqsafe - receive frame + * + * Like ieee80211_rx() but can be called in IRQ context + * (internally defers to a tasklet.) + * + * Calls to this function, ieee80211_rx() or ieee80211_rx_ni() may not + * be mixed for a single hardware. + * + * @hw: the hardware this frame came in on + * @skb: the buffer to receive, owned by mac80211 after this call + */ +void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb); + +/** + * ieee80211_rx_ni - receive frame (in process context) + * + * Like ieee80211_rx() but can be called in process context + * (internally disables bottom halves). + * + * Calls to this function, ieee80211_rx() and ieee80211_rx_irqsafe() may + * not be mixed for a single hardware. + * + * @hw: the hardware this frame came in on + * @skb: the buffer to receive, owned by mac80211 after this call + */ +static inline void ieee80211_rx_ni(struct ieee80211_hw *hw, + struct sk_buff *skb) +{ + local_bh_disable(); + ieee80211_rx(hw, skb); + local_bh_enable(); +} + +/** + * ieee80211_sta_ps_transition - PS transition for connected sta + * + * When operating in AP mode with the %IEEE80211_HW_AP_LINK_PS + * flag set, use this function to inform mac80211 about a connected station + * entering/leaving PS mode. + * + * This function may not be called in IRQ context or with softirqs enabled. + * + * Calls to this function for a single hardware must be synchronized against + * each other. + * + * The function returns -EINVAL when the requested PS mode is already set. + * + * @sta: currently connected sta + * @start: start or stop PS + */ +int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start); + +/** + * ieee80211_sta_ps_transition_ni - PS transition for connected sta + * (in process context) + * + * Like ieee80211_sta_ps_transition() but can be called in process context + * (internally disables bottom halves). Concurrent call restriction still + * applies. + * + * @sta: currently connected sta + * @start: start or stop PS + */ +static inline int ieee80211_sta_ps_transition_ni(struct ieee80211_sta *sta, + bool start) +{ + int ret; + + local_bh_disable(); + ret = ieee80211_sta_ps_transition(sta, start); + local_bh_enable(); + + return ret; +} + +/* + * The TX headroom reserved by mac80211 for its own tx_status functions. + * This is enough for the radiotap header. + */ +#define IEEE80211_TX_STATUS_HEADROOM 14 + +/** + * ieee80211_sta_set_buffered - inform mac80211 about driver-buffered frames + * @sta: &struct ieee80211_sta pointer for the sleeping station + * @tid: the TID that has buffered frames + * @buffered: indicates whether or not frames are buffered for this TID + * + * If a driver buffers frames for a powersave station instead of passing + * them back to mac80211 for retransmission, the station may still need + * to be told that there are buffered frames via the TIM bit. + * + * This function informs mac80211 whether or not there are frames that are + * buffered in the driver for a given TID; mac80211 can then use this data + * to set the TIM bit (NOTE: This may call back into the driver's set_tim + * call! Beware of the locking!) + * + * If all frames are released to the station (due to PS-poll or uAPSD) + * then the driver needs to inform mac80211 that there no longer are + * frames buffered. However, when the station wakes up mac80211 assumes + * that all buffered frames will be transmitted and clears this data, + * drivers need to make sure they inform mac80211 about all buffered + * frames on the sleep transition (sta_notify() with %STA_NOTIFY_SLEEP). + * + * Note that technically mac80211 only needs to know this per AC, not per + * TID, but since driver buffering will inevitably happen per TID (since + * it is related to aggregation) it is easier to make mac80211 map the + * TID to the AC as required instead of keeping track in all drivers that + * use this API. + */ +void ieee80211_sta_set_buffered(struct ieee80211_sta *sta, + u8 tid, bool buffered); + +/** + * ieee80211_tx_status - transmit status callback + * + * Call this function for all transmitted frames after they have been + * transmitted. It is permissible to not call this function for + * multicast frames but this can affect statistics. + * + * This function may not be called in IRQ context. Calls to this function + * for a single hardware must be synchronized against each other. Calls + * to this function, ieee80211_tx_status_ni() and ieee80211_tx_status_irqsafe() + * may not be mixed for a single hardware. + * + * @hw: the hardware the frame was transmitted by + * @skb: the frame that was transmitted, owned by mac80211 after this call + */ +void ieee80211_tx_status(struct ieee80211_hw *hw, + struct sk_buff *skb); + +/** + * ieee80211_tx_status_ni - transmit status callback (in process context) + * + * Like ieee80211_tx_status() but can be called in process context. + * + * Calls to this function, ieee80211_tx_status() and + * ieee80211_tx_status_irqsafe() may not be mixed + * for a single hardware. + * + * @hw: the hardware the frame was transmitted by + * @skb: the frame that was transmitted, owned by mac80211 after this call + */ +static inline void ieee80211_tx_status_ni(struct ieee80211_hw *hw, + struct sk_buff *skb) +{ + local_bh_disable(); + ieee80211_tx_status(hw, skb); + local_bh_enable(); +} + +/** + * ieee80211_tx_status_irqsafe - IRQ-safe transmit status callback + * + * Like ieee80211_tx_status() but can be called in IRQ context + * (internally defers to a tasklet.) + * + * Calls to this function, ieee80211_tx_status() and + * ieee80211_tx_status_ni() may not be mixed for a single hardware. + * + * @hw: the hardware the frame was transmitted by + * @skb: the frame that was transmitted, owned by mac80211 after this call + */ +void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, + struct sk_buff *skb); + +/** + * ieee80211_report_low_ack - report non-responding station + * + * When operating in AP-mode, call this function to report a non-responding + * connected STA. + * + * @sta: the non-responding connected sta + * @num_packets: number of packets sent to @sta without a response + */ +void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets); + +/** + * ieee80211_beacon_get_tim - beacon generation function + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @tim_offset: pointer to variable that will receive the TIM IE offset. + * Set to 0 if invalid (in non-AP modes). + * @tim_length: pointer to variable that will receive the TIM IE length, + * (including the ID and length bytes!). + * Set to 0 if invalid (in non-AP modes). + * + * If the driver implements beaconing modes, it must use this function to + * obtain the beacon frame/template. + * + * If the beacon frames are generated by the host system (i.e., not in + * hardware/firmware), the driver uses this function to get each beacon + * frame from mac80211 -- it is responsible for calling this function + * before the beacon is needed (e.g. based on hardware interrupt). + * + * If the beacon frames are generated by the device, then the driver + * must use the returned beacon as the template and change the TIM IE + * according to the current DTIM parameters/TIM bitmap. + * + * The driver is responsible for freeing the returned skb. + */ +struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u16 *tim_offset, u16 *tim_length); + +/** + * ieee80211_beacon_get - beacon generation function + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * See ieee80211_beacon_get_tim(). + */ +static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + return ieee80211_beacon_get_tim(hw, vif, NULL, NULL); +} + +/** + * ieee80211_proberesp_get - retrieve a Probe Response template + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * Creates a Probe Response template which can, for example, be uploaded to + * hardware. The destination address should be set by the caller. + * + * Can only be called in AP mode. + */ +struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +/** + * ieee80211_pspoll_get - retrieve a PS Poll template + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * Creates a PS Poll a template which can, for example, uploaded to + * hardware. The template must be updated after association so that correct + * AID, BSSID and MAC address is used. + * + * Note: Caller (or hardware) is responsible for setting the + * &IEEE80211_FCTL_PM bit. + */ +struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +/** + * ieee80211_nullfunc_get - retrieve a nullfunc template + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * Creates a Nullfunc template which can, for example, uploaded to + * hardware. The template must be updated after association so that correct + * BSSID and address is used. + * + * Note: Caller (or hardware) is responsible for setting the + * &IEEE80211_FCTL_PM bit as well as Duration and Sequence Control fields. + */ +struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +/** + * ieee80211_probereq_get - retrieve a Probe Request template + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @ssid: SSID buffer + * @ssid_len: length of SSID + * @ie: buffer containing all IEs except SSID for the template + * @ie_len: length of the IE buffer + * + * Creates a Probe Request template which can, for example, be uploaded to + * hardware. + */ +struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *ssid, size_t ssid_len, + const u8 *ie, size_t ie_len); + +/** + * ieee80211_rts_get - RTS frame generation function + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @frame: pointer to the frame that is going to be protected by the RTS. + * @frame_len: the frame length (in octets). + * @frame_txctl: &struct ieee80211_tx_info of the frame. + * @rts: The buffer where to store the RTS frame. + * + * If the RTS frames are generated by the host system (i.e., not in + * hardware/firmware), the low-level driver uses this function to receive + * the next RTS frame from the 802.11 code. The low-level is responsible + * for calling this function before and RTS frame is needed. + */ +void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + const void *frame, size_t frame_len, + const struct ieee80211_tx_info *frame_txctl, + struct ieee80211_rts *rts); + +/** + * ieee80211_rts_duration - Get the duration field for an RTS frame + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @frame_len: the length of the frame that is going to be protected by the RTS. + * @frame_txctl: &struct ieee80211_tx_info of the frame. + * + * If the RTS is generated in firmware, but the host system must provide + * the duration field, the low-level driver uses this function to receive + * the duration field value in little-endian byteorder. + */ +__le16 ieee80211_rts_duration(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, size_t frame_len, + const struct ieee80211_tx_info *frame_txctl); + +/** + * ieee80211_ctstoself_get - CTS-to-self frame generation function + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @frame: pointer to the frame that is going to be protected by the CTS-to-self. + * @frame_len: the frame length (in octets). + * @frame_txctl: &struct ieee80211_tx_info of the frame. + * @cts: The buffer where to store the CTS-to-self frame. + * + * If the CTS-to-self frames are generated by the host system (i.e., not in + * hardware/firmware), the low-level driver uses this function to receive + * the next CTS-to-self frame from the 802.11 code. The low-level is responsible + * for calling this function before and CTS-to-self frame is needed. + */ +void ieee80211_ctstoself_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const void *frame, size_t frame_len, + const struct ieee80211_tx_info *frame_txctl, + struct ieee80211_cts *cts); + +/** + * ieee80211_ctstoself_duration - Get the duration field for a CTS-to-self frame + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @frame_len: the length of the frame that is going to be protected by the CTS-to-self. + * @frame_txctl: &struct ieee80211_tx_info of the frame. + * + * If the CTS-to-self is generated in firmware, but the host system must provide + * the duration field, the low-level driver uses this function to receive + * the duration field value in little-endian byteorder. + */ +__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + size_t frame_len, + const struct ieee80211_tx_info *frame_txctl); + +/** + * ieee80211_generic_frame_duration - Calculate the duration field for a frame + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @frame_len: the length of the frame. + * @rate: the rate at which the frame is going to be transmitted. + * + * Calculate the duration field of some generic frame, given its + * length and transmission rate (in 100kbps). + */ +__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + size_t frame_len, + struct ieee80211_rate *rate); + +/** + * ieee80211_get_buffered_bc - accessing buffered broadcast and multicast frames + * @hw: pointer as obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * Function for accessing buffered broadcast and multicast frames. If + * hardware/firmware does not implement buffering of broadcast/multicast + * frames when power saving is used, 802.11 code buffers them in the host + * memory. The low-level driver uses this function to fetch next buffered + * frame. In most cases, this is used when generating beacon frame. This + * function returns a pointer to the next buffered skb or NULL if no more + * buffered frames are available. + * + * Note: buffered frames are returned only after DTIM beacon frame was + * generated with ieee80211_beacon_get() and the low-level driver must thus + * call ieee80211_beacon_get() first. ieee80211_get_buffered_bc() returns + * NULL if the previous generated beacon was not DTIM, so the low-level driver + * does not need to check for DTIM beacons separately and should be able to + * use common code for all beacons. + */ +struct sk_buff * +ieee80211_get_buffered_bc(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + +/** + * ieee80211_get_tkip_p1k_iv - get a TKIP phase 1 key for IV32 + * + * This function returns the TKIP phase 1 key for the given IV32. + * + * @keyconf: the parameter passed with the set key + * @iv32: IV32 to get the P1K for + * @p1k: a buffer to which the key will be written, as 5 u16 values + */ +void ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *keyconf, + u32 iv32, u16 *p1k); + +/** + * ieee80211_get_tkip_p1k - get a TKIP phase 1 key + * + * This function returns the TKIP phase 1 key for the IV32 taken + * from the given packet. + * + * @keyconf: the parameter passed with the set key + * @skb: the packet to take the IV32 value from that will be encrypted + * with this P1K + * @p1k: a buffer to which the key will be written, as 5 u16 values + */ +static inline void ieee80211_get_tkip_p1k(struct ieee80211_key_conf *keyconf, + struct sk_buff *skb, u16 *p1k) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + const u8 *data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control); + u32 iv32 = get_unaligned_le32(&data[4]); + + ieee80211_get_tkip_p1k_iv(keyconf, iv32, p1k); +} + +/** + * ieee80211_get_tkip_rx_p1k - get a TKIP phase 1 key for RX + * + * This function returns the TKIP phase 1 key for the given IV32 + * and transmitter address. + * + * @keyconf: the parameter passed with the set key + * @ta: TA that will be used with the key + * @iv32: IV32 to get the P1K for + * @p1k: a buffer to which the key will be written, as 5 u16 values + */ +void ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf, + const u8 *ta, u32 iv32, u16 *p1k); + +/** + * ieee80211_get_tkip_p2k - get a TKIP phase 2 key + * + * This function computes the TKIP RC4 key for the IV values + * in the packet. + * + * @keyconf: the parameter passed with the set key + * @skb: the packet to take the IV32/IV16 values from that will be + * encrypted with this key + * @p2k: a buffer to which the key will be written, 16 bytes + */ +void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf, + struct sk_buff *skb, u8 *p2k); + +/** + * struct ieee80211_key_seq - key sequence counter + * + * @tkip: TKIP data, containing IV32 and IV16 in host byte order + * @ccmp: PN data, most significant byte first (big endian, + * reverse order than in packet) + * @aes_cmac: PN data, most significant byte first (big endian, + * reverse order than in packet) + */ +struct ieee80211_key_seq { + union { + struct { + u32 iv32; + u16 iv16; + } tkip; + struct { + u8 pn[6]; + } ccmp; + struct { + u8 pn[6]; + } aes_cmac; + }; +}; + +/** + * ieee80211_get_key_tx_seq - get key TX sequence counter + * + * @keyconf: the parameter passed with the set key + * @seq: buffer to receive the sequence data + * + * This function allows a driver to retrieve the current TX IV/PN + * for the given key. It must not be called if IV generation is + * offloaded to the device. + * + * Note that this function may only be called when no TX processing + * can be done concurrently, for example when queues are stopped + * and the stop has been synchronized. + */ +void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf, + struct ieee80211_key_seq *seq); + +/** + * ieee80211_get_key_rx_seq - get key RX sequence counter + * + * @keyconf: the parameter passed with the set key + * @tid: The TID, or -1 for the management frame value (CCMP only); + * the value on TID 0 is also used for non-QoS frames. For + * CMAC, only TID 0 is valid. + * @seq: buffer to receive the sequence data + * + * This function allows a driver to retrieve the current RX IV/PNs + * for the given key. It must not be called if IV checking is done + * by the device and not by mac80211. + * + * Note that this function may only be called when no RX processing + * can be done concurrently. + */ +void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, + int tid, struct ieee80211_key_seq *seq); + +/** + * ieee80211_gtk_rekey_notify - notify userspace supplicant of rekeying + * @vif: virtual interface the rekeying was done on + * @bssid: The BSSID of the AP, for checking association + * @replay_ctr: the new replay counter after GTK rekeying + * @gfp: allocation flags + */ +void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid, + const u8 *replay_ctr, gfp_t gfp); + +/** + * ieee80211_wake_queue - wake specific queue + * @hw: pointer as obtained from ieee80211_alloc_hw(). + * @queue: queue number (counted from zero). + * + * Drivers should use this function instead of netif_wake_queue. + */ +void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue); + +/** + * ieee80211_stop_queue - stop specific queue + * @hw: pointer as obtained from ieee80211_alloc_hw(). + * @queue: queue number (counted from zero). + * + * Drivers should use this function instead of netif_stop_queue. + */ +void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue); + +/** + * ieee80211_queue_stopped - test status of the queue + * @hw: pointer as obtained from ieee80211_alloc_hw(). + * @queue: queue number (counted from zero). + * + * Drivers should use this function instead of netif_stop_queue. + */ + +int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue); + +/** + * ieee80211_stop_queues - stop all queues + * @hw: pointer as obtained from ieee80211_alloc_hw(). + * + * Drivers should use this function instead of netif_stop_queue. + */ +void ieee80211_stop_queues(struct ieee80211_hw *hw); + +/** + * ieee80211_wake_queues - wake all queues + * @hw: pointer as obtained from ieee80211_alloc_hw(). + * + * Drivers should use this function instead of netif_wake_queue. + */ +void ieee80211_wake_queues(struct ieee80211_hw *hw); + +/** + * ieee80211_scan_completed - completed hardware scan + * + * When hardware scan offload is used (i.e. the hw_scan() callback is + * assigned) this function needs to be called by the driver to notify + * mac80211 that the scan finished. This function can be called from + * any context, including hardirq context. + * + * @hw: the hardware that finished the scan + * @aborted: set to true if scan was aborted + */ +void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted); + +/** + * ieee80211_sched_scan_results - got results from scheduled scan + * + * When a scheduled scan is running, this function needs to be called by the + * driver whenever there are new scan results available. + * + * @hw: the hardware that is performing scheduled scans + */ +void ieee80211_sched_scan_results(struct ieee80211_hw *hw); + +/** + * ieee80211_sched_scan_stopped - inform that the scheduled scan has stopped + * + * When a scheduled scan is running, this function can be called by + * the driver if it needs to stop the scan to perform another task. + * Usual scenarios are drivers that cannot continue the scheduled scan + * while associating, for instance. + * + * @hw: the hardware that is performing scheduled scans + */ +void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw); + +/** + * ieee80211_iterate_active_interfaces - iterate active interfaces + * + * This function iterates over the interfaces associated with a given + * hardware that are currently active and calls the callback for them. + * This function allows the iterator function to sleep, when the iterator + * function is atomic @ieee80211_iterate_active_interfaces_atomic can + * be used. + * Does not iterate over a new interface during add_interface() + * + * @hw: the hardware struct of which the interfaces should be iterated over + * @iterator: the iterator function to call + * @data: first argument of the iterator function + */ +void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, + void (*iterator)(void *data, u8 *mac, + struct ieee80211_vif *vif), + void *data); + +/** + * ieee80211_iterate_active_interfaces_atomic - iterate active interfaces + * + * This function iterates over the interfaces associated with a given + * hardware that are currently active and calls the callback for them. + * This function requires the iterator callback function to be atomic, + * if that is not desired, use @ieee80211_iterate_active_interfaces instead. + * Does not iterate over a new interface during add_interface() + * + * @hw: the hardware struct of which the interfaces should be iterated over + * @iterator: the iterator function to call, cannot sleep + * @data: first argument of the iterator function + */ +void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw, + void (*iterator)(void *data, + u8 *mac, + struct ieee80211_vif *vif), + void *data); + +/** + * ieee80211_queue_work - add work onto the mac80211 workqueue + * + * Drivers and mac80211 use this to add work onto the mac80211 workqueue. + * This helper ensures drivers are not queueing work when they should not be. + * + * @hw: the hardware struct for the interface we are adding work for + * @work: the work we want to add onto the mac80211 workqueue + */ +void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work); + +/** + * ieee80211_queue_delayed_work - add work onto the mac80211 workqueue + * + * Drivers and mac80211 use this to queue delayed work onto the mac80211 + * workqueue. + * + * @hw: the hardware struct for the interface we are adding work for + * @dwork: delayable work to queue onto the mac80211 workqueue + * @delay: number of jiffies to wait before queueing + */ +void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, + struct delayed_work *dwork, + unsigned long delay); + +/** + * ieee80211_start_tx_ba_session - Start a tx Block Ack session. + * @sta: the station for which to start a BA session + * @tid: the TID to BA on. + * @timeout: session timeout value (in TUs) + * + * Return: success if addBA request was sent, failure otherwise + * + * Although mac80211/low level driver/user space application can estimate + * the need to start aggregation on a certain RA/TID, the session level + * will be managed by the mac80211. + */ +int ieee80211_start_tx_ba_session(struct ieee80211_sta *sta, u16 tid, + u16 timeout); + +/** + * ieee80211_start_tx_ba_cb_irqsafe - low level driver ready to aggregate. + * @vif: &struct ieee80211_vif pointer from the add_interface callback + * @ra: receiver address of the BA session recipient. + * @tid: the TID to BA on. + * + * This function must be called by low level driver once it has + * finished with preparations for the BA session. It can be called + * from any context. + */ +void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, const u8 *ra, + u16 tid); + +/** + * ieee80211_stop_tx_ba_session - Stop a Block Ack session. + * @sta: the station whose BA session to stop + * @tid: the TID to stop BA. + * + * Return: negative error if the TID is invalid, or no aggregation active + * + * Although mac80211/low level driver/user space application can estimate + * the need to stop aggregation on a certain RA/TID, the session level + * will be managed by the mac80211. + */ +int ieee80211_stop_tx_ba_session(struct ieee80211_sta *sta, u16 tid); + +/** + * ieee80211_stop_tx_ba_cb_irqsafe - low level driver ready to stop aggregate. + * @vif: &struct ieee80211_vif pointer from the add_interface callback + * @ra: receiver address of the BA session recipient. + * @tid: the desired TID to BA on. + * + * This function must be called by low level driver once it has + * finished with preparations for the BA session tear down. It + * can be called from any context. + */ +void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, const u8 *ra, + u16 tid); + +/** + * ieee80211_find_sta - find a station + * + * @vif: virtual interface to look for station on + * @addr: station's address + * + * This function must be called under RCU lock and the + * resulting pointer is only valid under RCU lock as well. + */ +struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif, + const u8 *addr); + +/** + * ieee80211_find_sta_by_ifaddr - find a station on hardware + * + * @hw: pointer as obtained from ieee80211_alloc_hw() + * @addr: remote station's address + * @localaddr: local address (vif->sdata->vif.addr). Use NULL for 'any'. + * + * This function must be called under RCU lock and the + * resulting pointer is only valid under RCU lock as well. + * + * NOTE: You may pass NULL for localaddr, but then you will just get + * the first STA that matches the remote address 'addr'. + * We can have multiple STA associated with multiple + * logical stations (e.g. consider a station connecting to another + * BSSID on the same AP hardware without disconnecting first). + * In this case, the result of this method with localaddr NULL + * is not reliable. + * + * DO NOT USE THIS FUNCTION with localaddr NULL if at all possible. + */ +struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, + const u8 *addr, + const u8 *localaddr); + +/** + * ieee80211_sta_block_awake - block station from waking up + * @hw: the hardware + * @pubsta: the station + * @block: whether to block or unblock + * + * Some devices require that all frames that are on the queues + * for a specific station that went to sleep are flushed before + * a poll response or frames after the station woke up can be + * delivered to that it. Note that such frames must be rejected + * by the driver as filtered, with the appropriate status flag. + * + * This function allows implementing this mode in a race-free + * manner. + * + * To do this, a driver must keep track of the number of frames + * still enqueued for a specific station. If this number is not + * zero when the station goes to sleep, the driver must call + * this function to force mac80211 to consider the station to + * be asleep regardless of the station's actual state. Once the + * number of outstanding frames reaches zero, the driver must + * call this function again to unblock the station. That will + * cause mac80211 to be able to send ps-poll responses, and if + * the station queried in the meantime then frames will also + * be sent out as a result of this. Additionally, the driver + * will be notified that the station woke up some time after + * it is unblocked, regardless of whether the station actually + * woke up while blocked or not. + */ +void ieee80211_sta_block_awake(struct ieee80211_hw *hw, + struct ieee80211_sta *pubsta, bool block); + +/** + * ieee80211_sta_eosp - notify mac80211 about end of SP + * @pubsta: the station + * + * When a device transmits frames in a way that it can't tell + * mac80211 in the TX status about the EOSP, it must clear the + * %IEEE80211_TX_STATUS_EOSP bit and call this function instead. + * This applies for PS-Poll as well as uAPSD. + * + * Note that there is no non-_irqsafe version right now as + * it wasn't needed, but just like _tx_status() and _rx() + * must not be mixed in irqsafe/non-irqsafe versions, this + * function must not be mixed with those either. Use the + * all irqsafe, or all non-irqsafe, don't mix! If you need + * the non-irqsafe version of this, you need to add it. + */ +void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta); + +/** + * ieee80211_iter_keys - iterate keys programmed into the device + * @hw: pointer obtained from ieee80211_alloc_hw() + * @vif: virtual interface to iterate, may be %NULL for all + * @iter: iterator function that will be called for each key + * @iter_data: custom data to pass to the iterator function + * + * This function can be used to iterate all the keys known to + * mac80211, even those that weren't previously programmed into + * the device. This is intended for use in WoWLAN if the device + * needs reprogramming of the keys during suspend. Note that due + * to locking reasons, it is also only safe to call this at few + * spots since it must hold the RTNL and be able to sleep. + * + * The order in which the keys are iterated matches the order + * in which they were originally installed and handed to the + * set_key callback. + */ +void ieee80211_iter_keys(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void (*iter)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *data), + void *iter_data); + +/** + * ieee80211_ap_probereq_get - retrieve a Probe Request template + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * Creates a Probe Request template which can, for example, be uploaded to + * hardware. The template is filled with bssid, ssid and supported rate + * information. This function must only be called from within the + * .bss_info_changed callback function and only in managed mode. The function + * is only useful when the interface is associated, otherwise it will return + * NULL. + */ +struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +/** + * ieee80211_beacon_loss - inform hardware does not receive beacons + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTER and + * %IEEE80211_CONF_PS is set, the driver needs to inform whenever the + * hardware is not receiving beacons with this function. + */ +void ieee80211_beacon_loss(struct ieee80211_vif *vif); + +/** + * ieee80211_connection_loss - inform hardware has lost connection to the AP + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTER, and + * %IEEE80211_CONF_PS and %IEEE80211_HW_CONNECTION_MONITOR are set, the driver + * needs to inform if the connection to the AP has been lost. + * + * This function will cause immediate change to disassociated state, + * without connection recovery attempts. + */ +void ieee80211_connection_loss(struct ieee80211_vif *vif); + +/** + * ieee80211_resume_disconnect - disconnect from AP after resume + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * Instructs mac80211 to disconnect from the AP after resume. + * Drivers can use this after WoWLAN if they know that the + * connection cannot be kept up, for example because keys were + * used while the device was asleep but the replay counters or + * similar cannot be retrieved from the device during resume. + * + * Note that due to implementation issues, if the driver uses + * the reconfiguration functionality during resume the interface + * will still be added as associated first during resume and then + * disconnect normally later. + * + * This function can only be called from the resume callback and + * the driver must not be holding any of its own locks while it + * calls this function, or at least not any locks it needs in the + * key configuration paths (if it supports HW crypto). + */ +void ieee80211_resume_disconnect(struct ieee80211_vif *vif); + +/** + * ieee80211_disable_dyn_ps - force mac80211 to temporarily disable dynamic psm + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * Some hardware require full power save to manage simultaneous BT traffic + * on the WLAN frequency. Full PSM is required periodically, whenever there are + * burst of BT traffic. The hardware gets information of BT traffic via + * hardware co-existence lines, and consequentially requests mac80211 to + * (temporarily) enter full psm. + * This function will only temporarily disable dynamic PS, not enable PSM if + * it was not already enabled. + * The driver must make sure to re-enable dynamic PS using + * ieee80211_enable_dyn_ps() if the driver has disabled it. + * + */ +void ieee80211_disable_dyn_ps(struct ieee80211_vif *vif); + +/** + * ieee80211_enable_dyn_ps - restore dynamic psm after being disabled + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * This function restores dynamic PS after being temporarily disabled via + * ieee80211_disable_dyn_ps(). Each ieee80211_disable_dyn_ps() call must + * be coupled with an eventual call to this function. + * + */ +void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif); + +/** + * ieee80211_cqm_rssi_notify - inform a configured connection quality monitoring + * rssi threshold triggered + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @rssi_event: the RSSI trigger event type + * @gfp: context flags + * + * When the %IEEE80211_HW_SUPPORTS_CQM_RSSI is set, and a connection quality + * monitoring is configured with an rssi threshold, the driver will inform + * whenever the rssi level reaches the threshold. + */ +void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp); + +/** + * ieee80211_get_operstate - get the operstate of the vif + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * The driver might need to know the operstate of the net_device + * (specifically, whether the link is IF_OPER_UP after resume) + */ +unsigned char ieee80211_get_operstate(struct ieee80211_vif *vif); + +/** + * ieee80211_chswitch_done - Complete channel switch process + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @success: make the channel switch successful or not + * + * Complete the channel switch post-process: set the new operational channel + * and wake up the suspended queues. + */ +void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success); + +/** + * ieee80211_request_smps - request SM PS transition + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @smps_mode: new SM PS mode + * + * This allows the driver to request an SM PS transition in managed + * mode. This is useful when the driver has more information than + * the stack about possible interference, for example by bluetooth. + */ +void ieee80211_request_smps(struct ieee80211_vif *vif, + enum ieee80211_smps_mode smps_mode); + +/** + * ieee80211_key_removed - disable hw acceleration for key + * @key_conf: The key hw acceleration should be disabled for + * + * This allows drivers to indicate that the given key has been + * removed from hardware acceleration, due to a new key that + * was added. Don't use this if the key can continue to be used + * for TX, if the key restriction is on RX only it is permitted + * to keep the key for TX only and not call this function. + * + * Due to locking constraints, it may only be called during + * @set_key. This function must be allowed to sleep, and the + * key it tries to disable may still be used until it returns. + */ +void ieee80211_key_removed(struct ieee80211_key_conf *key_conf); + +/** + * ieee80211_ready_on_channel - notification of remain-on-channel start + * @hw: pointer as obtained from ieee80211_alloc_hw() + */ +void ieee80211_ready_on_channel(struct ieee80211_hw *hw); + +/** + * ieee80211_remain_on_channel_expired - remain_on_channel duration expired + * @hw: pointer as obtained from ieee80211_alloc_hw() + */ +void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw); + +/** + * ieee80211_stop_rx_ba_session - callback to stop existing BA sessions + * + * in order not to harm the system performance and user experience, the device + * may request not to allow any rx ba session and tear down existing rx ba + * sessions based on system constraints such as periodic BT activity that needs + * to limit wlan activity (eg.sco or a2dp)." + * in such cases, the intention is to limit the duration of the rx ppdu and + * therefore prevent the peer device to use a-mpdu aggregation. + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @ba_rx_bitmap: Bit map of open rx ba per tid + * @addr: & to bssid mac address + */ +void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, + const u8 *addr); + +/** + * ieee80211_send_bar - send a BlockAckReq frame + * + * can be used to flush pending frames from the peer's aggregation reorder + * buffer. + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @ra: the peer's destination address + * @tid: the TID of the aggregation session + * @ssn: the new starting sequence number for the receiver + */ +void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn); + +/* Rate control API */ + +/** + * enum rate_control_changed - flags to indicate which parameter changed + * + * @IEEE80211_RC_HT_CHANGED: The HT parameters of the operating channel have + * changed, rate control algorithm can update its internal state if needed. + */ +enum rate_control_changed { + IEEE80211_RC_HT_CHANGED = BIT(0) +}; + +/** + * struct ieee80211_tx_rate_control - rate control information for/from RC algo + * + * @hw: The hardware the algorithm is invoked for. + * @sband: The band this frame is being transmitted on. + * @bss_conf: the current BSS configuration + * @reported_rate: The rate control algorithm can fill this in to indicate + * which rate should be reported to userspace as the current rate and + * used for rate calculations in the mesh network. + * @rts: whether RTS will be used for this frame because it is longer than the + * RTS threshold + * @short_preamble: whether mac80211 will request short-preamble transmission + * if the selected rate supports it + * @max_rate_idx: user-requested maximum rate (not MCS for now) + * (deprecated; this will be removed once drivers get updated to use + * rate_idx_mask) + * @rate_idx_mask: user-requested rate mask (not MCS for now) + * @skb: the skb that will be transmitted, the control information in it needs + * to be filled in + * @bss: whether this frame is sent out in AP or IBSS mode + */ +struct ieee80211_tx_rate_control { + struct ieee80211_hw *hw; + struct ieee80211_supported_band *sband; + struct ieee80211_bss_conf *bss_conf; + struct sk_buff *skb; + struct ieee80211_tx_rate reported_rate; + bool rts, short_preamble; + u8 max_rate_idx; + u32 rate_idx_mask; + bool bss; +}; + +struct rate_control_ops { + struct module *module; + const char *name; + void *(*alloc)(struct ieee80211_hw *hw, struct dentry *debugfsdir); + void (*free)(void *priv); + + void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp); + void (*rate_init)(void *priv, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta); + void (*rate_update)(void *priv, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, + void *priv_sta, u32 changed, + enum nl80211_channel_type oper_chan_type); + void (*free_sta)(void *priv, struct ieee80211_sta *sta, + void *priv_sta); + + void (*tx_status)(void *priv, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta, + struct sk_buff *skb); + void (*get_rate)(void *priv, struct ieee80211_sta *sta, void *priv_sta, + struct ieee80211_tx_rate_control *txrc); + + void (*add_sta_debugfs)(void *priv, void *priv_sta, + struct dentry *dir); + void (*remove_sta_debugfs)(void *priv, void *priv_sta); +}; + +static inline int rate_supported(struct ieee80211_sta *sta, + enum ieee80211_band band, + int index) +{ + return (sta == NULL || sta->supp_rates[band] & BIT(index)); +} + +/** + * rate_control_send_low - helper for drivers for management/no-ack frames + * + * Rate control algorithms that agree to use the lowest rate to + * send management frames and NO_ACK data with the respective hw + * retries should use this in the beginning of their mac80211 get_rate + * callback. If true is returned the rate control can simply return. + * If false is returned we guarantee that sta and sta and priv_sta is + * not null. + * + * Rate control algorithms wishing to do more intelligent selection of + * rate for multicast/broadcast frames may choose to not use this. + * + * @sta: &struct ieee80211_sta pointer to the target destination. Note + * that this may be null. + * @priv_sta: private rate control structure. This may be null. + * @txrc: rate control information we sholud populate for mac80211. + */ +bool rate_control_send_low(struct ieee80211_sta *sta, + void *priv_sta, + struct ieee80211_tx_rate_control *txrc); + + +static inline s8 +rate_lowest_index(struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta) +{ + int i; + + for (i = 0; i < sband->n_bitrates; i++) + if (rate_supported(sta, sband->band, i)) + return i; + + /* warn when we cannot find a rate. */ + WARN_ON_ONCE(1); + + /* and return 0 (the lowest index) */ + return 0; +} + +static inline +bool rate_usable_index_exists(struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta) +{ + unsigned int i; + + for (i = 0; i < sband->n_bitrates; i++) + if (rate_supported(sta, sband->band, i)) + return true; + return false; +} + +int ieee80211_rate_control_register(struct rate_control_ops *ops); +void ieee80211_rate_control_unregister(struct rate_control_ops *ops); + +static inline bool +conf_is_ht20(struct ieee80211_conf *conf) +{ + return conf->channel_type == NL80211_CHAN_HT20; +} + +static inline bool +conf_is_ht40_minus(struct ieee80211_conf *conf) +{ + return conf->channel_type == NL80211_CHAN_HT40MINUS; +} + +static inline bool +conf_is_ht40_plus(struct ieee80211_conf *conf) +{ + return conf->channel_type == NL80211_CHAN_HT40PLUS; +} + +static inline bool +conf_is_ht40(struct ieee80211_conf *conf) +{ + return conf_is_ht40_minus(conf) || conf_is_ht40_plus(conf); +} + +static inline bool +conf_is_ht(struct ieee80211_conf *conf) +{ + return conf->channel_type != NL80211_CHAN_NO_HT; +} + +static inline enum nl80211_iftype +ieee80211_iftype_p2p(enum nl80211_iftype type, bool p2p) +{ + if (p2p) { + switch (type) { + case NL80211_IFTYPE_STATION: + return NL80211_IFTYPE_P2P_CLIENT; + case NL80211_IFTYPE_AP: + return NL80211_IFTYPE_P2P_GO; + default: + break; + } + } + return type; +} + +static inline enum nl80211_iftype +ieee80211_vif_type_p2p(struct ieee80211_vif *vif) +{ + return ieee80211_iftype_p2p(vif->type, vif->p2p); +} + +void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, + int rssi_min_thold, + int rssi_max_thold); + +void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif); + +int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb); + +int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif, + struct sk_buff *skb); +#endif /* MAC80211_H */ diff --git a/include/compat/net/net_namespace.h b/include/compat/net/net_namespace.h new file mode 100644 index 00000000..0f74944b --- /dev/null +++ b/include/compat/net/net_namespace.h @@ -0,0 +1,10 @@ +#ifndef _COMPAT_NET_NET_NAMESPACE_H +#define _COMPAT_NET_NET_NAMESPACE_H 1 + +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)) +#include_next +#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)) */ + +#endif /* _COMPAT_NET_NET_NAMESPACE_H */ diff --git a/include/compat/net/regulatory.h b/include/compat/net/regulatory.h new file mode 100644 index 00000000..eb7d3c2d --- /dev/null +++ b/include/compat/net/regulatory.h @@ -0,0 +1,116 @@ +#ifndef __NET_REGULATORY_H +#define __NET_REGULATORY_H +/* + * regulatory support structures + * + * Copyright 2008-2009 Luis R. Rodriguez + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +/** + * enum environment_cap - Environment parsed from country IE + * @ENVIRON_ANY: indicates country IE applies to both indoor and + * outdoor operation. + * @ENVIRON_INDOOR: indicates country IE applies only to indoor operation + * @ENVIRON_OUTDOOR: indicates country IE applies only to outdoor operation + */ +enum environment_cap { + ENVIRON_ANY, + ENVIRON_INDOOR, + ENVIRON_OUTDOOR, +}; + +/** + * struct regulatory_request - used to keep track of regulatory requests + * + * @wiphy_idx: this is set if this request's initiator is + * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This + * can be used by the wireless core to deal with conflicts + * and potentially inform users of which devices specifically + * cased the conflicts. + * @initiator: indicates who sent this request, could be any of + * of those set in nl80211_reg_initiator (%NL80211_REGDOM_SET_BY_*) + * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested + * regulatory domain. We have a few special codes: + * 00 - World regulatory domain + * 99 - built by driver but a specific alpha2 cannot be determined + * 98 - result of an intersection between two regulatory domains + * 97 - regulatory domain has not yet been configured + * @intersect: indicates whether the wireless core should intersect + * the requested regulatory domain with the presently set regulatory + * domain. + * @processed: indicates whether or not this requests has already been + * processed. When the last request is processed it means that the + * currently regulatory domain set on cfg80211 is updated from + * CRDA and can be used by other regulatory requests. When a + * the last request is not yet processed we must yield until it + * is processed before processing any new requests. + * @country_ie_checksum: checksum of the last processed and accepted + * country IE + * @country_ie_env: lets us know if the AP is telling us we are outdoor, + * indoor, or if it doesn't matter + * @list: used to insert into the reg_requests_list linked list + */ +struct regulatory_request { + int wiphy_idx; + enum nl80211_reg_initiator initiator; + char alpha2[2]; + bool intersect; + bool processed; + enum environment_cap country_ie_env; + struct list_head list; +}; + +struct ieee80211_freq_range { + u32 start_freq_khz; + u32 end_freq_khz; + u32 max_bandwidth_khz; +}; + +struct ieee80211_power_rule { + u32 max_antenna_gain; + u32 max_eirp; +}; + +struct ieee80211_reg_rule { + struct ieee80211_freq_range freq_range; + struct ieee80211_power_rule power_rule; + u32 flags; +}; + +struct ieee80211_regdomain { + u32 n_reg_rules; + char alpha2[2]; + struct ieee80211_reg_rule reg_rules[]; +}; + +#define MHZ_TO_KHZ(freq) ((freq) * 1000) +#define KHZ_TO_MHZ(freq) ((freq) / 1000) +#define DBI_TO_MBI(gain) ((gain) * 100) +#define MBI_TO_DBI(gain) ((gain) / 100) +#define DBM_TO_MBM(gain) ((gain) * 100) +#define MBM_TO_DBM(gain) ((gain) / 100) + +#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \ +{ \ + .freq_range.start_freq_khz = MHZ_TO_KHZ(start), \ + .freq_range.end_freq_khz = MHZ_TO_KHZ(end), \ + .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw), \ + .power_rule.max_antenna_gain = DBI_TO_MBI(gain),\ + .power_rule.max_eirp = DBM_TO_MBM(eirp), \ + .flags = reg_flags, \ +} + +#endif diff --git a/include/linux/Kbuild b/include/linux/Kbuild index dc15414c..3251d7d4 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -260,7 +260,6 @@ header-y += mroute6.h header-y += msdos_fs.h header-y += msg.h header-y += msm_adc.h -header-y += msm_ion.h header-y += mtio.h header-y += n_r3964.h header-y += nbd.h diff --git a/include/linux/audit.h b/include/linux/audit.h index acc4ff37..89381a27 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -70,6 +70,8 @@ #define AUDIT_TTY_GET 1016 /* Get TTY auditing status */ #define AUDIT_TTY_SET 1017 /* Set TTY auditing status */ +#define LSM_AUDIT_DATA_IOCTL_OP 11 + #define AUDIT_FIRST_USER_MSG 1100 /* Userspace messages mostly uninteresting to kernel */ #define AUDIT_USER_AVC 1107 /* We filter this differently */ #define AUDIT_USER_TTY 1124 /* Non-ICANON TTY input meaning */ @@ -480,14 +482,14 @@ static inline void audit_syscall_entry(int arch, int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3) { - if (unlikely(current->audit_context)) + if (unlikely(!audit_dummy_context())) __audit_syscall_entry(arch, major, a0, a1, a2, a3); } static inline void audit_syscall_exit(void *pt_regs) { if (unlikely(current->audit_context)) { int success = is_syscall_success(pt_regs); - long return_code = regs_return_value(pt_regs); + int return_code = regs_return_value(pt_regs); __audit_syscall_exit(success, return_code); } diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 68117aa6..4f88b069 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -117,30 +117,50 @@ enum { /* Need to know about CPUs going up/down? */ #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) #define cpu_notifier(fn, pri) { \ - static struct notifier_block fn##_nb = \ + static struct notifier_block fn##_nb __cpuinitdata = \ { .notifier_call = fn, .priority = pri }; \ register_cpu_notifier(&fn##_nb); \ } + +#define __cpu_notifier(fn, pri) { \ + static struct notifier_block fn##_nb = \ + { .notifier_call = fn, .priority = pri }; \ + __register_cpu_notifier(&fn##_nb); \ +} #else /* #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) */ #define cpu_notifier(fn, pri) do { (void)(fn); } while (0) +#define __cpu_notifier(fn, pri) do { (void)(fn); } while (0) #endif /* #else #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) */ + #ifdef CONFIG_HOTPLUG_CPU extern int register_cpu_notifier(struct notifier_block *nb); +extern int __register_cpu_notifier(struct notifier_block *nb); extern void unregister_cpu_notifier(struct notifier_block *nb); +extern void __unregister_cpu_notifier(struct notifier_block *nb); #else #ifndef MODULE extern int register_cpu_notifier(struct notifier_block *nb); +extern int __register_cpu_notifier(struct notifier_block *nb); #else static inline int register_cpu_notifier(struct notifier_block *nb) { return 0; } + +static inline int __register_cpu_notifier(struct notifier_block *nb) +{ + return 0; +} #endif static inline void unregister_cpu_notifier(struct notifier_block *nb) { } + +static inline void __unregister_cpu_notifier(struct notifier_block *nb) +{ +} #endif int cpu_up(unsigned int cpu); @@ -148,19 +168,32 @@ void notify_cpu_starting(unsigned int cpu); extern void cpu_maps_update_begin(void); extern void cpu_maps_update_done(void); +#define cpu_notifier_register_begin cpu_maps_update_begin +#define cpu_notifier_register_done cpu_maps_update_done + #else /* CONFIG_SMP */ #define cpu_notifier(fn, pri) do { (void)(fn); } while (0) +#define __cpu_notifier(fn, pri) do { (void)(fn); } while (0) static inline int register_cpu_notifier(struct notifier_block *nb) { return 0; } +static inline int __register_cpu_notifier(struct notifier_block *nb) +{ + return 0; +} + static inline void unregister_cpu_notifier(struct notifier_block *nb) { } +static inline void __unregister_cpu_notifier(struct notifier_block *nb) +{ +} + static inline void cpu_maps_update_begin(void) { } @@ -169,6 +202,14 @@ static inline void cpu_maps_update_done(void) { } +static inline void cpu_notifier_register_begin(void) +{ +} + +static inline void cpu_notifier_register_done(void) +{ +} + #endif /* CONFIG_SMP */ extern struct bus_type cpu_subsys; @@ -180,8 +221,11 @@ extern void put_online_cpus(void); extern void cpu_hotplug_disable(void); extern void cpu_hotplug_enable(void); #define hotcpu_notifier(fn, pri) cpu_notifier(fn, pri) +#define __hotcpu_notifier(fn, pri) __cpu_notifier(fn, pri) #define register_hotcpu_notifier(nb) register_cpu_notifier(nb) +#define __register_hotcpu_notifier(nb) __register_cpu_notifier(nb) #define unregister_hotcpu_notifier(nb) unregister_cpu_notifier(nb) +#define __unregister_hotcpu_notifier(nb) __unregister_cpu_notifier(nb) int cpu_down(unsigned int cpu); #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE @@ -204,9 +248,12 @@ static inline void cpu_hotplug_driver_unlock(void) #define cpu_hotplug_disable() do { } while (0) #define cpu_hotplug_enable() do { } while (0) #define hotcpu_notifier(fn, pri) do { (void)(fn); } while (0) +#define __hotcpu_notifier(fn, pri) do { (void)(fn); } while (0) /* These aren't inline functions due to a GCC bug. */ #define register_hotcpu_notifier(nb) ({ (void)(nb); 0; }) +#define __register_hotcpu_notifier(nb) ({ (void)(nb); 0; }) #define unregister_hotcpu_notifier(nb) ({ (void)(nb); }) +#define __unregister_hotcpu_notifier(nb) ({ (void)(nb); }) #endif /* CONFIG_HOTPLUG_CPU */ #ifdef CONFIG_PM_SLEEP_SMP diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 8ca59c07..997efc68 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -360,27 +360,116 @@ extern struct cpufreq_governor cpufreq_gov_performance; #endif #ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE #define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_performance) -#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE) -extern struct cpufreq_governor cpufreq_gov_powersave; -#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_powersave) -#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE) -extern struct cpufreq_governor cpufreq_gov_userspace; -#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_userspace) -#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND) -extern struct cpufreq_governor cpufreq_gov_ondemand; -#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_ondemand) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_ADAPTIVE) +extern struct cpufreq_governor cpufreq_gov_adaptive; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_adaptive) #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_HYPER) +extern struct cpufreq_governor cpufreq_gov_hyper; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_hyper) #elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE) extern struct cpufreq_governor cpufreq_gov_interactive; #define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_interactive) -#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_HOTPLUG) -extern struct cpufreq_governor cpufreq_gov_hotplug; -#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_hotplug) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_LULZACTIVE) +extern struct cpufreq_governor cpufreq_gov_lulzactive; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_lulzactive) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVEX) +extern struct cpufreq_governor cpufreq_gov_interactivex; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_interactivex) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_LIONHEART) +extern struct cpufreq_governor cpufreq_gov_lionheart; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_lionheart) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND) +extern struct cpufreq_governor cpufreq_gov_ondemand; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_ondemand) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMANDX) +extern struct cpufreq_governor cpufreq_gov_ondemandx; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_ondemandx) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE) +extern struct cpufreq_governor cpufreq_gov_powersave; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_powersave) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2) +extern struct cpufreq_governor cpufreq_gov_smartass2; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_smartass2) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE) +extern struct cpufreq_governor cpufreq_gov_userspace; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_userspace) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIDEMAND) +extern struct cpufreq_governor cpufreq_gov_intellidemand; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_intellidemand) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_ASSWAX) +extern struct cpufreq_governor cpufreq_gov_asswax; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_asswax) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_BADASS) +extern struct cpufreq_governor cpufreq_gov_badass; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_badass) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_DANCEDANCE) +extern struct cpufreq_governor cpufreq_gov_dancedance; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_dancedance) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASSH3) +extern struct cpufreq_governor cpufreq_gov_smartassh3; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_smartassh3) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_WHEATLEY) +extern struct cpufreq_governor cpufreq_gov_wheatley; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_wheatley) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_LAGFREE) +extern struct cpufreq_governor cpufreq_gov_lagfree; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_lagfree) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_SAVAGEDZEN) +extern struct cpufreq_governor cpufreq_gov_savagedzen; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_savagedzen) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_BRAZILLIANWAX) +extern struct cpufreq_governor cpufreq_gov_brazilianwax; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_brazilianwax) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_LULZACTIVEW) +extern struct cpufreq_governor cpufreq_gov_lulzactivew; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_lulzactivew) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_MINMAX) +extern struct cpufreq_governor cpufreq_gov_minmax; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_minmax) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_SCARY) +extern struct cpufreq_governor cpufreq_gov_scary; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_scary) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_ZZMOOVE) +extern struct cpufreq_governor cpufreq_gov_zzmoove; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_zzmoove) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_SKYWALKER) +extern struct cpufreq_governor cpufreq_gov_skywalker; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_skywalker) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_VIRTUOUS) +extern struct cpufreq_governor cpufreq_gov_virtuous; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_virtuous) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTMAX_EPS) +extern struct cpufreq_governor cpufreq_gov_smartmax_eps; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_smartmax_eps) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_SUPERBAD) +extern struct cpufreq_governor cpufreq_gov_superbad; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_superbad) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_SMOOTHASS) +extern struct cpufreq_governor cpufreq_gov_smoothass; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_smoothass) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIACTIVE) +extern struct cpufreq_governor cpufreq_gov_intelliactive; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_intelliactive) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIMM) +extern struct cpufreq_governor cpufreq_gov_intellimm; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_intellimm) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_STOCKDEMAND) +extern struct cpufreq_governor cpufreq_gov_stockdemand; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_stockdemand) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_YANKDEMAND) +extern struct cpufreq_governor cpufreq_gov_yankdemand; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_yankdemand) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_HYPER) +extern struct cpufreq_governor cpufreq_gov_hyper; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_hyper) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_ALUCARD) +extern struct cpufreq_governor cpufreq_gov_alucard; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_alucard) #endif - /********************************************************************* * FREQUENCY TABLE HELPERS * *********************************************************************/ diff --git a/include/linux/dma-attrs.h b/include/linux/dma-attrs.h index 547ab568..18513e37 100644 --- a/include/linux/dma-attrs.h +++ b/include/linux/dma-attrs.h @@ -15,6 +15,8 @@ enum dma_attr { DMA_ATTR_WEAK_ORDERING, DMA_ATTR_WRITE_COMBINE, DMA_ATTR_NON_CONSISTENT, + DMA_ATTR_NO_KERNEL_MAPPING, + DMA_ATTR_STRONGLY_ORDERED, DMA_ATTR_MAX, }; diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 7d4e0356..bc53c52b 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -22,6 +22,9 @@ typedef void (elevator_bio_merged_fn) (struct request_queue *, typedef int (elevator_dispatch_fn) (struct request_queue *, int); typedef void (elevator_add_req_fn) (struct request_queue *, struct request *); +typedef int (elevator_reinsert_req_fn) (struct request_queue *, + struct request *); +typedef bool (elevator_is_urgent_fn) (struct request_queue *); typedef struct request *(elevator_request_list_fn) (struct request_queue *, struct request *); typedef void (elevator_completed_req_fn) (struct request_queue *, struct request *); typedef int (elevator_may_queue_fn) (struct request_queue *, int); @@ -35,6 +38,7 @@ typedef void (elevator_deactivate_req_fn) (struct request_queue *, struct reques typedef void *(elevator_init_fn) (struct request_queue *); typedef void (elevator_exit_fn) (struct elevator_queue *); +typedef void (elevator_registered_fn) (struct request_queue *); struct elevator_ops { @@ -46,6 +50,9 @@ struct elevator_ops elevator_dispatch_fn *elevator_dispatch_fn; elevator_add_req_fn *elevator_add_req_fn; + elevator_reinsert_req_fn *elevator_reinsert_req_fn; + elevator_is_urgent_fn *elevator_is_urgent_fn; + elevator_activate_req_fn *elevator_activate_req_fn; elevator_deactivate_req_fn *elevator_deactivate_req_fn; @@ -64,6 +71,7 @@ struct elevator_ops elevator_init_fn *elevator_init_fn; elevator_exit_fn *elevator_exit_fn; + elevator_registered_fn *elevator_registered_fn; }; #define ELV_NAME_MAX (16) @@ -122,6 +130,7 @@ extern void elv_merged_request(struct request_queue *, struct request *, int); extern void elv_bio_merged(struct request_queue *q, struct request *, struct bio *); extern void elv_requeue_request(struct request_queue *, struct request *); +extern int elv_reinsert_request(struct request_queue *, struct request *); extern struct request *elv_former_request(struct request_queue *, struct request *); extern struct request *elv_latter_request(struct request_queue *, struct request *); extern int elv_register_queue(struct request_queue *q); diff --git a/include/linux/fib_rules.h b/include/linux/fib_rules.h index 51da65b6..9dcdb625 100644 --- a/include/linux/fib_rules.h +++ b/include/linux/fib_rules.h @@ -49,6 +49,8 @@ enum { FRA_TABLE, /* Extended table id */ FRA_FWMASK, /* mask for netfilter mark */ FRA_OIFNAME, + FRA_UID_START, /* UID range */ + FRA_UID_END, __FRA_MAX }; diff --git a/include/linux/freezer.h b/include/linux/freezer.h index 23fd3840..9f61f6d9 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -167,7 +167,7 @@ static inline bool freezer_should_skip(struct task_struct *p) __retval; \ }) -#define wait_event_freezable_exclusive(wq, condition) \ +#define wait_event_freezable_exclusive(wq, condition) \ ({ \ int __retval; \ freezer_do_not_count(); \ diff --git a/include/linux/frontswap.h b/include/linux/frontswap.h new file mode 100644 index 00000000..30442547 --- /dev/null +++ b/include/linux/frontswap.h @@ -0,0 +1,129 @@ +#ifndef _LINUX_FRONTSWAP_H +#define _LINUX_FRONTSWAP_H + +#include +#include +#include + +struct frontswap_ops { + void (*init)(unsigned); + int (*store)(unsigned, pgoff_t, struct page *); + int (*load)(unsigned, pgoff_t, struct page *); + void (*invalidate_page)(unsigned, pgoff_t); + void (*invalidate_area)(unsigned); +}; + +extern bool frontswap_enabled; +extern struct frontswap_ops + frontswap_register_ops(struct frontswap_ops *ops); +extern void frontswap_shrink(unsigned long); +extern unsigned long frontswap_curr_pages(void); +extern void frontswap_writethrough(bool); +#define FRONTSWAP_HAS_EXCLUSIVE_GETS +extern void frontswap_tmem_exclusive_gets(bool); + +extern void __frontswap_init(unsigned type); +extern int __frontswap_store(struct page *page); +extern int __frontswap_load(struct page *page); +extern void __frontswap_invalidate_page(unsigned, pgoff_t); +extern void __frontswap_invalidate_area(unsigned); + +#ifdef CONFIG_FRONTSWAP + +static inline bool frontswap_test(struct swap_info_struct *sis, pgoff_t offset) +{ + bool ret = false; + + if (frontswap_enabled && sis->frontswap_map) + ret = test_bit(offset, sis->frontswap_map); + return ret; +} + +static inline void frontswap_set(struct swap_info_struct *sis, pgoff_t offset) +{ + if (frontswap_enabled && sis->frontswap_map) + set_bit(offset, sis->frontswap_map); +} + +static inline void frontswap_clear(struct swap_info_struct *sis, pgoff_t offset) +{ + if (frontswap_enabled && sis->frontswap_map) + clear_bit(offset, sis->frontswap_map); +} + +static inline void frontswap_map_set(struct swap_info_struct *p, + unsigned long *map) +{ + p->frontswap_map = map; +} + +static inline unsigned long *frontswap_map_get(struct swap_info_struct *p) +{ + return p->frontswap_map; +} +#else +/* all inline routines become no-ops and all externs are ignored */ + +#define frontswap_enabled (0) + +static inline bool frontswap_test(struct swap_info_struct *sis, pgoff_t offset) +{ + return false; +} + +static inline void frontswap_set(struct swap_info_struct *sis, pgoff_t offset) +{ +} + +static inline void frontswap_clear(struct swap_info_struct *sis, pgoff_t offset) +{ +} + +static inline void frontswap_map_set(struct swap_info_struct *p, + unsigned long *map) +{ +} + +static inline unsigned long *frontswap_map_get(struct swap_info_struct *p) +{ + return NULL; +} +#endif + +static inline int frontswap_store(struct page *page) +{ + int ret = -1; + + if (frontswap_enabled) + ret = __frontswap_store(page); + return ret; +} + +static inline int frontswap_load(struct page *page) +{ + int ret = -1; + + if (frontswap_enabled) + ret = __frontswap_load(page); + return ret; +} + +static inline void frontswap_invalidate_page(unsigned type, pgoff_t offset) +{ + if (frontswap_enabled) + __frontswap_invalidate_page(type, offset); +} + +static inline void frontswap_invalidate_area(unsigned type) +{ + if (frontswap_enabled) + __frontswap_invalidate_area(type); +} + +static inline void frontswap_init(unsigned type) +{ + if (frontswap_enabled) + __frontswap_init(type); +} + +#endif /* _LINUX_FRONTSWAP_H */ diff --git a/include/linux/genlock.h b/include/linux/genlock.h index 587c49df..e2336629 100644 --- a/include/linux/genlock.h +++ b/include/linux/genlock.h @@ -8,6 +8,7 @@ struct genlock_handle; struct genlock_handle *genlock_get_handle(void); struct genlock_handle *genlock_get_handle_fd(int fd); +int genlock_get_fd_handle(struct genlock_handle *handle); void genlock_put_handle(struct genlock_handle *handle); struct genlock *genlock_create_lock(struct genlock_handle *); struct genlock *genlock_attach_lock(struct genlock_handle *, int fd); diff --git a/include/linux/ion.h b/include/linux/ion.h index 02aebb68..b47cb8aa 100644 --- a/include/linux/ion.h +++ b/include/linux/ion.h @@ -40,18 +40,15 @@ enum ion_heap_type { ION_HEAP_TYPE_SYSTEM, ION_HEAP_TYPE_SYSTEM_CONTIG, ION_HEAP_TYPE_CARVEOUT, - ION_HEAP_TYPE_IOMMU, - ION_HEAP_TYPE_CP, ION_HEAP_TYPE_DMA, ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always are at the end of this enum */ - ION_NUM_HEAPS = 16, + ION_NUM_HEAPS, }; #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_HEAP_CP_MASK (1 << ION_HEAP_TYPE_CP) #define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA) /** @@ -63,86 +60,6 @@ enum ion_heap_type { maintenance when the buffer is mapped for dma */ -/** - * These are the only ids that should be used for Ion heap ids. - * The ids listed are the order in which allocation will be attempted - * if specified. Don't swap the order of heap ids unless you know what - * you are doing! - * Id's are spaced by purpose to allow new Id's to be inserted in-between (for - * possible fallbacks) - */ - -enum ion_heap_ids { - INVALID_HEAP_ID = -1, - ION_CP_MM_HEAP_ID = 8, - ION_CP_MFC_HEAP_ID = 12, - ION_CP_WB_HEAP_ID = 16, /* 8660 only */ - ION_CAMERA_HEAP_ID = 20, /* 8660 only */ - ION_SF_HEAP_ID = 24, - ION_IOMMU_HEAP_ID = 25, - ION_QSECOM_HEAP_ID = 26, - ION_AUDIO_HEAP_BL_ID = 27, - ION_AUDIO_HEAP_ID = 28, - - ION_MM_FIRMWARE_HEAP_ID = 29, - ION_SYSTEM_HEAP_ID = 30, - - ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_SECURE flag */ -}; - -enum ion_fixed_position { - NOT_FIXED, - FIXED_LOW, - FIXED_MIDDLE, - FIXED_HIGH, -}; - -enum cp_mem_usage { - VIDEO_BITSTREAM = 0x1, - VIDEO_PIXEL = 0x2, - VIDEO_NONPIXEL = 0x3, - MAX_USAGE = 0x4, - UNKNOWN = 0x7FFFFFFF, -}; - -/** - * Flag to use when allocating to indicate that a heap is secure. - */ -#define ION_SECURE (1 << ION_HEAP_ID_RESERVED) - -/** - * Macro should be used with ion_heap_ids defined above. - */ -#define ION_HEAP(bit) (1 << (bit)) - -#define ION_VMALLOC_HEAP_NAME "vmalloc" -#define ION_AUDIO_HEAP_NAME "audio" -#define ION_AUDIO_BL_HEAP_NAME "bl_mem_audio" -#define ION_SF_HEAP_NAME "sf" -#define ION_MM_HEAP_NAME "mm" -#define ION_CAMERA_HEAP_NAME "camera_preview" -#define ION_IOMMU_HEAP_NAME "iommu" -#define ION_MFC_HEAP_NAME "mfc" -#define ION_WB_HEAP_NAME "wb" -#define ION_MM_FIRMWARE_HEAP_NAME "mm_fw" -#define ION_QSECOM_HEAP_NAME "qsecom" -#define ION_FMEM_HEAP_NAME "fmem" - -#define CACHED 1 -#define UNCACHED 0 - -#define ION_CACHE_SHIFT 0 - -#define ION_SET_CACHE(__cache) ((__cache) << ION_CACHE_SHIFT) - -#define ION_IS_CACHED(__flags) ((__flags) & (1 << ION_CACHE_SHIFT)) - -/* - * This flag allows clients when mapping into the IOMMU to specify to - * defer un-mapping from the IOMMU until the buffer memory is freed. - */ -#define ION_IOMMU_UNMAP_DELAYED 1 - #ifdef __KERNEL__ #include #include @@ -184,73 +101,6 @@ struct ion_platform_heap { void *priv; }; -/** - * struct ion_cp_heap_pdata - defines a content protection heap in the given - * platform - * @permission_type: Memory ID used to identify the memory to TZ - * @align: Alignment requirement for the memory - * @secure_base: Base address for securing the heap. - * Note: This might be different from actual base address - * of this heap in the case of a shared heap. - * @secure_size: Memory size for securing the heap. - * Note: This might be different from actual size - * of this heap in the case of a shared heap. - * @reusable Flag indicating whether this heap is reusable of not. - * (see FMEM) - * @mem_is_fmem Flag indicating whether this memory is coming from fmem - * or not. - * @fixed_position If nonzero, position in the fixed area. - * @virt_addr: Virtual address used when using fmem. - * @iommu_map_all: Indicates whether we should map whole heap into IOMMU. - * @iommu_2x_map_domain: Indicates the domain to use for overmapping. - * @request_region: function to be called when the number of allocations - * goes from 0 -> 1 - * @release_region: function to be called when the number of allocations - * goes from 1 -> 0 - * @setup_region: function to be called upon ion registration - * - */ -struct ion_cp_heap_pdata { - enum ion_permission_type permission_type; - unsigned int align; - ion_phys_addr_t secure_base; /* Base addr used when heap is shared */ - size_t secure_size; /* Size used for securing heap when heap is shared*/ - int reusable; - int mem_is_fmem; - int is_cma; - enum ion_fixed_position fixed_position; - int iommu_map_all; - int iommu_2x_map_domain; - ion_virt_addr_t *virt_addr; - int (*request_region)(void *); - int (*release_region)(void *); - void *(*setup_region)(void); -}; - -/** - * struct ion_co_heap_pdata - defines a carveout heap in the given platform - * @adjacent_mem_id: Id of heap that this heap must be adjacent to. - * @align: Alignment requirement for the memory - * @mem_is_fmem Flag indicating whether this memory is coming from fmem - * or not. - * @fixed_position If nonzero, position in the fixed area. - * @request_region: function to be called when the number of allocations - * goes from 0 -> 1 - * @release_region: function to be called when the number of allocations - * goes from 1 -> 0 - * @setup_region: function to be called upon ion registration - * - */ -struct ion_co_heap_pdata { - int adjacent_mem_id; - unsigned int align; - int mem_is_fmem; - enum ion_fixed_position fixed_position; - int (*request_region)(void *); - int (*release_region)(void *); - void *(*setup_region)(void); -}; - /** * struct ion_platform_data - array of platform heaps passed from board file * @has_outer_cache: set to 1 if outer cache is used, 0 otherwise. @@ -321,14 +171,18 @@ void ion_client_destroy(struct ion_client *client); * @len: size of the allocation * @align: requested allocation alignment, lots of hardware blocks have * alignment requirements of some kind - * @flags: mask of heaps to allocate from, if multiple bits are set + * @heap_mask: mask of heaps to allocate from, if multiple bits are set * heaps will be tried in order from lowest to highest order bit + * @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 flags); + size_t align, unsigned int heap_mask, + unsigned int flags); /** * ion_free - free a handle @@ -379,8 +233,7 @@ struct sg_table *ion_sg_table(struct ion_client *client, * can be used to access this address. If no flags are specified, this * will return a non-secure uncached mapping. */ -void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle, - unsigned long flags); +void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle); /** * ion_unmap_kernel() - destroy a kernel mapping for a handle @@ -508,50 +361,6 @@ int ion_secure_heap(struct ion_device *dev, int heap_id, int version, int ion_unsecure_heap(struct ion_device *dev, int heap_id, int version, void *data); -/** - * msm_ion_secure_heap - secure a heap. Wrapper around ion_secure_heap. - * - * @heap_id - heap id to secure. - * - * Secure a heap - * Returns 0 on success - */ -int msm_ion_secure_heap(int heap_id); - -/** - * msm_ion_unsecure_heap - unsecure a heap. Wrapper around ion_unsecure_heap. - * - * @heap_id - heap id to secure. - * - * Un-secure a heap - * Returns 0 on success - */ -int msm_ion_unsecure_heap(int heap_id); - -/** - * msm_ion_secure_heap_2_0 - secure a heap using 2.0 APIs - * Wrapper around ion_secure_heap. - * - * @heap_id - heap id to secure. - * @usage - usage hint to TZ - * - * Secure a heap - * Returns 0 on success - */ -int msm_ion_secure_heap_2_0(int heap_id, enum cp_mem_usage usage); - -/** - * msm_ion_unsecure_heap - unsecure a heap secured with 3.0 APIs. - * Wrapper around ion_unsecure_heap. - * - * @heap_id - heap id to secure. - * @usage - usage hint to TZ - * - * Un-secure a heap - * Returns 0 on success - */ -int msm_ion_unsecure_heap_2_0(int heap_id, enum cp_mem_usage usage); - /** * msm_ion_do_cache_op - do cache operations. * @@ -590,7 +399,9 @@ static inline struct ion_client *msm_ion_client_create(unsigned int heap_mask, static inline void ion_client_destroy(struct ion_client *client) { } static inline struct ion_handle *ion_alloc(struct ion_client *client, - size_t len, size_t align, unsigned int flags) + size_t len, size_t align, + unsigned int heap_mask, + unsigned int flags) { return ERR_PTR(-ENODEV); } @@ -667,28 +478,6 @@ static inline int ion_unsecure_heap(struct ion_device *dev, int heap_id, return -ENODEV; } -static inline int msm_ion_secure_heap(int heap_id) -{ - return -ENODEV; - -} - -static inline int msm_ion_unsecure_heap(int heap_id) -{ - return -ENODEV; -} - -static inline int msm_ion_secure_heap_2_0(int heap_id, enum cp_mem_usage usage) -{ - return -ENODEV; -} - -static inline int msm_ion_unsecure_heap_2_0(int heap_id, - enum cp_mem_usage usage) -{ - return -ENODEV; -} - static inline int msm_ion_do_cache_op(struct ion_client *client, struct ion_handle *handle, void *vaddr, unsigned long len, unsigned int cmd) @@ -711,6 +500,7 @@ static inline int msm_ion_do_cache_op(struct ion_client *client, * struct ion_allocation_data - metadata passed from userspace for allocations * @len: size of the allocation * @align: required alignment of the allocation + * @heap_mask: mask of heaps to allocate from * @flags: flags passed to heap * @handle: pointer that will be populated with a cookie to use to refer * to this allocation @@ -718,6 +508,14 @@ static inline int msm_ion_do_cache_op(struct ion_client *client, * Provided by userspace as an argument to the ioctl */ struct ion_allocation_data { + size_t len; + size_t align; + unsigned int heap_mask; + unsigned int flags; + struct ion_handle *handle; +}; + +struct ion_allocation_data_old { size_t len; size_t align; unsigned int flags; @@ -759,41 +557,6 @@ struct ion_custom_data { unsigned int cmd; unsigned long arg; }; - - -/* struct ion_flush_data - data passed to ion for flushing caches - * - * @handle: handle with data to flush - * @fd: fd to flush - * @vaddr: userspace virtual address mapped with mmap - * @offset: offset into the handle to flush - * @length: length of handle to flush - * - * Performs cache operations on the handle. If p is the start address - * of the handle, p + offset through p + offset + length will have - * the cache operations performed - */ -struct ion_flush_data { - struct ion_handle *handle; - int fd; - void *vaddr; - unsigned int offset; - unsigned int length; -}; - -/* struct ion_flag_data - information about flags for this buffer - * - * @handle: handle to get flags from - * @flags: flags of this handle - * - * Takes handle as an input and outputs the flags from the handle - * in the flag field. - */ -struct ion_flag_data { - struct ion_handle *handle; - unsigned long flags; -}; - #define ION_IOC_MAGIC 'I' /** @@ -804,6 +567,8 @@ struct ion_flag_data { */ #define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ struct ion_allocation_data) +#define ION_IOC_ALLOC_COMPAT _IOWR(ION_IOC_MAGIC, 0, \ + struct ion_allocation_data_old) /** * DOC: ION_IOC_FREE - free memory @@ -840,7 +605,7 @@ struct ion_flag_data { * 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, int) +#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data) /** * DOC: ION_IOC_CUSTOM - call architecture specific ion ioctl @@ -851,26 +616,27 @@ struct ion_flag_data { #define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data) +/* For compat with old-API blobs. These were moved to the MSM header */ /** * DOC: ION_IOC_CLEAN_CACHES - clean the caches * * Clean the caches of the handle specified. */ -#define ION_IOC_CLEAN_CACHES _IOWR(ION_IOC_MAGIC, 7, \ +#define ION_IOC_CLEAN_CACHES_COMPAT _IOWR(ION_IOC_MAGIC, 7, \ struct ion_flush_data) /** * DOC: ION_MSM_IOC_INV_CACHES - invalidate the caches * * Invalidate the caches of the handle specified. */ -#define ION_IOC_INV_CACHES _IOWR(ION_IOC_MAGIC, 8, \ +#define ION_IOC_INV_CACHES_COMPAT _IOWR(ION_IOC_MAGIC, 8, \ struct ion_flush_data) /** * DOC: ION_MSM_IOC_CLEAN_CACHES - clean and invalidate the caches * * Clean and invalidate the caches of the handle specified. */ -#define ION_IOC_CLEAN_INV_CACHES _IOWR(ION_IOC_MAGIC, 9, \ +#define ION_IOC_CLEAN_INV_CACHES_COMPAT _IOWR(ION_IOC_MAGIC, 9, \ struct ion_flush_data) /** @@ -879,15 +645,8 @@ struct ion_flag_data { * Gets the flags of the current handle which indicate cachability, * secure state etc. */ -#define ION_IOC_GET_FLAGS _IOWR(ION_IOC_MAGIC, 10, \ +#define ION_IOC_GET_FLAGS_COMPAT _IOWR(ION_IOC_MAGIC, 10, \ struct ion_flag_data) -/** - * DOC: ION_IOC_SYNC - BOGUS - * - * NOT SUPPORTED - */ -#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 42, \ - struct ion_flag_data) #endif /* _LINUX_ION_H */ diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index fad48aab..9323a986 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -40,6 +40,11 @@ struct lsm_network_audit { } fam; }; +struct lsm_ioctlop_audit { + struct path path; + u16 cmd; +}; + /* Auxiliary data to use in generating the audit record. */ struct common_audit_data { char type; @@ -53,6 +58,7 @@ struct common_audit_data { #define LSM_AUDIT_DATA_KMOD 8 #define LSM_AUDIT_DATA_INODE 9 #define LSM_AUDIT_DATA_DENTRY 10 +#define LSM_AUDIT_DATA_IOCTL_OP 11 struct task_struct *tsk; union { struct path path; @@ -69,6 +75,7 @@ struct common_audit_data { } key_struct; #endif char *kmod_name; + struct lsm_ioctlop_audit *op; } u; /* this union contains LSM specific data */ union { diff --git a/include/linux/msm_ion.h b/include/linux/msm_ion.h index 0e28e541..599e4362 100644 --- a/include/linux/msm_ion.h +++ b/include/linux/msm_ion.h @@ -1,7 +1,6 @@ /* - * include/linux/ion.h * - * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -19,4 +18,316 @@ #include +enum msm_ion_heap_types { + ION_HEAP_TYPE_MSM_START = ION_HEAP_TYPE_CUSTOM + 1, + ION_HEAP_TYPE_IOMMU = ION_HEAP_TYPE_MSM_START, + ION_HEAP_TYPE_CP, +}; + +/** + * These are the only ids that should be used for Ion heap ids. + * The ids listed are the order in which allocation will be attempted + * if specified. Don't swap the order of heap ids unless you know what + * you are doing! + * Id's are spaced by purpose to allow new Id's to be inserted in-between (for + * possible fallbacks) + */ + +enum ion_heap_ids { + INVALID_HEAP_ID = -1, + ION_CP_MM_HEAP_ID = 8, + ION_CP_MFC_HEAP_ID = 12, + ION_CP_WB_HEAP_ID = 16, /* 8660 only */ + ION_CAMERA_HEAP_ID = 20, /* 8660 only */ + ION_SYSTEM_CONTIG_HEAP_ID = 21, + ION_ADSP_HEAP_ID = 22, + ION_SF_HEAP_ID = 24, + ION_IOMMU_HEAP_ID = 25, + ION_QSECOM_HEAP_ID = 26, + ION_AUDIO_HEAP_BL_ID = 27, + ION_AUDIO_HEAP_ID = 28, + + ION_MM_FIRMWARE_HEAP_ID = 29, + ION_SYSTEM_HEAP_ID = 30, + + ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_SECURE flag */ +}; + +enum ion_fixed_position { + NOT_FIXED, + FIXED_LOW, + FIXED_MIDDLE, + FIXED_HIGH, +}; + +enum cp_mem_usage { + VIDEO_BITSTREAM = 0x1, + VIDEO_PIXEL = 0x2, + VIDEO_NONPIXEL = 0x3, + MAX_USAGE = 0x4, + UNKNOWN = 0x7FFFFFFF, +}; + +#define ION_HEAP_CP_MASK (1 << ION_HEAP_TYPE_CP) + +/** + * Flag to use when allocating to indicate that a heap is secure. + */ +#define ION_FLAG_SECURE (1 << ION_HEAP_ID_RESERVED) + +/** + * Flag for clients to force contiguous memort allocation + * + * Use of this flag is carefully monitored! + */ +#define ION_FLAG_FORCE_CONTIGUOUS (1 << 30) + +/** +* Deprecated! Please use the corresponding ION_FLAG_* +*/ +#define ION_SECURE ION_FLAG_SECURE +#define ION_FORCE_CONTIGUOUS ION_FLAG_FORCE_CONTIGUOUS + +/** + * Macro should be used with ion_heap_ids defined above. + */ +#define ION_HEAP(bit) (1 << (bit)) + +#define ION_ADSP_HEAP_NAME "adsp" +#define ION_VMALLOC_HEAP_NAME "vmalloc" +#define ION_KMALLOC_HEAP_NAME "kmalloc" +#define ION_AUDIO_HEAP_NAME "audio" +#define ION_AUDIO_BL_HEAP_NAME "bl_mem_audio" +#define ION_SF_HEAP_NAME "sf" +#define ION_MM_HEAP_NAME "mm" +#define ION_CAMERA_HEAP_NAME "camera_preview" +#define ION_IOMMU_HEAP_NAME "iommu" +#define ION_MFC_HEAP_NAME "mfc" +#define ION_WB_HEAP_NAME "wb" +#define ION_MM_FIRMWARE_HEAP_NAME "mm_fw" +#define ION_QSECOM_HEAP_NAME "qsecom" +#define ION_FMEM_HEAP_NAME "fmem" + +#define ION_SET_CACHED(__cache) (__cache | ION_FLAG_CACHED) +#define ION_SET_UNCACHED(__cache) (__cache & ~ION_FLAG_CACHED) + +#define ION_IS_CACHED(__flags) ((__flags) & ION_FLAG_CACHED) + +#ifdef __KERNEL__ + +/* + * This flag allows clients when mapping into the IOMMU to specify to + * defer un-mapping from the IOMMU until the buffer memory is freed. + */ +#define ION_IOMMU_UNMAP_DELAYED 1 + +/** + * struct ion_cp_heap_pdata - defines a content protection heap in the given + * platform + * @permission_type: Memory ID used to identify the memory to TZ + * @align: Alignment requirement for the memory + * @secure_base: Base address for securing the heap. + * Note: This might be different from actual base address + * of this heap in the case of a shared heap. + * @secure_size: Memory size for securing the heap. + * Note: This might be different from actual size + * of this heap in the case of a shared heap. + * @reusable Flag indicating whether this heap is reusable of not. + * (see FMEM) + * @mem_is_fmem Flag indicating whether this memory is coming from fmem + * or not. + * @fixed_position If nonzero, position in the fixed area. + * @virt_addr: Virtual address used when using fmem. + * @iommu_map_all: Indicates whether we should map whole heap into IOMMU. + * @iommu_2x_map_domain: Indicates the domain to use for overmapping. + * @request_region: function to be called when the number of allocations + * goes from 0 -> 1 + * @release_region: function to be called when the number of allocations + * goes from 1 -> 0 + * @setup_region: function to be called upon ion registration + * @memory_type:Memory type used for the heap + * @no_nonsecure_alloc: don't allow non-secure allocations from this heap + * + */ +struct ion_cp_heap_pdata { + enum ion_permission_type permission_type; + unsigned int align; + ion_phys_addr_t secure_base; /* Base addr used when heap is shared */ + size_t secure_size; /* Size used for securing heap when heap is shared*/ + int reusable; + int mem_is_fmem; + int is_cma; + enum ion_fixed_position fixed_position; + int iommu_map_all; + int iommu_2x_map_domain; + ion_virt_addr_t *virt_addr; + int (*request_region)(void *); + int (*release_region)(void *); + void *(*setup_region)(void); + enum ion_memory_types memory_type; + int no_nonsecure_alloc; +}; + +/** + * struct ion_co_heap_pdata - defines a carveout heap in the given platform + * @adjacent_mem_id: Id of heap that this heap must be adjacent to. + * @align: Alignment requirement for the memory + * @mem_is_fmem Flag indicating whether this memory is coming from fmem + * or not. + * @fixed_position If nonzero, position in the fixed area. + * @request_region: function to be called when the number of allocations + * goes from 0 -> 1 + * @release_region: function to be called when the number of allocations + * goes from 1 -> 0 + * @setup_region: function to be called upon ion registration + * @memory_type:Memory type used for the heap + * + */ +struct ion_co_heap_pdata { + int adjacent_mem_id; + unsigned int align; + int mem_is_fmem; + enum ion_fixed_position fixed_position; + int (*request_region)(void *); + int (*release_region)(void *); + void *(*setup_region)(void); + enum ion_memory_types memory_type; +}; + +#ifdef CONFIG_ION +/** + * msm_ion_secure_heap - secure a heap. Wrapper around ion_secure_heap. + * + * @heap_id - heap id to secure. + * + * Secure a heap + * Returns 0 on success + */ +int msm_ion_secure_heap(int heap_id); + +/** + * msm_ion_unsecure_heap - unsecure a heap. Wrapper around ion_unsecure_heap. + * + * @heap_id - heap id to secure. + * + * Un-secure a heap + * Returns 0 on success + */ +int msm_ion_unsecure_heap(int heap_id); + +/** + * msm_ion_secure_heap_2_0 - secure a heap using 2.0 APIs + * Wrapper around ion_secure_heap. + * + * @heap_id - heap id to secure. + * @usage - usage hint to TZ + * + * Secure a heap + * Returns 0 on success + */ +int msm_ion_secure_heap_2_0(int heap_id, enum cp_mem_usage usage); + +/** + * msm_ion_unsecure_heap - unsecure a heap secured with 3.0 APIs. + * Wrapper around ion_unsecure_heap. + * + * @heap_id - heap id to secure. + * @usage - usage hint to TZ + * + * Un-secure a heap + * Returns 0 on success + */ +int msm_ion_unsecure_heap_2_0(int heap_id, enum cp_mem_usage usage); +#else +static inline int msm_ion_secure_heap(int heap_id) +{ + return -ENODEV; + +} + +static inline int msm_ion_unsecure_heap(int heap_id) +{ + return -ENODEV; +} + +static inline int msm_ion_secure_heap_2_0(int heap_id, enum cp_mem_usage usage) +{ + return -ENODEV; +} + +static inline int msm_ion_unsecure_heap_2_0(int heap_id, + enum cp_mem_usage usage) +{ + return -ENODEV; +} +#endif /* CONFIG_ION */ + +#endif /* __KERNEL */ + +/* struct ion_flush_data - data passed to ion for flushing caches + * + * @handle: handle with data to flush + * @fd: fd to flush + * @vaddr: userspace virtual address mapped with mmap + * @offset: offset into the handle to flush + * @length: length of handle to flush + * + * Performs cache operations on the handle. If p is the start address + * of the handle, p + offset through p + offset + length will have + * the cache operations performed + */ +struct ion_flush_data { + struct ion_handle *handle; + int fd; + void *vaddr; + unsigned int offset; + unsigned int length; +}; + +/* struct ion_flag_data - information about flags for this buffer + * + * @handle: handle to get flags from + * @flags: flags of this handle + * + * Takes handle as an input and outputs the flags from the handle + * in the flag field. + */ +struct ion_flag_data { + struct ion_handle *handle; + unsigned long flags; +}; + +#define ION_IOC_MSM_MAGIC 'M' + +/** + * DOC: ION_IOC_CLEAN_CACHES - clean the caches + * + * Clean the caches of the handle specified. + */ +#define ION_IOC_CLEAN_CACHES _IOWR(ION_IOC_MSM_MAGIC, 0, \ + struct ion_flush_data) +/** + * DOC: ION_IOC_INV_CACHES - invalidate the caches + * + * Invalidate the caches of the handle specified. + */ +#define ION_IOC_INV_CACHES _IOWR(ION_IOC_MSM_MAGIC, 1, \ + struct ion_flush_data) +/** + * DOC: ION_IOC_CLEAN_INV_CACHES - clean and invalidate the caches + * + * Clean and invalidate the caches of the handle specified. + */ +#define ION_IOC_CLEAN_INV_CACHES _IOWR(ION_IOC_MSM_MAGIC, 2, \ + struct ion_flush_data) + +/** + * DOC: ION_IOC_GET_FLAGS - get the flags of the handle + * + * Gets the flags of the current handle which indicate cachability, + * secure state etc. + */ +#define ION_IOC_GET_FLAGS _IOWR(ION_IOC_MSM_MAGIC, 3, \ + struct ion_flag_data) + #endif diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index 1449b68b..f4898397 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -1,7 +1,7 @@ /* include/linux/msm_mdp.h * * Copyright (C) 2007 Google Incorporated - * Copyright (c) 2012 The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -70,7 +70,16 @@ #define MSMFB_MDP_PP _IOWR(MSMFB_IOCTL_MAGIC, 156, struct msmfb_mdp_pp) #define MSMFB_OVERLAY_VSYNC_CTRL _IOW(MSMFB_IOCTL_MAGIC, 160, unsigned int) #define MSMFB_VSYNC_CTRL _IOW(MSMFB_IOCTL_MAGIC, 161, unsigned int) -#define MSMFB_METADATA_SET _IOW(MSMFB_IOCTL_MAGIC, 162, struct msmfb_metadata) +#define MSMFB_BUFFER_SYNC _IOW(MSMFB_IOCTL_MAGIC, 162, struct mdp_buf_sync) +#define MSMFB_OVERLAY_COMMIT _IO(MSMFB_IOCTL_MAGIC, 163) +#define MSMFB_DISPLAY_COMMIT _IOW(MSMFB_IOCTL_MAGIC, 164, \ + struct mdp_display_commit) +#define MSMFB_METADATA_SET _IOW(MSMFB_IOCTL_MAGIC, 165, struct msmfb_metadata) +#define MSMFB_METADATA_GET _IOW(MSMFB_IOCTL_MAGIC, 166, struct msmfb_metadata) +#define MSMFB_WRITEBACK_SET_MIRRORING_HINT _IOW(MSMFB_IOCTL_MAGIC, 167, \ + unsigned int) +#define MSMFB_ASYNC_BLIT _IOW(MSMFB_IOCTL_MAGIC, 168, unsigned int) + #define FB_TYPE_3D_PANEL 0x10101010 #define MDP_IMGTYPE2_START 0x10000 #define MSMFB_DRIVER_VERSION 0xF9E8D701 @@ -89,6 +98,7 @@ enum { MDP_RGB_888, /* RGB 888 planer */ MDP_Y_CRCB_H2V2, /* Y and CrCb, pseudo planer w/ Cr is in MSB */ MDP_YCRYCB_H2V1, /* YCrYCb interleave */ + MDP_CBYCRY_H2V1, /* CbYCrY interleave */ MDP_Y_CRCB_H2V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ MDP_Y_CBCR_H2V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ MDP_Y_CRCB_H1V2, @@ -106,6 +116,18 @@ enum { MDP_YCRCB_H1V1, /* YCrCb interleave */ MDP_YCBCR_H1V1, /* YCbCr interleave */ MDP_BGR_565, /* BGR 565 planer */ + MDP_BGR_888, /* BGR 888 */ + MDP_Y_CBCR_H2V2_VENUS, + MDP_BGRX_8888, /* BGRX 8888 */ + MDP_RGBA_8888_TILE, /* RGBA 8888 in tile format */ + MDP_ARGB_8888_TILE, /* ARGB 8888 in tile format */ + MDP_ABGR_8888_TILE, /* ABGR 8888 in tile format */ + MDP_BGRA_8888_TILE, /* BGRA 8888 in tile format */ + MDP_RGBX_8888_TILE, /* RGBX 8888 in tile format */ + MDP_XRGB_8888_TILE, /* XRGB 8888 in tile format */ + MDP_XBGR_8888_TILE, /* XBGR 8888 in tile format */ + MDP_BGRX_8888_TILE, /* BGRX 8888 in tile format */ + MDP_YCBYCR_H2V1, /* YCbYCr interleave */ MDP_IMGTYPE_LIMIT, MDP_RGB_BORDERFILL, /* border fill pipe */ MDP_FB_FORMAT = MDP_IMGTYPE2_START, /* framebuffer format */ @@ -127,6 +149,7 @@ enum { #define MDSS_MDP_ROT_ONLY 0x80 #define MDSS_MDP_RIGHT_MIXER 0x100 +#define MDSS_MDP_DUAL_PIPE 0x200 /* mdp_blit_req flag values */ #define MDP_ROT_NOP 0 @@ -154,12 +177,14 @@ enum { #define MDP_DEINTERLACE_ODD 0x00400000 #define MDP_OV_PLAY_NOWAIT 0x00200000 #define MDP_SOURCE_ROTATED_90 0x00100000 -#define MDP_DPP_HSIC 0x00080000 +#define MDP_OVERLAY_PP_CFG_EN 0x00080000 #define MDP_BACKEND_COMPOSITION 0x00040000 #define MDP_BORDERFILL_SUPPORTED 0x00010000 #define MDP_SECURE_OVERLAY_SESSION 0x00008000 +#define MDP_OV_PIPE_FORCE_DMA 0x00004000 #define MDP_MEMORY_ID_TYPE_FB 0x00001000 - +#define MDP_BWC_EN 0x00000400 +#define MDP_DECIMATION_EN 0x00000800 #define MDP_TRANSP_NOP 0xffffffff #define MDP_ALPHA_NOP 0xff @@ -271,15 +296,132 @@ struct msmfb_writeback_data { struct msmfb_img img; }; -struct dpp_ctrl { - /* - *'sharp_strength' has inputs = -128 <-> 127 - * Increasingly positive values correlate with increasingly sharper - * picture. Increasingly negative values correlate with increasingly - * smoothed picture. - */ - int8_t sharp_strength; - int8_t hsic_params[NUM_HSIC_PARAM]; +#define MDP_PP_OPS_ENABLE 0x1 +#define MDP_PP_OPS_READ 0x2 +#define MDP_PP_OPS_WRITE 0x4 +#define MDP_PP_OPS_DISABLE 0x8 +#define MDP_PP_IGC_FLAG_ROM0 0x10 +#define MDP_PP_IGC_FLAG_ROM1 0x20 + +#define MDSS_PP_DSPP_CFG 0x000 +#define MDSS_PP_SSPP_CFG 0x100 +#define MDSS_PP_LM_CFG 0x200 +#define MDSS_PP_WB_CFG 0x300 + +#define MDSS_PP_ARG_MASK 0x3C00 +#define MDSS_PP_ARG_NUM 4 +#define MDSS_PP_ARG_SHIFT 10 +#define MDSS_PP_LOCATION_MASK 0x0300 +#define MDSS_PP_LOGICAL_MASK 0x00FF + +#define MDSS_PP_ADD_ARG(var, arg) ((var) | (0x1 << (MDSS_PP_ARG_SHIFT + (arg)))) +#define PP_ARG(x, var) ((var) & (0x1 << (MDSS_PP_ARG_SHIFT + (x)))) +#define PP_LOCAT(var) ((var) & MDSS_PP_LOCATION_MASK) +#define PP_BLOCK(var) ((var) & MDSS_PP_LOGICAL_MASK) + +struct mdp_qseed_cfg { + uint32_t table_num; + uint32_t ops; + uint32_t len; + uint32_t *data; +}; + +struct mdp_qseed_cfg_data { + uint32_t block; + struct mdp_qseed_cfg qseed_data; +}; + +struct mdp_sharp_cfg { + uint32_t flags; + uint32_t strength; + uint32_t edge_thr; + uint32_t smooth_thr; + uint32_t noise_thr; +}; + +#define MDP_OVERLAY_PP_CSC_CFG 0x1 +#define MDP_OVERLAY_PP_QSEED_CFG 0x2 +#define MDP_OVERLAY_PP_PA_CFG 0x4 +#define MDP_OVERLAY_PP_IGC_CFG 0x8 +#define MDP_OVERLAY_PP_SHARP_CFG 0x10 + +#define MDP_CSC_FLAG_ENABLE 0x1 +#define MDP_CSC_FLAG_YUV_IN 0x2 +#define MDP_CSC_FLAG_YUV_OUT 0x4 + +struct mdp_csc_cfg { + /* flags for enable CSC, toggling RGB,YUV input/output */ + uint32_t flags; + uint32_t csc_mv[9]; + uint32_t csc_pre_bv[3]; + uint32_t csc_post_bv[3]; + uint32_t csc_pre_lv[6]; + uint32_t csc_post_lv[6]; +}; + +struct mdp_csc_cfg_data { + uint32_t block; + struct mdp_csc_cfg csc_data; +}; + +struct mdp_pa_cfg { + uint32_t flags; + uint32_t hue_adj; + uint32_t sat_adj; + uint32_t val_adj; + uint32_t cont_adj; +}; + +struct mdp_igc_lut_data { + uint32_t block; + uint32_t len, ops; + uint32_t *c0_c1_data; + uint32_t *c2_data; +}; + +struct mdp_histogram_cfg { + uint32_t ops; + uint32_t block; + uint8_t frame_cnt; + uint8_t bit_mask; + uint16_t num_bins; +}; + +struct mdp_overlay_pp_params { + uint32_t config_ops; + struct mdp_csc_cfg csc_cfg; + struct mdp_qseed_cfg qseed_cfg[2]; + struct mdp_pa_cfg pa_cfg; + struct mdp_igc_lut_data igc_cfg; + struct mdp_sharp_cfg sharp_cfg; + struct mdp_histogram_cfg hist_cfg; +}; + +/** + * enum mdss_mdp_blend_op - Different blend operations set by userspace + * + * @BLEND_OP_NOT_DEFINED: No blend operation defined for the layer. + * @BLEND_OP_OPAQUE: Apply a constant blend operation. The layer + * would appear opaque in case fg plane alpha is + * 0xff. + * @BLEND_OP_PREMULTIPLIED: Apply source over blend rule. Layer already has + * alpha pre-multiplication done. If fg plane alpha + * is less than 0xff, apply modulation as well. This + * operation is intended on layers having alpha + * channel. + * @BLEND_OP_COVERAGE: Apply source over blend rule. Layer is not alpha + * pre-multiplied. Apply pre-multiplication. If fg + * plane alpha is less than 0xff, apply modulation as + * well. + * @BLEND_OP_MAX: Used to track maximum blend operation possible by + * mdp. + */ +enum { + BLEND_OP_NOT_DEFINED = 0, + BLEND_OP_OPAQUE, + BLEND_OP_PREMULTIPLIED, + BLEND_OP_COVERAGE, + BLEND_OP_MAX, }; struct mdp_overlay { @@ -290,10 +432,11 @@ struct mdp_overlay { uint32_t is_fg; /* control alpha & transp */ uint32_t alpha; uint32_t transp_mask; + uint32_t blend_op; uint32_t flags; uint32_t id; uint32_t user_data[8]; - struct dpp_ctrl dpp; + struct mdp_overlay_pp_params overlay_pp_cfg; }; struct msmfb_overlay_3d { @@ -319,15 +462,44 @@ struct mdp_histogram { uint32_t *b; }; +enum { + DISPLAY_MISR_EDP, + DISPLAY_MISR_DSI0, + DISPLAY_MISR_DSI1, + DISPLAY_MISR_HDMI, + DISPLAY_MISR_LCDC, + DISPLAY_MISR_ATV, + DISPLAY_MISR_DSI_CMD, + DISPLAY_MISR_MAX +}; + +enum { + MISR_OP_NONE, + MISR_OP_SFM, + MISR_OP_MFM, + MISR_OP_BM, + MISR_OP_MAX +}; + +struct mdp_misr { + uint32_t block_id; + uint32_t frame_count; + uint32_t crc_op_mode; + uint32_t crc_value[32]; +}; + /* - mdp_block_type defines the identifiers for each of pipes in MDP 4.3 + mdp_block_type defines the identifiers for pipes in MDP 4.3 and up MDP_BLOCK_RESERVED is provided for backward compatibility and is deprecated. It corresponds to DMA_P. So MDP_BLOCK_DMA_P should be used instead. + MDP_LOGICAL_BLOCK_DISP_0 identifies the display pipe which fb0 uses, + same for others. + */ enum { @@ -342,6 +514,9 @@ enum { MDP_BLOCK_DMA_S, MDP_BLOCK_DMA_E, MDP_BLOCK_OVERLAY_2, + MDP_LOGICAL_BLOCK_DISP_0 = 0x1000, + MDP_LOGICAL_BLOCK_DISP_1, + MDP_LOGICAL_BLOCK_DISP_2, MDP_BLOCK_MAX, }; @@ -381,25 +556,6 @@ struct mdp_pcc_cfg_data { struct mdp_pcc_coeff r, g, b; }; -#define MDP_CSC_FLAG_ENABLE 0x1 -#define MDP_CSC_FLAG_YUV_IN 0x2 -#define MDP_CSC_FLAG_YUV_OUT 0x4 - -struct mdp_csc_cfg { - /* flags for enable CSC, toggling RGB,YUV input/output */ - uint32_t flags; - uint32_t csc_mv[9]; - uint32_t csc_pre_bv[3]; - uint32_t csc_post_bv[3]; - uint32_t csc_pre_lv[6]; - uint32_t csc_post_lv[6]; -}; - -struct mdp_csc_cfg_data { - uint32_t block; - struct mdp_csc_cfg csc_data; -}; - enum { mdp_lut_igc, mdp_lut_pgc, @@ -407,14 +563,6 @@ enum { mdp_lut_max, }; - -struct mdp_igc_lut_data { - uint32_t block; - uint32_t len, ops; - uint32_t *c0_c1_data; - uint32_t *c2_data; -}; - struct mdp_ar_gc_lut_data { uint32_t x_start; uint32_t slope; @@ -449,25 +597,30 @@ struct mdp_lut_cfg_data { } data; }; -struct mdp_qseed_cfg_data { - uint32_t block; - uint32_t table_num; - uint32_t ops; - uint32_t len; - uint32_t *data; -}; - struct mdp_bl_scale_data { uint32_t min_lvl; uint32_t scale; }; +struct mdp_calib_config_data { + uint32_t ops; + uint32_t addr; + uint32_t data; +}; + +struct mdp_pa_cfg_data { + uint32_t block; + struct mdp_pa_cfg pa_data; +}; + enum { mdp_op_pcc_cfg, mdp_op_csc_cfg, mdp_op_lut_cfg, mdp_op_qseed_cfg, mdp_bl_scale_cfg, + mdp_op_calib_cfg, + mdp_op_pa_cfg, mdp_op_max, }; @@ -479,12 +632,15 @@ struct msmfb_mdp_pp { struct mdp_lut_cfg_data lut_cfg_data; struct mdp_qseed_cfg_data qseed_cfg_data; struct mdp_bl_scale_data bl_scale_data; + struct mdp_calib_config_data calib_cfg; + struct mdp_pa_cfg_data pa_cfg_data; } data; }; enum { metadata_op_none, metadata_op_base_blend, + metadata_op_frame_rate, metadata_op_max }; @@ -492,13 +648,46 @@ struct mdp_blend_cfg { uint32_t is_premultiplied; }; +struct mdp_mixer_cfg { + uint32_t writeback_format; + uint32_t alpha; +}; + struct msmfb_metadata { uint32_t op; uint32_t flags; union { struct mdp_blend_cfg blend_cfg; + uint32_t panel_frame_rate; } data; }; + +#define MDP_MAX_FENCE_FD 32 +#define MDP_BUF_SYNC_FLAG_WAIT 1 + +struct mdp_buf_sync { + uint32_t flags; + uint32_t acq_fen_fd_cnt; + uint32_t session_id; + int *acq_fen_fd; + int *rel_fen_fd; + int *retire_fen_fd; +}; + +struct mdp_async_blit_req_list { + struct mdp_buf_sync sync; + uint32_t count; + struct mdp_blit_req req[]; +}; + +#define MDP_DISPLAY_COMMIT_OVERLAY 0x00000001 + +struct mdp_display_commit { + uint32_t flags; + uint32_t wait_for_finish; + struct fb_var_screeninfo var; +}; + struct mdp_page_protection { uint32_t page_protection; }; @@ -512,7 +701,7 @@ struct mdp_mixer_info { int z_order; }; -#define MAX_PIPE_PER_MIXER 4 +#define MAX_PIPE_PER_MIXER 5 struct msmfb_mixer_info_req { int mixer_num; @@ -525,6 +714,13 @@ enum { ROTATOR_SUBSYSTEM_ID, }; +enum { + MDP_WRITEBACK_MIRROR_OFF, + MDP_WRITEBACK_MIRROR_ON, + MDP_WRITEBACK_MIRROR_PAUSE, + MDP_WRITEBACK_MIRROR_RESUME, +}; + #ifdef __KERNEL__ /* get the framebuffer physical address information */ diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 577592ea..9db33093 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -283,6 +283,8 @@ enum rtattr_type_t { RTA_MP_ALGO, /* no longer used */ RTA_TABLE, RTA_MARK, + RTA_MFC_STATS, /* not used - backported from the future */ + RTA_UID, __RTA_MAX }; diff --git a/include/linux/sched.h b/include/linux/sched.h index 0b309364..f09ab4c4 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -122,7 +122,7 @@ extern void get_avenrun(unsigned long *loads, unsigned long offset, int shift); #define FSHIFT 11 /* nr of bits of precision */ #define FIXED_1 (1< #ifdef __KERNEL__ +#include #include #include #include @@ -24,6 +25,7 @@ struct sync_timeline; struct sync_pt; struct sync_fence; +struct seq_file; /** * struct sync_timeline_ops - sync object implementation ops @@ -75,6 +77,14 @@ struct sync_timeline_ops { /* 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); + }; /** @@ -89,6 +99,7 @@ struct sync_timeline_ops { * @sync_timeline_list: membership in global sync_timeline_list */ struct sync_timeline { + struct kref kref; const struct sync_timeline_ops *ops; char name[32]; @@ -119,6 +130,7 @@ struct sync_pt { struct sync_timeline *parent; struct list_head child_list; + struct list_head signaled_list; struct list_head active_list; struct sync_fence *fence; @@ -145,6 +157,7 @@ struct sync_pt { */ struct sync_fence { struct file *file; + struct kref kref; char name[32]; /* this list is immutable once the fence is created */ @@ -272,6 +285,15 @@ struct sync_fence *sync_fence_merge(const char *name, */ struct sync_fence *sync_fence_fdget(int fd); +/** + * 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 @@ -384,7 +406,7 @@ struct sync_fence_info_data { * * pass timeout in milliseconds. */ -#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __u32) +#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32) /** * DOC: SYNC_IOC_MERGE - merge two fences diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 0f06a016..3ff94f08 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -141,6 +141,12 @@ extern unsigned int dirty_writeback_active_interval; extern unsigned int dirty_writeback_suspend_interval; #endif +#ifdef CONFIG_DYNAMIC_PAGE_WRITEBACK +extern int dyn_dirty_writeback_enabled; +extern unsigned int dirty_writeback_active_interval; +extern unsigned int dirty_writeback_suspend_interval; +#endif + extern int dirty_background_ratio_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); diff --git a/drivers/staging/zsmalloc/zsmalloc.h b/include/linux/zsmalloc.h similarity index 77% rename from drivers/staging/zsmalloc/zsmalloc.h rename to include/linux/zsmalloc.h index fbe6bec4..e44d634e 100644 --- a/drivers/staging/zsmalloc/zsmalloc.h +++ b/include/linux/zsmalloc.h @@ -2,6 +2,7 @@ * zsmalloc memory allocator * * Copyright (C) 2011 Nitin Gupta + * Copyright (C) 2012, 2013 Minchan Kim * * This code is released using a dual license strategy: BSD/GPL * You can choose the license that better fits your requirements. @@ -18,12 +19,19 @@ /* * zsmalloc mapping modes * - * NOTE: These only make a difference when a mapped object spans pages + * NOTE: These only make a difference when a mapped object spans pages. + * They also have no effect when PGTABLE_MAPPING is selected. */ enum zs_mapmode { ZS_MM_RW, /* normal read-write mapping */ ZS_MM_RO, /* read-only (no copy-out at unmap time) */ ZS_MM_WO /* write-only (no copy-in at map time) */ + /* + * NOTE: ZS_MM_WO should only be used for initializing new + * (uninitialized) allocations. Partial writes to already + * initialized allocations should use ZS_MM_RW to preserve the + * existing data. + */ }; struct zs_pool; diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 15d1817f..0bfccb14 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2010-2012 The Linux Foundation. All rights reserved. + Copyright (c) 2010-2011 Code Aurora Forum. 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 and @@ -10,6 +10,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ +// rollback to original BlueZ #ifndef __AMP_H #define __AMP_H @@ -115,7 +116,7 @@ int amp_init(void); void amp_exit(void); /* L2CAP-AMP fixed channel interface */ -void amp_conn_ind(struct hci_conn *hcon, struct sk_buff *skb); +void amp_conn_ind(struct l2cap_conn *conn, struct sk_buff *skb); /* L2CAP-AMP link interface */ void amp_create_physical(struct l2cap_conn *conn, struct sock *sk); @@ -256,7 +257,7 @@ struct amp_work_state_change { }; struct amp_work_conn_ind { struct work_struct work; - struct hci_conn *hcon; + struct l2cap_conn *conn; struct sk_buff *skb; }; struct amp_work_create_physical { diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index badbd8e5..6579356c 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010-2012 The Linux Foundation. All rights reserved. + Copyright (c) 2000-2001, 2010-2011 Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky @@ -21,6 +21,7 @@ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ +// rollback to original BlueZ #ifndef __BLUETOOTH_H #define __BLUETOOTH_H diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index dd4e8f93..f1a90625 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010-2012 The Linux Foundation. All rights reserved. + Copyright (c) 2000-2001, 2010-2011 Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky @@ -21,6 +21,7 @@ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ +// rollback to original BlueZ #ifndef __HCI_H #define __HCI_H @@ -700,12 +701,6 @@ struct hci_cp_write_voice_setting { __le16 voice_setting; } __packed; -#define HCI_OP_WRITE_AUTOMATIC_FLUSH_TIMEOUT 0x0c28 -struct hci_cp_write_automatic_flush_timeout { - __le16 handle; - __le16 timeout; -} __packed; - #define HCI_OP_HOST_BUFFER_SIZE 0x0c33 struct hci_cp_host_buffer_size { __le16 acl_mtu; @@ -869,17 +864,6 @@ struct hci_rp_read_data_block_size { __le16 num_blocks; } __packed; -#define HCI_OP_READ_RSSI 0x1405 -struct hci_cp_read_rssi { - __le16 handle; -} __packed; - -struct hci_rp_read_rssi { - __u8 status; - __le16 handle; - __s8 rssi; -} __packed; - #define HCI_OP_READ_LOCAL_AMP_INFO 0x1409 struct hci_rp_read_local_amp_info { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4f06adf2..f1e42166 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010-2012, The Linux Foundation. All rights reserved. + Copyright (c) 2000-2001, 2010-2011, Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky @@ -21,12 +21,13 @@ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ +// rollback to original BlueZ #ifndef __HCI_CORE_H #define __HCI_CORE_H #include -#include +#include /* HCI upper protocols */ #define HCI_PROTO_L2CAP 0 #define HCI_PROTO_SCO 1 @@ -240,11 +241,8 @@ struct hci_dev { rwlock_t adv_entries_lock; struct timer_list adv_timer; - struct timer_list disco_timer; - struct timer_list disco_le_timer; - __u8 disco_state; - int disco_int_phase; - int disco_int_count; + struct timer_list disc_timer; + struct timer_list disc_le_timer; struct hci_dev_stats stat; @@ -304,29 +302,22 @@ struct hci_conn { __u8 auth_initiator; __u8 power_save; __u16 disc_timeout; - __u16 conn_timeout; unsigned long pend; __u8 remote_cap; __u8 remote_oob; __u8 remote_auth; - __s8 rssi_threshold; - __u16 rssi_update_interval; - __u8 rssi_update_thresh_exceed; - unsigned int sent; struct sk_buff_head data_q; struct timer_list disc_timer; struct timer_list idle_timer; - struct delayed_work rssi_update_work; - struct timer_list encrypt_pause_timer; struct work_struct work_add; struct work_struct work_del; - struct wake_lock idle_lock; + struct device dev; atomic_t devref; @@ -354,7 +345,6 @@ struct hci_conn { __u8 auth; void *smp_conn; struct timer_list smp_timer; - __u8 conn_valid; void (*connect_cfm_cb) (struct hci_conn *conn, u8 status); @@ -578,7 +568,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, struct hci_conn *hci_le_conn_add(struct hci_dev *hdev, bdaddr_t *dst, __u8 addr_type); int hci_conn_del(struct hci_conn *conn); -void hci_conn_hash_flush(struct hci_dev *hdev, u8 is_process); +void hci_conn_hash_flush(struct hci_dev *hdev); void hci_conn_check_pending(struct hci_dev *hdev); struct hci_chan *hci_chan_add(struct hci_dev *hdev); @@ -589,7 +579,10 @@ static inline void hci_chan_hold(struct hci_chan *chan) } int hci_chan_put(struct hci_chan *chan); -struct hci_chan *hci_chan_create(struct hci_chan *chan, +struct hci_chan *hci_chan_accept(struct hci_conn *hcon, + struct hci_ext_fs *tx_fs, + struct hci_ext_fs *rx_fs); +struct hci_chan *hci_chan_create(struct hci_conn *hcon, struct hci_ext_fs *tx_fs, struct hci_ext_fs *rx_fs); void hci_chan_modify(struct hci_chan *chan, @@ -599,10 +592,6 @@ void hci_chan_modify(struct hci_chan *chan, 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 *hci_le_connect(struct hci_dev *hdev, __u16 pkt_type, - bdaddr_t *dst, __u8 sec_level, - __u8 auth_type, - struct bt_le_params *le_params); int hci_conn_check_link_mode(struct hci_conn *conn); int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type); int hci_conn_change_link_key(struct hci_conn *conn); @@ -616,10 +605,6 @@ void hci_conn_enter_sniff_mode(struct hci_conn *conn); void hci_conn_hold_device(struct hci_conn *conn); void hci_conn_put_device(struct hci_conn *conn); -void hci_conn_set_rssi_reporter(struct hci_conn *conn, - s8 rssi_threshold, u16 interval, u8 updateOnThreshExceed); -void hci_conn_unset_rssi_reporter(struct hci_conn *conn); - static inline void hci_conn_hold(struct hci_conn *conn) { atomic_inc(&conn->refcnt); @@ -634,6 +619,7 @@ static inline void hci_conn_put(struct hci_conn *conn) del_timer(&conn->idle_timer); if (conn->state == BT_CONNECTED) { timeo = msecs_to_jiffies(conn->disc_timeout); + /* ACL_LINK or LE_LINK disconnet timeout from 4secs(timeo *= 2) to 8secs(timeo *= 4)*/ if (!conn->out) timeo *= 4; } else @@ -764,8 +750,7 @@ struct hci_proto { int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type); int (*connect_cfm) (struct hci_conn *conn, __u8 status); int (*disconn_ind) (struct hci_conn *conn); - int (*disconn_cfm) (struct hci_conn *conn, __u8 reason, - __u8 is_process); + int (*disconn_cfm) (struct hci_conn *conn, __u8 reason); int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb, __u16 flags); int (*recv_scodata) (struct hci_conn *conn, struct sk_buff *skb); int (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); @@ -822,18 +807,17 @@ static inline int hci_proto_disconn_ind(struct hci_conn *conn) return reason; } -static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason, - __u8 is_process) +static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason) { register struct hci_proto *hp; hp = hci_proto[HCI_PROTO_L2CAP]; if (hp && hp->disconn_cfm) - hp->disconn_cfm(conn, reason, is_process); + hp->disconn_cfm(conn, reason); hp = hci_proto[HCI_PROTO_SCO]; if (hp && hp->disconn_cfm) - hp->disconn_cfm(conn, reason, is_process); + hp->disconn_cfm(conn, reason); if (conn->disconn_cfm_cb) conn->disconn_cfm_cb(conn, reason); @@ -946,7 +930,7 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encr if (conn->sec_level == BT_SECURITY_SDP) conn->sec_level = BT_SECURITY_LOW; - if (!status && encrypt && conn->pending_sec_level > conn->sec_level) + if (conn->pending_sec_level > conn->sec_level) conn->sec_level = conn->pending_sec_level; hci_proto_encrypt_cfm(conn, status, encrypt); @@ -1054,21 +1038,13 @@ int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer, u8 status); int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le, u8 *dev_class, s8 rssi, u8 eir_len, u8 *eir); -void mgmt_read_rssi_complete(u16 index, s8 rssi, bdaddr_t *bdaddr, - u16 handle, u8 status); int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 status, u8 *name); void mgmt_inquiry_started(u16 index); void mgmt_inquiry_complete_evt(u16 index, u8 status); -void mgmt_disco_timeout(unsigned long data); -void mgmt_disco_le_timeout(unsigned long data); int mgmt_encrypt_change(u16 index, bdaddr_t *bdaddr, u8 status); /* LE SMP Management interface */ int le_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, void *cp); -int mgmt_remote_class(u16 index, bdaddr_t *bdaddr, u8 dev_class[3]); -int mgmt_remote_version(u16 index, bdaddr_t *bdaddr, u8 ver, u16 mnf, - u16 sub_ver); -int mgmt_remote_features(u16 index, bdaddr_t *bdaddr, u8 features[8]); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) @@ -1106,6 +1082,4 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], __u8 ltk[16]); void hci_le_ltk_neg_reply(struct hci_conn *conn); -void hci_read_rssi(struct hci_conn *conn); - #endif /* __HCI_CORE_H */ diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 9c2b735c..b99ffe51 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010-2012 The Linux Foundation. All rights reserved. + Copyright (c) 2000-2001, 2010-2011 Code Aurora Forum. All rights reserved. Copyright (C) 2009-2010 Gustavo F. Padovan Copyright (C) 2010 Google Inc. @@ -24,6 +24,8 @@ SOFTWARE IS DISCLAIMED. */ +// rollback to original BlueZ + #ifndef __L2CAP_H #define __L2CAP_H @@ -32,7 +34,6 @@ #define L2CAP_DEFAULT_MIN_MTU 48 #define L2CAP_DEFAULT_MAX_SDU_SIZE 0xffff #define L2CAP_DEFAULT_FLUSH_TO 0xffff -#define L2CAP_MAX_FLUSH_TO 0x7ff #define L2CAP_DEFAULT_TX_WINDOW 63 #define L2CAP_DEFAULT_MAX_TX 3 #define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */ @@ -51,7 +52,7 @@ #define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */ #define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */ -#define L2CAP_MOVE_TIMEOUT (4*HZ) /* 4 seconds */ +#define L2CAP_MOVE_TIMEOUT (2*HZ) /* 2 seconds */ #define L2CAP_MOVE_ERTX_TIMEOUT (60*HZ) /* 60 seconds */ /* L2CAP socket address */ @@ -294,6 +295,8 @@ struct l2cap_conf_ext_fs { struct l2cap_conf_prm { __u8 fcs; + __le16 retrans_timeout; + __le16 monitor_timeout; __le32 flush_to; }; @@ -489,7 +492,6 @@ struct l2cap_pinfo { __u8 fixed_channel; __u8 num_conf_req; __u8 num_conf_rsp; - __u8 incoming; __u8 fcs; __u8 sec_level; @@ -650,8 +652,6 @@ struct l2cap_pinfo { #define L2CAP_AMP_STATE_RESEGMENT 12 #define L2CAP_ATT_ERROR 0x01 -#define L2CAP_ATT_MTU_REQ 0x02 -#define L2CAP_ATT_MTU_RSP 0x03 #define L2CAP_ATT_RESPONSE_BIT 0x01 #define L2CAP_ATT_INDICATE 0x1D #define L2CAP_ATT_CONFIRM 0x1E @@ -687,7 +687,6 @@ int l2cap_strm_tx(struct sock *sk, struct sk_buff_head *skbs); int l2cap_ertm_tx(struct sock *sk, struct bt_l2cap_control *control, struct sk_buff_head *skbs, u8 event); -int l2cap_sock_le_params_valid(struct bt_le_params *le_params); void l2cap_sock_set_timer(struct sock *sk, long timeout); void l2cap_sock_clear_timer(struct sock *sk); void __l2cap_sock_close(struct sock *sk, int reason); @@ -695,8 +694,6 @@ void l2cap_sock_kill(struct sock *sk); void l2cap_sock_init(struct sock *sk, struct sock *parent); struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); -struct sock *l2cap_find_sock_by_fixed_cid_and_dir(__le16 cid, bdaddr_t *src, - bdaddr_t *dst, int server); void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err); void l2cap_chan_del(struct sock *sk, int err); int l2cap_do_connect(struct sock *sk); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 3048339b..228d2790 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -20,6 +20,7 @@ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ +// rollback to original BlueZ #define MGMT_INDEX_NONE 0xFFFF @@ -161,6 +162,7 @@ struct mgmt_cp_set_io_capability { struct mgmt_cp_pair_device { bdaddr_t bdaddr; __u8 io_cap; + __u8 ssp_cap; } __packed; struct mgmt_rp_pair_device { bdaddr_t bdaddr; diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index e736f18f..160e3f0c 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -1,7 +1,7 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Technologies, Inc. - Copyright (c) 2011, The Linux Foundation. All rights reserved. + Copyright (C) 2000-2001 Qualcomm Incorporated + Copyright (c) 2011, Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 075f1e3a..58895709 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -23,6 +23,8 @@ struct fib_rule { struct fib_rule __rcu *ctarget; char iifname[IFNAMSIZ]; char oifname[IFNAMSIZ]; + uid_t uid_start; + uid_t uid_end; struct rcu_head rcu; struct net * fr_net; }; @@ -79,7 +81,9 @@ struct fib_rules_ops { [FRA_FWMARK] = { .type = NLA_U32 }, \ [FRA_FWMASK] = { .type = NLA_U32 }, \ [FRA_TABLE] = { .type = NLA_U32 }, \ - [FRA_GOTO] = { .type = NLA_U32 } + [FRA_GOTO] = { .type = NLA_U32 }, \ + [FRA_UID_START] = { .type = NLA_U32 }, \ + [FRA_UID_END] = { .type = NLA_U32 } static inline void fib_rule_get(struct fib_rule *rule) { diff --git a/include/net/flow.h b/include/net/flow.h index 6c469dbd..964a4945 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -23,6 +23,7 @@ struct flowi_common { #define FLOWI_FLAG_PRECOW_METRICS 0x02 #define FLOWI_FLAG_CAN_SLEEP 0x04 __u32 flowic_secid; + uid_t flowic_uid; }; union flowi_uli { @@ -59,6 +60,7 @@ struct flowi4 { #define flowi4_proto __fl_common.flowic_proto #define flowi4_flags __fl_common.flowic_flags #define flowi4_secid __fl_common.flowic_secid +#define flowi4_uid __fl_common.flowic_uid /* (saddr,daddr) must be grouped, same order as in IP header */ __be32 saddr; @@ -78,7 +80,8 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, __u32 mark, __u8 tos, __u8 scope, __u8 proto, __u8 flags, __be32 daddr, __be32 saddr, - __be16 dport, __be16 sport) + __be16 dport, __be32 sport, + uid_t uid) { fl4->flowi4_oif = oif; fl4->flowi4_iif = 0; @@ -88,6 +91,7 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, fl4->flowi4_proto = proto; fl4->flowi4_flags = flags; fl4->flowi4_secid = 0; + fl4->flowi4_uid = uid; fl4->daddr = daddr; fl4->saddr = saddr; fl4->fl4_dport = dport; @@ -115,6 +119,7 @@ struct flowi6 { #define flowi6_proto __fl_common.flowic_proto #define flowi6_flags __fl_common.flowic_flags #define flowi6_secid __fl_common.flowic_secid +#define flowi6_uid __fl_common.flowic_uid struct in6_addr daddr; struct in6_addr saddr; __be32 flowlabel; @@ -158,6 +163,7 @@ struct flowi { #define flowi_proto u.__fl_common.flowic_proto #define flowi_flags u.__fl_common.flowic_flags #define flowi_secid u.__fl_common.flowic_secid +#define flowi_uid u.__fl_common.flowic_uid } __attribute__((__aligned__(BITS_PER_LONG/8))); static inline struct flowi *flowi4_to_flowi(struct flowi4 *fl4) @@ -223,4 +229,5 @@ extern void flow_cache_flush(void); extern void flow_cache_flush_deferred(void); extern atomic_t flow_cache_genid; + #endif diff --git a/include/net/ip.h b/include/net/ip.h index 34d00489..b3523201 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -165,6 +165,7 @@ struct ip_reply_arg { int csumoffset; /* u16 offset of csum in iov[0].iov_base */ /* -1 if not needed */ int bound_dev_if; + uid_t uid; u8 tos; }; diff --git a/include/net/route.h b/include/net/route.h index b1c0d5b5..7488c9ed 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -60,6 +60,7 @@ struct rtable { int rt_iif; int rt_oif; __u32 rt_mark; + uid_t rt_uid; /* Info on neighbour */ __be32 rt_gateway; @@ -146,7 +147,7 @@ static inline struct rtable *ip_route_output_ports(struct net *net, struct flowi flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos, RT_SCOPE_UNIVERSE, proto, sk ? inet_sk_flowi_flags(sk) : 0, - daddr, saddr, dport, sport); + daddr, saddr, dport, sport, sk ? sock_i_uid(sk) : 0); if (sk) security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); @@ -250,7 +251,8 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, __be32 flow_flags |= FLOWI_FLAG_CAN_SLEEP; flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, - protocol, flow_flags, dst, src, dport, sport); + protocol, flow_flags, dst, src, dport, sport, + sock_i_uid(sk)); } static inline struct rtable *ip_route_connect(struct flowi4 *fl4, diff --git a/include/trace/binder_trace.h b/include/trace/binder_trace.h new file mode 100644 index 00000000..12ef1e93 --- /dev/null +++ b/include/trace/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 "trace/define_trace.h" diff --git a/include/trace/events/sync.h b/include/trace/events/sync.h new file mode 100644 index 00000000..bee06c88 --- /dev/null +++ b/include/trace/events/sync.h @@ -0,0 +1,125 @@ +#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) + ); + + +TRACE_EVENT(sync_alloc, + TP_PROTO(struct sync_fence *fence, int pid), + + TP_ARGS(fence, pid), + + TP_STRUCT__entry( + __string(name, fence->name) + __field(s32, status) + __field(u32, pid) + ), + + TP_fast_assign( + __assign_str(name, fence->name); + __entry->status = fence->status; + __entry->pid = pid; + ), + + TP_printk("PID:%d Fence=%s status=%d", __entry->pid, + __get_str(name), __entry->status) +); + +TRACE_EVENT(sync_free, + TP_PROTO(struct sync_fence *fence, int pid), + + TP_ARGS(fence, pid), + + TP_STRUCT__entry( + __string(name, fence->name) + __field(s32, status) + __field(u32, pid) + ), + + TP_fast_assign( + __assign_str(name, fence->name); + __entry->status = fence->status; + __entry->pid = pid; + ), + + TP_printk("PID:%d Fence =%s status=%d", __entry->pid, + __get_str(name), __entry->status) +); + +#endif /* if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ) */ + +/* This part must be outside protection */ +#include diff --git a/kernel/cpu.c b/kernel/cpu.c index 8837eebe..224affbf 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -16,6 +16,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -25,18 +26,23 @@ static DEFINE_MUTEX(cpu_add_remove_lock); /* - * The following two API's must be used when attempting - * to serialize the updates to cpu_online_mask, cpu_present_mask. + * The following two APIs (cpu_maps_update_begin/done) must be used when + * attempting to serialize the updates to cpu_online_mask & cpu_present_mask. + * The APIs cpu_notifier_register_begin/done() must be used to protect CPU + * hotplug callback (un)registration performed using __register_cpu_notifier() + * or __unregister_cpu_notifier(). */ void cpu_maps_update_begin(void) { mutex_lock(&cpu_add_remove_lock); } +EXPORT_SYMBOL(cpu_notifier_register_begin); void cpu_maps_update_done(void) { mutex_unlock(&cpu_add_remove_lock); } +EXPORT_SYMBOL(cpu_notifier_register_done); static RAW_NOTIFIER_HEAD(cpu_chain); @@ -58,17 +64,30 @@ static struct { * an ongoing cpu hotplug operation. */ int refcount; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif } cpu_hotplug = { .active_writer = NULL, .lock = __MUTEX_INITIALIZER(cpu_hotplug.lock), .refcount = 0, +#ifdef CONFIG_DEBUG_LOCK_ALLOC + .dep_map = {.name = "cpu_hotplug.lock" }, +#endif }; +/* Lockdep annotations for get/put_online_cpus() and cpu_hotplug_begin/end() */ +#define cpuhp_lock_acquire_read() lock_map_acquire_read(&cpu_hotplug.dep_map) +#define cpuhp_lock_acquire() lock_map_acquire(&cpu_hotplug.dep_map) +#define cpuhp_lock_release() lock_map_release(&cpu_hotplug.dep_map) + void get_online_cpus(void) { might_sleep(); if (cpu_hotplug.active_writer == current) return; + cpuhp_lock_acquire_read(); mutex_lock(&cpu_hotplug.lock); cpu_hotplug.refcount++; mutex_unlock(&cpu_hotplug.lock); @@ -84,6 +103,7 @@ void put_online_cpus(void) if (!--cpu_hotplug.refcount && unlikely(cpu_hotplug.active_writer)) wake_up_process(cpu_hotplug.active_writer); mutex_unlock(&cpu_hotplug.lock); + cpuhp_lock_release(); } EXPORT_SYMBOL_GPL(put_online_cpus); @@ -114,6 +134,7 @@ static void cpu_hotplug_begin(void) { cpu_hotplug.active_writer = current; + cpuhp_lock_acquire(); for (;;) { mutex_lock(&cpu_hotplug.lock); if (likely(!cpu_hotplug.refcount)) @@ -128,6 +149,7 @@ static void cpu_hotplug_done(void) { cpu_hotplug.active_writer = NULL; mutex_unlock(&cpu_hotplug.lock); + cpuhp_lock_release(); } /* @@ -166,6 +188,11 @@ int __ref register_cpu_notifier(struct notifier_block *nb) return ret; } +int __ref __register_cpu_notifier(struct notifier_block *nb) +{ + return raw_notifier_chain_register(&cpu_chain, nb); +} + static int __cpu_notify(unsigned long val, void *v, int nr_to_call, int *nr_calls) { @@ -189,6 +216,7 @@ static void cpu_notify_nofail(unsigned long val, void *v) BUG_ON(cpu_notify(val, v)); } EXPORT_SYMBOL(register_cpu_notifier); +EXPORT_SYMBOL(__register_cpu_notifier); void __ref unregister_cpu_notifier(struct notifier_block *nb) { @@ -198,6 +226,12 @@ void __ref unregister_cpu_notifier(struct notifier_block *nb) } EXPORT_SYMBOL(unregister_cpu_notifier); +void __ref __unregister_cpu_notifier(struct notifier_block *nb) +{ + raw_notifier_chain_unregister(&cpu_chain, nb); +} +EXPORT_SYMBOL(__unregister_cpu_notifier); + static inline void check_for_tasks(int cpu) { struct task_struct *p; @@ -322,7 +356,7 @@ EXPORT_SYMBOL(cpu_down); #endif /*CONFIG_HOTPLUG_CPU*/ /* Requires cpu_add_remove_lock to be held */ -static int _cpu_up(unsigned int cpu, int tasks_frozen) +static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen) { int ret, nr_calls = 0; void *hcpu = (void *)(long)cpu; @@ -357,7 +391,7 @@ static int _cpu_up(unsigned int cpu, int tasks_frozen) return ret; } -int cpu_up(unsigned int cpu) +int __cpuinit cpu_up(unsigned int cpu) { int err = 0; @@ -570,7 +604,7 @@ core_initcall(cpu_hotplug_pm_sync_init); * It must be called by the arch code on the new cpu, before the new cpu * enables interrupts and before the "boot" cpu returns from __cpu_up(). */ -void notify_cpu_starting(unsigned int cpu) +void __cpuinit notify_cpu_starting(unsigned int cpu) { unsigned long val = CPU_STARTING; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 63319e34..9378313c 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -87,6 +87,8 @@ #define CREATE_TRACE_POINTS #include +ATOMIC_NOTIFIER_HEAD(migration_notifier_head); + void start_bandwidth_timer(struct hrtimer *period_timer, ktime_t period) { unsigned long delta; @@ -110,6 +112,10 @@ void start_bandwidth_timer(struct hrtimer *period_timer, ktime_t period) DEFINE_MUTEX(sched_domains_mutex); DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues); +#ifdef CONFIG_INTELLI_PLUG +DEFINE_PER_CPU_SHARED_ALIGNED(struct nr_stats_s, runqueue_stats); +#endif + static void update_rq_clock_task(struct rq *rq, s64 delta); void update_rq_clock(struct rq *rq) @@ -1579,15 +1585,17 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) { unsigned long flags; - int cpu, success = 0; + int cpu, src_cpu, success = 0; smp_wmb(); raw_spin_lock_irqsave(&p->pi_lock, flags); + src_cpu = task_cpu(p); + cpu = src_cpu; + if (!(p->state & state)) goto out; success = 1; /* we're going to change ->state */ - cpu = task_cpu(p); if (p->on_rq && ttwu_remote(p, wake_flags)) goto stat; @@ -1624,7 +1632,7 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) p->sched_class->task_waking(p); cpu = select_task_rq(p, SD_BALANCE_WAKE, wake_flags); - if (task_cpu(p) != cpu) { + if (src_cpu != cpu) { wake_flags |= WF_MIGRATED; set_task_cpu(p, cpu); } @@ -1636,6 +1644,9 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) out: raw_spin_unlock_irqrestore(&p->pi_lock, flags); + if (src_cpu != cpu && task_notify_on_migrate(p)) + atomic_notifier_call_chain(&migration_notifier_head, + cpu, (void *)src_cpu); return success; } @@ -2171,6 +2182,60 @@ unsigned long this_cpu_load(void) return this->cpu_load[0]; } +#ifdef CONFIG_INTELLI_PLUG +unsigned long avg_nr_running(void) +{ + unsigned long i, sum = 0; + unsigned int seqcnt, ave_nr_running; + + for_each_online_cpu(i) { + struct nr_stats_s *stats = &per_cpu(runqueue_stats, i); + struct rq *q = cpu_rq(i); + + /* + * Update average to avoid reading stalled value if there were + * no run-queue changes for a long time. On the other hand if + * the changes are happening right now, just read current value + * directly. + */ + seqcnt = read_seqcount_begin(&stats->ave_seqcnt); + ave_nr_running = do_avg_nr_running(q); + if (read_seqcount_retry(&stats->ave_seqcnt, seqcnt)) { + read_seqcount_begin(&stats->ave_seqcnt); + ave_nr_running = stats->ave_nr_running; + } + + sum += ave_nr_running; + } + + return sum; +} +EXPORT_SYMBOL(avg_nr_running); + +unsigned long avg_cpu_nr_running(unsigned int cpu) +{ + unsigned int seqcnt, ave_nr_running; + + struct nr_stats_s *stats = &per_cpu(runqueue_stats, cpu); + struct rq *q = cpu_rq(cpu); + + /* + * Update average to avoid reading stalled value if there were + * no run-queue changes for a long time. On the other hand if + * the changes are happening right now, just read current value + * directly. + */ + seqcnt = read_seqcount_begin(&stats->ave_seqcnt); + ave_nr_running = do_avg_nr_running(q); + if (read_seqcount_retry(&stats->ave_seqcnt, seqcnt)) { + read_seqcount_begin(&stats->ave_seqcnt); + ave_nr_running = stats->ave_nr_running; + } + + return ave_nr_running; +} +EXPORT_SYMBOL(avg_cpu_nr_running); +#endif /* * Global load-average calculations @@ -5271,6 +5336,7 @@ EXPORT_SYMBOL_GPL(set_cpus_allowed_ptr); static int __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu) { struct rq *rq_dest, *rq_src; + bool moved = false; int ret = 0; if (unlikely(!cpu_active(dest_cpu))) @@ -5297,12 +5363,16 @@ static int __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu) set_task_cpu(p, dest_cpu); enqueue_task(rq_dest, p, 0); check_preempt_curr(rq_dest, p, 0); + moved = true; } done: ret = 1; fail: double_rq_unlock(rq_src, rq_dest); raw_spin_unlock(&p->pi_lock); + if (moved && task_notify_on_migrate(p)) + atomic_notifier_call_chain(&migration_notifier_head, + dest_cpu, (void *)src_cpu); return ret; } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 90933dfd..dfcab643 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3109,6 +3109,8 @@ struct lb_env { unsigned int loop_max; }; +static DEFINE_PER_CPU(bool, dbs_boost_needed); + /* * move_task - move a task from one runqueue to another runqueue. * Both runqueues must be locked. @@ -3119,6 +3121,8 @@ static void move_task(struct task_struct *p, struct lb_env *env) set_task_cpu(p, env->dst_cpu); activate_task(env->dst_rq, p, 0); check_preempt_curr(env->dst_rq, p, 0); + if (task_notify_on_migrate(p)) + per_cpu(dbs_boost_needed, env->dst_cpu) = true; } /* diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 71429db2..bf6a0689 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1647,6 +1647,7 @@ static int push_rt_task(struct rq *rq) struct task_struct *next_task; struct rq *lowest_rq; int ret = 0; + bool moved = false; if (!rq->rt.overloaded) return 0; @@ -1716,6 +1717,7 @@ static int push_rt_task(struct rq *rq) deactivate_task(rq, next_task, 0); set_task_cpu(next_task, lowest_rq->cpu); + moved = true; activate_task(lowest_rq, next_task, 0); ret = 1; @@ -1726,6 +1728,11 @@ static int push_rt_task(struct rq *rq) out: put_task_struct(next_task); + if (moved && task_notify_on_migrate(next_task)) + atomic_notifier_call_chain(&migration_notifier_head, + cpu_of(lowest_rq), + (void *)cpu_of(rq)); + return ret; } @@ -1739,8 +1746,10 @@ static void push_rt_tasks(struct rq *rq) static int pull_rt_task(struct rq *this_rq) { int this_cpu = this_rq->cpu, ret = 0, cpu; - struct task_struct *p; + struct task_struct *p = NULL; struct rq *src_rq; + bool moved = false; + int src_cpu = 0; if (likely(!rt_overloaded(this_rq))) return 0; @@ -1801,6 +1810,10 @@ static int pull_rt_task(struct rq *this_rq) deactivate_task(src_rq, p, 0); set_task_cpu(p, this_cpu); activate_task(this_rq, p, 0); + + moved = true; + src_cpu = cpu_of(src_rq); + /* * We continue with the search, just in * case there's an even higher prio task @@ -1812,6 +1825,11 @@ static int pull_rt_task(struct rq *this_rq) double_unlock_balance(this_rq, src_rq); } + if (moved && task_notify_on_migrate(p)) + atomic_notifier_call_chain(&migration_notifier_head, + this_cpu, + (void *)src_cpu); + return ret; } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 4a5e7398..23994fd4 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -104,6 +104,8 @@ struct cfs_bandwidth { struct task_group { struct cgroup_subsys_state css; + bool notify_on_migrate; + #ifdef CONFIG_FAIR_GROUP_SCHED /* schedulable entities of this group on each cpu */ struct sched_entity **se; @@ -482,6 +484,27 @@ DECLARE_PER_CPU(struct rq, runqueues); #define cpu_curr(cpu) (cpu_rq(cpu)->curr) #define raw_rq() (&__raw_get_cpu_var(runqueues)) +#ifdef CONFIG_INTELLI_PLUG +struct nr_stats_s { + /* time-based average load */ + u64 nr_last_stamp; + unsigned int ave_nr_running; + seqcount_t ave_seqcnt; +}; + +/* 27 ~= 134217728ns = 134.2ms + * 26 ~= 67108864ns = 67.1ms + * 25 ~= 33554432ns = 33.5ms + * 24 ~= 16777216ns = 16.8ms + */ +#define NR_AVE_PERIOD_EXP 27 +#define NR_AVE_SCALE(x) ((x) << FSHIFT) +#define NR_AVE_PERIOD (1 << NR_AVE_PERIOD_EXP) +#define NR_AVE_DIV_PERIOD(x) ((x) >> NR_AVE_PERIOD_EXP) + +DECLARE_PER_CPU(struct nr_stats_s, runqueue_stats); +#endif + #ifdef CONFIG_SMP #define rcu_dereference_check_sched_domain(p) \ @@ -551,6 +574,11 @@ static inline struct task_group *task_group(struct task_struct *p) return p->sched_task_group; } +static inline bool task_notify_on_migrate(struct task_struct *p) +{ + return task_group(p)->notify_on_migrate; +} + /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */ static inline void set_task_rq(struct task_struct *p, unsigned int cpu) { @@ -911,14 +939,54 @@ extern void cpuacct_charge(struct task_struct *tsk, u64 cputime); static inline void cpuacct_charge(struct task_struct *tsk, u64 cputime) {} #endif +#ifdef CONFIG_INTELLI_PLUG +static inline unsigned int do_avg_nr_running(struct rq *rq) +{ + + struct nr_stats_s *nr_stats = &per_cpu(runqueue_stats, rq->cpu); + unsigned int ave_nr_running = nr_stats->ave_nr_running; + s64 nr, deltax; + + deltax = rq->clock_task - nr_stats->nr_last_stamp; + nr = NR_AVE_SCALE(rq->nr_running); + + if (deltax > NR_AVE_PERIOD) + ave_nr_running = nr; + else + ave_nr_running += + NR_AVE_DIV_PERIOD(deltax * (nr - ave_nr_running)); + + return ave_nr_running; +} +#endif + static inline void inc_nr_running(struct rq *rq) { +#ifdef CONFIG_INTELLI_PLUG + struct nr_stats_s *nr_stats = &per_cpu(runqueue_stats, rq->cpu); +#endif + rq->nr_running++; +#ifdef CONFIG_INTELLI_PLUG + write_seqcount_end(&nr_stats->ave_seqcnt); +#endif } static inline void dec_nr_running(struct rq *rq) { +#ifdef CONFIG_INTELLI_PLUG + struct nr_stats_s *nr_stats = &per_cpu(runqueue_stats, rq->cpu); +#endif + +#ifdef CONFIG_INTELLI_PLUG + write_seqcount_begin(&nr_stats->ave_seqcnt); + nr_stats->ave_nr_running = do_avg_nr_running(rq); + nr_stats->nr_last_stamp = rq->clock_task; +#endif rq->nr_running--; +#ifdef CONFIG_INTELLI_PLUG + write_seqcount_end(&nr_stats->ave_seqcnt); +#endif } extern void update_rq_clock(struct rq *rq); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index abdb54bf..82b247ed 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1133,6 +1133,7 @@ static struct ctl_table vm_table[] = { .extra1 = &zero, }, #endif +#endif { .procname = "dirty_expire_centisecs", .data = &dirty_expire_interval, @@ -1598,7 +1599,7 @@ static struct ctl_table debug_table[] = { #endif { } }; - +#if defined(CONFIG_SYSCTL) static struct ctl_table dev_table[] = { { } }; diff --git a/mm/Kconfig b/mm/Kconfig index 621c1d34..8c55931c 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -382,13 +382,30 @@ config CLEANCACHE config DYNAMIC_PAGE_WRITEBACK bool "Dynamically manage the dirty page writebacks during suspend/resume" - default n + default y help This feature allows to use different dirty page writeback intervals when the system is suspended and when the system has been resumed. If unsure, say N to disable this feature +config FRONTSWAP + bool "Enable frontswap to cache swap pages if tmem is present" + depends on SWAP + default n + help + Frontswap is so named because it can be thought of as the opposite + of a "backing" store for a swap device. The data is stored into + "transcendent memory", memory that is not directly accessible or + addressable by the kernel and is of unknown and possibly + time-varying size. When space in transcendent memory is available, + a significant swap I/O reduction may be achieved. When none is + available, all frontswap calls are reduced to a single pointer- + compare-against-NULL resulting in a negligible performance hit + and swap data is stored as normal on the matching swap device. + + If unsure, say Y to enable frontswap. + config MEMORY_HOLE_CARVEOUT bool help @@ -410,3 +427,27 @@ config USE_USER_ACCESSIBLE_TIMERS architecture-specific code that will need to be enabled separately. +config ZSMALLOC + bool "Memory allocator for compressed pages" + depends on MMU + default n + help + zsmalloc is a slab-based memory allocator designed to store + compressed RAM pages. zsmalloc uses virtual memory mapping + in order to reduce fragmentation. However, this results in a + non-standard allocator interface where a handle, not a pointer, is + returned by an alloc(). This handle must be mapped in order to + access the allocated space. + +config PGTABLE_MAPPING + bool "Use page table mapping to access object in zsmalloc" + depends on ZSMALLOC + help + By default, zsmalloc uses a copy-based object mapping method to + access allocations that span two pages. However, if a particular + architecture (ex, ARM) performs VM mapping faster than copying, + then you should select this. This causes zsmalloc to use page table + mapping rather than copying for object mapping. + + You can check speed with zsmalloc benchmark[1]. + [1] https://github.com/spartacus06/zsmalloc diff --git a/mm/Makefile b/mm/Makefile index 8aada89e..3ee79b0b 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_HAVE_MEMBLOCK) += memblock.o obj-$(CONFIG_BOUNCE) += bounce.o obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o obj-$(CONFIG_HAS_DMA) += dmapool.o +obj-$(CONFIG_FRONTSWAP) += frontswap.o obj-$(CONFIG_HUGETLBFS) += hugetlb.o obj-$(CONFIG_NUMA) += mempolicy.o obj-$(CONFIG_SPARSEMEM) += sparse.o @@ -50,3 +51,4 @@ obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o obj-$(CONFIG_CLEANCACHE) += cleancache.o +obj-$(CONFIG_ZSMALLOC) += zsmalloc.o diff --git a/mm/frontswap.c b/mm/frontswap.c new file mode 100644 index 00000000..2890e67d --- /dev/null +++ b/mm/frontswap.c @@ -0,0 +1,370 @@ +/* + * Frontswap frontend + * + * This code provides the generic "frontend" layer to call a matching + * "backend" driver implementation of frontswap. See + * Documentation/vm/frontswap.txt for more information. + * + * Copyright (C) 2009-2012 Oracle Corp. All rights reserved. + * Author: Dan Magenheimer + * + * This work is licensed under the terms of the GNU GPL, version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * frontswap_ops is set by frontswap_register_ops to contain the pointers + * to the frontswap "backend" implementation functions. + */ +static struct frontswap_ops frontswap_ops __read_mostly; + +/* + * This global enablement flag reduces overhead on systems where frontswap_ops + * has not been registered, so is preferred to the slower alternative: a + * function call that checks a non-global. + */ +bool frontswap_enabled __read_mostly; +EXPORT_SYMBOL(frontswap_enabled); + +/* + * If enabled, frontswap_store will return failure even on success. As + * a result, the swap subsystem will always write the page to swap, in + * effect converting frontswap into a writethrough cache. In this mode, + * there is no direct reduction in swap writes, but a frontswap backend + * can unilaterally "reclaim" any pages in use with no data loss, thus + * providing increases control over maximum memory usage due to frontswap. + */ +static bool frontswap_writethrough_enabled __read_mostly; + +/* + * If enabled, the underlying tmem implementation is capable of doing + * exclusive gets, so frontswap_load, on a successful tmem_get must + * mark the page as no longer in frontswap AND mark it dirty. + */ +static bool frontswap_tmem_exclusive_gets_enabled __read_mostly; + +#ifdef CONFIG_DEBUG_FS +/* + * Counters available via /sys/kernel/debug/frontswap (if debugfs is + * properly configured). These are for information only so are not protected + * against increment races. + */ +static u64 frontswap_loads; +static u64 frontswap_succ_stores; +static u64 frontswap_failed_stores; +static u64 frontswap_invalidates; + +static inline void inc_frontswap_loads(void) { + frontswap_loads++; +} +static inline void inc_frontswap_succ_stores(void) { + frontswap_succ_stores++; +} +static inline void inc_frontswap_failed_stores(void) { + frontswap_failed_stores++; +} +static inline void inc_frontswap_invalidates(void) { + frontswap_invalidates++; +} +#else +static inline void inc_frontswap_loads(void) { } +static inline void inc_frontswap_succ_stores(void) { } +static inline void inc_frontswap_failed_stores(void) { } +static inline void inc_frontswap_invalidates(void) { } +#endif +/* + * Register operations for frontswap, returning previous thus allowing + * detection of multiple backends and possible nesting. + */ +struct frontswap_ops frontswap_register_ops(struct frontswap_ops *ops) +{ + struct frontswap_ops old = frontswap_ops; + + frontswap_ops = *ops; + frontswap_enabled = true; + return old; +} +EXPORT_SYMBOL(frontswap_register_ops); + +/* + * Enable/disable frontswap writethrough (see above). + */ +void frontswap_writethrough(bool enable) +{ + frontswap_writethrough_enabled = enable; +} +EXPORT_SYMBOL(frontswap_writethrough); + +/* + * Enable/disable frontswap exclusive gets (see above). + */ +void frontswap_tmem_exclusive_gets(bool enable) +{ + frontswap_tmem_exclusive_gets_enabled = enable; +} +EXPORT_SYMBOL(frontswap_tmem_exclusive_gets); + +/* + * Called when a swap device is swapon'd. + */ +void __frontswap_init(unsigned type) +{ + struct swap_info_struct *sis = swap_info[type]; + + BUG_ON(sis == NULL); + if (sis->frontswap_map == NULL) + return; + frontswap_ops.init(type); +} +EXPORT_SYMBOL(__frontswap_init); + +static inline void __frontswap_clear(struct swap_info_struct *sis, pgoff_t offset) +{ + frontswap_clear(sis, offset); + atomic_dec(&sis->frontswap_pages); +} + +/* + * "Store" data from a page to frontswap and associate it with the page's + * swaptype and offset. Page must be locked and in the swap cache. + * If frontswap already contains a page with matching swaptype and + * offset, the frontswap implementation may either overwrite the data and + * return success or invalidate the page from frontswap and return failure. + */ +int __frontswap_store(struct page *page) +{ + int ret = -1, dup = 0; + swp_entry_t entry = { .val = page_private(page), }; + int type = swp_type(entry); + struct swap_info_struct *sis = swap_info[type]; + pgoff_t offset = swp_offset(entry); + + BUG_ON(!PageLocked(page)); + BUG_ON(sis == NULL); + if (frontswap_test(sis, offset)) + dup = 1; + ret = frontswap_ops.store(type, offset, page); + if (ret == 0) { + frontswap_set(sis, offset); + inc_frontswap_succ_stores(); + if (!dup) + atomic_inc(&sis->frontswap_pages); + } else { + /* + failed dup always results in automatic invalidate of + the (older) page from frontswap + */ + inc_frontswap_failed_stores(); + if (dup) + __frontswap_clear(sis, offset); + } + if (frontswap_writethrough_enabled) + /* report failure so swap also writes to swap device */ + ret = -1; + return ret; +} +EXPORT_SYMBOL(__frontswap_store); + +/* + * "Get" data from frontswap associated with swaptype and offset that were + * specified when the data was put to frontswap and use it to fill the + * specified page with data. Page must be locked and in the swap cache. + */ +int __frontswap_load(struct page *page) +{ + int ret = -1; + swp_entry_t entry = { .val = page_private(page), }; + int type = swp_type(entry); + struct swap_info_struct *sis = swap_info[type]; + pgoff_t offset = swp_offset(entry); + + BUG_ON(!PageLocked(page)); + BUG_ON(sis == NULL); + if (frontswap_test(sis, offset)) + ret = frontswap_ops.load(type, offset, page); + if (ret == 0) { + inc_frontswap_loads(); + if (frontswap_tmem_exclusive_gets_enabled) { + SetPageDirty(page); + frontswap_clear(sis, offset); + } + } + return ret; +} +EXPORT_SYMBOL(__frontswap_load); + +/* + * Invalidate any data from frontswap associated with the specified swaptype + * and offset so that a subsequent "get" will fail. + */ +void __frontswap_invalidate_page(unsigned type, pgoff_t offset) +{ + struct swap_info_struct *sis = swap_info[type]; + + BUG_ON(sis == NULL); + if (frontswap_test(sis, offset)) { + frontswap_ops.invalidate_page(type, offset); + __frontswap_clear(sis, offset); + inc_frontswap_invalidates(); + } +} +EXPORT_SYMBOL(__frontswap_invalidate_page); + +/* + * Invalidate all data from frontswap associated with all offsets for the + * specified swaptype. + */ +void __frontswap_invalidate_area(unsigned type) +{ + struct swap_info_struct *sis = swap_info[type]; + + BUG_ON(sis == NULL); + if (sis->frontswap_map == NULL) + return; + frontswap_ops.invalidate_area(type); + atomic_set(&sis->frontswap_pages, 0); + memset(sis->frontswap_map, 0, sis->max / sizeof(long)); +} +EXPORT_SYMBOL(__frontswap_invalidate_area); + +static unsigned long __frontswap_curr_pages(void) +{ + int type; + unsigned long totalpages = 0; + struct swap_info_struct *si = NULL; + + assert_spin_locked(&swap_lock); + for (type = swap_list.head; type >= 0; type = si->next) { + si = swap_info[type]; + totalpages += atomic_read(&si->frontswap_pages); + } + return totalpages; +} + +static int __frontswap_unuse_pages(unsigned long total, unsigned long *unused, + int *swapid) +{ + int ret = -EINVAL; + struct swap_info_struct *si = NULL; + int si_frontswap_pages; + unsigned long total_pages_to_unuse = total; + unsigned long pages = 0, pages_to_unuse = 0; + int type; + + assert_spin_locked(&swap_lock); + for (type = swap_list.head; type >= 0; type = si->next) { + si = swap_info[type]; + si_frontswap_pages = atomic_read(&si->frontswap_pages); + if (total_pages_to_unuse < si_frontswap_pages) { + pages = pages_to_unuse = total_pages_to_unuse; + } else { + pages = si_frontswap_pages; + pages_to_unuse = 0; /* unuse all */ + } + /* ensure there is enough RAM to fetch pages from frontswap */ + if (security_vm_enough_memory_mm(current->mm, pages)) { + ret = -ENOMEM; + continue; + } + vm_unacct_memory(pages); + *unused = pages_to_unuse; + *swapid = type; + ret = 0; + break; + } + + return ret; +} + +/* + * Used to check if it's necessory and feasible to unuse pages. + * Return 1 when nothing to do, 0 when need to shink pages, + * error code when there is an error. + */ +static int __frontswap_shrink(unsigned long target_pages, + unsigned long *pages_to_unuse, + int *type) +{ + unsigned long total_pages = 0, total_pages_to_unuse; + + assert_spin_locked(&swap_lock); + + total_pages = __frontswap_curr_pages(); + if (total_pages <= target_pages) { + /* Nothing to do */ + *pages_to_unuse = 0; + return 1; + } + total_pages_to_unuse = total_pages - target_pages; + return __frontswap_unuse_pages(total_pages_to_unuse, pages_to_unuse, type); +} + +/* + * Frontswap, like a true swap device, may unnecessarily retain pages + * under certain circumstances; "shrink" frontswap is essentially a + * "partial swapoff" and works by calling try_to_unuse to attempt to + * unuse enough frontswap pages to attempt to -- subject to memory + * constraints -- reduce the number of pages in frontswap to the + * number given in the parameter target_pages. + */ +void frontswap_shrink(unsigned long target_pages) +{ + unsigned long pages_to_unuse = 0; + int uninitialized_var(type), ret; + + /* + * we don't want to hold swap_lock while doing a very + * lengthy try_to_unuse, but swap_list may change + * so restart scan from swap_list.head each time + */ + spin_lock(&swap_lock); + ret = __frontswap_shrink(target_pages, &pages_to_unuse, &type); + spin_unlock(&swap_lock); + if (ret == 0) + try_to_unuse(type, true, pages_to_unuse); + return; +} +EXPORT_SYMBOL(frontswap_shrink); + +/* + * Count and return the number of frontswap pages across all + * swap devices. This is exported so that backend drivers can + * determine current usage without reading debugfs. + */ +unsigned long frontswap_curr_pages(void) +{ + unsigned long totalpages = 0; + + spin_lock(&swap_lock); + totalpages = __frontswap_curr_pages(); + spin_unlock(&swap_lock); + + return totalpages; +} +EXPORT_SYMBOL(frontswap_curr_pages); + +static int __init init_frontswap(void) +{ +#ifdef CONFIG_DEBUG_FS + struct dentry *root = debugfs_create_dir("frontswap", NULL); + if (root == NULL) + return -ENXIO; + debugfs_create_u64("loads", S_IRUGO, root, &frontswap_loads); + debugfs_create_u64("succ_stores", S_IRUGO, root, &frontswap_succ_stores); + debugfs_create_u64("failed_stores", S_IRUGO, root, + &frontswap_failed_stores); + debugfs_create_u64("invalidates", S_IRUGO, + root, &frontswap_invalidates); +#endif + return 0; +} + +module_init(init_frontswap); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index a33f2a0f..26adea8c 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -35,9 +35,6 @@ #include /* __set_page_dirty_buffers */ #include #include -#ifdef CONFIG_DYNAMIC_PAGE_WRITEBACK -#include -#endif /* * Sleep at most 200ms at a time in balance_dirty_pages(). @@ -93,37 +90,12 @@ int vm_dirty_ratio = 20; */ unsigned long vm_dirty_bytes; -/* - * The default intervals between `kupdate'-style writebacks - */ -#define DEFAULT_DIRTY_WRITEBACK_INTERVAL 5 * 100 /* centiseconds */ -#define HIGH_DIRTY_WRITEBACK_INTERVAL 15 * 100 /* centiseconds */ - /* * The interval between `kupdate'-style writebacks */ -unsigned int dirty_writeback_interval = DEFAULT_DIRTY_WRITEBACK_INTERVAL; /* centiseconds */ -EXPORT_SYMBOL_GPL(dirty_writeback_interval); - -#ifdef CONFIG_DYNAMIC_PAGE_WRITEBACK -/* - * The dynamic writeback activation status - */ -int dyn_dirty_writeback_enabled = 1; -EXPORT_SYMBOL_GPL(dyn_dirty_writeback_enabled); +unsigned int dirty_writeback_interval = 5 * 100; /* centiseconds */ -/* - * The interval between `kupdate'-style writebacks when the system is active - */ -unsigned int dirty_writeback_active_interval = HIGH_DIRTY_WRITEBACK_INTERVAL; /* centiseconds */ -EXPORT_SYMBOL_GPL(dirty_writeback_active_interval); - -/* - * The interval between `kupdate'-style writebacks when the system is suspended - */ -unsigned int dirty_writeback_suspend_interval = DEFAULT_DIRTY_WRITEBACK_INTERVAL; /* centiseconds */ -EXPORT_SYMBOL_GPL(dirty_writeback_suspend_interval); -#endif +EXPORT_SYMBOL_GPL(dirty_writeback_interval); /* * The longest time for which data is allowed to remain dirty @@ -214,18 +186,6 @@ static unsigned long highmem_dirtyable_memory(unsigned long total) x += zone_page_state(z, NR_FREE_PAGES) + zone_reclaimable_pages(z) - z->dirty_balance_reserve; } - /* - * Unreclaimable memory (kernel memory or anonymous memory - * without swap) can bring down the dirtyable pages below - * the zone's dirty balance reserve and the above calculation - * will underflow. However we still want to add in nodes - * which are below threshold (negative values) to get a more - * accurate calculation but make sure that the total never - * underflows. - */ - if ((long)x < 0) - x = 0; - /* * Make sure that the number of highmem pages is never larger * than the number of the total dirtyable memory. This can only @@ -248,8 +208,8 @@ unsigned long global_dirtyable_memory(void) { unsigned long x; - x = global_page_state(NR_FREE_PAGES) + global_reclaimable_pages(); - x -= min(x, dirty_balance_reserve); + x = global_page_state(NR_FREE_PAGES) + global_reclaimable_pages() - + dirty_balance_reserve; if (!vm_highmem_is_dirtyable) x -= highmem_dirtyable_memory(x); @@ -316,12 +276,9 @@ static unsigned long zone_dirtyable_memory(struct zone *zone) * highmem zone can hold its share of dirty pages, so we don't * care about vm_highmem_is_dirtyable here. */ - unsigned long nr_pages = zone_page_state(zone, NR_FREE_PAGES) + - zone_reclaimable_pages(zone); - - /* don't allow this to underflow */ - nr_pages -= min(nr_pages, zone->dirty_balance_reserve); - return nr_pages; + return zone_page_state(zone, NR_FREE_PAGES) + + zone_reclaimable_pages(zone) - + zone->dirty_balance_reserve; } /** @@ -1100,11 +1057,11 @@ static unsigned long dirty_poll_interval(unsigned long dirty, return 1; } -static unsigned long bdi_max_pause(struct backing_dev_info *bdi, - unsigned long bdi_dirty) +static long bdi_max_pause(struct backing_dev_info *bdi, + unsigned long bdi_dirty) { - unsigned long bw = bdi->avg_write_bandwidth; - unsigned long t; + long bw = bdi->avg_write_bandwidth; + long t; /* * Limit pause time for small memory systems. If sleeping for too long @@ -1116,7 +1073,7 @@ static unsigned long bdi_max_pause(struct backing_dev_info *bdi, t = bdi_dirty / (1 + bw / roundup_pow_of_two(1 + HZ / 8)); t++; - return min_t(unsigned long, t, MAX_PAUSE); + return min_t(long, t, MAX_PAUSE); } static long bdi_min_pause(struct backing_dev_info *bdi, @@ -1551,68 +1508,6 @@ int dirty_writeback_centisecs_handler(ctl_table *table, int write, return 0; } -#ifdef CONFIG_DYNAMIC_PAGE_WRITEBACK -/* - * Manages the dirty page writebacks activation status - */ -static void set_dirty_writeback_status(bool active) { - /* Change the current dirty writeback interval according to the - * status provided */ - dirty_writeback_interval = (active) ? - dirty_writeback_active_interval : - dirty_writeback_suspend_interval; - - /* Update the timer related to dirty writebacks interval */ - bdi_arm_supers_timer(); - - /* Print debug info */ - pr_debug("%s: Set dirty_writeback_interval = %d centisecs\n", - __func__, dirty_writeback_interval); -} - -/* - * sysctl handler for /proc/sys/vm/dyn_dirty_writeback_enabled - */ -int dynamic_dirty_writeback_handler(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - int ret; - int old_status = dyn_dirty_writeback_enabled; - - /* Get and store the new status */ - ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); - - /* If the dynamic writeback has been enabled then set the active - * dirty writebacks interval, otherwise if the feature has been - * disabled, set the suspend interval (the default interval) - * to restore the standard functionality */ - if (ret == 0 && write && dyn_dirty_writeback_enabled != old_status) - set_dirty_writeback_status(!!dyn_dirty_writeback_enabled); - - return ret; -} - -/* - * sysctl handler for /proc/sys/vm/dirty_writeback_active_centisecs - */ -int dirty_writeback_active_centisecs_handler(ctl_table *table, int write, - void __user *buffer, size_t *length, loff_t *ppos) -{ - proc_dointvec_minmax(table, write, buffer, length, ppos); - return 0; -} - -/* - * sysctl handler for /proc/sys/vm/dirty_writeback_suspend_centisecs - */ -int dirty_writeback_suspend_centisecs_handler(ctl_table *table, int write, - void __user *buffer, size_t *length, loff_t *ppos) -{ - proc_dointvec_minmax(table, write, buffer, length, ppos); - return 0; -} -#endif - #ifdef CONFIG_BLOCK void laptop_mode_timer_fn(unsigned long data) { @@ -1678,46 +1573,18 @@ void writeback_set_ratelimit(void) ratelimit_pages = 16; } -static int +static int __cpuinit ratelimit_handler(struct notifier_block *self, unsigned long u, void *v) { writeback_set_ratelimit(); return NOTIFY_DONE; } -static struct notifier_block ratelimit_nb = { +static struct notifier_block __cpuinitdata ratelimit_nb = { .notifier_call = ratelimit_handler, .next = NULL, }; -#ifdef CONFIG_DYNAMIC_PAGE_WRITEBACK -/* - * Sets the dirty page writebacks interval for suspended system - */ -static void dirty_writeback_early_suspend(struct early_suspend *handler) -{ - if (dyn_dirty_writeback_enabled) - set_dirty_writeback_status(false); -} - -/* - * Sets the dirty page writebacks interval for active system - */ -static void dirty_writeback_late_resume(struct early_suspend *handler) -{ - if (dyn_dirty_writeback_enabled) - set_dirty_writeback_status(true); -} - -/* - * Struct for the dirty page writeback management during suspend/resume - */ -static struct early_suspend dirty_writeback_suspend = { - .suspend = dirty_writeback_early_suspend, - .resume = dirty_writeback_late_resume, -}; -#endif - /* * Called early on to tune the page writeback dirty limits. * @@ -1740,11 +1607,6 @@ void __init page_writeback_init(void) { int shift; -#ifdef CONFIG_DYNAMIC_PAGE_WRITEBACK - /* Register the dirty page writeback management during suspend/resume */ - register_early_suspend(&dirty_writeback_suspend); -#endif - writeback_set_ratelimit(); register_cpu_notifier(&ratelimit_nb); @@ -2116,12 +1978,11 @@ int __set_page_dirty_nobuffers(struct page *page) if (!TestSetPageDirty(page)) { struct address_space *mapping = page_mapping(page); struct address_space *mapping2; - unsigned long flags; if (!mapping) return 1; - spin_lock_irqsave(&mapping->tree_lock, flags); + spin_lock_irq(&mapping->tree_lock); mapping2 = page_mapping(page); if (mapping2) { /* Race with truncate? */ BUG_ON(mapping2 != mapping); @@ -2130,7 +1991,7 @@ int __set_page_dirty_nobuffers(struct page *page) radix_tree_tag_set(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); } - spin_unlock_irqrestore(&mapping->tree_lock, flags); + spin_unlock_irq(&mapping->tree_lock); if (mapping->host) { /* !PageAnon && !swapper_space */ __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); diff --git a/mm/swapfile.c b/mm/swapfile.c index 7fc4c406..3913f025 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -31,6 +31,9 @@ #include #include #include +#include +#include +#include #include #include @@ -42,7 +45,7 @@ static bool swap_count_continued(struct swap_info_struct *, pgoff_t, static void free_swap_count_continuations(struct swap_info_struct *); static sector_t map_swap_entry(swp_entry_t, struct block_device**); -static DEFINE_SPINLOCK(swap_lock); +DEFINE_SPINLOCK(swap_lock); static unsigned int nr_swapfiles; long nr_swap_pages; long total_swap_pages; @@ -53,9 +56,9 @@ static const char Unused_file[] = "Unused swap file entry "; static const char Bad_offset[] = "Bad swap offset entry "; static const char Unused_offset[] = "Unused swap offset entry "; -static struct swap_list_t swap_list = {-1, -1}; +struct swap_list_t swap_list = {-1, -1}; -static struct swap_info_struct *swap_info[MAX_SWAPFILES]; +struct swap_info_struct *swap_info[MAX_SWAPFILES]; static DEFINE_MUTEX(swapon_mutex); @@ -288,7 +291,7 @@ static unsigned long scan_swap_map(struct swap_info_struct *si, scan_base = offset = si->lowest_bit; /* reuse swap entry of cache-only swap if not busy. */ - if (vm_swap_full() && si->swap_map[offset] == SWAP_HAS_CACHE) { + if (si->swap_map[offset] == SWAP_HAS_CACHE) { int swap_was_freed; spin_unlock(&swap_lock); swap_was_freed = __try_to_reclaim_swap(si, offset); @@ -377,7 +380,7 @@ static unsigned long scan_swap_map(struct swap_info_struct *si, spin_lock(&swap_lock); goto checks; } - if (vm_swap_full() && si->swap_map[offset] == SWAP_HAS_CACHE) { + if (si->swap_map[offset] == SWAP_HAS_CACHE) { spin_lock(&swap_lock); goto checks; } @@ -392,7 +395,7 @@ static unsigned long scan_swap_map(struct swap_info_struct *si, spin_lock(&swap_lock); goto checks; } - if (vm_swap_full() && si->swap_map[offset] == SWAP_HAS_CACHE) { + if (si->swap_map[offset] == SWAP_HAS_CACHE) { spin_lock(&swap_lock); goto checks; } @@ -556,6 +559,7 @@ static unsigned char swap_entry_free(struct swap_info_struct *p, swap_list.next = p->type; nr_swap_pages++; p->inuse_pages--; + frontswap_invalidate_page(p->type, offset); if ((p->flags & SWP_BLKDEV) && disk->fops->swap_slot_free_notify) disk->fops->swap_slot_free_notify(p->bdev, offset); @@ -601,7 +605,7 @@ void swapcache_free(swp_entry_t entry, struct page *page) * This does not give an exact answer when swap count is continued, * but does include the high COUNT_CONTINUED flag to allow for that. */ -static inline int page_swapcount(struct page *page) +int page_swapcount(struct page *page) { int count = 0; struct swap_info_struct *p; @@ -706,8 +710,7 @@ int free_swap_and_cache(swp_entry_t entry) * Not mapped elsewhere, or swap space full? Free it! * Also recheck PageSwapCache now page is locked (above). */ - if (PageSwapCache(page) && !PageWriteback(page) && - (!page_mapped(page) || vm_swap_full())) { + if (PageSwapCache(page) && !PageWriteback(page)) { delete_from_swap_cache(page); SetPageDirty(page); } @@ -1016,11 +1019,12 @@ static int unuse_mm(struct mm_struct *mm, } /* - * Scan swap_map from current position to next entry still in use. + * Scan swap_map (or frontswap_map if frontswap parameter is true) + * from current position to next entry still in use. * Recycle to start on reaching the end, returning 0 when empty. */ static unsigned int find_next_to_unuse(struct swap_info_struct *si, - unsigned int prev) + unsigned int prev, bool frontswap) { unsigned int max = si->max; unsigned int i = prev; @@ -1046,6 +1050,12 @@ static unsigned int find_next_to_unuse(struct swap_info_struct *si, prev = 0; i = 1; } + if (frontswap) { + if (frontswap_test(si, i)) + break; + else + continue; + } count = si->swap_map[i]; if (count && swap_count(count) != SWAP_MAP_BAD) break; @@ -1057,8 +1067,12 @@ static unsigned int find_next_to_unuse(struct swap_info_struct *si, * We completely avoid races by reading each swap page in advance, * and then search for the process using it. All the necessary * page table adjustments can then be made atomically. + * + * if the boolean frontswap is true, only unuse pages_to_unuse pages; + * pages_to_unuse==0 means all pages; ignored if frontswap is false */ -static int try_to_unuse(unsigned int type) +int try_to_unuse(unsigned int type, bool frontswap, + unsigned long pages_to_unuse) { struct swap_info_struct *si = swap_info[type]; struct mm_struct *start_mm; @@ -1091,7 +1105,7 @@ static int try_to_unuse(unsigned int type) * one pass through swap_map is enough, but not necessarily: * there are races when an instance of an entry might be missed. */ - while ((i = find_next_to_unuse(si, i)) != 0) { + while ((i = find_next_to_unuse(si, i, frontswap)) != 0) { if (signal_pending(current)) { retval = -EINTR; break; @@ -1258,6 +1272,10 @@ static int try_to_unuse(unsigned int type) * interactive performance. */ cond_resched(); + if (frontswap && pages_to_unuse > 0) { + if (!--pages_to_unuse) + break; + } } mmput(start_mm); @@ -1517,7 +1535,8 @@ static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span) } static void enable_swap_info(struct swap_info_struct *p, int prio, - unsigned char *swap_map) + unsigned char *swap_map, + unsigned long *frontswap_map) { int i, prev; @@ -1527,6 +1546,7 @@ static void enable_swap_info(struct swap_info_struct *p, int prio, else p->prio = --least_priority; p->swap_map = swap_map; + frontswap_map_set(p, frontswap_map); p->flags |= SWP_WRITEOK; nr_swap_pages += p->pages; total_swap_pages += p->pages; @@ -1543,6 +1563,7 @@ static void enable_swap_info(struct swap_info_struct *p, int prio, swap_list.head = swap_list.next = p->type; else swap_info[prev]->next = p->type; + frontswap_init(p->type); spin_unlock(&swap_lock); } @@ -1616,7 +1637,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) spin_unlock(&swap_lock); oom_score_adj = test_set_oom_score_adj(OOM_SCORE_ADJ_MAX); - err = try_to_unuse(type); + err = try_to_unuse(type, false, 0); /* force all pages to be unused */ compare_swap_oom_score_adj(OOM_SCORE_ADJ_MAX, oom_score_adj); if (err) { @@ -1627,7 +1648,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) * sys_swapoff for this swap_info_struct at this point. */ /* re-insert swap space back into swap_list */ - enable_swap_info(p, p->prio, p->swap_map); + enable_swap_info(p, p->prio, p->swap_map, frontswap_map_get(p)); goto out_dput; } @@ -1654,8 +1675,10 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) p->swap_map = NULL; p->flags = 0; spin_unlock(&swap_lock); + frontswap_invalidate_area(type); mutex_unlock(&swapon_mutex); vfree(swap_map); + vfree(frontswap_map_get(p)); /* Destroy swap account informatin */ swap_cgroup_swapoff(type); @@ -2015,6 +2038,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) sector_t span; unsigned long maxpages; unsigned char *swap_map = NULL; + unsigned long *frontswap_map = NULL; struct page *page = NULL; struct inode *inode = NULL; @@ -2098,6 +2122,9 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) error = nr_extents; goto bad_swap; } + /* frontswap enabled? set up bit-per-page map for frontswap */ + if (frontswap_enabled) + frontswap_map = vzalloc(maxpages / sizeof(long)); if (p->bdev) { if (blk_queue_nonrot(bdev_get_queue(p->bdev))) { @@ -2113,14 +2140,15 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) if (swap_flags & SWAP_FLAG_PREFER) prio = (swap_flags & SWAP_FLAG_PRIO_MASK) >> SWAP_FLAG_PRIO_SHIFT; - enable_swap_info(p, prio, swap_map); + enable_swap_info(p, prio, swap_map, frontswap_map); printk(KERN_INFO "Adding %uk swap on %s. " - "Priority:%d extents:%d across:%lluk %s%s\n", + "Priority:%d extents:%d across:%lluk %s%s%s\n", p->pages<<(PAGE_SHIFT-10), name, p->prio, nr_extents, (unsigned long long)span<<(PAGE_SHIFT-10), (p->flags & SWP_SOLIDSTATE) ? "SS" : "", - (p->flags & SWP_DISCARDABLE) ? "D" : ""); + (p->flags & SWP_DISCARDABLE) ? "D" : "", + (frontswap_map) ? "FS" : ""); mutex_unlock(&swapon_mutex); atomic_inc(&proc_poll_event); @@ -2295,6 +2323,24 @@ struct swap_info_struct *page_swap_info(struct page *page) return swap_info[swp_type(swap)]; } +/* + * out-of-line __page_file_ methods to avoid include hell. + */ +struct address_space *__page_file_mapping(struct page *page) +{ + VM_BUG_ON(!PageSwapCache(page)); + return page_swap_info(page)->swap_file->f_mapping; +} +EXPORT_SYMBOL_GPL(__page_file_mapping); + +pgoff_t __page_file_index(struct page *page) +{ + swp_entry_t swap = { .val = page_private(page) }; + VM_BUG_ON(!PageSwapCache(page)); + return swp_offset(swap); +} +EXPORT_SYMBOL_GPL(__page_file_index); + /* * add_swap_count_continuation - called when a swap count is duplicated * beyond SWAP_MAP_MAX, it allocates a new page and links that to the entry's diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/mm/zsmalloc.c similarity index 88% rename from drivers/staging/zsmalloc/zsmalloc-main.c rename to mm/zsmalloc.c index 41a68032..9b6f696d 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/mm/zsmalloc.c @@ -2,6 +2,7 @@ * zsmalloc memory allocator * * Copyright (C) 2011 Nitin Gupta + * Copyright (C) 2012, 2013 Minchan Kim * * This code is released using a dual license strategy: BSD/GPL * You can choose the license that better fits your requirements. @@ -10,16 +11,14 @@ * Released under the terms of GNU General Public License Version 2.0 */ - /* - * This allocator is designed for use with zcache and zram. Thus, the - * allocator is supposed to work well under low memory conditions. In - * particular, it never attempts higher order page allocation which is - * very likely to fail under memory pressure. On the other hand, if we - * just use single (0-order) pages, it would suffer from very high - * fragmentation -- any object of size PAGE_SIZE/2 or larger would occupy - * an entire page. This was one of the major issues with its predecessor - * (xvmalloc). + * This allocator is designed for use with zram. Thus, the allocator is + * supposed to work well under low memory conditions. In particular, it + * never attempts higher order page allocation which is very likely to + * fail under memory pressure. On the other hand, if we just use single + * (0-order) pages, it would suffer from very high fragmentation -- + * any object of size PAGE_SIZE/2 or larger would occupy an entire page. + * This was one of the major issues with its predecessor (xvmalloc). * * To overcome these issues, zsmalloc allocates a bunch of 0-order pages * and links them together using various 'struct page' fields. These linked @@ -27,6 +26,21 @@ * page boundaries. The code refers to these linked pages as a single entity * called zspage. * + * For simplicity, zsmalloc can only allocate objects of size up to PAGE_SIZE + * since this satisfies the requirements of all its current users (in the + * worst case, page is incompressible and is thus stored "as-is" i.e. in + * uncompressed form). For allocation requests larger than this size, failure + * is returned (see zs_malloc). + * + * Additionally, zs_malloc() does not return a dereferenceable pointer. + * Instead, it returns an opaque handle (unsigned long) which encodes actual + * location of the allocated object. The reason for this indirection is that + * zsmalloc does not keep zspages permanently mapped since that would cause + * issues on 32-bit systems where the VA region for kernel space mappings + * is very small. So, before using the allocating memory, the object has to + * be mapped using zs_map_object() to get a usable pointer and subsequently + * unmapped using zs_unmap_object(). + * * Following is how we use various fields and flags of underlying * struct page(s) to form a zspage. * @@ -67,7 +81,6 @@ #include #include #include -#include #include #include #include @@ -78,8 +91,7 @@ #include #include #include - -#include "zsmalloc.h" +#include /* * This must be power of 2 and greater than of equal to sizeof(link_free). @@ -98,7 +110,7 @@ /* * Object location (, ) is encoded as - * as single (void *) handle value. + * as single (unsigned long) handle value. * * Note that object index is relative to system * page it is stored in, so for each sub-page belonging @@ -218,19 +230,8 @@ struct zs_pool { #define CLASS_IDX_MASK ((1 << CLASS_IDX_BITS) - 1) #define FULLNESS_MASK ((1 << FULLNESS_BITS) - 1) -/* - * By default, zsmalloc uses a copy-based object mapping method to access - * allocations that span two pages. However, if a particular architecture - * performs VM mapping faster than copying, then it should be added here - * so that USE_PGTABLE_MAPPING is defined. This causes zsmalloc to use - * page table mapping rather than copying for object mapping. - */ -#if defined(CONFIG_ARM) && !defined(MODULE) -#define USE_PGTABLE_MAPPING -#endif - struct mapping_area { -#ifdef USE_PGTABLE_MAPPING +#ifdef CONFIG_PGTABLE_MAPPING struct vm_struct *vm; /* vm area for mapping object that span pages */ #else char *vm_buf; /* copy buffer for objects that span pages */ @@ -275,6 +276,13 @@ static void set_zspage_mapping(struct page *page, unsigned int class_idx, page->mapping = (struct address_space *)m; } +/* + * zsmalloc divides the pool into various size classes where each + * class maintains a list of zspages where each zspage is divided + * into equal sized chunks. Each allocation falls into one of these + * classes depending on its size. This function returns index of the + * size class which has chunk size big enough to hold the give size. + */ static int get_size_class_index(int size) { int idx = 0; @@ -286,6 +294,13 @@ static int get_size_class_index(int size) return idx; } +/* + * For each size class, zspages are divided into different groups + * depending on how "full" they are. This was done so that we could + * easily find empty or nearly empty zspages when we try to shrink + * the pool (not yet implemented). This function returns fullness + * status of the given page. + */ static enum fullness_group get_fullness_group(struct page *page) { int inuse, max_objects; @@ -307,6 +322,12 @@ static enum fullness_group get_fullness_group(struct page *page) return fg; } +/* + * Each size class maintains various freelists and zspages are assigned + * to one of these freelists based on the number of live objects they + * have. This functions inserts the given zspage into the freelist + * identified by . + */ static void insert_zspage(struct page *page, struct size_class *class, enum fullness_group fullness) { @@ -324,6 +345,10 @@ static void insert_zspage(struct page *page, struct size_class *class, *head = page; } +/* + * This function removes the given zspage from the freelist identified + * by . + */ static void remove_zspage(struct page *page, struct size_class *class, enum fullness_group fullness) { @@ -345,6 +370,15 @@ static void remove_zspage(struct page *page, struct size_class *class, list_del_init(&page->lru); } +/* + * Each size class maintains zspages in different fullness groups depending + * on the number of live objects they contain. When allocating or freeing + * objects, the fullness status of the page can change, say, from ALMOST_FULL + * to ALMOST_EMPTY when freeing an object. This function checks if such + * a status change has occurred for the given page and accordingly moves the + * page from the freelist of the old fullness group to that of the new + * fullness group. + */ static enum fullness_group fix_fullness_group(struct zs_pool *pool, struct page *page) { @@ -631,7 +665,7 @@ static struct page *find_get_zspage(struct size_class *class) return page; } -#ifdef USE_PGTABLE_MAPPING +#ifdef CONFIG_PGTABLE_MAPPING static inline int __zs_cpu_up(struct mapping_area *area) { /* @@ -669,7 +703,7 @@ static inline void __zs_unmap_object(struct mapping_area *area, unmap_kernel_range(addr, PAGE_SIZE * 2); } -#else /* USE_PGTABLE_MAPPING */ +#else /* CONFIG_PGTABLE_MAPPING */ static inline int __zs_cpu_up(struct mapping_area *area) { @@ -747,7 +781,7 @@ static void __zs_unmap_object(struct mapping_area *area, pagefault_enable(); } -#endif /* USE_PGTABLE_MAPPING */ +#endif /* CONFIG_PGTABLE_MAPPING */ static int zs_cpu_notifier(struct notifier_block *nb, unsigned long action, void *pcpu) @@ -780,21 +814,32 @@ static void zs_exit(void) { int cpu; + cpu_notifier_register_begin(); + for_each_online_cpu(cpu) zs_cpu_notifier(NULL, CPU_DEAD, (void *)(long)cpu); - unregister_cpu_notifier(&zs_cpu_nb); + __unregister_cpu_notifier(&zs_cpu_nb); + + cpu_notifier_register_done(); } static int zs_init(void) { int cpu, ret; - register_cpu_notifier(&zs_cpu_nb); + cpu_notifier_register_begin(); + + __register_cpu_notifier(&zs_cpu_nb); for_each_online_cpu(cpu) { ret = zs_cpu_notifier(NULL, CPU_UP_PREPARE, (void *)(long)cpu); - if (notifier_to_errno(ret)) + if (notifier_to_errno(ret)) { + cpu_notifier_register_done(); goto fail; + } } + + cpu_notifier_register_done(); + return 0; fail: zs_exit(); diff --git a/net/Makefile b/net/Makefile index 6865dab6..eee6577c 100644 --- a/net/Makefile +++ b/net/Makefile @@ -48,7 +48,7 @@ endif obj-$(CONFIG_IP_DCCP) += dccp/ obj-$(CONFIG_IP_SCTP) += sctp/ obj-$(CONFIG_RDS) += rds/ -obj-$(CONFIG_WIRELESS) += wireless/ +obj-$(CONFIG_WIRELESS) += wireless_ath/ obj-$(CONFIG_MAC80211) += mac80211/ obj-$(CONFIG_TIPC) += tipc/ obj-$(CONFIG_NETLABEL) += netlabel/ diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c old mode 100644 new mode 100755 index 30b1ec03..984f9f6b --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Technologies, Inc. + Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky @@ -290,8 +290,6 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, return err; } - msg->msg_namelen = 0; - copied = skb->len; if (len < copied) { msg->msg_flags |= MSG_TRUNC; @@ -349,8 +347,6 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, if (flags & MSG_OOB) return -EOPNOTSUPP; - msg->msg_namelen = 0; - BT_DBG("sk %p size %zu", sk, size); lock_sock(sk); diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index c19be91e..ed3bff83 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2010-2012 The Linux Foundation. All rights reserved. + Copyright (c) 2010-2011 Code Aurora Forum. 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 and @@ -10,6 +10,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ +// rollback to original BlueZ #include #include @@ -88,15 +89,15 @@ static struct amp_mgr *get_amp_mgr_sk(struct sock *sk) return found; } -static struct amp_mgr *get_create_amp_mgr(struct hci_conn *hcon, +static struct amp_mgr *get_create_amp_mgr(struct l2cap_conn *conn, struct sk_buff *skb) { struct amp_mgr *mgr; write_lock(&_mgr_list_lock); list_for_each_entry(mgr, &_mgr_list, list) { - if (mgr->l2cap_conn == hcon->l2cap_data) { - BT_DBG("found %p", mgr); + if (mgr->l2cap_conn == conn) { + BT_DBG("conn %p found %p", conn, mgr); write_unlock(&_mgr_list_lock); goto gc_finished; } @@ -107,13 +108,13 @@ static struct amp_mgr *get_create_amp_mgr(struct hci_conn *hcon, if (!mgr) return NULL; - mgr->l2cap_conn = hcon->l2cap_data; + mgr->l2cap_conn = conn; mgr->next_ident = 1; INIT_LIST_HEAD(&mgr->ctx_list); rwlock_init(&mgr->ctx_list_lock); mgr->skb = skb; - BT_DBG("hcon %p mgr %p", hcon, mgr); - mgr->a2mp_sock = open_fixed_channel(&hcon->hdev->bdaddr, &hcon->dst); + BT_DBG("conn %p mgr %p", conn, mgr); + mgr->a2mp_sock = open_fixed_channel(conn->src, conn->dst); if (!mgr->a2mp_sock) { kfree(mgr); return NULL; @@ -375,11 +376,13 @@ static void send_a2mp_change_notify(void) { struct amp_mgr *mgr; + read_lock(&_mgr_list_lock); list_for_each_entry(mgr, &_mgr_list, list) { if (mgr->discovered) send_a2mp_cl(mgr, next_ident(mgr), A2MP_CHANGE_NOTIFY, 0, NULL); } + read_unlock(&_mgr_list_lock); } static inline int discover_req(struct amp_mgr *mgr, struct sk_buff *skb) @@ -483,7 +486,7 @@ static void create_physical(struct l2cap_conn *conn, struct sock *sk) struct amp_ctx *ctx = NULL; BT_DBG("conn %p", conn); - mgr = get_create_amp_mgr(conn->hcon, NULL); + mgr = get_create_amp_mgr(conn, NULL); if (!mgr) goto cp_finished; BT_DBG("mgr %p", mgr); @@ -513,7 +516,7 @@ static void accept_physical(struct l2cap_conn *lcon, u8 id, struct sock *sk) if (!hdev) goto ap_finished; BT_DBG("hdev %p", hdev); - mgr = get_create_amp_mgr(lcon->hcon, NULL); + mgr = get_create_amp_mgr(lcon, NULL); if (!mgr) goto ap_finished; BT_DBG("mgr %p", mgr); @@ -1157,7 +1160,6 @@ static u8 createphyslink_handler(struct amp_ctx *ctx, u8 evt_type, void *data) if (skb->len < sizeof(*grsp)) goto cpl_finished; grsp = (struct a2mp_getinfo_rsp *) skb_pull(skb, sizeof(*hdr)); - skb_pull(skb, sizeof(*grsp)); if (grsp->status) goto cpl_finished; if (grsp->id != ctx->d.cpl.remote_id) @@ -1171,6 +1173,7 @@ static u8 createphyslink_handler(struct amp_ctx *ctx, u8 evt_type, void *data) ctrl->min_latency = le32_to_cpu(grsp->min_latency); ctrl->pal_cap = le16_to_cpu(grsp->pal_cap); ctrl->max_assoc_size = le16_to_cpu(grsp->assoc_size); + skb_pull(skb, sizeof(*grsp)); ctx->d.cpl.max_len = ctrl->max_assoc_size; @@ -1190,6 +1193,8 @@ static u8 createphyslink_handler(struct amp_ctx *ctx, u8 evt_type, void *data) goto cpl_finished; hdr = (void *) skb->data; arsp = (void *) skb_pull(skb, sizeof(*hdr)); + if (arsp->id != ctx->d.cpl.remote_id) + goto cpl_finished; if (arsp->status != 0) goto cpl_finished; @@ -1197,12 +1202,12 @@ static u8 createphyslink_handler(struct amp_ctx *ctx, u8 evt_type, void *data) assoc = (u8 *) skb_pull(skb, sizeof(*arsp)); ctx->d.cpl.len_so_far = 0; ctx->d.cpl.rem_len = hdr->len - sizeof(*arsp); - skb_pull(skb, ctx->d.cpl.rem_len); rassoc = kmalloc(ctx->d.cpl.rem_len, GFP_ATOMIC); if (!rassoc) goto cpl_finished; memcpy(rassoc, assoc, ctx->d.cpl.rem_len); ctx->d.cpl.remote_assoc = rassoc; + skb_pull(skb, ctx->d.cpl.rem_len); /* set up CPL command */ ctx->d.cpl.phy_handle = physlink_handle(ctx->hdev); @@ -1772,13 +1777,12 @@ static struct socket *open_fixed_channel(bdaddr_t *src, bdaddr_t *dst) static void conn_ind_worker(struct work_struct *w) { struct amp_work_conn_ind *work = (struct amp_work_conn_ind *) w; - struct hci_conn *hcon = work->hcon; + struct l2cap_conn *conn = work->conn; struct sk_buff *skb = work->skb; struct amp_mgr *mgr; - mgr = get_create_amp_mgr(hcon, skb); + mgr = get_create_amp_mgr(conn, skb); BT_DBG("mgr %p", mgr); - hci_conn_put(hcon); kfree(work); } @@ -1804,20 +1808,17 @@ static void accept_physical_worker(struct work_struct *w) /* L2CAP Fixed Channel interface */ -void amp_conn_ind(struct hci_conn *hcon, struct sk_buff *skb) +void amp_conn_ind(struct l2cap_conn *conn, struct sk_buff *skb) { struct amp_work_conn_ind *work; - BT_DBG("hcon %p, skb %p", hcon, skb); + BT_DBG("conn %p, skb %p", conn, skb); work = kmalloc(sizeof(*work), GFP_ATOMIC); if (work) { INIT_WORK((struct work_struct *) work, conn_ind_worker); - hci_conn_hold(hcon); - work->hcon = hcon; + work->conn = conn; work->skb = skb; - if (!queue_work(amp_workqueue, (struct work_struct *) work)) { - hci_conn_put(hcon); + if (queue_work(amp_workqueue, (struct work_struct *) work) == 0) kfree(work); - } } } diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 5ac5c52a..f5049219 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -56,9 +56,6 @@ #include "bnep.h" #define VERSION "1.3" -/* As this feature is dummy for BNEP net device -** disabling support */ -#undef CONFIG_BT_BNEP_MC_FILTER static bool compress_src = 1; static bool compress_dst = 1; diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index a8e54493..155ff740 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -44,9 +44,6 @@ #include "bnep.h" #define BNEP_TX_QUEUE_LEN 20 -/* As this feature is dummy for BNEP net device -** disabling support */ -#undef CONFIG_BT_BNEP_MC_FILTER static int bnep_net_open(struct net_device *dev) { diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 91dd3f90..dbc6378a 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010-2012 The Linux Foundation. All rights reserved. + Copyright (c) 2000-2001, 2010-2011 Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky @@ -23,6 +23,7 @@ */ /* Bluetooth HCI connection handling. */ +// rollback to original BlueZ #include @@ -44,90 +45,39 @@ #include #include -#include -struct hci_conn *hci_le_connect(struct hci_dev *hdev, __u16 pkt_type, - bdaddr_t *dst, __u8 sec_level, __u8 auth_type, - struct bt_le_params *le_params) +static void hci_le_connect(struct hci_conn *conn) { - struct hci_conn *le; + struct hci_dev *hdev = conn->hdev; struct hci_cp_le_create_conn cp; - struct adv_entry *entry; - struct link_key *key; - - BT_DBG("%p", hdev); - - le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst); - if (le) { - hci_conn_hold(le); - return le; - } - - key = hci_find_link_key_type(hdev, dst, KEY_TYPE_LTK); - if (!key) { - entry = hci_find_adv_entry(hdev, dst); - if (entry) - le = hci_le_conn_add(hdev, dst, - entry->bdaddr_type); - else - le = hci_le_conn_add(hdev, dst, 0); - } else { - le = hci_le_conn_add(hdev, dst, key->addr_type); - } - - if (!le) - return ERR_PTR(-ENOMEM); - hci_conn_hold(le); + BT_DBG("%p", conn); - le->state = BT_CONNECT; - le->out = 1; - le->link_mode |= HCI_LM_MASTER; - le->sec_level = BT_SECURITY_LOW; - le->type = LE_LINK; + conn->state = BT_CONNECT; + conn->out = 1; + conn->link_mode |= HCI_LM_MASTER; + conn->sec_level = BT_SECURITY_LOW; + conn->type = LE_LINK; memset(&cp, 0, sizeof(cp)); - if (l2cap_sock_le_params_valid(le_params)) { - cp.supervision_timeout = - cpu_to_le16(le_params->supervision_timeout); - cp.scan_interval = cpu_to_le16(le_params->scan_interval); - cp.scan_window = cpu_to_le16(le_params->scan_window); - cp.conn_interval_min = cpu_to_le16(le_params->interval_min); - cp.conn_interval_max = cpu_to_le16(le_params->interval_max); - cp.conn_latency = cpu_to_le16(le_params->latency); - cp.min_ce_len = cpu_to_le16(le_params->min_ce_len); - cp.max_ce_len = cpu_to_le16(le_params->max_ce_len); - le->conn_timeout = le_params->conn_timeout; - } else { - cp.supervision_timeout = cpu_to_le16(BT_LE_SUP_TO_DEFAULT); - cp.scan_interval = cpu_to_le16(BT_LE_SCAN_INTERVAL_DEF); - cp.scan_window = cpu_to_le16(BT_LE_SCAN_WINDOW_DEF); - cp.conn_interval_min = cpu_to_le16(BT_LE_CONN_INTERVAL_MIN_DEF); - cp.conn_interval_max = cpu_to_le16(BT_LE_CONN_INTERVAL_MAX_DEF); - cp.conn_latency = cpu_to_le16(BT_LE_LATENCY_DEF); - le->conn_timeout = 5; - } - bacpy(&cp.peer_addr, &le->dst); - cp.peer_addr_type = le->dst_type; + cp.scan_interval = cpu_to_le16(0x0004); + cp.scan_window = cpu_to_le16(0x0004); + bacpy(&cp.peer_addr, &conn->dst); + cp.peer_addr_type = conn->dst_type; + cp.conn_interval_min = cpu_to_le16(0x0008); + cp.conn_interval_max = cpu_to_le16(0x0100); + cp.supervision_timeout = cpu_to_le16(0x0064); + cp.min_ce_len = cpu_to_le16(0x0001); + cp.max_ce_len = cpu_to_le16(0x0001); hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp); - - return le; } -EXPORT_SYMBOL(hci_le_connect); static void hci_le_connect_cancel(struct hci_conn *conn) { hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL); } -static inline bool is_role_switch_possible(struct hci_dev *hdev) -{ - if (hci_conn_hash_lookup_state(hdev, ACL_LINK, BT_CONNECTED)) - return false; - return true; -} - void hci_acl_connect(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; @@ -163,8 +113,7 @@ void hci_acl_connect(struct hci_conn *conn) } cp.pkt_type = cpu_to_le16(conn->pkt_type); - if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER) - && is_role_switch_possible(hdev)) + if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER)) cp.role_switch = 0x01; else cp.role_switch = 0x00; @@ -277,18 +226,6 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, } EXPORT_SYMBOL(hci_le_conn_update); -void hci_read_rssi(struct hci_conn *conn) -{ - struct hci_cp_read_rssi cp; - struct hci_dev *hdev = conn->hdev; - - memset(&cp, 0, sizeof(cp)); - cp.handle = cpu_to_le16(conn->handle); - - hci_send_cmd(hdev, HCI_OP_READ_RSSI, sizeof(cp), &cp); -} -EXPORT_SYMBOL(hci_read_rssi); - void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], __u8 ltk[16]) { @@ -385,37 +322,9 @@ static void hci_conn_idle(unsigned long arg) BT_DBG("conn %p mode %d", conn, conn->mode); + hci_dev_lock(conn->hdev); hci_conn_enter_sniff_mode(conn); -} - -static void hci_conn_rssi_update(struct work_struct *work) -{ - struct delayed_work *delayed = - container_of(work, struct delayed_work, work); - struct hci_conn *conn = - container_of(delayed, struct hci_conn, rssi_update_work); - - BT_DBG("conn %p mode %d", conn, conn->mode); - - hci_read_rssi(conn); -} - -static void encryption_disabled_timeout(unsigned long userdata) -{ - struct hci_conn *conn = (struct hci_conn *)userdata; - BT_INFO("conn %p Grace Prd Exp ", conn); - - hci_encrypt_cfm(conn, 0, 0); - - if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { - struct hci_cp_set_conn_encrypt cp; - BT_INFO("HCI_CONN_ENCRYPT_PEND is set"); - cp.handle = cpu_to_le16(conn->handle); - cp.encrypt = 1; - hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, - sizeof(cp), &cp); - } - + hci_dev_unlock(conn->hdev); } struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, @@ -440,14 +349,10 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, conn->power_save = 1; conn->disc_timeout = HCI_DISCONN_TIMEOUT; - conn->conn_valid = true; - spin_lock_init(&conn->lock); - wake_lock_init(&conn->idle_lock, WAKE_LOCK_SUSPEND, "bt_idle"); switch (type) { case ACL_LINK: conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK; - conn->link_policy = hdev->link_policy; break; case SCO_LINK: if (!pkt_type) @@ -473,9 +378,6 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn); setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn); - INIT_DELAYED_WORK(&conn->rssi_update_work, hci_conn_rssi_update); - setup_timer(&conn->encrypt_pause_timer, encryption_disabled_timeout, - (unsigned long)conn); atomic_set(&conn->refcnt, 0); @@ -514,17 +416,9 @@ int hci_conn_del(struct hci_conn *conn) BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle); - spin_lock_bh(&conn->lock); - conn->conn_valid = false; /* conn data is being released */ - spin_unlock_bh(&conn->lock); - - /* Make sure no timers are running */ del_timer(&conn->idle_timer); - wake_lock_destroy(&conn->idle_lock); + del_timer(&conn->disc_timer); - del_timer(&conn->smp_timer); - __cancel_delayed_work(&conn->rssi_update_work); - del_timer(&conn->encrypt_pause_timer); if (conn->type == ACL_LINK) { struct hci_conn *sco = conn->link; @@ -552,8 +446,6 @@ int hci_conn_del(struct hci_conn *conn) if (hdev->notify) hdev->notify(hdev, HCI_NOTIFY_CONN_DEL); - tasklet_schedule(&hdev->tx_task); - tasklet_enable(&hdev->tx_task); skb_queue_purge(&conn->data_q); @@ -585,7 +477,6 @@ struct hci_chan *hci_chan_add(struct hci_dev *hdev) return chan; } -EXPORT_SYMBOL(hci_chan_add); int hci_chan_del(struct hci_chan *chan) { @@ -604,24 +495,18 @@ int hci_chan_del(struct hci_chan *chan) int hci_chan_put(struct hci_chan *chan) { struct hci_cp_disconn_logical_link cp; - struct hci_conn *hcon; - u16 ll_handle; BT_DBG("chan %p refcnt %d", chan, atomic_read(&chan->refcnt)); if (!atomic_dec_and_test(&chan->refcnt)) return 0; - hcon = chan->conn; - ll_handle = chan->ll_handle; - - hci_chan_del(chan); - - BT_DBG("chan->conn->state %d", hcon->state); - if (hcon->state == BT_CONNECTED) { - cp.log_handle = cpu_to_le16(ll_handle); - hci_send_cmd(hcon->hdev, HCI_OP_DISCONN_LOGICAL_LINK, + BT_DBG("chan->conn->state %d", chan->conn->state); + if (chan->conn->state == BT_CONNECTED) { + cp.log_handle = cpu_to_le16(chan->ll_handle); + hci_send_cmd(chan->conn->hdev, HCI_OP_DISCONN_LOGICAL_LINK, sizeof(cp), &cp); - } + } else + hci_chan_del(chan); return 1; } @@ -729,12 +614,41 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, { struct hci_conn *acl; struct hci_conn *sco; + struct hci_conn *le; BT_DBG("%s dst %s", hdev->name, batostr(dst)); - if (type == LE_LINK) - return hci_le_connect(hdev, pkt_type, dst, sec_level, - auth_type, NULL); + if (type == LE_LINK) { + struct adv_entry *entry; + struct link_key *key; + + le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst); + if (le) { + hci_conn_hold(le); + return le; + } + + key = hci_find_link_key_type(hdev, dst, KEY_TYPE_LTK); + if (!key) { + entry = hci_find_adv_entry(hdev, dst); + if (entry) + le = hci_le_conn_add(hdev, dst, + entry->bdaddr_type); + else + le = hci_le_conn_add(hdev, dst, 0); + } else { + le = hci_le_conn_add(hdev, dst, key->addr_type); + } + + if (!le) + return ERR_PTR(-ENOMEM); + + hci_le_connect(le); + + hci_conn_hold(le); + + return le; + } acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); if (!acl) { @@ -791,7 +705,7 @@ void hci_disconnect(struct hci_conn *conn, __u8 reason) { BT_DBG("conn %p", conn); - hci_proto_disconn_cfm(conn, reason, 0); + hci_proto_disconn_cfm(conn, reason); } EXPORT_SYMBOL(hci_disconnect); @@ -850,10 +764,6 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { struct hci_cp_auth_requested cp; - - /* encrypt must be pending if auth is also pending */ - set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); - cp.handle = cpu_to_le16(conn->handle); hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); @@ -884,16 +794,12 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) return 0; } else if (conn->link_mode & HCI_LM_ENCRYPT) { return hci_conn_auth(conn, sec_level, auth_type); - } else if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { + } else if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { return 0; } if (hci_conn_auth(conn, sec_level, auth_type)) { struct hci_cp_set_conn_encrypt cp; - if (timer_pending(&conn->encrypt_pause_timer)) { - BT_INFO("encrypt_pause_timer is pending"); - return 0; - } cp.handle = cpu_to_le16(conn->handle); cp.encrypt = 1; hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, @@ -965,52 +871,9 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) } timer: - if (hdev->idle_timeout > 0) { - spin_lock_bh(&conn->lock); - if (conn->conn_valid) { - mod_timer(&conn->idle_timer, - jiffies + msecs_to_jiffies(hdev->idle_timeout)); - wake_lock(&conn->idle_lock); - } - spin_unlock_bh(&conn->lock); - } -} - -static inline void hci_conn_stop_rssi_timer(struct hci_conn *conn) -{ - BT_DBG("conn %p", conn); - cancel_delayed_work(&conn->rssi_update_work); -} - -static inline void hci_conn_start_rssi_timer(struct hci_conn *conn, - u16 interval) -{ - struct hci_dev *hdev = conn->hdev; - BT_DBG("conn %p, pending %d", conn, - delayed_work_pending(&conn->rssi_update_work)); - if (!delayed_work_pending(&conn->rssi_update_work)) { - queue_delayed_work(hdev->workqueue, &conn->rssi_update_work, - msecs_to_jiffies(interval)); - } -} - -void hci_conn_set_rssi_reporter(struct hci_conn *conn, - s8 rssi_threshold, u16 interval, u8 updateOnThreshExceed) -{ - if (conn) { - conn->rssi_threshold = rssi_threshold; - conn->rssi_update_interval = interval; - conn->rssi_update_thresh_exceed = updateOnThreshExceed; - hci_conn_start_rssi_timer(conn, interval); - } -} - -void hci_conn_unset_rssi_reporter(struct hci_conn *conn) -{ - if (conn) { - BT_DBG("Deleting the rssi_update_timer"); - hci_conn_stop_rssi_timer(conn); - } + if (hdev->idle_timeout > 0) + mod_timer(&conn->idle_timer, + jiffies + msecs_to_jiffies(hdev->idle_timeout)); } /* Enter sniff mode */ @@ -1029,9 +892,7 @@ void hci_conn_enter_sniff_mode(struct hci_conn *conn) if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn)) return; - if (conn->mode != HCI_CM_ACTIVE || - !(conn->link_policy & HCI_LP_SNIFF) || - (hci_find_link_key(hdev, &conn->dst) == NULL)) + if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF)) return; if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) { @@ -1054,12 +915,18 @@ void hci_conn_enter_sniff_mode(struct hci_conn *conn) } } -struct hci_chan *hci_chan_create(struct hci_chan *chan, +struct hci_chan *hci_chan_accept(struct hci_conn *conn, struct hci_ext_fs *tx_fs, struct hci_ext_fs *rx_fs) { + struct hci_chan *chan; struct hci_cp_create_logical_link cp; + chan = hci_chan_add(conn->hdev); + if (!chan) + return NULL; + chan->state = BT_CONNECT; + chan->conn = conn; chan->tx_fs = *tx_fs; chan->rx_fs = *rx_fs; cp.phy_handle = chan->conn->handle; @@ -1076,12 +943,40 @@ struct hci_chan *hci_chan_create(struct hci_chan *chan, cp.rx_fs.acc_latency = cpu_to_le32(chan->rx_fs.acc_latency); cp.rx_fs.flush_to = cpu_to_le32(chan->rx_fs.flush_to); hci_conn_hold(chan->conn); - if (chan->conn->out) - hci_send_cmd(chan->conn->hdev, HCI_OP_CREATE_LOGICAL_LINK, - sizeof(cp), &cp); - else - hci_send_cmd(chan->conn->hdev, HCI_OP_ACCEPT_LOGICAL_LINK, - sizeof(cp), &cp); + hci_send_cmd(conn->hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp), &cp); + return chan; +} +EXPORT_SYMBOL(hci_chan_accept); + +struct hci_chan *hci_chan_create(struct hci_conn *conn, + struct hci_ext_fs *tx_fs, struct hci_ext_fs *rx_fs) +{ + struct hci_chan *chan; + struct hci_cp_create_logical_link cp; + + chan = hci_chan_add(conn->hdev); + if (!chan) + return NULL; + + chan->state = BT_CONNECT; + chan->conn = conn; + chan->tx_fs = *tx_fs; + chan->rx_fs = *rx_fs; + cp.phy_handle = chan->conn->handle; + cp.tx_fs.id = chan->tx_fs.id; + cp.tx_fs.type = chan->tx_fs.type; + cp.tx_fs.max_sdu = cpu_to_le16(chan->tx_fs.max_sdu); + cp.tx_fs.sdu_arr_time = cpu_to_le32(chan->tx_fs.sdu_arr_time); + cp.tx_fs.acc_latency = cpu_to_le32(chan->tx_fs.acc_latency); + cp.tx_fs.flush_to = cpu_to_le32(chan->tx_fs.flush_to); + cp.rx_fs.id = chan->rx_fs.id; + cp.rx_fs.type = chan->rx_fs.type; + cp.rx_fs.max_sdu = cpu_to_le16(chan->rx_fs.max_sdu); + cp.rx_fs.sdu_arr_time = cpu_to_le32(chan->rx_fs.sdu_arr_time); + cp.rx_fs.acc_latency = cpu_to_le32(chan->rx_fs.acc_latency); + cp.rx_fs.flush_to = cpu_to_le32(chan->rx_fs.flush_to); + hci_conn_hold(chan->conn); + hci_send_cmd(conn->hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp), &cp); return chan; } EXPORT_SYMBOL(hci_chan_create); @@ -1113,7 +1008,7 @@ void hci_chan_modify(struct hci_chan *chan, EXPORT_SYMBOL(hci_chan_modify); /* Drop all connection on the device */ -void hci_conn_hash_flush(struct hci_dev *hdev, u8 is_process) +void hci_conn_hash_flush(struct hci_dev *hdev) { struct hci_conn_hash *h = &hdev->conn_hash; struct list_head *p; @@ -1129,7 +1024,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev, u8 is_process) c->state = BT_CLOSED; - hci_proto_disconn_cfm(c, 0x16, is_process); + hci_proto_disconn_cfm(c, 0x16); hci_conn_del(c); } } @@ -1306,3 +1201,4 @@ int hci_set_auth_info(struct hci_dev *hdev, void __user *arg) return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0; } + diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c old mode 100644 new mode 100755 index 1928f431..cbde45b3 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010-2012 The Linux Foundation. All rights reserved. + Copyright (c) 2000-2001, 2010-2011 Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky @@ -23,6 +23,7 @@ */ /* Bluetooth HCI core. */ +// rollback to original BlueZ #include #include @@ -561,19 +562,6 @@ int hci_dev_open(__u16 dev) goto done; } - if (!skb_queue_empty(&hdev->cmd_q)) { - BT_ERR("command queue is not empty, purging"); - skb_queue_purge(&hdev->cmd_q); - } - if (!skb_queue_empty(&hdev->rx_q)) { - BT_ERR("rx queue is not empty, purging"); - skb_queue_purge(&hdev->rx_q); - } - if (!skb_queue_empty(&hdev->raw_q)) { - BT_ERR("raw queue is not empty, purging"); - skb_queue_purge(&hdev->raw_q); - } - if (!test_bit(HCI_RAW, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); set_bit(HCI_INIT, &hdev->flags); @@ -626,7 +614,7 @@ int hci_dev_open(__u16 dev) return ret; } -static int hci_dev_do_close(struct hci_dev *hdev, u8 is_process) +static int hci_dev_do_close(struct hci_dev *hdev) { unsigned long keepflags = 0; @@ -647,17 +635,11 @@ static int hci_dev_do_close(struct hci_dev *hdev, u8 is_process) hci_dev_lock_bh(hdev); inquiry_cache_flush(hdev); - hci_conn_hash_flush(hdev, is_process); + hci_conn_hash_flush(hdev); hci_dev_unlock_bh(hdev); hci_notify(hdev, HCI_DEV_DOWN); - if (hdev->dev_type == HCI_BREDR) { - hci_dev_lock_bh(hdev); - mgmt_powered(hdev->id, 0); - hci_dev_unlock_bh(hdev); - } - if (hdev->flush) hdev->flush(hdev); @@ -690,6 +672,12 @@ static int hci_dev_do_close(struct hci_dev *hdev, u8 is_process) * and no tasks are scheduled. */ hdev->close(hdev); + if (hdev->dev_type == HCI_BREDR) { + hci_dev_lock_bh(hdev); + mgmt_powered(hdev->id, 0); + hci_dev_unlock_bh(hdev); + } + /* Clear only non-persistent flags */ if (test_bit(HCI_MGMT, &hdev->flags)) set_bit(HCI_MGMT, &keepflags); @@ -714,7 +702,7 @@ int hci_dev_close(__u16 dev) hdev = hci_dev_get(dev); if (!hdev) return -ENODEV; - err = hci_dev_do_close(hdev, 1); + err = hci_dev_do_close(hdev); hci_dev_put(hdev); return err; } @@ -740,7 +728,7 @@ int hci_dev_reset(__u16 dev) hci_dev_lock_bh(hdev); inquiry_cache_flush(hdev); - hci_conn_hash_flush(hdev, 0); + hci_conn_hash_flush(hdev); hci_dev_unlock_bh(hdev); if (hdev->flush) @@ -953,7 +941,7 @@ static int hci_rfkill_set_block(void *data, bool blocked) if (!blocked) return 0; - hci_dev_do_close(hdev, 0); + hci_dev_do_close(hdev); return 0; } @@ -1468,10 +1456,16 @@ int hci_register_dev(struct hci_dev *hdev) hdev->sniff_max_interval = 800; hdev->sniff_min_interval = 80; + tasklet_init(&hdev->cmd_task, hci_cmd_task, (unsigned long) hdev); + tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev); + tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev); + skb_queue_head_init(&hdev->rx_q); skb_queue_head_init(&hdev->cmd_q); skb_queue_head_init(&hdev->raw_q); + setup_timer(&hdev->cmd_timer, hci_cmd_timer, (unsigned long) hdev); + for (i = 0; i < NUM_REASSEMBLY; i++) hdev->reassembly[i] = NULL; @@ -1493,27 +1487,16 @@ int hci_register_dev(struct hci_dev *hdev) INIT_LIST_HEAD(&hdev->adv_entries); rwlock_init(&hdev->adv_entries_lock); + setup_timer(&hdev->adv_timer, hci_adv_clear, (unsigned long) hdev); INIT_WORK(&hdev->power_on, hci_power_on); INIT_WORK(&hdev->power_off, hci_power_off); + setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev); memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); atomic_set(&hdev->promisc, 0); - //initial the task in case someone want to schedule it before other global variables init ready. - tasklet_init(&hdev->cmd_task, hci_cmd_task, (unsigned long) hdev); - tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev); - tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev); - - setup_timer(&hdev->cmd_timer, hci_cmd_timer, (unsigned long) hdev); - setup_timer(&hdev->disco_timer, mgmt_disco_timeout, - (unsigned long) hdev); - setup_timer(&hdev->disco_le_timer, mgmt_disco_le_timeout, - (unsigned long) hdev); - setup_timer(&hdev->adv_timer, hci_adv_clear, (unsigned long) hdev); - setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev); - write_unlock_bh(&hci_dev_list_lock); hdev->workqueue = create_singlethread_workqueue(hdev->name); @@ -1564,11 +1547,15 @@ int hci_unregister_dev(struct hci_dev *hdev) list_del(&hdev->list); write_unlock_bh(&hci_dev_list_lock); - hci_dev_do_close(hdev, hdev->bus == HCI_SMD); + hci_dev_do_close(hdev); for (i = 0; i < NUM_REASSEMBLY; i++) kfree_skb(hdev->reassembly[i]); + cancel_work_sync(&hdev->power_on); + + cancel_work_sync(&hdev->power_on); + if (!test_bit(HCI_INIT, &hdev->flags) && !test_bit(HCI_SETUP, &hdev->flags) && hdev->dev_type == HCI_BREDR) { @@ -1589,12 +1576,8 @@ int hci_unregister_dev(struct hci_dev *hdev) hci_unregister_sysfs(hdev); - /* Disable all timers */ hci_del_off_timer(hdev); del_timer(&hdev->adv_timer); - del_timer(&hdev->cmd_timer); - del_timer(&hdev->disco_timer); - del_timer(&hdev->disco_le_timer); destroy_workqueue(hdev->workqueue); @@ -2136,9 +2119,6 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int /* We don't have to lock device here. Connections are always * added and removed with TX task disabled. */ - - BUG_ON(!h); - list_for_each(p, &h->list) { struct hci_conn *c; c = list_entry(p, struct hci_conn, list); @@ -2238,6 +2218,7 @@ static inline void hci_sched_acl(struct hci_dev *hdev) if (count > hdev->acl_cnt) return; + hci_dev_lock(hdev); hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active); hci_send_frame(skb); @@ -2247,6 +2228,7 @@ static inline void hci_sched_acl(struct hci_dev *hdev) quote -= count; conn->sent += count; + hci_dev_unlock(hdev); } } } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index df1f4a86..40bfed84 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010-2012 The Linux Foundation. All rights reserved. + Copyright (c) 2000-2001, 2010-2011, Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky @@ -23,6 +23,7 @@ */ /* Bluetooth HCI event handling. */ +// rollback to original BlueZ #include @@ -940,21 +941,6 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, hci_dev_unlock(hdev); } -static void hci_cc_read_rssi(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_conn *conn; - struct hci_rp_read_rssi *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - BT_DBG("%s rssi : %d handle : %d", hdev->name, rp->rssi, rp->handle); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); - if (conn) - mgmt_read_rssi_complete(hdev->id, rp->rssi, &conn->dst, - __le16_to_cpu(rp->handle), rp->status); -} - static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, struct sk_buff *skb) { @@ -1121,25 +1107,9 @@ static void hci_cs_auth_requested(struct hci_dev *hdev, __u8 status) conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) { - if (status) { - mgmt_auth_failed(hdev->id, &conn->dst, status); - clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); - - if (conn->state == BT_CONFIG) { - conn->state = BT_CONNECTED; - hci_proto_connect_cfm(conn, status); - hci_conn_put(conn); - } else { - hci_auth_cfm(conn, status); - hci_conn_hold(conn); - conn->disc_timeout = HCI_DISCONN_TIMEOUT; - hci_conn_put(conn); - } - - if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { - clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); - hci_encrypt_cfm(conn, status, 0x00); - } + if (status && conn->state == BT_CONFIG) { + hci_proto_connect_cfm(conn, status); + hci_conn_put(conn); } conn->auth_initiator = 1; } @@ -1357,6 +1327,12 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status) if (conn) { clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend); + // when exit sniffer mode cmd failed error code equal 12 means command diallowed, we don't setup sco link. + if (12 == status) { + hci_dev_unlock(hdev); + return; + } + if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend)) hci_sco_setup(conn, status); } @@ -1368,7 +1344,6 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) { struct hci_cp_le_create_conn *cp; struct hci_conn *conn; - unsigned long exp = msecs_to_jiffies(5000); BT_DBG("%s status 0x%x", hdev->name, status); @@ -1397,11 +1372,11 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) conn->out = 1; else BT_ERR("No memory for new connection"); - } else - exp = msecs_to_jiffies(conn->conn_timeout * 1000); + } - if (conn && exp) - mod_timer(&conn->disc_timer, jiffies + exp); + if (conn) + mod_timer(&conn->disc_timer, + jiffies + msecs_to_jiffies(5000)); } hci_dev_unlock(hdev); @@ -1428,10 +1403,10 @@ static void hci_cs_accept_logical_link(struct hci_dev *hdev, __u8 status) if (chan && chan->state == BT_CONNECT) { chan->state = BT_CLOSED; hci_proto_create_cfm(chan, status); + hci_chan_del(chan); } - } else if (chan) { - chan->state = BT_CONNECT2; - } + } else if (chan) + chan->state = BT_CONNECT2; hci_dev_unlock(hdev); } @@ -1457,6 +1432,7 @@ static void hci_cs_create_logical_link(struct hci_dev *hdev, __u8 status) if (chan && chan->state == BT_CONNECT) { chan->state = BT_CLOSED; hci_proto_create_cfm(chan, status); + hci_chan_del(chan); } } else if (chan) chan->state = BT_CONNECT2; @@ -1549,7 +1525,7 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff BT_DBG("%s status %d", hdev->name, status); - if (!hdev->disco_state) + if (!lmp_le_capable(hdev)) clear_bit(HCI_INQUIRY, &hdev->flags); hci_req_complete(hdev, HCI_OP_INQUIRY, status); @@ -1638,12 +1614,12 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s if (test_bit(HCI_ENCRYPT, &hdev->flags)) conn->link_mode |= HCI_LM_ENCRYPT; - /* Get remote version */ + /* Get remote features */ if (conn->type == ACL_LINK) { - struct hci_cp_read_remote_version cp; + struct hci_cp_read_remote_features cp; cp.handle = ev->handle; - hci_send_cmd(hdev, HCI_OP_READ_REMOTE_VERSION, - sizeof(cp), &cp); + hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES, + sizeof(cp), &cp); } /* Set packet type for incoming connection */ @@ -1654,6 +1630,9 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, sizeof(cp), &cp); } + + + } else { conn->state = BT_CLOSED; if (conn->type == ACL_LINK || conn->type == LE_LINK) @@ -1675,15 +1654,6 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s 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; @@ -1718,8 +1688,6 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk } memcpy(conn->dev_class, ev->dev_class, 3); - /* For incoming connection update remote class to userspace */ - mgmt_remote_class(hdev->id, &ev->bdaddr, ev->dev_class); conn->state = BT_CONNECT; hci_dev_unlock(hdev); @@ -1729,8 +1697,7 @@ 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) - || is_sco_active(hdev))) + if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER)) cp.role = 0x00; /* Become master */ else cp.role = 0x01; /* Remain slave */ @@ -1790,7 +1757,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff if (conn->type == LE_LINK) del_timer(&conn->smp_timer); - hci_proto_disconn_cfm(conn, ev->reason, 0); + hci_proto_disconn_cfm(conn, ev->reason); hci_conn_del(conn); unlock: @@ -1811,24 +1778,8 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s if (ev->status == 0x06 && hdev->ssp_mode > 0 && conn->ssp_mode > 0) { struct hci_cp_auth_requested cp; - struct link_key *key; - key = hci_find_link_key(hdev, &conn->dst); - if (key && key->key_type == 0x05) { - BT_INFO("Previous key was authenticated, " - "updating the auth type for outgoing connection"); - conn->auth_type = HCI_AT_DEDICATED_BONDING_MITM; - } hci_remove_link_key(hdev, &conn->dst); cp.handle = cpu_to_le16(conn->handle); - /*Initiates dedicated bonding as pin or key is missing - on remote device*/ - /*In case if remote device is ssp supported, - reduce the security level to MEDIUM if it is HIGH*/ - if (conn->ssp_mode && conn->auth_initiator && - conn->io_capability != 0x03) { - conn->pending_sec_level = BT_SECURITY_HIGH; - conn->auth_type = HCI_AT_DEDICATED_BONDING_MITM; - } hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); hci_dev_unlock(hdev); @@ -1869,29 +1820,11 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { if (!ev->status) { - if (conn->link_mode & HCI_LM_ENCRYPT) { - /* Encryption implies authentication */ - conn->link_mode |= HCI_LM_AUTH; - conn->link_mode |= HCI_LM_ENCRYPT; - conn->sec_level = - conn->pending_sec_level; - clear_bit(HCI_CONN_ENCRYPT_PEND, - &conn->pend); - hci_encrypt_cfm(conn, ev->status, 1); - - if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_encrypt_change(hdev->id, - &conn->dst, - ev->status); - - } else { - struct hci_cp_set_conn_encrypt cp; - cp.handle = ev->handle; - cp.encrypt = 0x01; - hci_send_cmd(hdev, - HCI_OP_SET_CONN_ENCRYPT, - sizeof(cp), &cp); - } + struct hci_cp_set_conn_encrypt cp; + cp.handle = ev->handle; + cp.encrypt = 0x01; + hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, + sizeof(cp), &cp); } else { clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); hci_encrypt_cfm(conn, ev->status, 0x00); @@ -1955,25 +1888,8 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff * hci_proto_connect_cfm(conn, ev->status); hci_conn_put(conn); - } else { - /* - * If the remote device does not support - * Pause Encryption, usually during the - * roleSwitch we see Encryption disable - * for short duration. Allow remote device - * to disable encryption - * for short duration in this case. - */ - if ((ev->encrypt == 0) && (ev->status == 0) && - ((conn->features[5] & LMP_PAUSE_ENC) == 0)) { - mod_timer(&conn->encrypt_pause_timer, - jiffies + msecs_to_jiffies(500)); - BT_INFO("enc pause timer, enc_pend_flag set"); - } else { - del_timer(&conn->encrypt_pause_timer); - hci_encrypt_cfm(conn, ev->status, ev->encrypt); - } - } + } else + hci_encrypt_cfm(conn, ev->status, ev->encrypt); if (test_bit(HCI_MGMT, &hdev->flags)) mgmt_encrypt_change(hdev->id, &conn->dst, ev->status); @@ -2017,10 +1933,8 @@ static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff if (!conn) goto unlock; - if (!ev->status) { + if (!ev->status) memcpy(conn->features, ev->features, 8); - mgmt_remote_features(hdev->id, &conn->dst, ev->features); - } if (conn->state != BT_CONFIG) goto unlock; @@ -2032,9 +1946,8 @@ static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff hci_send_cmd(hdev, HCI_OP_READ_REMOTE_EXT_FEATURES, sizeof(cp), &cp); goto unlock; - } else if (!(lmp_ssp_capable(conn)) && conn->auth_initiator && - (conn->pending_sec_level == BT_SECURITY_HIGH)) { - conn->pending_sec_level = BT_SECURITY_MEDIUM; + + } if (!ev->status) { @@ -2057,24 +1970,7 @@ static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff static inline void hci_remote_version_evt(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_ev_remote_version *ev = (void *) skb->data; - struct hci_cp_read_remote_features cp; - struct hci_conn *conn; - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - cp.handle = ev->handle; - hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES, - sizeof(cp), &cp); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (!conn) - goto unlock; - if (!ev->status) - mgmt_remote_version(hdev->id, &conn->dst, ev->lmp_ver, - ev->manufacturer, ev->lmp_subver); -unlock: - hci_dev_unlock(hdev); + BT_DBG("%s", hdev->name); } static inline void hci_qos_setup_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) @@ -2086,14 +1982,12 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk { struct hci_ev_cmd_complete *ev = (void *) skb->data; __u16 opcode; + extern unsigned char fm_command_pending; skb_pull(skb, sizeof(*ev)); opcode = __le16_to_cpu(ev->opcode); - if (test_bit(HCI_RESET, &hdev->flags) && (opcode != HCI_OP_RESET)) - return; - switch (opcode) { case HCI_OP_INQUIRY_CANCEL: hci_cc_inquiry_cancel(hdev, skb); @@ -2260,10 +2154,6 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_le_read_buffer_size(hdev, skb); break; - case HCI_OP_READ_RSSI: - hci_cc_read_rssi(hdev, skb); - break; - case HCI_OP_USER_CONFIRM_REPLY: hci_cc_user_confirm_reply(hdev, skb); break; @@ -2286,13 +2176,18 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); + //delete + if (opcode == 0xfc15) //0xfc15 means fm cmd + { + fm_command_pending = 0; + } break; } if (ev->opcode != HCI_OP_NOP) del_timer(&hdev->cmd_timer); - if (ev->ncmd) { + if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) tasklet_schedule(&hdev->cmd_task); @@ -2420,7 +2315,8 @@ static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb if (ev->role) conn->link_mode &= ~HCI_LM_MASTER; else - conn->link_mode |= HCI_LM_MASTER; + conn->link_mode |= HCI_LM_MASTER; + } clear_bit(HCI_CONN_RSWITCH_PEND, &conn->pend); @@ -2575,9 +2471,6 @@ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb else conn->power_save = 0; } - if (conn->mode == HCI_CM_SNIFF) - if (wake_lock_active(&conn->idle_lock)) - wake_unlock(&conn->idle_lock); if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend)) hci_sco_setup(conn, ev->status); @@ -2600,7 +2493,6 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff hci_conn_hold(conn); conn->disc_timeout = HCI_PAIRING_TIMEOUT; hci_conn_put(conn); - hci_conn_enter_active_mode(conn, 0); } if (!test_bit(HCI_PAIRABLE, &hdev->flags)) @@ -2648,6 +2540,12 @@ static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff BT_DBG("Conn pending sec level is %d, ssp is %d, key len is %d", conn->pending_sec_level, conn->ssp_mode, key->pin_len); } + if (conn && (conn->ssp_mode == 0) && + (conn->pending_sec_level == BT_SECURITY_HIGH) && + (key->pin_len != 16)) { + BT_DBG("Security is high ignoring this key"); + goto not_found; + } if (key->key_type == 0x04 && conn && conn->auth_type != 0xff && (conn->auth_type & 0x01)) { @@ -2688,10 +2586,8 @@ static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff conn->key_type = ev->key_type; hci_disconnect_amp(conn, 0x06); - conn->link_mode &= ~HCI_LM_ENCRYPT; pin_len = conn->pin_length; hci_conn_put(conn); - hci_conn_enter_active_mode(conn, 0); } if (test_bit(HCI_LINK_KEYS, &hdev->flags)) @@ -2833,15 +2729,9 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b conn->ssp_mode = (ev->features[0] & 0x01); /*In case if remote device ssp supported/2.0 device reduce the security level to MEDIUM if it is HIGH*/ - if (!conn->ssp_mode && conn->auth_initiator && + if (!conn->ssp_mode && (conn->pending_sec_level == BT_SECURITY_HIGH)) conn->pending_sec_level = BT_SECURITY_MEDIUM; - - if (conn->ssp_mode && conn->auth_initiator && - conn->io_capability != 0x03) { - conn->pending_sec_level = BT_SECURITY_HIGH; - conn->auth_type = HCI_AT_DEDICATED_BONDING_MITM; - } } if (conn->state != BT_CONFIG) @@ -3375,15 +3265,19 @@ static inline void hci_log_link_complete(struct hci_dev *hdev, chan = hci_chan_list_lookup_id(hdev, ev->phy_handle); - if (chan) { - if (ev->status == 0) { + if (ev->status == 0) { + if (chan) { chan->ll_handle = __le16_to_cpu(ev->log_handle); chan->state = BT_CONNECTED; - } else { + hci_proto_create_cfm(chan, ev->status); + hci_chan_hold(chan); + } + } else { + if (chan) { chan->state = BT_CLOSED; + hci_proto_create_cfm(chan, ev->status); + hci_chan_del(chan); } - - hci_proto_create_cfm(chan, ev->status); } hci_dev_unlock(hdev); @@ -3422,8 +3316,10 @@ static inline void hci_disconn_log_link_complete_evt(struct hci_dev *hdev, hci_dev_lock(hdev); chan = hci_chan_list_lookup_handle(hdev, __le16_to_cpu(ev->log_handle)); - if (chan) + if (chan) { hci_proto_destroy_cfm(chan, ev->reason); + hci_chan_del(chan); + } hci_dev_unlock(hdev); } @@ -3445,7 +3341,7 @@ static inline void hci_disconn_phy_link_complete_evt(struct hci_dev *hdev, if (conn) { conn->state = BT_CLOSED; - hci_proto_disconn_cfm(conn, ev->reason, 0); + hci_proto_disconn_cfm(conn, ev->reason); hci_conn_del(conn); } diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c old mode 100644 new mode 100755 index 550f6019..b2f7e0c6 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2011, The Linux Foundation. All rights reserved. + Copyright (c) 2000-2001, 2011, Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky @@ -24,6 +24,8 @@ /* Bluetooth HCI sockets. */ +// rollback to original BlueZ + #include #include @@ -49,6 +51,8 @@ #include #include +//#define BT_DBG(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg) +unsigned char fm_command_pending = 0; static bool enable_mgmt = 1; /* ----- HCI socket interface ----- */ @@ -440,6 +444,7 @@ static int hci_sock_getname(struct socket *sock, struct sockaddr *addr, int *add *addr_len = sizeof(*haddr); haddr->hci_family = AF_BLUETOOTH; haddr->hci_dev = hdev->id; + haddr->hci_channel= 0; release_sock(sk); return 0; @@ -499,8 +504,6 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock, if (!skb) return err; - msg->msg_namelen = 0; - copied = skb->len; if (len < copied) { msg->msg_flags |= MSG_TRUNC; @@ -523,7 +526,6 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct sock *sk = sock->sk; struct hci_dev *hdev; struct sk_buff *skb; - int reserve = 0; int err; BT_DBG("sock %p sk %p", sock, sk); @@ -561,18 +563,10 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, goto done; } - /* Allocate extra headroom for Qualcomm PAL */ - if (hdev->dev_type == HCI_AMP && hdev->manufacturer == 0x001d) - reserve = BT_SKB_RESERVE_80211; - - skb = bt_skb_send_alloc(sk, len + reserve, - msg->msg_flags & MSG_DONTWAIT, &err); + skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) goto done; - if (reserve) - skb_reserve(skb, reserve); - if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { err = -EFAULT; goto drop; @@ -587,6 +581,16 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, u16 ogf = hci_opcode_ogf(opcode); u16 ocf = hci_opcode_ocf(opcode); + /* 4329 FM VSC checking, 0xfc15 means fm cmd */ + if (opcode == 0xfc15) { + while (fm_command_pending == 1) + { + BT_DBG("fm command is pending."); + msleep(2); + } + fm_command_pending = 1; + } + if (((ogf > HCI_SFLT_MAX_OGF) || !hci_test_bit(ocf & HCI_FLT_OCF_BITS, &hci_sec_filter.ocf_mask[ogf])) && !capable(CAP_NET_RAW)) { @@ -661,6 +665,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char { struct hci_filter *f = &hci_pi(sk)->filter; + memset(&uf, 0, sizeof(uf)); uf.type_mask = f->type_mask; uf.opcode = f->opcode; uf.event_mask[0] = *((u32 *) f->event_mask + 0); @@ -732,6 +737,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname, char { struct hci_filter *f = &hci_pi(sk)->filter; + memset(&uf, 0, sizeof(uf)); uf.type_mask = f->type_mask; uf.opcode = f->opcode; uf.event_mask[0] = *((u32 *) f->event_mask + 0); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index ba09f4a8..45496848 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -1,7 +1,7 @@ /* HIDP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2003-2004 Marcel Holtmann - Copyright (c) 2012 The Linux Foundation. All rights reserved. + Copyright (c) 2012 Code Aurora Forum. 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 @@ -93,15 +93,40 @@ static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr) return NULL; } +static struct device *hidp_get_device(struct hidp_session *session) +{ + bdaddr_t *dst = &session->bdaddr; + + struct device *device = NULL; + struct hci_dev *hdev; + + hdev = hci_get_route(dst, BDADDR_ANY); + if (!hdev) + return NULL; + + session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); + if (session->conn) + device = &session->conn->dev; + + hci_dev_put(hdev); + + return device; +} + static void __hidp_link_session(struct hidp_session *session) { __module_get(THIS_MODULE); list_add(&session->list, &hidp_session_list); + + hci_conn_hold_device(session->conn); } static void __hidp_unlink_session(struct hidp_session *session) { - if (session->conn) + struct device *dev; + + dev = hidp_get_device(session); + if (dev) hci_conn_put_device(session->conn); list_del(&session->list); @@ -635,28 +660,6 @@ static int hidp_session(void *arg) return 0; } -static struct hci_conn *hidp_get_connection(struct hidp_session *session) -{ - bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src; - bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst; - struct hci_conn *conn; - struct hci_dev *hdev; - - hdev = hci_get_route(dst, src); - if (!hdev) - return NULL; - - hci_dev_lock_bh(hdev); - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); - if (conn) - hci_conn_hold_device(conn); - hci_dev_unlock_bh(hdev); - - hci_dev_put(hdev); - - return conn; -} - static int hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req) { @@ -704,7 +707,7 @@ static int hidp_setup_input(struct hidp_session *session, input->relbit[0] |= BIT_MASK(REL_WHEEL); } - input->dev.parent = &session->conn->dev; + input->dev.parent = hidp_get_device(session); input->event = hidp_input_event; @@ -801,11 +804,11 @@ static int hidp_setup_hid(struct hidp_session *session, hid->version = req->version; hid->country = req->country; - strncpy(hid->name, req->name, 128); + strncpy(hid->name, req->name, sizeof(req->name) - 1); strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64); strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64); - hid->dev.parent = &session->conn->dev; + hid->dev.parent = hidp_get_device(session); hid->ll_driver = &hidp_hid_driver; hid->hid_output_raw_report = hidp_output_raw_report; @@ -863,12 +866,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, session->intr_sock = intr_sock; session->state = BT_CONNECTED; - session->conn = hidp_get_connection(session); - if (!session->conn) { - err = -ENOTCONN; - goto failed; - } - setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session); skb_queue_head_init(&session->ctrl_transmit); @@ -877,8 +874,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); session->idle_to = req->idle_to; - __hidp_link_session(session); - if (req->rd_size > 0) { err = hidp_setup_hid(session, req); if (err && err != -ENODEV) @@ -891,6 +886,8 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, goto purge; } + __hidp_link_session(session); + hidp_set_timer(session); err = kernel_thread(hidp_session, session, CLONE_KERNEL); @@ -912,6 +909,8 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, unlink: hidp_del_timer(session); + __hidp_unlink_session(session); + if (session->input) { input_unregister_device(session->input); session->input = NULL; @@ -926,8 +925,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, session->rd_data = NULL; purge: - __hidp_unlink_session(session); - skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->intr_transmit); diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c new file mode 100644 index 00000000..79650957 --- /dev/null +++ b/net/bluetooth/l2cap.c @@ -0,0 +1,4747 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2000-2001 Qualcomm Incorporated + + Written 2000,2001 by Maxim Krasnyansky + + 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; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +/* Bluetooth L2CAP core and sockets. */ + +#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 VERSION "2.14" + +#ifdef CONFIG_BT_L2CAP_EXT_FEATURES +static bool enable_ertm = 1; +#else +static bool enable_ertm = 0; +#endif +static uint max_transmit = L2CAP_DEFAULT_MAX_TX; +static uint tx_window = L2CAP_DEFAULT_TX_WINDOW; + +static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; +static u8 l2cap_fixed_chan[8] = { 0x02, }; + +static const struct proto_ops l2cap_sock_ops; + +static struct workqueue_struct *_busy_wq; + +static struct bt_sock_list l2cap_sk_list = { + .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock) +}; + +static void l2cap_busy_work(struct work_struct *work); + +static void __l2cap_sock_close(struct sock *sk, int reason); +static void l2cap_sock_close(struct sock *sk); +static void l2cap_sock_kill(struct sock *sk); + +static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, + u8 code, u8 ident, u16 dlen, void *data); + +/* ---- L2CAP timers ---- */ +static void l2cap_sock_timeout(unsigned long arg) +{ + struct sock *sk = (struct sock *) arg; + int reason; + + BT_DBG("sock %p state %d", sk, sk->sk_state); + + bh_lock_sock(sk); + + if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) + reason = ECONNREFUSED; + else if (sk->sk_state == BT_CONNECT && + l2cap_pi(sk)->sec_level != BT_SECURITY_SDP) + reason = ECONNREFUSED; + else + reason = ETIMEDOUT; + + __l2cap_sock_close(sk, reason); + + bh_unlock_sock(sk); + + l2cap_sock_kill(sk); + sock_put(sk); +} + +static void l2cap_sock_set_timer(struct sock *sk, long timeout) +{ + BT_DBG("sk %p state %d timeout %ld", sk, sk->sk_state, timeout); + sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout); +} + +static void l2cap_sock_clear_timer(struct sock *sk) +{ + BT_DBG("sock %p state %d", sk, sk->sk_state); + sk_stop_timer(sk, &sk->sk_timer); +} + +/* ---- L2CAP channels ---- */ +static struct sock *__l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, u16 cid) +{ + struct sock *s; + for (s = l->head; s; s = l2cap_pi(s)->next_c) { + if (l2cap_pi(s)->dcid == cid) + break; + } + return s; +} + +static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid) +{ + struct sock *s; + for (s = l->head; s; s = l2cap_pi(s)->next_c) { + if (l2cap_pi(s)->scid == cid) + break; + } + return s; +} + +/* Find channel with given SCID. + * Returns locked socket */ +static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid) +{ + struct sock *s; + read_lock(&l->lock); + s = __l2cap_get_chan_by_scid(l, cid); + if (s) + bh_lock_sock(s); + read_unlock(&l->lock); + return s; +} + +static struct sock *__l2cap_get_chan_by_ident(struct l2cap_chan_list *l, u8 ident) +{ + struct sock *s; + for (s = l->head; s; s = l2cap_pi(s)->next_c) { + if (l2cap_pi(s)->ident == ident) + break; + } + return s; +} + +static inline struct sock *l2cap_get_chan_by_ident(struct l2cap_chan_list *l, u8 ident) +{ + struct sock *s; + read_lock(&l->lock); + s = __l2cap_get_chan_by_ident(l, ident); + if (s) + bh_lock_sock(s); + read_unlock(&l->lock); + return s; +} + +static u16 l2cap_alloc_cid(struct l2cap_chan_list *l) +{ + u16 cid = L2CAP_CID_DYN_START; + + for (; cid < L2CAP_CID_DYN_END; cid++) { + if (!__l2cap_get_chan_by_scid(l, cid)) + return cid; + } + + return 0; +} + +static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk) +{ + sock_hold(sk); + + if (l->head) + l2cap_pi(l->head)->prev_c = sk; + + l2cap_pi(sk)->next_c = l->head; + l2cap_pi(sk)->prev_c = NULL; + l->head = sk; +} + +static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk) +{ + struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c; + + write_lock_bh(&l->lock); + if (sk == l->head) + l->head = next; + + if (next) + l2cap_pi(next)->prev_c = prev; + if (prev) + l2cap_pi(prev)->next_c = next; + write_unlock_bh(&l->lock); + + __sock_put(sk); +} + +static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent) +{ + struct l2cap_chan_list *l = &conn->chan_list; + + BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, + l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid); + + conn->disc_reason = 0x13; + + l2cap_pi(sk)->conn = conn; + + if (sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) { + /* Alloc CID for connection-oriented socket */ + l2cap_pi(sk)->scid = l2cap_alloc_cid(l); + } else if (sk->sk_type == SOCK_DGRAM) { + /* Connectionless socket */ + l2cap_pi(sk)->scid = L2CAP_CID_CONN_LESS; + l2cap_pi(sk)->dcid = L2CAP_CID_CONN_LESS; + l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; + } else { + /* Raw socket can send/recv signalling messages only */ + l2cap_pi(sk)->scid = L2CAP_CID_SIGNALING; + l2cap_pi(sk)->dcid = L2CAP_CID_SIGNALING; + l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; + } + + __l2cap_chan_link(l, sk); + + if (parent) + bt_accept_enqueue(parent, sk); +} + +/* Delete channel. + * Must be called on the locked socket. */ +static void l2cap_chan_del(struct sock *sk, int err) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sock *parent = bt_sk(sk)->parent; + + l2cap_sock_clear_timer(sk); + + BT_DBG("sk %p, conn %p, err %d", sk, conn, err); + + if (conn) { + if (sock_owned_by_user(sk)){ + printk("L2CAP ERROR: unlink owned sk!\n"); + } + /* Unlink from channel list */ + l2cap_chan_unlink(&conn->chan_list, sk); + l2cap_pi(sk)->conn = NULL; + hci_conn_put(conn->hcon); + } + + sk->sk_state = BT_CLOSED; + sock_set_flag(sk, SOCK_ZAPPED); + + if (err) + sk->sk_err = err; + + if (parent) { + bt_accept_unlink(sk); + parent->sk_data_ready(parent, 0); + } else + sk->sk_state_change(sk); +} + +/* Service level security */ +static inline int l2cap_check_security(struct sock *sk) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + __u8 auth_type; + + if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) { + if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH) + auth_type = HCI_AT_NO_BONDING_MITM; + else + auth_type = HCI_AT_NO_BONDING; + + if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW) + l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; + } else { + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_HIGH: + auth_type = HCI_AT_GENERAL_BONDING_MITM; + break; + case BT_SECURITY_MEDIUM: + auth_type = HCI_AT_GENERAL_BONDING; + break; + default: + auth_type = HCI_AT_NO_BONDING; + break; + } + } + + return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level, + auth_type); +} + +static inline u8 l2cap_get_ident(struct l2cap_conn *conn) +{ + u8 id; + + /* Get next available identificator. + * 1 - 128 are used by kernel. + * 129 - 199 are reserved. + * 200 - 254 are used by utilities like l2ping, etc. + */ + + spin_lock_bh(&conn->lock); + + if (++conn->tx_ident > 128) + conn->tx_ident = 1; + + id = conn->tx_ident; + + spin_unlock_bh(&conn->lock); + + return id; +} + +static inline void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) +{ + struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); + u8 flags; + + BT_DBG("code 0x%2.2x", code); + + if (!skb) + return; + + if (lmp_no_flush_capable(conn->hcon->hdev)) + flags = ACL_START_NO_FLUSH; + else + flags = ACL_START; + + hci_send_acl(conn->hcon, skb, flags); +} + +static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) +{ + struct sk_buff *skb; + struct l2cap_hdr *lh; + struct l2cap_conn *conn = pi->conn; + int count, hlen = L2CAP_HDR_SIZE + 2; + + if (pi->fcs == L2CAP_FCS_CRC16) + hlen += 2; + + BT_DBG("pi %p, control 0x%2.2x", pi, control); + + count = min_t(unsigned int, conn->mtu, hlen); + control |= L2CAP_CTRL_FRAME_TYPE; + + if (pi->conn_state & L2CAP_CONN_SEND_FBIT) { + control |= L2CAP_CTRL_FINAL; + pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; + } + + if (pi->conn_state & L2CAP_CONN_SEND_PBIT) { + control |= L2CAP_CTRL_POLL; + pi->conn_state &= ~L2CAP_CONN_SEND_PBIT; + } + + skb = bt_skb_alloc(count, GFP_ATOMIC); + if (!skb) + return; + + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); + lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE); + lh->cid = cpu_to_le16(pi->dcid); + put_unaligned_le16(control, skb_put(skb, 2)); + + if (pi->fcs == L2CAP_FCS_CRC16) { + u16 fcs = crc16(0, (u8 *)lh, count - 2); + put_unaligned_le16(fcs, skb_put(skb, 2)); + } + + hci_send_acl(pi->conn->hcon, skb, 0); +} + +static inline void l2cap_send_rr_or_rnr(struct l2cap_pinfo *pi, u16 control) +{ + if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { + control |= L2CAP_SUPER_RCV_NOT_READY; + pi->conn_state |= L2CAP_CONN_RNR_SENT; + } else + control |= L2CAP_SUPER_RCV_READY; + + control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + + l2cap_send_sframe(pi, control); +} + +static inline int __l2cap_no_conn_pending(struct sock *sk) +{ + return !(l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND); +} + +static void l2cap_do_start(struct sock *sk) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + + if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { + if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)) + return; + + if (l2cap_check_security(sk) && __l2cap_no_conn_pending(sk)) { + struct l2cap_conn_req req; + req.scid = cpu_to_le16(l2cap_pi(sk)->scid); + req.psm = l2cap_pi(sk)->psm; + + l2cap_pi(sk)->ident = l2cap_get_ident(conn); + l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; + + l2cap_send_cmd(conn, l2cap_pi(sk)->ident, + L2CAP_CONN_REQ, sizeof(req), &req); + } + } else { + struct l2cap_info_req req; + req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK); + + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT; + conn->info_ident = l2cap_get_ident(conn); + + mod_timer(&conn->info_timer, jiffies + + msecs_to_jiffies(L2CAP_INFO_TIMEOUT)); + + l2cap_send_cmd(conn, conn->info_ident, + L2CAP_INFO_REQ, sizeof(req), &req); + } +} + +static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk) +{ + struct l2cap_disconn_req req; + + req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid); + req.scid = cpu_to_le16(l2cap_pi(sk)->scid); + l2cap_send_cmd(conn, l2cap_get_ident(conn), + L2CAP_DISCONN_REQ, sizeof(req), &req); +} + +/* ---- L2CAP connections ---- */ +static void l2cap_conn_start(struct l2cap_conn *conn) +{ + struct l2cap_chan_list *l = &conn->chan_list; + struct sock *sk; + + BT_DBG("conn %p", conn); + + read_lock(&l->lock); + + for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { + bh_lock_sock(sk); + + if (sk->sk_type != SOCK_SEQPACKET && + sk->sk_type != SOCK_STREAM) { + bh_unlock_sock(sk); + continue; + } + + if (sk->sk_state == BT_CONNECT) { + if (l2cap_check_security(sk) && + __l2cap_no_conn_pending(sk)) { + struct l2cap_conn_req req; + req.scid = cpu_to_le16(l2cap_pi(sk)->scid); + req.psm = l2cap_pi(sk)->psm; + + l2cap_pi(sk)->ident = l2cap_get_ident(conn); + l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; + + l2cap_send_cmd(conn, l2cap_pi(sk)->ident, + L2CAP_CONN_REQ, sizeof(req), &req); + } + } else if (sk->sk_state == BT_CONNECT2) { + struct l2cap_conn_rsp rsp; + rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + + if (l2cap_check_security(sk)) { + if (bt_sk(sk)->defer_setup) { + struct sock *parent = bt_sk(sk)->parent; + rsp.result = cpu_to_le16(L2CAP_CR_PEND); + rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND); + if (parent) + parent->sk_data_ready(parent, 0); + + } else { + sk->sk_state = BT_CONFIG; + rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + } + } else { + rsp.result = cpu_to_le16(L2CAP_CR_PEND); + rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND); + } + + l2cap_send_cmd(conn, l2cap_pi(sk)->ident, + L2CAP_CONN_RSP, sizeof(rsp), &rsp); + } + + bh_unlock_sock(sk); + } + + read_unlock(&l->lock); +} + +static void l2cap_conn_ready(struct l2cap_conn *conn) +{ + struct l2cap_chan_list *l = &conn->chan_list; + struct sock *sk; + + BT_DBG("conn %p", conn); + + read_lock(&l->lock); + + for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { + bh_lock_sock(sk); + + if (sk->sk_type != SOCK_SEQPACKET && + sk->sk_type != SOCK_STREAM) { + l2cap_sock_clear_timer(sk); + sk->sk_state = BT_CONNECTED; + sk->sk_state_change(sk); + } else if (sk->sk_state == BT_CONNECT) + l2cap_do_start(sk); + + bh_unlock_sock(sk); + } + + read_unlock(&l->lock); +} + +/* Notify sockets that we cannot guaranty reliability anymore */ +static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) +{ + struct l2cap_chan_list *l = &conn->chan_list; + struct sock *sk; + + BT_DBG("conn %p", conn); + + read_lock(&l->lock); + + for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { + if (l2cap_pi(sk)->force_reliable) + sk->sk_err = err; + } + + read_unlock(&l->lock); +} + +static void l2cap_info_timeout(unsigned long arg) +{ + struct l2cap_conn *conn = (void *) arg; + + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + conn->info_ident = 0; + + l2cap_conn_start(conn); +} + +static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) +{ + struct l2cap_conn *conn = hcon->l2cap_data; + + if (conn || status) + return conn; + + conn = kzalloc(sizeof(struct l2cap_conn), GFP_ATOMIC); + if (!conn) + return NULL; + + hcon->l2cap_data = conn; + conn->hcon = hcon; + + BT_DBG("hcon %p conn %p", hcon, conn); + + conn->mtu = hcon->hdev->acl_mtu; + conn->src = &hcon->hdev->bdaddr; + conn->dst = &hcon->dst; + + conn->feat_mask = 0; + + spin_lock_init(&conn->lock); + rwlock_init(&conn->chan_list.lock); + + setup_timer(&conn->info_timer, l2cap_info_timeout, + (unsigned long) conn); + + conn->disc_reason = 0x13; + + return conn; +} + +static void l2cap_conn_del(struct hci_conn *hcon, int err) +{ + struct l2cap_conn *conn = hcon->l2cap_data; + struct sock *sk; + + if (!conn) + return; + + BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); + + kfree_skb(conn->rx_skb); + + /* Kill channels */ + while ((sk = conn->chan_list.head)) { + bh_lock_sock(sk); + l2cap_chan_del(sk, err); + bh_unlock_sock(sk); + l2cap_sock_kill(sk); + } + + if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) + del_timer_sync(&conn->info_timer); + + hcon->l2cap_data = NULL; + kfree(conn); +} + +static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent) +{ + struct l2cap_chan_list *l = &conn->chan_list; + write_lock_bh(&l->lock); + __l2cap_chan_add(conn, sk, parent); + write_unlock_bh(&l->lock); +} + +/* ---- Socket interface ---- */ +static struct sock *__l2cap_get_sock_by_addr(__le16 psm, bdaddr_t *src) +{ + struct sock *sk; + struct hlist_node *node; + sk_for_each(sk, node, &l2cap_sk_list.head) + if (l2cap_pi(sk)->sport == psm && !bacmp(&bt_sk(sk)->src, src)) + goto found; + sk = NULL; +found: + return sk; +} + +/* Find socket with psm and source bdaddr. + * Returns closest match. + */ +static struct sock *__l2cap_get_sock_by_psm(int state, __le16 psm, bdaddr_t *src) +{ + struct sock *sk = NULL, *sk1 = NULL; + struct hlist_node *node; + + sk_for_each(sk, node, &l2cap_sk_list.head) { + if (state && sk->sk_state != state) + continue; + + if (l2cap_pi(sk)->psm == psm) { + /* Exact match. */ + if (!bacmp(&bt_sk(sk)->src, src)) + break; + + /* Closest match */ + if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) + sk1 = sk; + } + } + return node ? sk : sk1; +} + +/* Find socket with given address (psm, src). + * Returns locked socket */ +static inline struct sock *l2cap_get_sock_by_psm(int state, __le16 psm, bdaddr_t *src) +{ + struct sock *s; + read_lock(&l2cap_sk_list.lock); + s = __l2cap_get_sock_by_psm(state, psm, src); + if (s) + bh_lock_sock(s); + read_unlock(&l2cap_sk_list.lock); + return s; +} + +static void l2cap_sock_destruct(struct sock *sk) +{ + BT_DBG("sk %p", sk); + + skb_queue_purge(&sk->sk_receive_queue); + skb_queue_purge(&sk->sk_write_queue); +} + +static void l2cap_sock_cleanup_listen(struct sock *parent) +{ + struct sock *sk; + + BT_DBG("parent %p", parent); + + /* Close not yet accepted channels */ + while ((sk = bt_accept_dequeue(parent, NULL))) + l2cap_sock_close(sk); + + parent->sk_state = BT_CLOSED; + sock_set_flag(parent, SOCK_ZAPPED); +} + +/* Kill socket (only if zapped and orphan) + * Must be called on unlocked socket. + */ +static void l2cap_sock_kill(struct sock *sk) +{ + if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) + return; + + BT_DBG("sk %p state %d", sk, sk->sk_state); + + /* Kill poor orphan */ + bt_sock_unlink(&l2cap_sk_list, sk); + sock_set_flag(sk, SOCK_DEAD); + sock_put(sk); +} + +static void __l2cap_sock_close(struct sock *sk, int reason) +{ + BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); + + switch (sk->sk_state) { + case BT_LISTEN: + l2cap_sock_cleanup_listen(sk); + break; + + case BT_CONNECTED: + case BT_CONFIG: + if (sk->sk_type == SOCK_SEQPACKET || + sk->sk_type == SOCK_STREAM) { + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + + sk->sk_state = BT_DISCONN; + l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + l2cap_send_disconn_req(conn, sk); + } else + l2cap_chan_del(sk, reason); + break; + + case BT_CONNECT2: + if (sk->sk_type == SOCK_SEQPACKET || + sk->sk_type == SOCK_STREAM) { + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct l2cap_conn_rsp rsp; + __u16 result; + + if (bt_sk(sk)->defer_setup) + result = L2CAP_CR_SEC_BLOCK; + else + result = L2CAP_CR_BAD_PSM; + + rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.result = cpu_to_le16(result); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + l2cap_send_cmd(conn, l2cap_pi(sk)->ident, + L2CAP_CONN_RSP, sizeof(rsp), &rsp); + } else + l2cap_chan_del(sk, reason); + break; + + case BT_CONNECT: + case BT_DISCONN: + l2cap_chan_del(sk, reason); + break; + + default: + sock_set_flag(sk, SOCK_ZAPPED); + break; + } +} + +/* Must be called on unlocked socket. */ +static void l2cap_sock_close(struct sock *sk) +{ + l2cap_sock_clear_timer(sk); + lock_sock(sk); + __l2cap_sock_close(sk, ECONNRESET); + release_sock(sk); + l2cap_sock_kill(sk); +} + +static void l2cap_sock_init(struct sock *sk, struct sock *parent) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + + BT_DBG("sk %p", sk); + + if (parent) { + sk->sk_type = parent->sk_type; + bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup; + + pi->imtu = l2cap_pi(parent)->imtu; + pi->omtu = l2cap_pi(parent)->omtu; + pi->mode = l2cap_pi(parent)->mode; + pi->fcs = l2cap_pi(parent)->fcs; + pi->max_tx = l2cap_pi(parent)->max_tx; + pi->tx_win = l2cap_pi(parent)->tx_win; + pi->sec_level = l2cap_pi(parent)->sec_level; + pi->role_switch = l2cap_pi(parent)->role_switch; + pi->force_reliable = l2cap_pi(parent)->force_reliable; + pi->flushable = l2cap_pi(parent)->flushable; + } else { + pi->imtu = L2CAP_DEFAULT_MTU; + pi->omtu = 0; + if (enable_ertm && sk->sk_type == SOCK_STREAM) + pi->mode = L2CAP_MODE_ERTM; + else + pi->mode = L2CAP_MODE_BASIC; + pi->max_tx = max_transmit; + pi->fcs = L2CAP_FCS_CRC16; + pi->tx_win = tx_window; + pi->sec_level = BT_SECURITY_LOW; + pi->role_switch = 0; + pi->force_reliable = 0; + pi->flushable = 0; + } + + /* Default config options */ + pi->conf_len = 0; + pi->flush_to = L2CAP_DEFAULT_FLUSH_TO; + skb_queue_head_init(TX_QUEUE(sk)); + skb_queue_head_init(SREJ_QUEUE(sk)); + skb_queue_head_init(BUSY_QUEUE(sk)); + INIT_LIST_HEAD(SREJ_LIST(sk)); +} + +static struct proto l2cap_proto = { + .name = "L2CAP", + .owner = THIS_MODULE, + .obj_size = sizeof(struct l2cap_pinfo) +}; + +static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) +{ + struct sock *sk; + + sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto); + if (!sk) + return NULL; + + sock_init_data(sock, sk); + INIT_LIST_HEAD(&bt_sk(sk)->accept_q); + + sk->sk_destruct = l2cap_sock_destruct; + sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT); + + sock_reset_flag(sk, SOCK_ZAPPED); + + sk->sk_protocol = proto; + sk->sk_state = BT_OPEN; + + setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk); + + bt_sock_link(&l2cap_sk_list, sk); + return sk; +} + +static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, + int kern) +{ + struct sock *sk; + + BT_DBG("sock %p", sock); + + sock->state = SS_UNCONNECTED; + + if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM && + sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) + return -ESOCKTNOSUPPORT; + + if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) + return -EPERM; + + sock->ops = &l2cap_sock_ops; + + sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC); + if (!sk) + return -ENOMEM; + + l2cap_sock_init(sk, NULL); + return 0; +} + +static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) +{ + struct sock *sk = sock->sk; + struct sockaddr_l2 la; + int len, err = 0; + + BT_DBG("sk %p", sk); + + if (!addr || addr->sa_family != AF_BLUETOOTH) + return -EINVAL; + + memset(&la, 0, sizeof(la)); + len = min_t(unsigned int, sizeof(la), alen); + memcpy(&la, addr, len); + + if (la.l2_cid) + return -EINVAL; + + lock_sock(sk); + + if (sk->sk_state != BT_OPEN) { + err = -EBADFD; + goto done; + } + + if (la.l2_psm && __le16_to_cpu(la.l2_psm) < 0x1001 && + !capable(CAP_NET_BIND_SERVICE)) { + err = -EACCES; + goto done; + } + + write_lock_bh(&l2cap_sk_list.lock); + + if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) { + err = -EADDRINUSE; + } else { + /* Save source address */ + bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); + l2cap_pi(sk)->psm = la.l2_psm; + l2cap_pi(sk)->sport = la.l2_psm; + sk->sk_state = BT_BOUND; + + if (__le16_to_cpu(la.l2_psm) == 0x0001 || + __le16_to_cpu(la.l2_psm) == 0x0003) + l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; + } + + write_unlock_bh(&l2cap_sk_list.lock); + +done: + release_sock(sk); + return err; +} + +static int l2cap_do_connect(struct sock *sk) +{ + bdaddr_t *src = &bt_sk(sk)->src; + bdaddr_t *dst = &bt_sk(sk)->dst; + struct l2cap_conn *conn; + struct hci_conn *hcon; + struct hci_dev *hdev; + __u8 auth_type; + int err; + + BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), + l2cap_pi(sk)->psm); + + hdev = hci_get_route(dst, src); + if (!hdev) + return -EHOSTUNREACH; + + hci_dev_lock_bh(hdev); + + err = -ENOMEM; + + if (sk->sk_type == SOCK_RAW) { + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_HIGH: + auth_type = HCI_AT_DEDICATED_BONDING_MITM; + break; + case BT_SECURITY_MEDIUM: + auth_type = HCI_AT_DEDICATED_BONDING; + break; + default: + auth_type = HCI_AT_NO_BONDING; + break; + } + } else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) { + if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH) + auth_type = HCI_AT_NO_BONDING_MITM; + else + auth_type = HCI_AT_NO_BONDING; + + if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW) + l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; + } else { + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_HIGH: + auth_type = HCI_AT_GENERAL_BONDING_MITM; + break; + case BT_SECURITY_MEDIUM: + auth_type = HCI_AT_GENERAL_BONDING; + break; + default: + auth_type = HCI_AT_NO_BONDING; + break; + } + } + + hcon = hci_connect(hdev, ACL_LINK, 0, dst, + l2cap_pi(sk)->sec_level, auth_type); + if (!hcon) + goto done; + + conn = l2cap_conn_add(hcon, 0); + if (!conn) { + hci_conn_put(hcon); + goto done; + } + + err = 0; + + /* Update source addr of the socket */ + bacpy(src, conn->src); + + l2cap_chan_add(conn, sk, NULL); + + sk->sk_state = BT_CONNECT; + l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + + if (hcon->state == BT_CONNECTED) { + if (sk->sk_type != SOCK_SEQPACKET && + sk->sk_type != SOCK_STREAM) { + l2cap_sock_clear_timer(sk); + sk->sk_state = BT_CONNECTED; + } else + l2cap_do_start(sk); + } + +done: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + return err; +} + +static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) +{ + struct sock *sk = sock->sk; + struct sockaddr_l2 la; + int len, err = 0; + + BT_DBG("sk %p", sk); + + if (!addr || alen < sizeof(addr->sa_family) || + addr->sa_family != AF_BLUETOOTH) + return -EINVAL; + + memset(&la, 0, sizeof(la)); + len = min_t(unsigned int, sizeof(la), alen); + memcpy(&la, addr, len); + + if (la.l2_cid) + return -EINVAL; + + lock_sock(sk); + + if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) + && !la.l2_psm) { + err = -EINVAL; + goto done; + } + + switch (l2cap_pi(sk)->mode) { + case L2CAP_MODE_BASIC: + break; + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + if (enable_ertm) + break; + /* fall through */ + default: + err = -ENOTSUPP; + goto done; + } + + switch (sk->sk_state) { + case BT_CONNECT: + case BT_CONNECT2: + case BT_CONFIG: + /* Already connecting */ + goto wait; + + case BT_CONNECTED: + /* Already connected */ + goto done; + + case BT_OPEN: + case BT_BOUND: + /* Can connect */ + break; + + default: + err = -EBADFD; + goto done; + } + + /* Set destination address and psm */ + bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr); + l2cap_pi(sk)->psm = la.l2_psm; + + err = l2cap_do_connect(sk); + if (err) + goto done; + +wait: + err = bt_sock_wait_state(sk, BT_CONNECTED, + sock_sndtimeo(sk, flags & O_NONBLOCK)); +done: + release_sock(sk); + return err; +} + +static int l2cap_sock_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + int err = 0; + + BT_DBG("sk %p backlog %d", sk, backlog); + + lock_sock(sk); + + if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) + || sk->sk_state != BT_BOUND) { + err = -EBADFD; + goto done; + } + + switch (l2cap_pi(sk)->mode) { + case L2CAP_MODE_BASIC: + break; + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + if (enable_ertm) + break; + /* fall through */ + default: + err = -ENOTSUPP; + goto done; + } + + if (!l2cap_pi(sk)->psm) { + bdaddr_t *src = &bt_sk(sk)->src; + u16 psm; + + err = -EINVAL; + + write_lock_bh(&l2cap_sk_list.lock); + + for (psm = 0x1001; psm < 0x1100; psm += 2) + if (!__l2cap_get_sock_by_addr(cpu_to_le16(psm), src)) { + l2cap_pi(sk)->psm = cpu_to_le16(psm); + l2cap_pi(sk)->sport = cpu_to_le16(psm); + err = 0; + break; + } + + write_unlock_bh(&l2cap_sk_list.lock); + + if (err < 0) + goto done; + } + + sk->sk_max_ack_backlog = backlog; + sk->sk_ack_backlog = 0; + sk->sk_state = BT_LISTEN; + +done: + release_sock(sk); + return err; +} + +static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) +{ + DECLARE_WAITQUEUE(wait, current); + struct sock *sk = sock->sk, *nsk; + long timeo; + int err = 0; + + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); + + if (sk->sk_state != BT_LISTEN) { + err = -EBADFD; + goto done; + } + + timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); + + BT_DBG("sk %p timeo %ld", sk, timeo); + + /* Wait for an incoming connection. (wake-one). */ + add_wait_queue_exclusive(sk_sleep(sk), &wait); + while (!(nsk = bt_accept_dequeue(sk, newsock))) { + set_current_state(TASK_INTERRUPTIBLE); + if (!timeo) { + err = -EAGAIN; + break; + } + + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); + + if (sk->sk_state != BT_LISTEN) { + err = -EBADFD; + break; + } + + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(sk_sleep(sk), &wait); + + if (err) + goto done; + + newsock->state = SS_CONNECTED; + + BT_DBG("new socket %p", nsk); + +done: + release_sock(sk); + return err; +} + +static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) +{ + struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; + struct sock *sk = sock->sk; + + BT_DBG("sock %p, sk %p", sock, sk); + + addr->sa_family = AF_BLUETOOTH; + *len = sizeof(struct sockaddr_l2); + + if (peer) { + la->l2_psm = l2cap_pi(sk)->psm; + bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); + la->l2_cid = cpu_to_le16(l2cap_pi(sk)->dcid); + } else { + la->l2_psm = l2cap_pi(sk)->sport; + bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); + la->l2_cid = cpu_to_le16(l2cap_pi(sk)->scid); + } + + return 0; +} + +static int __l2cap_wait_ack(struct sock *sk) +{ + DECLARE_WAITQUEUE(wait, current); + int err = 0; + int timeo = HZ/5; + + add_wait_queue(sk_sleep(sk), &wait); + while ((l2cap_pi(sk)->unacked_frames > 0 && l2cap_pi(sk)->conn)) { + set_current_state(TASK_INTERRUPTIBLE); + + if (!timeo) + timeo = HZ/5; + + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + break; + } + + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); + + err = sock_error(sk); + if (err) + break; + } + set_current_state(TASK_RUNNING); + remove_wait_queue(sk_sleep(sk), &wait); + return err; +} + +static void l2cap_monitor_timeout(unsigned long arg) +{ + struct sock *sk = (void *) arg; + + bh_lock_sock(sk); + if (l2cap_pi(sk)->retry_count >= l2cap_pi(sk)->remote_max_tx) { + l2cap_send_disconn_req(l2cap_pi(sk)->conn, sk); + bh_unlock_sock(sk); + return; + } + + l2cap_pi(sk)->retry_count++; + __mod_monitor_timer(); + + l2cap_send_rr_or_rnr(l2cap_pi(sk), L2CAP_CTRL_POLL); + bh_unlock_sock(sk); +} + +static void l2cap_retrans_timeout(unsigned long arg) +{ + struct sock *sk = (void *) arg; + + bh_lock_sock(sk); + l2cap_pi(sk)->retry_count = 1; + __mod_monitor_timer(); + + l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F; + + l2cap_send_rr_or_rnr(l2cap_pi(sk), L2CAP_CTRL_POLL); + bh_unlock_sock(sk); +} + +static void l2cap_drop_acked_frames(struct sock *sk) +{ + struct sk_buff *skb; + + while ((skb = skb_peek(TX_QUEUE(sk))) && + l2cap_pi(sk)->unacked_frames) { + if (bt_cb(skb)->tx_seq == l2cap_pi(sk)->expected_ack_seq) + break; + + skb = skb_dequeue(TX_QUEUE(sk)); + kfree_skb(skb); + + l2cap_pi(sk)->unacked_frames--; + } + + if (!l2cap_pi(sk)->unacked_frames) + del_timer(&l2cap_pi(sk)->retrans_timer); +} + +static inline void l2cap_do_send(struct sock *sk, struct sk_buff *skb) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct hci_conn *hcon = pi->conn->hcon; + u16 flags; + + BT_DBG("sk %p, skb %p len %d", sk, skb, skb->len); + + if (lmp_no_flush_capable(hcon->hdev) && !l2cap_pi(sk)->flushable) + flags = ACL_START_NO_FLUSH; + else + flags = ACL_START; + + hci_send_acl(hcon, skb, flags); +} + +static int l2cap_streaming_send(struct sock *sk) +{ + struct sk_buff *skb, *tx_skb; + struct l2cap_pinfo *pi = l2cap_pi(sk); + u16 control, fcs; + + while ((skb = sk->sk_send_head)) { + tx_skb = skb_clone(skb, GFP_ATOMIC); + + control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); + control |= pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; + put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + + if (pi->fcs == L2CAP_FCS_CRC16) { + fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2); + put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2); + } + + l2cap_do_send(sk, tx_skb); + + pi->next_tx_seq = (pi->next_tx_seq + 1) % 64; + + if (skb_queue_is_last(TX_QUEUE(sk), skb)) + sk->sk_send_head = NULL; + else + sk->sk_send_head = skb_queue_next(TX_QUEUE(sk), skb); + + skb = skb_dequeue(TX_QUEUE(sk)); + kfree_skb(skb); + } + return 0; +} + +static void l2cap_retransmit_one_frame(struct sock *sk, u8 tx_seq) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sk_buff *skb, *tx_skb; + u16 control, fcs; + + skb = skb_peek(TX_QUEUE(sk)); + if (!skb) + return; + + do { + if (bt_cb(skb)->tx_seq == tx_seq) + break; + + if (skb_queue_is_last(TX_QUEUE(sk), skb)) + return; + + } while ((skb = skb_queue_next(TX_QUEUE(sk), skb))); + + if (pi->remote_max_tx && + bt_cb(skb)->retries == pi->remote_max_tx) { + l2cap_send_disconn_req(pi->conn, sk); + return; + } + + tx_skb = skb_clone(skb, GFP_ATOMIC); + bt_cb(skb)->retries++; + control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); + control |= (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) + | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); + put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + + if (pi->fcs == L2CAP_FCS_CRC16) { + fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2); + put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2); + } + + l2cap_do_send(sk, tx_skb); +} + +static int l2cap_ertm_send(struct sock *sk) +{ + struct sk_buff *skb, *tx_skb; + struct l2cap_pinfo *pi = l2cap_pi(sk); + u16 control, fcs; + int nsent = 0; + + if (pi->conn_state & L2CAP_CONN_WAIT_F) + return 0; + + while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk)) && + !(pi->conn_state & L2CAP_CONN_REMOTE_BUSY)) { + + if (pi->remote_max_tx && + bt_cb(skb)->retries == pi->remote_max_tx) { + l2cap_send_disconn_req(pi->conn, sk); + break; + } + + tx_skb = skb_clone(skb, GFP_ATOMIC); + + bt_cb(skb)->retries++; + + control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); + if (pi->conn_state & L2CAP_CONN_SEND_FBIT) { + control |= L2CAP_CTRL_FINAL; + pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; + } + control |= (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) + | (pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); + put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + + + if (pi->fcs == L2CAP_FCS_CRC16) { + fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2); + put_unaligned_le16(fcs, skb->data + tx_skb->len - 2); + } + + l2cap_do_send(sk, tx_skb); + + __mod_retrans_timer(); + + bt_cb(skb)->tx_seq = pi->next_tx_seq; + pi->next_tx_seq = (pi->next_tx_seq + 1) % 64; + + pi->unacked_frames++; + pi->frames_sent++; + + if (skb_queue_is_last(TX_QUEUE(sk), skb)) + sk->sk_send_head = NULL; + else + sk->sk_send_head = skb_queue_next(TX_QUEUE(sk), skb); + + nsent++; + } + + return nsent; +} + +static int l2cap_retransmit_frames(struct sock *sk) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + int ret; + + spin_lock_bh(&pi->send_lock); + + if (!skb_queue_empty(TX_QUEUE(sk))) + sk->sk_send_head = TX_QUEUE(sk)->next; + + pi->next_tx_seq = pi->expected_ack_seq; + ret = l2cap_ertm_send(sk); + + spin_unlock_bh(&pi->send_lock); + + return ret; +} + +static void l2cap_send_ack(struct l2cap_pinfo *pi) +{ + struct sock *sk = (struct sock *)pi; + u16 control = 0; + int nframes; + + control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + + if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { + control |= L2CAP_SUPER_RCV_NOT_READY; + pi->conn_state |= L2CAP_CONN_RNR_SENT; + l2cap_send_sframe(pi, control); + return; + } + + spin_lock_bh(&pi->send_lock); + nframes = l2cap_ertm_send(sk); + spin_unlock_bh(&pi->send_lock); + + if (nframes > 0) + return; + + control |= L2CAP_SUPER_RCV_READY; + l2cap_send_sframe(pi, control); +} + +static void l2cap_send_srejtail(struct sock *sk) +{ + struct srej_list *tail; + u16 control; + + control = L2CAP_SUPER_SELECT_REJECT; + control |= L2CAP_CTRL_FINAL; + + tail = list_entry(SREJ_LIST(sk)->prev, struct srej_list, list); + control |= tail->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + + l2cap_send_sframe(l2cap_pi(sk), control); +} + +static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, int len, int count, struct sk_buff *skb) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sk_buff **frag; + int err, sent = 0; + + if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) + return -EFAULT; + + sent += count; + len -= count; + + /* Continuation fragments (no L2CAP header) */ + frag = &skb_shinfo(skb)->frag_list; + while (len) { + count = min_t(unsigned int, conn->mtu, len); + + *frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err); + if (!*frag) + return -EFAULT; + if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) + return -EFAULT; + + sent += count; + len -= count; + + frag = &(*frag)->next; + } + + return sent; +} + +static struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, size_t len) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sk_buff *skb; + int err, count, hlen = L2CAP_HDR_SIZE + 2; + struct l2cap_hdr *lh; + + BT_DBG("sk %p len %d", sk, (int)len); + + count = min_t(unsigned int, (conn->mtu - hlen), len); + skb = bt_skb_send_alloc(sk, count + hlen, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) + return ERR_PTR(-ENOMEM); + + /* Create L2CAP header */ + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); + lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); + lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); + put_unaligned_le16(l2cap_pi(sk)->psm, skb_put(skb, 2)); + + err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); + if (unlikely(err < 0)) { + kfree_skb(skb); + return ERR_PTR(err); + } + return skb; +} + +static struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size_t len) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sk_buff *skb; + int err, count, hlen = L2CAP_HDR_SIZE; + struct l2cap_hdr *lh; + + BT_DBG("sk %p len %d", sk, (int)len); + + count = min_t(unsigned int, (conn->mtu - hlen), len); + skb = bt_skb_send_alloc(sk, count + hlen, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) + return ERR_PTR(-ENOMEM); + + /* Create L2CAP header */ + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); + lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); + lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); + + err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); + if (unlikely(err < 0)) { + kfree_skb(skb); + return ERR_PTR(err); + } + return skb; +} + +static struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control, u16 sdulen) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sk_buff *skb; + int err, count, hlen = L2CAP_HDR_SIZE + 2; + struct l2cap_hdr *lh; + + BT_DBG("sk %p len %d", sk, (int)len); + + if (!conn) + return ERR_PTR(-ENOTCONN); + + if (sdulen) + hlen += 2; + + if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) + hlen += 2; + + count = min_t(unsigned int, (conn->mtu - hlen), len); + skb = bt_skb_send_alloc(sk, count + hlen, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) + return ERR_PTR(-ENOMEM); + + /* Create L2CAP header */ + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); + lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); + lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); + put_unaligned_le16(control, skb_put(skb, 2)); + if (sdulen) + put_unaligned_le16(sdulen, skb_put(skb, 2)); + + err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); + if (unlikely(err < 0)) { + kfree_skb(skb); + return ERR_PTR(err); + } + + if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) + put_unaligned_le16(0, skb_put(skb, 2)); + + bt_cb(skb)->retries = 0; + return skb; +} + +static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sk_buff *skb; + struct sk_buff_head sar_queue; + u16 control; + size_t size = 0; + + skb_queue_head_init(&sar_queue); + control = L2CAP_SDU_START; + skb = l2cap_create_iframe_pdu(sk, msg, pi->remote_mps, control, len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + __skb_queue_tail(&sar_queue, skb); + len -= pi->remote_mps; + size += pi->remote_mps; + + while (len > 0) { + size_t buflen; + + if (len > pi->remote_mps) { + control = L2CAP_SDU_CONTINUE; + buflen = pi->remote_mps; + } else { + control = L2CAP_SDU_END; + buflen = len; + } + + skb = l2cap_create_iframe_pdu(sk, msg, buflen, control, 0); + if (IS_ERR(skb)) { + skb_queue_purge(&sar_queue); + return PTR_ERR(skb); + } + + __skb_queue_tail(&sar_queue, skb); + len -= buflen; + size += buflen; + } + skb_queue_splice_tail(&sar_queue, TX_QUEUE(sk)); + spin_lock_bh(&pi->send_lock); + if (sk->sk_send_head == NULL) + sk->sk_send_head = sar_queue.next; + spin_unlock_bh(&pi->send_lock); + + return size; +} + +static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sk_buff *skb; + u16 control; + int err; + + BT_DBG("sock %p, sk %p", sock, sk); + + err = sock_error(sk); + if (err) + return err; + + if (msg->msg_flags & MSG_OOB) + return -EOPNOTSUPP; + + lock_sock(sk); + + if (sk->sk_state != BT_CONNECTED) { + err = -ENOTCONN; + goto done; + } + + /* Connectionless channel */ + if (sk->sk_type == SOCK_DGRAM) { + skb = l2cap_create_connless_pdu(sk, msg, len); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + } else { + l2cap_do_send(sk, skb); + err = len; + } + goto done; + } + + switch (pi->mode) { + case L2CAP_MODE_BASIC: + /* Check outgoing MTU */ + if (len > pi->omtu) { + err = -EINVAL; + goto done; + } + + /* Create a basic PDU */ + skb = l2cap_create_basic_pdu(sk, msg, len); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + goto done; + } + + l2cap_do_send(sk, skb); + err = len; + break; + + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + /* Entire SDU fits into one PDU */ + if (len <= pi->remote_mps) { + control = L2CAP_SDU_UNSEGMENTED; + skb = l2cap_create_iframe_pdu(sk, msg, len, control, 0); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + goto done; + } + __skb_queue_tail(TX_QUEUE(sk), skb); + + if (pi->mode == L2CAP_MODE_ERTM) + spin_lock_bh(&pi->send_lock); + + if (sk->sk_send_head == NULL) + sk->sk_send_head = skb; + + if (pi->mode == L2CAP_MODE_ERTM) + spin_unlock_bh(&pi->send_lock); + } else { + /* Segment SDU into multiples PDUs */ + err = l2cap_sar_segment_sdu(sk, msg, len); + if (err < 0) + goto done; + } + + if (pi->mode == L2CAP_MODE_STREAMING) { + err = l2cap_streaming_send(sk); + } else { + spin_lock_bh(&pi->send_lock); + err = l2cap_ertm_send(sk); + spin_unlock_bh(&pi->send_lock); + } + + if (err >= 0) + err = len; + break; + + default: + BT_DBG("bad state %1.1x", pi->mode); + err = -EINVAL; + } + +done: + release_sock(sk); + return err; +} + +static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) +{ + struct sock *sk = sock->sk; + + lock_sock(sk); + + if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) { + struct l2cap_conn_rsp rsp; + + sk->sk_state = BT_CONFIG; + + rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + l2cap_send_cmd(l2cap_pi(sk)->conn, l2cap_pi(sk)->ident, + L2CAP_CONN_RSP, sizeof(rsp), &rsp); + + release_sock(sk); + return 0; + } + + release_sock(sk); + + return bt_sock_recvmsg(iocb, sock, msg, len, flags); +} + +static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) +{ + struct sock *sk = sock->sk; + struct l2cap_options opts; + int len, err = 0; + u32 opt; + + BT_DBG("sk %p", sk); + + lock_sock(sk); + + switch (optname) { + case L2CAP_OPTIONS: + opts.imtu = l2cap_pi(sk)->imtu; + opts.omtu = l2cap_pi(sk)->omtu; + opts.flush_to = l2cap_pi(sk)->flush_to; + opts.mode = l2cap_pi(sk)->mode; + opts.fcs = l2cap_pi(sk)->fcs; + opts.max_tx = l2cap_pi(sk)->max_tx; + opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; + + len = min_t(unsigned int, sizeof(opts), optlen); + if (copy_from_user((char *) &opts, optval, len)) { + err = -EFAULT; + break; + } + + l2cap_pi(sk)->mode = opts.mode; + switch (l2cap_pi(sk)->mode) { + case L2CAP_MODE_BASIC: + break; + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + if (enable_ertm) + break; + /* fall through */ + default: + err = -EINVAL; + break; + } + + l2cap_pi(sk)->imtu = opts.imtu; + l2cap_pi(sk)->omtu = opts.omtu; + l2cap_pi(sk)->fcs = opts.fcs; + l2cap_pi(sk)->max_tx = opts.max_tx; + l2cap_pi(sk)->tx_win = (__u8)opts.txwin_size; + break; + + case L2CAP_LM: + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + if (opt & L2CAP_LM_AUTH) + l2cap_pi(sk)->sec_level = BT_SECURITY_LOW; + if (opt & L2CAP_LM_ENCRYPT) + l2cap_pi(sk)->sec_level = BT_SECURITY_MEDIUM; + if (opt & L2CAP_LM_SECURE) + l2cap_pi(sk)->sec_level = BT_SECURITY_HIGH; + + l2cap_pi(sk)->role_switch = (opt & L2CAP_LM_MASTER); + l2cap_pi(sk)->force_reliable = (opt & L2CAP_LM_RELIABLE); + l2cap_pi(sk)->flushable = (opt & L2CAP_LM_FLUSHABLE); + break; + + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + +static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) +{ + struct sock *sk = sock->sk; + struct bt_security sec; + int len, err = 0; + u32 opt; + + BT_DBG("sk %p", sk); + + if (level == SOL_L2CAP) + return l2cap_sock_setsockopt_old(sock, optname, optval, optlen); + + if (level != SOL_BLUETOOTH) + return -ENOPROTOOPT; + + lock_sock(sk); + + switch (optname) { + case BT_SECURITY: + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM + && sk->sk_type != SOCK_RAW) { + err = -EINVAL; + break; + } + + sec.level = BT_SECURITY_LOW; + + len = min_t(unsigned int, sizeof(sec), optlen); + if (copy_from_user((char *) &sec, optval, len)) { + err = -EFAULT; + break; + } + + if (sec.level < BT_SECURITY_LOW || + sec.level > BT_SECURITY_HIGH) { + err = -EINVAL; + break; + } + + l2cap_pi(sk)->sec_level = sec.level; + break; + + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } + + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + bt_sk(sk)->defer_setup = opt; + break; + + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + +static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + struct l2cap_options opts; + struct l2cap_conninfo cinfo; + int len, err = 0; + u32 opt; + + BT_DBG("sk %p", sk); + + if (get_user(len, optlen)) + return -EFAULT; + + lock_sock(sk); + + switch (optname) { + case L2CAP_OPTIONS: + opts.imtu = l2cap_pi(sk)->imtu; + opts.omtu = l2cap_pi(sk)->omtu; + opts.flush_to = l2cap_pi(sk)->flush_to; + opts.mode = l2cap_pi(sk)->mode; + opts.fcs = l2cap_pi(sk)->fcs; + opts.max_tx = l2cap_pi(sk)->max_tx; + opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; + + len = min_t(unsigned int, len, sizeof(opts)); + if (copy_to_user(optval, (char *) &opts, len)) + err = -EFAULT; + + break; + + case L2CAP_LM: + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_LOW: + opt = L2CAP_LM_AUTH; + break; + case BT_SECURITY_MEDIUM: + opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT; + break; + case BT_SECURITY_HIGH: + opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | + L2CAP_LM_SECURE; + break; + default: + opt = 0; + break; + } + + if (l2cap_pi(sk)->role_switch) + opt |= L2CAP_LM_MASTER; + + if (l2cap_pi(sk)->force_reliable) + opt |= L2CAP_LM_RELIABLE; + + if (l2cap_pi(sk)->flushable) + opt |= L2CAP_LM_FLUSHABLE; + + if (put_user(opt, (u32 __user *) optval)) + err = -EFAULT; + break; + + case L2CAP_CONNINFO: + if (sk->sk_state != BT_CONNECTED && + !(sk->sk_state == BT_CONNECT2 && + bt_sk(sk)->defer_setup)) { + err = -ENOTCONN; + break; + } + + cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle; + memcpy(cinfo.dev_class, l2cap_pi(sk)->conn->hcon->dev_class, 3); + + len = min_t(unsigned int, len, sizeof(cinfo)); + if (copy_to_user(optval, (char *) &cinfo, len)) + err = -EFAULT; + + break; + + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + +static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + struct bt_security sec; + int len, err = 0; + + BT_DBG("sk %p", sk); + + if (level == SOL_L2CAP) + return l2cap_sock_getsockopt_old(sock, optname, optval, optlen); + + if (level != SOL_BLUETOOTH) + return -ENOPROTOOPT; + + if (get_user(len, optlen)) + return -EFAULT; + + lock_sock(sk); + + switch (optname) { + case BT_SECURITY: + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM + && sk->sk_type != SOCK_RAW) { + err = -EINVAL; + break; + } + + sec.level = l2cap_pi(sk)->sec_level; + + len = min_t(unsigned int, len, sizeof(sec)); + if (copy_to_user(optval, (char *) &sec, len)) + err = -EFAULT; + + break; + + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } + + if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval)) + err = -EFAULT; + + break; + + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + +static int l2cap_sock_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + int err = 0; + + BT_DBG("sock %p, sk %p", sock, sk); + + if (!sk) + return 0; + + lock_sock(sk); + if (!sk->sk_shutdown) { + if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) + err = __l2cap_wait_ack(sk); + + sk->sk_shutdown = SHUTDOWN_MASK; + l2cap_sock_clear_timer(sk); + __l2cap_sock_close(sk, 0); + + if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) + err = bt_sock_wait_state(sk, BT_CLOSED, + sk->sk_lingertime); + } + release_sock(sk); + return err; +} + +static int l2cap_sock_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + int err; + + BT_DBG("sock %p, sk %p", sock, sk); + + if (!sk) + return 0; + + err = l2cap_sock_shutdown(sock, 2); + + sock_orphan(sk); + l2cap_sock_kill(sk); + return err; +} + +static void l2cap_chan_ready(struct sock *sk) +{ + struct sock *parent = bt_sk(sk)->parent; + + BT_DBG("sk %p, parent %p", sk, parent); + + l2cap_pi(sk)->conf_state = 0; + l2cap_sock_clear_timer(sk); + + if (!parent) { + /* Outgoing channel. + * Wake up socket sleeping on connect. + */ + sk->sk_state = BT_CONNECTED; + sk->sk_state_change(sk); + } else { + /* Incoming channel. + * Wake up socket sleeping on accept. + */ + parent->sk_data_ready(parent, 0); + } +} + +/* Copy frame to all raw sockets on that connection */ +static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct l2cap_chan_list *l = &conn->chan_list; + struct sk_buff *nskb; + struct sock *sk; + + BT_DBG("conn %p", conn); + + read_lock(&l->lock); + for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { + if (sk->sk_type != SOCK_RAW) + continue; + + /* Don't send frame to the socket it came from */ + if (skb->sk == sk) + continue; + nskb = skb_clone(skb, GFP_ATOMIC); + if (!nskb) + continue; + + if (sock_queue_rcv_skb(sk, nskb)) + kfree_skb(nskb); + } + read_unlock(&l->lock); +} + +/* ---- L2CAP signalling commands ---- */ +static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, + u8 code, u8 ident, u16 dlen, void *data) +{ + struct sk_buff *skb, **frag; + struct l2cap_cmd_hdr *cmd; + struct l2cap_hdr *lh; + int len, count; + + BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %d", + conn, code, ident, dlen); + + len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen; + count = min_t(unsigned int, conn->mtu, len); + + skb = bt_skb_alloc(count, GFP_ATOMIC); + if (!skb) + return NULL; + + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); + lh->len = cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen); + lh->cid = cpu_to_le16(L2CAP_CID_SIGNALING); + + cmd = (struct l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE); + cmd->code = code; + cmd->ident = ident; + cmd->len = cpu_to_le16(dlen); + + if (dlen) { + count -= L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE; + memcpy(skb_put(skb, count), data, count); + data += count; + } + + len -= skb->len; + + /* Continuation fragments (no L2CAP header) */ + frag = &skb_shinfo(skb)->frag_list; + while (len) { + count = min_t(unsigned int, conn->mtu, len); + + *frag = bt_skb_alloc(count, GFP_ATOMIC); + if (!*frag) + goto fail; + + memcpy(skb_put(*frag, count), data, count); + + len -= count; + data += count; + + frag = &(*frag)->next; + } + + return skb; + +fail: + kfree_skb(skb); + return NULL; +} + +static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val) +{ + struct l2cap_conf_opt *opt = *ptr; + int len; + + len = L2CAP_CONF_OPT_SIZE + opt->len; + *ptr += len; + + *type = opt->type; + *olen = opt->len; + + switch (opt->len) { + case 1: + *val = *((u8 *) opt->val); + break; + + case 2: + *val = __le16_to_cpu(*((__le16 *) opt->val)); + break; + + case 4: + *val = __le32_to_cpu(*((__le32 *) opt->val)); + break; + + default: + *val = (unsigned long) opt->val; + break; + } + + BT_DBG("type 0x%2.2x len %d val 0x%lx", *type, opt->len, *val); + return len; +} + +static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val) +{ + struct l2cap_conf_opt *opt = *ptr; + + BT_DBG("type 0x%2.2x len %d val 0x%lx", type, len, val); + + opt->type = type; + opt->len = len; + + switch (len) { + case 1: + *((u8 *) opt->val) = val; + break; + + case 2: + *((__le16 *) opt->val) = cpu_to_le16(val); + break; + + case 4: + *((__le32 *) opt->val) = cpu_to_le32(val); + break; + + default: + memcpy(opt->val, (void *) val, len); + break; + } + + *ptr += L2CAP_CONF_OPT_SIZE + len; +} + +static void l2cap_ack_timeout(unsigned long arg) +{ + struct sock *sk = (void *) arg; + + bh_lock_sock(sk); + l2cap_send_ack(l2cap_pi(sk)); + bh_unlock_sock(sk); +} + +static inline void l2cap_ertm_init(struct sock *sk) +{ + l2cap_pi(sk)->expected_ack_seq = 0; + l2cap_pi(sk)->unacked_frames = 0; + l2cap_pi(sk)->buffer_seq = 0; + l2cap_pi(sk)->num_acked = 0; + l2cap_pi(sk)->frames_sent = 0; + + setup_timer(&l2cap_pi(sk)->retrans_timer, + l2cap_retrans_timeout, (unsigned long) sk); + setup_timer(&l2cap_pi(sk)->monitor_timer, + l2cap_monitor_timeout, (unsigned long) sk); + setup_timer(&l2cap_pi(sk)->ack_timer, + l2cap_ack_timeout, (unsigned long) sk); + + __skb_queue_head_init(SREJ_QUEUE(sk)); + __skb_queue_head_init(BUSY_QUEUE(sk)); + spin_lock_init(&l2cap_pi(sk)->send_lock); + + INIT_WORK(&l2cap_pi(sk)->busy_work, l2cap_busy_work); +} + +static int l2cap_mode_supported(__u8 mode, __u32 feat_mask) +{ + u32 local_feat_mask = l2cap_feat_mask; + if (enable_ertm) + local_feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING; + + switch (mode) { + case L2CAP_MODE_ERTM: + return L2CAP_FEAT_ERTM & feat_mask & local_feat_mask; + case L2CAP_MODE_STREAMING: + return L2CAP_FEAT_STREAMING & feat_mask & local_feat_mask; + default: + return 0x00; + } +} + +static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) +{ + switch (mode) { + case L2CAP_MODE_STREAMING: + case L2CAP_MODE_ERTM: + if (l2cap_mode_supported(mode, remote_feat_mask)) + return mode; + /* fall through */ + default: + return L2CAP_MODE_BASIC; + } +} + +static int l2cap_build_conf_req(struct sock *sk, void *data) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct l2cap_conf_req *req = data; + struct l2cap_conf_rfc rfc = { .mode = pi->mode }; + void *ptr = req->data; + + BT_DBG("sk %p", sk); + + if (pi->num_conf_req || pi->num_conf_rsp) + goto done; + + switch (pi->mode) { + case L2CAP_MODE_STREAMING: + case L2CAP_MODE_ERTM: + pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; + if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask)) + l2cap_send_disconn_req(pi->conn, sk); + break; + default: + pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask); + break; + } + +done: + switch (pi->mode) { + case L2CAP_MODE_BASIC: + if (pi->imtu != L2CAP_DEFAULT_MTU) + l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu); + break; + + case L2CAP_MODE_ERTM: + rfc.mode = L2CAP_MODE_ERTM; + rfc.txwin_size = pi->tx_win; + rfc.max_transmit = pi->max_tx; + rfc.retrans_timeout = 0; + rfc.monitor_timeout = 0; + rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); + if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10) + rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10); + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, + sizeof(rfc), (unsigned long) &rfc); + + if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS)) + break; + + if (pi->fcs == L2CAP_FCS_NONE || + pi->conf_state & L2CAP_CONF_NO_FCS_RECV) { + pi->fcs = L2CAP_FCS_NONE; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs); + } + break; + + case L2CAP_MODE_STREAMING: + rfc.mode = L2CAP_MODE_STREAMING; + rfc.txwin_size = 0; + rfc.max_transmit = 0; + rfc.retrans_timeout = 0; + rfc.monitor_timeout = 0; + rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); + if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10) + rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10); + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, + sizeof(rfc), (unsigned long) &rfc); + + if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS)) + break; + + if (pi->fcs == L2CAP_FCS_NONE || + pi->conf_state & L2CAP_CONF_NO_FCS_RECV) { + pi->fcs = L2CAP_FCS_NONE; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs); + } + break; + } + + /* FIXME: Need actual value of the flush timeout */ + //if (flush_to != L2CAP_DEFAULT_FLUSH_TO) + // l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to); + + req->dcid = cpu_to_le16(pi->dcid); + req->flags = cpu_to_le16(0); + + return ptr - data; +} + +static int l2cap_parse_conf_req(struct sock *sk, void *data) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct l2cap_conf_rsp *rsp = data; + void *ptr = rsp->data; + void *req = pi->conf_req; + int len = pi->conf_len; + int type, hint, olen; + unsigned long val; + struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; + u16 mtu = L2CAP_DEFAULT_MTU; + u16 result = L2CAP_CONF_SUCCESS; + + BT_DBG("sk %p", sk); + + while (len >= L2CAP_CONF_OPT_SIZE) { + len -= l2cap_get_conf_opt(&req, &type, &olen, &val); + + hint = type & L2CAP_CONF_HINT; + type &= L2CAP_CONF_MASK; + + switch (type) { + case L2CAP_CONF_MTU: + mtu = val; + break; + + case L2CAP_CONF_FLUSH_TO: + pi->flush_to = val; + break; + + case L2CAP_CONF_QOS: + break; + + case L2CAP_CONF_RFC: + if (olen == sizeof(rfc)) + memcpy(&rfc, (void *) val, olen); + break; + + case L2CAP_CONF_FCS: + if (val == L2CAP_FCS_NONE) + pi->conf_state |= L2CAP_CONF_NO_FCS_RECV; + + break; + + default: + if (hint) + break; + + result = L2CAP_CONF_UNKNOWN; + *((u8 *) ptr++) = type; + break; + } + } + + if (pi->num_conf_rsp || pi->num_conf_req) + goto done; + + switch (pi->mode) { + case L2CAP_MODE_STREAMING: + case L2CAP_MODE_ERTM: + pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; + if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask)) + return -ECONNREFUSED; + break; + default: + pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask); + break; + } + +done: + if (pi->mode != rfc.mode) { + result = L2CAP_CONF_UNACCEPT; + rfc.mode = pi->mode; + + if (pi->num_conf_rsp == 1) + return -ECONNREFUSED; + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, + sizeof(rfc), (unsigned long) &rfc); + } + + + if (result == L2CAP_CONF_SUCCESS) { + /* Configure output options and let the other side know + * which ones we don't like. */ + + if (mtu < L2CAP_DEFAULT_MIN_MTU) + result = L2CAP_CONF_UNACCEPT; + else { + pi->omtu = mtu; + pi->conf_state |= L2CAP_CONF_MTU_DONE; + } + l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu); + + switch (rfc.mode) { + case L2CAP_MODE_BASIC: + pi->fcs = L2CAP_FCS_NONE; + pi->conf_state |= L2CAP_CONF_MODE_DONE; + break; + + case L2CAP_MODE_ERTM: + pi->remote_tx_win = rfc.txwin_size; + pi->remote_max_tx = rfc.max_transmit; + if (rfc.max_pdu_size > pi->conn->mtu - 10) + rfc.max_pdu_size = le16_to_cpu(pi->conn->mtu - 10); + + pi->remote_mps = le16_to_cpu(rfc.max_pdu_size); + + rfc.retrans_timeout = + le16_to_cpu(L2CAP_DEFAULT_RETRANS_TO); + rfc.monitor_timeout = + le16_to_cpu(L2CAP_DEFAULT_MONITOR_TO); + + pi->conf_state |= L2CAP_CONF_MODE_DONE; + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, + sizeof(rfc), (unsigned long) &rfc); + + break; + + case L2CAP_MODE_STREAMING: + if (rfc.max_pdu_size > pi->conn->mtu - 10) + rfc.max_pdu_size = le16_to_cpu(pi->conn->mtu - 10); + + pi->remote_mps = le16_to_cpu(rfc.max_pdu_size); + + pi->conf_state |= L2CAP_CONF_MODE_DONE; + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, + sizeof(rfc), (unsigned long) &rfc); + + break; + + default: + result = L2CAP_CONF_UNACCEPT; + + memset(&rfc, 0, sizeof(rfc)); + rfc.mode = pi->mode; + } + + if (result == L2CAP_CONF_SUCCESS) + pi->conf_state |= L2CAP_CONF_OUTPUT_DONE; + } + rsp->scid = cpu_to_le16(pi->dcid); + rsp->result = cpu_to_le16(result); + rsp->flags = cpu_to_le16(0x0000); + + return ptr - data; +} + +static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, u16 *result) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct l2cap_conf_req *req = data; + void *ptr = req->data; + int type, olen; + unsigned long val; + struct l2cap_conf_rfc rfc; + + BT_DBG("sk %p, rsp %p, len %d, req %p", sk, rsp, len, data); + + while (len >= L2CAP_CONF_OPT_SIZE) { + len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); + + switch (type) { + case L2CAP_CONF_MTU: + if (val < L2CAP_DEFAULT_MIN_MTU) { + *result = L2CAP_CONF_UNACCEPT; + pi->omtu = L2CAP_DEFAULT_MIN_MTU; + } else + pi->omtu = val; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu); + break; + + case L2CAP_CONF_FLUSH_TO: + pi->flush_to = val; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, + 2, pi->flush_to); + break; + + case L2CAP_CONF_RFC: + if (olen == sizeof(rfc)) + memcpy(&rfc, (void *)val, olen); + + if ((pi->conf_state & L2CAP_CONF_STATE2_DEVICE) && + rfc.mode != pi->mode) + return -ECONNREFUSED; + + pi->mode = rfc.mode; + pi->fcs = 0; + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, + sizeof(rfc), (unsigned long) &rfc); + break; + } + } + + if (*result == L2CAP_CONF_SUCCESS) { + switch (rfc.mode) { + case L2CAP_MODE_ERTM: + pi->remote_tx_win = rfc.txwin_size; + pi->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); + pi->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); + pi->mps = le16_to_cpu(rfc.max_pdu_size); + break; + case L2CAP_MODE_STREAMING: + pi->mps = le16_to_cpu(rfc.max_pdu_size); + } + } + + req->dcid = cpu_to_le16(pi->dcid); + req->flags = cpu_to_le16(0x0000); + + return ptr - data; +} + +static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 result, u16 flags) +{ + struct l2cap_conf_rsp *rsp = data; + void *ptr = rsp->data; + + BT_DBG("sk %p", sk); + + rsp->scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp->result = cpu_to_le16(result); + rsp->flags = cpu_to_le16(flags); + + return ptr - data; +} + +static void l2cap_conf_rfc_get(struct sock *sk, void *rsp, int len) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + int type, olen; + unsigned long val; + struct l2cap_conf_rfc rfc; + + BT_DBG("sk %p, rsp %p, len %d", sk, rsp, len); + + if ((pi->mode != L2CAP_MODE_ERTM) && (pi->mode != L2CAP_MODE_STREAMING)) + return; + + while (len >= L2CAP_CONF_OPT_SIZE) { + len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); + + switch (type) { + case L2CAP_CONF_RFC: + if (olen == sizeof(rfc)) + memcpy(&rfc, (void *)val, olen); + goto done; + } + } + +done: + switch (rfc.mode) { + case L2CAP_MODE_ERTM: + pi->remote_tx_win = rfc.txwin_size; + pi->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); + pi->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); + pi->mps = le16_to_cpu(rfc.max_pdu_size); + break; + case L2CAP_MODE_STREAMING: + pi->mps = le16_to_cpu(rfc.max_pdu_size); + } +} + +static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +{ + struct l2cap_cmd_rej *rej = (struct l2cap_cmd_rej *) data; + + if (rej->reason != 0x0000) + return 0; + + if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) && + cmd->ident == conn->info_ident) { + del_timer(&conn->info_timer); + + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + conn->info_ident = 0; + + l2cap_conn_start(conn); + } + + return 0; +} + +static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +{ + struct l2cap_chan_list *list = &conn->chan_list; + struct l2cap_conn_req *req = (struct l2cap_conn_req *) data; + struct l2cap_conn_rsp rsp; + struct sock *sk, *parent; + int result, status = L2CAP_CS_NO_INFO; + + u16 dcid = 0, scid = __le16_to_cpu(req->scid); + __le16 psm = req->psm; + + BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid); + + /* Check if we have socket listening on psm */ + parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src); + if (!parent) { + result = L2CAP_CR_BAD_PSM; + goto sendresp; + } + + /* Check if the ACL is secure enough (if not SDP) */ + if (psm != cpu_to_le16(0x0001) && + !hci_conn_check_link_mode(conn->hcon)) { + conn->disc_reason = 0x05; + result = L2CAP_CR_SEC_BLOCK; + goto response; + } + + result = L2CAP_CR_NO_MEM; + + /* Check for backlog size */ + if (sk_acceptq_is_full(parent)) { + BT_DBG("backlog full %d", parent->sk_ack_backlog); + goto response; + } + + sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC); + if (!sk) + goto response; + + write_lock_bh(&list->lock); + + /* Check if we already have channel with that dcid */ + if (__l2cap_get_chan_by_dcid(list, scid)) { + write_unlock_bh(&list->lock); + sock_set_flag(sk, SOCK_ZAPPED); + l2cap_sock_kill(sk); + goto response; + } + + hci_conn_hold(conn->hcon); + + l2cap_sock_init(sk, parent); + bacpy(&bt_sk(sk)->src, conn->src); + bacpy(&bt_sk(sk)->dst, conn->dst); + l2cap_pi(sk)->psm = psm; + l2cap_pi(sk)->dcid = scid; + + __l2cap_chan_add(conn, sk, parent); + dcid = l2cap_pi(sk)->scid; + + l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + + l2cap_pi(sk)->ident = cmd->ident; + + if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) { + if (l2cap_check_security(sk)) { + if (bt_sk(sk)->defer_setup) { + sk->sk_state = BT_CONNECT2; + result = L2CAP_CR_PEND; + status = L2CAP_CS_AUTHOR_PEND; + parent->sk_data_ready(parent, 0); + } else { + sk->sk_state = BT_CONFIG; + result = L2CAP_CR_SUCCESS; + status = L2CAP_CS_NO_INFO; + } + } else { + sk->sk_state = BT_CONNECT2; + result = L2CAP_CR_PEND; + status = L2CAP_CS_AUTHEN_PEND; + } + } else { + sk->sk_state = BT_CONNECT2; + result = L2CAP_CR_PEND; + status = L2CAP_CS_NO_INFO; + } + + write_unlock_bh(&list->lock); + +response: + bh_unlock_sock(parent); + +sendresp: + rsp.scid = cpu_to_le16(scid); + rsp.dcid = cpu_to_le16(dcid); + rsp.result = cpu_to_le16(result); + rsp.status = cpu_to_le16(status); + l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); + + if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) { + struct l2cap_info_req info; + info.type = cpu_to_le16(L2CAP_IT_FEAT_MASK); + + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT; + conn->info_ident = l2cap_get_ident(conn); + + mod_timer(&conn->info_timer, jiffies + + msecs_to_jiffies(L2CAP_INFO_TIMEOUT)); + + l2cap_send_cmd(conn, conn->info_ident, + L2CAP_INFO_REQ, sizeof(info), &info); + } + + return 0; +} + +static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +{ + struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data; + u16 scid, dcid, result, status; + struct sock *sk; + u8 req[128]; + + scid = __le16_to_cpu(rsp->scid); + dcid = __le16_to_cpu(rsp->dcid); + result = __le16_to_cpu(rsp->result); + status = __le16_to_cpu(rsp->status); + + BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status); + + if (scid) { + sk = l2cap_get_chan_by_scid(&conn->chan_list, scid); + if (!sk) + return 0; + } else { + sk = l2cap_get_chan_by_ident(&conn->chan_list, cmd->ident); + if (!sk) + return 0; + } + + switch (result) { + case L2CAP_CR_SUCCESS: + sk->sk_state = BT_CONFIG; + l2cap_pi(sk)->ident = 0; + l2cap_pi(sk)->dcid = dcid; + l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; + l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_CONNECT_PEND; + + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, + l2cap_build_conf_req(sk, req), req); + l2cap_pi(sk)->num_conf_req++; + break; + + case L2CAP_CR_PEND: + l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; + break; + + default: + /* sync kernel.org patch. commit a49184c229535ebedbb659214db2d4d1d77b7c07 */ + /* don't delete l2cap channel if sk is owned by user */ + if (sock_owned_by_user(sk)) { + sk->sk_state = BT_DISCONN; + l2cap_sock_clear_timer(sk); + l2cap_sock_set_timer(sk, HZ / 5); + break; + } + + l2cap_chan_del(sk, ECONNREFUSED); + break; + } + + bh_unlock_sock(sk); + return 0; +} + +static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) +{ + struct l2cap_conf_req *req = (struct l2cap_conf_req *) data; + u16 dcid, flags; + u8 rsp[64]; + struct sock *sk; + int len; + + dcid = __le16_to_cpu(req->dcid); + flags = __le16_to_cpu(req->flags); + + BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags); + + sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid); + if (!sk) + return -ENOENT; + + if (sk->sk_state == BT_DISCONN) + goto unlock; + + /* Reject if config buffer is too small. */ + len = cmd_len - sizeof(*req); + if (l2cap_pi(sk)->conf_len + len > sizeof(l2cap_pi(sk)->conf_req)) { + l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, + l2cap_build_conf_rsp(sk, rsp, + L2CAP_CONF_REJECT, flags), rsp); + goto unlock; + } + + /* Store config. */ + memcpy(l2cap_pi(sk)->conf_req + l2cap_pi(sk)->conf_len, req->data, len); + l2cap_pi(sk)->conf_len += len; + + if (flags & 0x0001) { + /* Incomplete config. Send empty response. */ + l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, + l2cap_build_conf_rsp(sk, rsp, + L2CAP_CONF_SUCCESS, 0x0001), rsp); + goto unlock; + } + + /* Complete config. */ + len = l2cap_parse_conf_req(sk, rsp); + if (len < 0) { + l2cap_send_disconn_req(conn, sk); + goto unlock; + } + + l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp); + l2cap_pi(sk)->num_conf_rsp++; + + /* Reset config buffer. */ + l2cap_pi(sk)->conf_len = 0; + + if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE)) + goto unlock; + + if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) { + if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_NO_FCS_RECV) || + l2cap_pi(sk)->fcs != L2CAP_FCS_NONE) + l2cap_pi(sk)->fcs = L2CAP_FCS_CRC16; + + sk->sk_state = BT_CONNECTED; + + l2cap_pi(sk)->next_tx_seq = 0; + l2cap_pi(sk)->expected_tx_seq = 0; + __skb_queue_head_init(TX_QUEUE(sk)); + if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) + l2cap_ertm_init(sk); + + l2cap_chan_ready(sk); + goto unlock; + } + + if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) { + u8 buf[64]; + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, + l2cap_build_conf_req(sk, buf), buf); + l2cap_pi(sk)->num_conf_req++; + } + +unlock: + bh_unlock_sock(sk); + return 0; +} + +static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +{ + struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data; + u16 scid, flags, result; + struct sock *sk; + int len = cmd->len - sizeof(*rsp); + + scid = __le16_to_cpu(rsp->scid); + flags = __le16_to_cpu(rsp->flags); + result = __le16_to_cpu(rsp->result); + + BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", + scid, flags, result); + + sk = l2cap_get_chan_by_scid(&conn->chan_list, scid); + if (!sk) + return 0; + + switch (result) { + case L2CAP_CONF_SUCCESS: + l2cap_conf_rfc_get(sk, rsp->data, len); + break; + + case L2CAP_CONF_UNACCEPT: + if (l2cap_pi(sk)->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) { + char req[64]; + + if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) { + l2cap_send_disconn_req(conn, sk); + goto done; + } + + /* throw out any old stored conf requests */ + result = L2CAP_CONF_SUCCESS; + len = l2cap_parse_conf_rsp(sk, rsp->data, + len, req, &result); + if (len < 0) { + l2cap_send_disconn_req(conn, sk); + goto done; + } + + l2cap_send_cmd(conn, l2cap_get_ident(conn), + L2CAP_CONF_REQ, len, req); + l2cap_pi(sk)->num_conf_req++; + if (result != L2CAP_CONF_SUCCESS) + goto done; + break; + } + + default: + sk->sk_state = BT_DISCONN; + sk->sk_err = ECONNRESET; + l2cap_sock_set_timer(sk, HZ * 5); + l2cap_send_disconn_req(conn, sk); + goto done; + } + + if (flags & 0x01) + goto done; + + l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE; + + if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) { + if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_NO_FCS_RECV) || + l2cap_pi(sk)->fcs != L2CAP_FCS_NONE) + l2cap_pi(sk)->fcs = L2CAP_FCS_CRC16; + + sk->sk_state = BT_CONNECTED; + l2cap_pi(sk)->next_tx_seq = 0; + l2cap_pi(sk)->expected_tx_seq = 0; + __skb_queue_head_init(TX_QUEUE(sk)); + if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) + l2cap_ertm_init(sk); + + l2cap_chan_ready(sk); + } + +done: + bh_unlock_sock(sk); + return 0; +} + +static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +{ + struct l2cap_disconn_req *req = (struct l2cap_disconn_req *) data; + struct l2cap_disconn_rsp rsp; + u16 dcid, scid; + struct sock *sk; + + scid = __le16_to_cpu(req->scid); + dcid = __le16_to_cpu(req->dcid); + + BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid); + + sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid); + if (!sk) + return 0; + + rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); + l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp); + + sk->sk_shutdown = SHUTDOWN_MASK; + + skb_queue_purge(TX_QUEUE(sk)); + + if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) { + skb_queue_purge(SREJ_QUEUE(sk)); + skb_queue_purge(BUSY_QUEUE(sk)); + del_timer(&l2cap_pi(sk)->retrans_timer); + del_timer(&l2cap_pi(sk)->monitor_timer); + del_timer(&l2cap_pi(sk)->ack_timer); + } + + /* sync kernel.org patch. commit a49184c229535ebedbb659214db2d4d1d77b7c07 */ + /* don't delete l2cap channel if sk is owned by user */ + if (sock_owned_by_user(sk)) { + sk->sk_state = BT_DISCONN; + l2cap_sock_clear_timer(sk); + l2cap_sock_set_timer(sk, HZ / 5); + bh_unlock_sock(sk); + return 0; + } + + l2cap_chan_del(sk, ECONNRESET); + bh_unlock_sock(sk); + + l2cap_sock_kill(sk); + return 0; +} + +static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +{ + struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data; + u16 dcid, scid; + struct sock *sk; + + scid = __le16_to_cpu(rsp->scid); + dcid = __le16_to_cpu(rsp->dcid); + + BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid); + + sk = l2cap_get_chan_by_scid(&conn->chan_list, scid); + if (!sk) + return 0; + + skb_queue_purge(TX_QUEUE(sk)); + + if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) { + skb_queue_purge(SREJ_QUEUE(sk)); + skb_queue_purge(BUSY_QUEUE(sk)); + del_timer(&l2cap_pi(sk)->retrans_timer); + del_timer(&l2cap_pi(sk)->monitor_timer); + del_timer(&l2cap_pi(sk)->ack_timer); + } + + /* sync kernel.org patch. commit a49184c229535ebedbb659214db2d4d1d77b7c07 */ + /* don't delete l2cap channel if sk is owned by user */ + if (sock_owned_by_user(sk)) { + sk->sk_state = BT_DISCONN; + l2cap_sock_clear_timer(sk); + l2cap_sock_set_timer(sk, HZ / 5); + bh_unlock_sock(sk); + return 0; + } + l2cap_chan_del(sk, 0); + bh_unlock_sock(sk); + + l2cap_sock_kill(sk); + return 0; +} + +static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +{ + struct l2cap_info_req *req = (struct l2cap_info_req *) data; + u16 type; + + type = __le16_to_cpu(req->type); + + BT_DBG("type 0x%4.4x", type); + + if (type == L2CAP_IT_FEAT_MASK) { + u8 buf[8]; + u32 feat_mask = l2cap_feat_mask; + struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; + rsp->type = cpu_to_le16(L2CAP_IT_FEAT_MASK); + rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); + if (enable_ertm) + feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING + | L2CAP_FEAT_FCS; + put_unaligned_le32(feat_mask, rsp->data); + l2cap_send_cmd(conn, cmd->ident, + L2CAP_INFO_RSP, sizeof(buf), buf); + } else if (type == L2CAP_IT_FIXED_CHAN) { + u8 buf[12]; + struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; + rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); + rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); + memcpy(buf + 4, l2cap_fixed_chan, 8); + l2cap_send_cmd(conn, cmd->ident, + L2CAP_INFO_RSP, sizeof(buf), buf); + } else { + struct l2cap_info_rsp rsp; + rsp.type = cpu_to_le16(type); + rsp.result = cpu_to_le16(L2CAP_IR_NOTSUPP); + l2cap_send_cmd(conn, cmd->ident, + L2CAP_INFO_RSP, sizeof(rsp), &rsp); + } + + return 0; +} + +static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +{ + struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) data; + u16 type, result; + + type = __le16_to_cpu(rsp->type); + result = __le16_to_cpu(rsp->result); + + BT_DBG("type 0x%4.4x result 0x%2.2x", type, result); + + del_timer(&conn->info_timer); + + if (type == L2CAP_IT_FEAT_MASK) { + conn->feat_mask = get_unaligned_le32(rsp->data); + + if (conn->feat_mask & L2CAP_FEAT_FIXED_CHAN) { + struct l2cap_info_req req; + req.type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); + + conn->info_ident = l2cap_get_ident(conn); + + l2cap_send_cmd(conn, conn->info_ident, + L2CAP_INFO_REQ, sizeof(req), &req); + } else { + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + conn->info_ident = 0; + + l2cap_conn_start(conn); + } + } else if (type == L2CAP_IT_FIXED_CHAN) { + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + conn->info_ident = 0; + + l2cap_conn_start(conn); + } + + return 0; +} + +static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) +{ + u8 *data = skb->data; + int len = skb->len; + struct l2cap_cmd_hdr cmd; + int err = 0; + + l2cap_raw_recv(conn, skb); + + while (len >= L2CAP_CMD_HDR_SIZE) { + u16 cmd_len; + memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE); + data += L2CAP_CMD_HDR_SIZE; + len -= L2CAP_CMD_HDR_SIZE; + + cmd_len = le16_to_cpu(cmd.len); + + BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len, cmd.ident); + + if (cmd_len > len || !cmd.ident) { + BT_DBG("corrupted command"); + break; + } + + switch (cmd.code) { + case L2CAP_COMMAND_REJ: + l2cap_command_rej(conn, &cmd, data); + break; + + case L2CAP_CONN_REQ: + err = l2cap_connect_req(conn, &cmd, data); + break; + + case L2CAP_CONN_RSP: + err = l2cap_connect_rsp(conn, &cmd, data); + break; + + case L2CAP_CONF_REQ: + err = l2cap_config_req(conn, &cmd, cmd_len, data); + break; + + case L2CAP_CONF_RSP: + err = l2cap_config_rsp(conn, &cmd, data); + break; + + case L2CAP_DISCONN_REQ: + err = l2cap_disconnect_req(conn, &cmd, data); + break; + + case L2CAP_DISCONN_RSP: + err = l2cap_disconnect_rsp(conn, &cmd, data); + break; + + case L2CAP_ECHO_REQ: + l2cap_send_cmd(conn, cmd.ident, L2CAP_ECHO_RSP, cmd_len, data); + break; + + case L2CAP_ECHO_RSP: + break; + + case L2CAP_INFO_REQ: + err = l2cap_information_req(conn, &cmd, data); + break; + + case L2CAP_INFO_RSP: + err = l2cap_information_rsp(conn, &cmd, data); + break; + + default: + BT_ERR("Unknown signaling command 0x%2.2x", cmd.code); + err = -EINVAL; + break; + } + + if (err) { + struct l2cap_cmd_rej rej; + BT_DBG("error %d", err); + + /* FIXME: Map err to a valid reason */ + rej.reason = cpu_to_le16(0); + l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); + } + + data += cmd_len; + len -= cmd_len; + } + + kfree_skb(skb); +} + +static int l2cap_check_fcs(struct l2cap_pinfo *pi, struct sk_buff *skb) +{ + u16 our_fcs, rcv_fcs; + int hdr_size = L2CAP_HDR_SIZE + 2; + + if (pi->fcs == L2CAP_FCS_CRC16) { + skb_trim(skb, skb->len - 2); + rcv_fcs = get_unaligned_le16(skb->data + skb->len); + our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size); + + if (our_fcs != rcv_fcs) + return -EINVAL; + } + return 0; +} + +static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + u16 control = 0; + + pi->frames_sent = 0; + pi->conn_state |= L2CAP_CONN_SEND_FBIT; + + control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + + if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { + control |= L2CAP_SUPER_RCV_NOT_READY | L2CAP_CTRL_FINAL; + l2cap_send_sframe(pi, control); + pi->conn_state |= L2CAP_CONN_RNR_SENT; + pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; + } + + if (pi->conn_state & L2CAP_CONN_REMOTE_BUSY && pi->unacked_frames > 0) + __mod_retrans_timer(); + + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + + spin_lock_bh(&pi->send_lock); + l2cap_ertm_send(sk); + spin_unlock_bh(&pi->send_lock); + + if (!(pi->conn_state & L2CAP_CONN_LOCAL_BUSY) && + pi->frames_sent == 0) { + control |= L2CAP_SUPER_RCV_READY; + l2cap_send_sframe(pi, control); + } +} + +static int l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_seq, u8 sar) +{ + struct sk_buff *next_skb; + + bt_cb(skb)->tx_seq = tx_seq; + bt_cb(skb)->sar = sar; + + next_skb = skb_peek(SREJ_QUEUE(sk)); + if (!next_skb) { + __skb_queue_tail(SREJ_QUEUE(sk), skb); + return 0; + } + + do { + if (bt_cb(next_skb)->tx_seq == tx_seq) + return -EINVAL; + + if (bt_cb(next_skb)->tx_seq > tx_seq) { + __skb_queue_before(SREJ_QUEUE(sk), next_skb, skb); + return 0; + } + + if (skb_queue_is_last(SREJ_QUEUE(sk), next_skb)) + break; + + } while ((next_skb = skb_queue_next(SREJ_QUEUE(sk), next_skb))); + + __skb_queue_tail(SREJ_QUEUE(sk), skb); + + return 0; +} + +static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sk_buff *_skb; + int err; + + switch (control & L2CAP_CTRL_SAR) { + case L2CAP_SDU_UNSEGMENTED: + if (pi->conn_state & L2CAP_CONN_SAR_SDU) + goto drop; + + err = sock_queue_rcv_skb(sk, skb); + if (!err) + return err; + + break; + + case L2CAP_SDU_START: + if (pi->conn_state & L2CAP_CONN_SAR_SDU) + goto drop; + + pi->sdu_len = get_unaligned_le16(skb->data); + + if (pi->sdu_len > pi->imtu) + goto disconnect; + + pi->sdu = bt_skb_alloc(pi->sdu_len, GFP_ATOMIC); + if (!pi->sdu) + return -ENOMEM; + + /* pull sdu_len bytes only after alloc, because of Local Busy + * condition we have to be sure that this will be executed + * only once, i.e., when alloc does not fail */ + skb_pull(skb, 2); + + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + + pi->conn_state |= L2CAP_CONN_SAR_SDU; + pi->partial_sdu_len = skb->len; + break; + + case L2CAP_SDU_CONTINUE: + if (!(pi->conn_state & L2CAP_CONN_SAR_SDU)) + goto disconnect; + + if (!pi->sdu) + goto disconnect; + + pi->partial_sdu_len += skb->len; + if (pi->partial_sdu_len > pi->sdu_len) + goto drop; + + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + + break; + + case L2CAP_SDU_END: + if (!(pi->conn_state & L2CAP_CONN_SAR_SDU)) + goto disconnect; + + if (!pi->sdu) + goto disconnect; + + if (!(pi->conn_state & L2CAP_CONN_SAR_RETRY)) { + pi->partial_sdu_len += skb->len; + + if (pi->partial_sdu_len > pi->imtu) + goto drop; + + if (pi->partial_sdu_len != pi->sdu_len) + goto drop; + + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + } + + _skb = skb_clone(pi->sdu, GFP_ATOMIC); + if (!_skb) { + pi->conn_state |= L2CAP_CONN_SAR_RETRY; + return -ENOMEM; + } + + err = sock_queue_rcv_skb(sk, _skb); + if (err < 0) { + kfree_skb(_skb); + pi->conn_state |= L2CAP_CONN_SAR_RETRY; + return err; + } + + pi->conn_state &= ~L2CAP_CONN_SAR_RETRY; + pi->conn_state &= ~L2CAP_CONN_SAR_SDU; + + kfree_skb(pi->sdu); + break; + } + + kfree_skb(skb); + return 0; + +drop: + kfree_skb(pi->sdu); + pi->sdu = NULL; + +disconnect: + l2cap_send_disconn_req(pi->conn, sk); + kfree_skb(skb); + return 0; +} + +static void l2cap_busy_work(struct work_struct *work) +{ + DECLARE_WAITQUEUE(wait, current); + struct l2cap_pinfo *pi = + container_of(work, struct l2cap_pinfo, busy_work); + struct sock *sk = (struct sock *)pi; + int n_tries = 0, timeo = HZ/5, err; + struct sk_buff *skb; + u16 control; + + lock_sock(sk); + + add_wait_queue(sk_sleep(sk), &wait); + while ((skb = skb_peek(BUSY_QUEUE(sk)))) { + set_current_state(TASK_INTERRUPTIBLE); + + if (n_tries++ > L2CAP_LOCAL_BUSY_TRIES) { + err = -EBUSY; + l2cap_send_disconn_req(pi->conn, sk); + goto done; + } + + if (!timeo) + timeo = HZ/5; + + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + goto done; + } + + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); + + err = sock_error(sk); + if (err) + goto done; + + while ((skb = skb_dequeue(BUSY_QUEUE(sk)))) { + control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; + err = l2cap_ertm_reassembly_sdu(sk, skb, control); + if (err < 0) { + skb_queue_head(BUSY_QUEUE(sk), skb); + break; + } + + pi->buffer_seq = (pi->buffer_seq + 1) % 64; + } + + if (!skb) + break; + } + + if (!(pi->conn_state & L2CAP_CONN_RNR_SENT)) + goto done; + + control = pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= L2CAP_SUPER_RCV_READY | L2CAP_CTRL_POLL; + l2cap_send_sframe(pi, control); + l2cap_pi(sk)->retry_count = 1; + + del_timer(&pi->retrans_timer); + __mod_monitor_timer(); + + l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F; + +done: + pi->conn_state &= ~L2CAP_CONN_LOCAL_BUSY; + pi->conn_state &= ~L2CAP_CONN_RNR_SENT; + + set_current_state(TASK_RUNNING); + remove_wait_queue(sk_sleep(sk), &wait); + + release_sock(sk); +} + +static int l2cap_push_rx_skb(struct sock *sk, struct sk_buff *skb, u16 control) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + int sctrl, err; + + if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { + bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; + __skb_queue_tail(BUSY_QUEUE(sk), skb); + return -EBUSY; + } + + err = l2cap_ertm_reassembly_sdu(sk, skb, control); + if (err >= 0) { + pi->buffer_seq = (pi->buffer_seq + 1) % 64; + return err; + } + + /* Busy Condition */ + pi->conn_state |= L2CAP_CONN_LOCAL_BUSY; + bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; + __skb_queue_tail(BUSY_QUEUE(sk), skb); + + sctrl = pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + sctrl |= L2CAP_SUPER_RCV_NOT_READY; + l2cap_send_sframe(pi, sctrl); + + pi->conn_state |= L2CAP_CONN_RNR_SENT; + + queue_work(_busy_wq, &pi->busy_work); + + return err; +} + +static int l2cap_streaming_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sk_buff *_skb; + int err = -EINVAL; + + /* + * TODO: We have to notify the userland if some data is lost with the + * Streaming Mode. + */ + + switch (control & L2CAP_CTRL_SAR) { + case L2CAP_SDU_UNSEGMENTED: + if (pi->conn_state & L2CAP_CONN_SAR_SDU) { + kfree_skb(pi->sdu); + break; + } + + err = sock_queue_rcv_skb(sk, skb); + if (!err) + return 0; + + break; + + case L2CAP_SDU_START: + if (pi->conn_state & L2CAP_CONN_SAR_SDU) { + kfree_skb(pi->sdu); + break; + } + + pi->sdu_len = get_unaligned_le16(skb->data); + skb_pull(skb, 2); + + if (pi->sdu_len > pi->imtu) { + err = -EMSGSIZE; + break; + } + + pi->sdu = bt_skb_alloc(pi->sdu_len, GFP_ATOMIC); + if (!pi->sdu) { + err = -ENOMEM; + break; + } + + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + + pi->conn_state |= L2CAP_CONN_SAR_SDU; + pi->partial_sdu_len = skb->len; + err = 0; + break; + + case L2CAP_SDU_CONTINUE: + if (!(pi->conn_state & L2CAP_CONN_SAR_SDU)) + break; + + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + + pi->partial_sdu_len += skb->len; + if (pi->partial_sdu_len > pi->sdu_len) + kfree_skb(pi->sdu); + else + err = 0; + + break; + + case L2CAP_SDU_END: + if (!(pi->conn_state & L2CAP_CONN_SAR_SDU)) + break; + + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + + pi->conn_state &= ~L2CAP_CONN_SAR_SDU; + pi->partial_sdu_len += skb->len; + + if (pi->partial_sdu_len > pi->imtu) + goto drop; + + if (pi->partial_sdu_len == pi->sdu_len) { + _skb = skb_clone(pi->sdu, GFP_ATOMIC); + err = sock_queue_rcv_skb(sk, _skb); + if (err < 0) + kfree_skb(_skb); + } + err = 0; + +drop: + kfree_skb(pi->sdu); + break; + } + + kfree_skb(skb); + return err; +} + +static void l2cap_check_srej_gap(struct sock *sk, u8 tx_seq) +{ + struct sk_buff *skb; + u16 control; + + while ((skb = skb_peek(SREJ_QUEUE(sk)))) { + if (bt_cb(skb)->tx_seq != tx_seq) + break; + + skb = skb_dequeue(SREJ_QUEUE(sk)); + control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; + l2cap_ertm_reassembly_sdu(sk, skb, control); + l2cap_pi(sk)->buffer_seq_srej = + (l2cap_pi(sk)->buffer_seq_srej + 1) % 64; + tx_seq++; + } +} + +static void l2cap_resend_srejframe(struct sock *sk, u8 tx_seq) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct srej_list *l, *tmp; + u16 control; + + list_for_each_entry_safe(l, tmp, SREJ_LIST(sk), list) { + if (l->tx_seq == tx_seq) { + list_del(&l->list); + kfree(l); + return; + } + control = L2CAP_SUPER_SELECT_REJECT; + control |= l->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + l2cap_send_sframe(pi, control); + list_del(&l->list); + list_add_tail(&l->list, SREJ_LIST(sk)); + } +} + +static void l2cap_send_srejframe(struct sock *sk, u8 tx_seq) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct srej_list *new; + u16 control; + + while (tx_seq != pi->expected_tx_seq) { + control = L2CAP_SUPER_SELECT_REJECT; + control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + l2cap_send_sframe(pi, control); + + new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); + new->tx_seq = pi->expected_tx_seq++; + list_add_tail(&new->list, SREJ_LIST(sk)); + } + pi->expected_tx_seq++; +} + +static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + u8 tx_seq = __get_txseq(rx_control); + u8 req_seq = __get_reqseq(rx_control); + u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT; + u8 tx_seq_offset, expected_tx_seq_offset; + int num_to_ack = (pi->tx_win/6) + 1; + int err = 0; + + BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); + + if (L2CAP_CTRL_FINAL & rx_control && + l2cap_pi(sk)->conn_state & L2CAP_CONN_WAIT_F) { + del_timer(&pi->monitor_timer); + if (pi->unacked_frames > 0) + __mod_retrans_timer(); + pi->conn_state &= ~L2CAP_CONN_WAIT_F; + } + + pi->expected_ack_seq = req_seq; + l2cap_drop_acked_frames(sk); + + if (tx_seq == pi->expected_tx_seq) + goto expected; + + tx_seq_offset = (tx_seq - pi->buffer_seq) % 64; + if (tx_seq_offset < 0) + tx_seq_offset += 64; + + /* invalid tx_seq */ + if (tx_seq_offset >= pi->tx_win) { + l2cap_send_disconn_req(pi->conn, sk); + goto drop; + } + + if (pi->conn_state == L2CAP_CONN_LOCAL_BUSY) + goto drop; + + if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { + struct srej_list *first; + + first = list_first_entry(SREJ_LIST(sk), + struct srej_list, list); + if (tx_seq == first->tx_seq) { + l2cap_add_to_srej_queue(sk, skb, tx_seq, sar); + l2cap_check_srej_gap(sk, tx_seq); + + list_del(&first->list); + kfree(first); + + if (list_empty(SREJ_LIST(sk))) { + pi->buffer_seq = pi->buffer_seq_srej; + pi->conn_state &= ~L2CAP_CONN_SREJ_SENT; + l2cap_send_ack(pi); + } + } else { + struct srej_list *l; + + /* duplicated tx_seq */ + if (l2cap_add_to_srej_queue(sk, skb, tx_seq, sar) < 0) + goto drop; + + list_for_each_entry(l, SREJ_LIST(sk), list) { + if (l->tx_seq == tx_seq) { + l2cap_resend_srejframe(sk, tx_seq); + return 0; + } + } + l2cap_send_srejframe(sk, tx_seq); + } + } else { + expected_tx_seq_offset = + (pi->expected_tx_seq - pi->buffer_seq) % 64; + if (expected_tx_seq_offset < 0) + expected_tx_seq_offset += 64; + + /* duplicated tx_seq */ + if (tx_seq_offset < expected_tx_seq_offset) + goto drop; + + pi->conn_state |= L2CAP_CONN_SREJ_SENT; + + INIT_LIST_HEAD(SREJ_LIST(sk)); + pi->buffer_seq_srej = pi->buffer_seq; + + __skb_queue_head_init(SREJ_QUEUE(sk)); + __skb_queue_head_init(BUSY_QUEUE(sk)); + l2cap_add_to_srej_queue(sk, skb, tx_seq, sar); + + pi->conn_state |= L2CAP_CONN_SEND_PBIT; + + l2cap_send_srejframe(sk, tx_seq); + } + return 0; + +expected: + pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; + + if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { + bt_cb(skb)->tx_seq = tx_seq; + bt_cb(skb)->sar = sar; + __skb_queue_tail(SREJ_QUEUE(sk), skb); + return 0; + } + + if (rx_control & L2CAP_CTRL_FINAL) { + if (pi->conn_state & L2CAP_CONN_REJ_ACT) + pi->conn_state &= ~L2CAP_CONN_REJ_ACT; + else + l2cap_retransmit_frames(sk); + } + + err = l2cap_push_rx_skb(sk, skb, rx_control); + if (err < 0) + return 0; + + __mod_ack_timer(); + + pi->num_acked = (pi->num_acked + 1) % num_to_ack; + if (pi->num_acked == num_to_ack - 1) + l2cap_send_ack(pi); + + return 0; + +drop: + kfree_skb(skb); + return 0; +} + +static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + + pi->expected_ack_seq = __get_reqseq(rx_control); + l2cap_drop_acked_frames(sk); + + if (rx_control & L2CAP_CTRL_POLL) { + if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { + if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && + (pi->unacked_frames > 0)) + __mod_retrans_timer(); + + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + l2cap_send_srejtail(sk); + } else { + l2cap_send_i_or_rr_or_rnr(sk); + } + + } else if (rx_control & L2CAP_CTRL_FINAL) { + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + + if (pi->conn_state & L2CAP_CONN_REJ_ACT) + pi->conn_state &= ~L2CAP_CONN_REJ_ACT; + else + l2cap_retransmit_frames(sk); + + } else { + if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && + (pi->unacked_frames > 0)) + __mod_retrans_timer(); + + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { + l2cap_send_ack(pi); + } else { + spin_lock_bh(&pi->send_lock); + l2cap_ertm_send(sk); + spin_unlock_bh(&pi->send_lock); + } + } +} + +static inline void l2cap_data_channel_rejframe(struct sock *sk, u16 rx_control) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + u8 tx_seq = __get_reqseq(rx_control); + + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + + pi->expected_ack_seq = tx_seq; + l2cap_drop_acked_frames(sk); + + if (rx_control & L2CAP_CTRL_FINAL) { + if (pi->conn_state & L2CAP_CONN_REJ_ACT) + pi->conn_state &= ~L2CAP_CONN_REJ_ACT; + else + l2cap_retransmit_frames(sk); + } else { + l2cap_retransmit_frames(sk); + + if (pi->conn_state & L2CAP_CONN_WAIT_F) + pi->conn_state |= L2CAP_CONN_REJ_ACT; + } +} +static inline void l2cap_data_channel_srejframe(struct sock *sk, u16 rx_control) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + u8 tx_seq = __get_reqseq(rx_control); + + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + + if (rx_control & L2CAP_CTRL_POLL) { + pi->expected_ack_seq = tx_seq; + l2cap_drop_acked_frames(sk); + l2cap_retransmit_one_frame(sk, tx_seq); + + spin_lock_bh(&pi->send_lock); + l2cap_ertm_send(sk); + spin_unlock_bh(&pi->send_lock); + + if (pi->conn_state & L2CAP_CONN_WAIT_F) { + pi->srej_save_reqseq = tx_seq; + pi->conn_state |= L2CAP_CONN_SREJ_ACT; + } + } else if (rx_control & L2CAP_CTRL_FINAL) { + if ((pi->conn_state & L2CAP_CONN_SREJ_ACT) && + pi->srej_save_reqseq == tx_seq) + pi->conn_state &= ~L2CAP_CONN_SREJ_ACT; + else + l2cap_retransmit_one_frame(sk, tx_seq); + } else { + l2cap_retransmit_one_frame(sk, tx_seq); + if (pi->conn_state & L2CAP_CONN_WAIT_F) { + pi->srej_save_reqseq = tx_seq; + pi->conn_state |= L2CAP_CONN_SREJ_ACT; + } + } +} + +static inline void l2cap_data_channel_rnrframe(struct sock *sk, u16 rx_control) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + u8 tx_seq = __get_reqseq(rx_control); + + pi->conn_state |= L2CAP_CONN_REMOTE_BUSY; + pi->expected_ack_seq = tx_seq; + l2cap_drop_acked_frames(sk); + + if (!(pi->conn_state & L2CAP_CONN_SREJ_SENT)) { + del_timer(&pi->retrans_timer); + if (rx_control & L2CAP_CTRL_POLL) + l2cap_send_rr_or_rnr(pi, L2CAP_CTRL_FINAL); + return; + } + + if (rx_control & L2CAP_CTRL_POLL) + l2cap_send_srejtail(sk); + else + l2cap_send_sframe(pi, L2CAP_SUPER_RCV_READY); +} + +static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) +{ + BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); + + if (L2CAP_CTRL_FINAL & rx_control && + l2cap_pi(sk)->conn_state & L2CAP_CONN_WAIT_F) { + del_timer(&l2cap_pi(sk)->monitor_timer); + if (l2cap_pi(sk)->unacked_frames > 0) + __mod_retrans_timer(); + l2cap_pi(sk)->conn_state &= ~L2CAP_CONN_WAIT_F; + } + + switch (rx_control & L2CAP_CTRL_SUPERVISE) { + case L2CAP_SUPER_RCV_READY: + l2cap_data_channel_rrframe(sk, rx_control); + break; + + case L2CAP_SUPER_REJECT: + l2cap_data_channel_rejframe(sk, rx_control); + break; + + case L2CAP_SUPER_SELECT_REJECT: + l2cap_data_channel_srejframe(sk, rx_control); + break; + + case L2CAP_SUPER_RCV_NOT_READY: + l2cap_data_channel_rnrframe(sk, rx_control); + break; + } + + kfree_skb(skb); + return 0; +} + +static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) +{ + struct sock *sk; + struct l2cap_pinfo *pi; + u16 control, len; + u8 tx_seq, req_seq, next_tx_seq_offset, req_seq_offset; + + sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); + if (!sk) { + BT_DBG("unknown cid 0x%4.4x", cid); + goto drop; + } + + pi = l2cap_pi(sk); + + BT_DBG("sk %p, len %d", sk, skb->len); + + if (sk->sk_state != BT_CONNECTED) + goto drop; + + switch (pi->mode) { + case L2CAP_MODE_BASIC: + /* If socket recv buffers overflows we drop data here + * which is *bad* because L2CAP has to be reliable. + * But we don't have any other choice. L2CAP doesn't + * provide flow control mechanism. */ + + if (pi->imtu < skb->len) + goto drop; + + if (!sock_queue_rcv_skb(sk, skb)) + goto done; + break; + + case L2CAP_MODE_ERTM: + control = get_unaligned_le16(skb->data); + skb_pull(skb, 2); + len = skb->len; + + if (__is_sar_start(control)) + len -= 2; + + if (pi->fcs == L2CAP_FCS_CRC16) + len -= 2; + + /* + * We can just drop the corrupted I-frame here. + * Receiver will miss it and start proper recovery + * procedures and ask retransmission. + */ + if (len > pi->mps) { + l2cap_send_disconn_req(pi->conn, sk); + goto drop; + } + + if (l2cap_check_fcs(pi, skb)) + goto drop; + + req_seq = __get_reqseq(control); + req_seq_offset = (req_seq - pi->expected_ack_seq) % 64; + if (req_seq_offset < 0) + req_seq_offset += 64; + + next_tx_seq_offset = + (pi->next_tx_seq - pi->expected_ack_seq) % 64; + if (next_tx_seq_offset < 0) + next_tx_seq_offset += 64; + + /* check for invalid req-seq */ + if (req_seq_offset > next_tx_seq_offset) { + l2cap_send_disconn_req(pi->conn, sk); + goto drop; + } + + if (__is_iframe(control)) { + if (len < 4) { + l2cap_send_disconn_req(pi->conn, sk); + goto drop; + } + + l2cap_data_channel_iframe(sk, control, skb); + } else { + if (len != 0) { + l2cap_send_disconn_req(pi->conn, sk); + goto drop; + } + + l2cap_data_channel_sframe(sk, control, skb); + } + + goto done; + + case L2CAP_MODE_STREAMING: + control = get_unaligned_le16(skb->data); + skb_pull(skb, 2); + len = skb->len; + + if (__is_sar_start(control)) + len -= 2; + + if (pi->fcs == L2CAP_FCS_CRC16) + len -= 2; + + if (len > pi->mps || len < 4 || __is_sframe(control)) + goto drop; + + if (l2cap_check_fcs(pi, skb)) + goto drop; + + tx_seq = __get_txseq(control); + + if (pi->expected_tx_seq == tx_seq) + pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; + else + pi->expected_tx_seq = (tx_seq + 1) % 64; + + l2cap_streaming_reassembly_sdu(sk, skb, control); + + goto done; + + default: + BT_DBG("sk %p: bad mode 0x%2.2x", sk, pi->mode); + break; + } + +drop: + kfree_skb(skb); + +done: + if (sk) + bh_unlock_sock(sk); + + return 0; +} + +static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct sk_buff *skb) +{ + struct sock *sk; + + sk = l2cap_get_sock_by_psm(0, psm, conn->src); + if (!sk) + goto drop; + + BT_DBG("sk %p, len %d", sk, skb->len); + + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED) + goto drop; + + if (l2cap_pi(sk)->imtu < skb->len) + goto drop; + + if (!sock_queue_rcv_skb(sk, skb)) + goto done; + +drop: + kfree_skb(skb); + +done: + if (sk) + bh_unlock_sock(sk); + return 0; +} + +static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct l2cap_hdr *lh = (void *) skb->data; + u16 cid, len; + __le16 psm; + + skb_pull(skb, L2CAP_HDR_SIZE); + cid = __le16_to_cpu(lh->cid); + len = __le16_to_cpu(lh->len); + + if (len != skb->len) { + kfree_skb(skb); + return; + } + + BT_DBG("len %d, cid 0x%4.4x", len, cid); + + switch (cid) { + case L2CAP_CID_SIGNALING: + l2cap_sig_channel(conn, skb); + break; + + case L2CAP_CID_CONN_LESS: + psm = get_unaligned_le16(skb->data); + skb_pull(skb, 2); + l2cap_conless_channel(conn, psm, skb); + break; + + default: + l2cap_data_channel(conn, cid, skb); + break; + } +} + +/* ---- L2CAP interface with lower layer (HCI) ---- */ + +static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) +{ + int exact = 0, lm1 = 0, lm2 = 0; + register struct sock *sk; + struct hlist_node *node; + + if (type != ACL_LINK) + return 0; + + BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); + + /* Find listening sockets and check their link_mode */ + read_lock(&l2cap_sk_list.lock); + sk_for_each(sk, node, &l2cap_sk_list.head) { + if (sk->sk_state != BT_LISTEN) + continue; + + if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) { + lm1 |= HCI_LM_ACCEPT; + if (l2cap_pi(sk)->role_switch) + lm1 |= HCI_LM_MASTER; + exact++; + } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { + lm2 |= HCI_LM_ACCEPT; + if (l2cap_pi(sk)->role_switch) + lm2 |= HCI_LM_MASTER; + } + } + read_unlock(&l2cap_sk_list.lock); + + return exact ? lm1 : lm2; +} + +static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status) +{ + struct l2cap_conn *conn; + + BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); + + if (hcon->type != ACL_LINK) + return 0; + + if (!status) { + conn = l2cap_conn_add(hcon, status); + if (conn) + l2cap_conn_ready(conn); + } else + l2cap_conn_del(hcon, bt_err(status)); + + return 0; +} + +static int l2cap_disconn_ind(struct hci_conn *hcon) +{ + struct l2cap_conn *conn = hcon->l2cap_data; + + BT_DBG("hcon %p", hcon); + + if (hcon->type != ACL_LINK || !conn) + return 0x13; + + return conn->disc_reason; +} + +static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) +{ + BT_DBG("hcon %p reason %d", hcon, reason); + + if (hcon->type != ACL_LINK) + return 0; + + l2cap_conn_del(hcon, bt_err(reason)); + + return 0; +} + +static inline void l2cap_check_encryption(struct sock *sk, u8 encrypt) +{ + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM) + return; + + if (encrypt == 0x00) { + if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM) { + l2cap_sock_clear_timer(sk); + l2cap_sock_set_timer(sk, HZ * 5); + } else if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH) + __l2cap_sock_close(sk, ECONNREFUSED); + } else { + if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM) + l2cap_sock_clear_timer(sk); + } +} + +static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) +{ + struct l2cap_chan_list *l; + struct l2cap_conn *conn = hcon->l2cap_data; + struct sock *sk; + + if (!conn) + return 0; + + l = &conn->chan_list; + + BT_DBG("conn %p", conn); + + read_lock(&l->lock); + + for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { + bh_lock_sock(sk); + + if (l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND) { + bh_unlock_sock(sk); + continue; + } + + if (!status && (sk->sk_state == BT_CONNECTED || + sk->sk_state == BT_CONFIG)) { + l2cap_check_encryption(sk, encrypt); + bh_unlock_sock(sk); + continue; + } + + if (sk->sk_state == BT_CONNECT) { + if (!status) { + struct l2cap_conn_req req; + req.scid = cpu_to_le16(l2cap_pi(sk)->scid); + req.psm = l2cap_pi(sk)->psm; + + l2cap_pi(sk)->ident = l2cap_get_ident(conn); + l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; + + l2cap_send_cmd(conn, l2cap_pi(sk)->ident, + L2CAP_CONN_REQ, sizeof(req), &req); + } else { + l2cap_sock_clear_timer(sk); + l2cap_sock_set_timer(sk, HZ / 10); + } + } else if (sk->sk_state == BT_CONNECT2) { + struct l2cap_conn_rsp rsp; + __u16 result; + + if (!status) { + sk->sk_state = BT_CONFIG; + result = L2CAP_CR_SUCCESS; + } else { + sk->sk_state = BT_DISCONN; + l2cap_sock_set_timer(sk, HZ / 10); + result = L2CAP_CR_SEC_BLOCK; + } + + rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.result = cpu_to_le16(result); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + l2cap_send_cmd(conn, l2cap_pi(sk)->ident, + L2CAP_CONN_RSP, sizeof(rsp), &rsp); + } + + bh_unlock_sock(sk); + } + + read_unlock(&l->lock); + + return 0; +} + +static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) +{ + struct l2cap_conn *conn = hcon->l2cap_data; + + if (!conn && !(conn = l2cap_conn_add(hcon, 0))) + goto drop; + + BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags); + + if (!(flags & ACL_CONT)) { + struct l2cap_hdr *hdr; + int len; + + if (conn->rx_len) { + BT_ERR("Unexpected start frame (len %d)", skb->len); + kfree_skb(conn->rx_skb); + conn->rx_skb = NULL; + conn->rx_len = 0; + l2cap_conn_unreliable(conn, ECOMM); + } + + if (skb->len < 2) { + BT_ERR("Frame is too short (len %d)", skb->len); + l2cap_conn_unreliable(conn, ECOMM); + goto drop; + } + + hdr = (struct l2cap_hdr *) skb->data; + len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE; + + if (len == skb->len) { + /* Complete frame received */ + l2cap_recv_frame(conn, skb); + return 0; + } + + BT_DBG("Start: total len %d, frag len %d", len, skb->len); + + if (skb->len > len) { + BT_ERR("Frame is too long (len %d, expected len %d)", + skb->len, len); + l2cap_conn_unreliable(conn, ECOMM); + goto drop; + } + + /* Allocate skb for the complete frame (with header) */ + conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC); + if (!conn->rx_skb) + goto drop; + + skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), + skb->len); + conn->rx_len = len - skb->len; + } else { + BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); + + if (!conn->rx_len) { + BT_ERR("Unexpected continuation frame (len %d)", skb->len); + l2cap_conn_unreliable(conn, ECOMM); + goto drop; + } + + if (skb->len > conn->rx_len) { + BT_ERR("Fragment is too long (len %d, expected %d)", + skb->len, conn->rx_len); + kfree_skb(conn->rx_skb); + conn->rx_skb = NULL; + conn->rx_len = 0; + l2cap_conn_unreliable(conn, ECOMM); + goto drop; + } + + skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), + skb->len); + conn->rx_len -= skb->len; + + if (!conn->rx_len) { + /* Complete frame received */ + l2cap_recv_frame(conn, conn->rx_skb); + conn->rx_skb = NULL; + } + } + +drop: + kfree_skb(skb); + return 0; +} + +static int l2cap_debugfs_show(struct seq_file *f, void *p) +{ + struct sock *sk; + struct hlist_node *node; + + read_lock_bh(&l2cap_sk_list.lock); + + sk_for_each(sk, node, &l2cap_sk_list.head) { + struct l2cap_pinfo *pi = l2cap_pi(sk); + + seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d\n", + batostr(&bt_sk(sk)->src), + batostr(&bt_sk(sk)->dst), + sk->sk_state, __le16_to_cpu(pi->psm), + pi->scid, pi->dcid, + pi->imtu, pi->omtu, pi->sec_level); + } + + read_unlock_bh(&l2cap_sk_list.lock); + + return 0; +} + +static int l2cap_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, l2cap_debugfs_show, inode->i_private); +} + +static const struct file_operations l2cap_debugfs_fops = { + .open = l2cap_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *l2cap_debugfs; + +static const struct proto_ops l2cap_sock_ops = { + .family = PF_BLUETOOTH, + .owner = THIS_MODULE, + .release = l2cap_sock_release, + .bind = l2cap_sock_bind, + .connect = l2cap_sock_connect, + .listen = l2cap_sock_listen, + .accept = l2cap_sock_accept, + .getname = l2cap_sock_getname, + .sendmsg = l2cap_sock_sendmsg, + .recvmsg = l2cap_sock_recvmsg, + .poll = bt_sock_poll, + .ioctl = bt_sock_ioctl, + .mmap = sock_no_mmap, + .socketpair = sock_no_socketpair, + .shutdown = l2cap_sock_shutdown, + .setsockopt = l2cap_sock_setsockopt, + .getsockopt = l2cap_sock_getsockopt +}; + +static const struct net_proto_family l2cap_sock_family_ops = { + .family = PF_BLUETOOTH, + .owner = THIS_MODULE, + .create = l2cap_sock_create, +}; + +static struct hci_proto l2cap_hci_proto = { + .name = "L2CAP", + .id = HCI_PROTO_L2CAP, + .connect_ind = l2cap_connect_ind, + .connect_cfm = l2cap_connect_cfm, + .disconn_ind = l2cap_disconn_ind, + .disconn_cfm = l2cap_disconn_cfm, + .security_cfm = l2cap_security_cfm, + .recv_acldata = l2cap_recv_acldata +}; + +static int __init l2cap_init(void) +{ + int err; + + err = proto_register(&l2cap_proto, 0); + if (err < 0) + return err; + + _busy_wq = create_singlethread_workqueue("l2cap"); + if (!_busy_wq) + goto error; + + err = bt_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops); + if (err < 0) { + BT_ERR("L2CAP socket registration failed"); + goto error; + } + + err = hci_register_proto(&l2cap_hci_proto); + if (err < 0) { + BT_ERR("L2CAP protocol registration failed"); + bt_sock_unregister(BTPROTO_L2CAP); + goto error; + } + + if (bt_debugfs) { + l2cap_debugfs = debugfs_create_file("l2cap", 0444, + bt_debugfs, NULL, &l2cap_debugfs_fops); + if (!l2cap_debugfs) + BT_ERR("Failed to create L2CAP debug file"); + } + + BT_INFO("L2CAP ver %s", VERSION); + BT_INFO("L2CAP socket layer initialized"); + + return 0; + +error: + proto_unregister(&l2cap_proto); + return err; +} + +static void __exit l2cap_exit(void) +{ + debugfs_remove(l2cap_debugfs); + + flush_workqueue(_busy_wq); + destroy_workqueue(_busy_wq); + + if (bt_sock_unregister(BTPROTO_L2CAP) < 0) + BT_ERR("L2CAP socket unregistration failed"); + + if (hci_unregister_proto(&l2cap_hci_proto) < 0) + BT_ERR("L2CAP protocol unregistration failed"); + + proto_unregister(&l2cap_proto); +} + +void l2cap_load(void) +{ + /* Dummy function to trigger automatic L2CAP module loading by + * other modules that use L2CAP sockets but don't use any other + * symbols from it. */ +} +EXPORT_SYMBOL(l2cap_load); + +module_init(l2cap_init); +module_exit(l2cap_exit); + +module_param(enable_ertm, bool, 0644); +MODULE_PARM_DESC(enable_ertm, "Enable enhanced retransmission mode"); + +module_param(max_transmit, uint, 0644); +MODULE_PARM_DESC(max_transmit, "Max transmit value (default = 3)"); + +module_param(tx_window, uint, 0644); +MODULE_PARM_DESC(tx_window, "Transmission window size value (default = 63)"); + +MODULE_AUTHOR("Marcel Holtmann "); +MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("bt-proto-0"); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 9b1c7dc6..37c485b7 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010-2012 The Linux Foundation. All rights reserved. + Copyright (c) 2000-2001, 2010-2011 Code Aurora Forum. All rights reserved. Copyright (C) 2009-2010 Gustavo F. Padovan Copyright (C) 2010 Google Inc. @@ -25,6 +25,7 @@ */ /* Bluetooth L2CAP core. */ +// rollback to original BlueZ #include @@ -59,11 +60,10 @@ #include bool disable_ertm; -bool enable_hs; bool enable_reconfig; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; -static u8 l2cap_fc_mask = L2CAP_FC_L2CAP; +static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP | L2CAP_FC_A2MP, }; struct workqueue_struct *_l2cap_wq; @@ -90,9 +90,7 @@ static int l2cap_answer_move_poll(struct sock *sk); static int l2cap_create_cfm(struct hci_chan *chan, u8 status); static int l2cap_deaggregate(struct hci_chan *chan, struct l2cap_pinfo *pi); static void l2cap_chan_ready(struct sock *sk); -static void l2cap_conn_del(struct hci_conn *hcon, int err, u8 is_process); -static u16 l2cap_get_smallest_flushto(struct l2cap_chan_list *l); -static void l2cap_set_acl_flushto(struct hci_conn *hcon, u16 flush_to); +static void l2cap_conn_del(struct hci_conn *hcon, int err); /* ---- L2CAP channels ---- */ static struct sock *__l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, u16 cid) @@ -522,11 +520,6 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk) l2cap_pi(sk)->dcid = L2CAP_CID_SIGNALING; l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; } - - if (l2cap_get_smallest_flushto(l) > l2cap_pi(sk)->flush_to) { - /*if flush timeout of the channel is lesser than existing */ - l2cap_set_acl_flushto(conn->hcon, l2cap_pi(sk)->flush_to); - } /* Otherwise, do not set scid/dcid/omtu. These will be set up * by l2cap_fixed_channel_config() */ @@ -546,30 +539,23 @@ void l2cap_chan_del(struct sock *sk, int err) BT_DBG("sk %p, conn %p, err %d", sk, conn, err); if (conn) { - struct l2cap_chan_list *l = &conn->chan_list; /* Unlink from channel list */ - l2cap_chan_unlink(l, sk); + l2cap_chan_unlink(&conn->chan_list, sk); l2cap_pi(sk)->conn = NULL; if (!l2cap_pi(sk)->fixed_channel) hci_conn_put(conn->hcon); - - read_lock(&l->lock); - if (l2cap_pi(sk)->flush_to < l2cap_get_smallest_flushto(l)) - l2cap_set_acl_flushto(conn->hcon, - l2cap_get_smallest_flushto(l)); - read_unlock(&l->lock); } - if (l2cap_pi(sk)->ampchan) { - struct hci_chan *ampchan = l2cap_pi(sk)->ampchan; - struct hci_conn *ampcon = l2cap_pi(sk)->ampcon; - l2cap_pi(sk)->ampchan = NULL; + if (l2cap_pi(sk)->ampcon) { + l2cap_pi(sk)->ampcon->l2cap_data = NULL; l2cap_pi(sk)->ampcon = NULL; + if (l2cap_pi(sk)->ampchan) { + if (!hci_chan_put(l2cap_pi(sk)->ampchan)) + l2cap_deaggregate(l2cap_pi(sk)->ampchan, + l2cap_pi(sk)); + } + l2cap_pi(sk)->ampchan = NULL; l2cap_pi(sk)->amp_id = 0; - if (hci_chan_put(ampchan)) - ampcon->l2cap_data = NULL; - else - l2cap_deaggregate(ampchan, l2cap_pi(sk)); } sk->sk_state = BT_CLOSED; @@ -584,7 +570,6 @@ void l2cap_chan_del(struct sock *sk, int err) } else sk->sk_state_change(sk); - sk->sk_send_head = NULL; skb_queue_purge(TX_QUEUE(sk)); if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) { @@ -755,7 +740,6 @@ static void l2cap_do_start(struct sock *sk) if (l2cap_pi(sk)->amp_pref == BT_AMP_POLICY_PREFER_AMP && - enable_hs && conn->fc_mask & L2CAP_FC_A2MP) amp_create_physical(conn, sk); else @@ -799,7 +783,6 @@ void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err) if (!conn) return; - sk->sk_send_head = NULL; skb_queue_purge(TX_QUEUE(sk)); if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) { @@ -864,7 +847,6 @@ static void l2cap_conn_start(struct l2cap_conn *conn) if (l2cap_pi(sk)->amp_pref == BT_AMP_POLICY_PREFER_AMP && - enable_hs && conn->fc_mask & L2CAP_FC_A2MP) amp_create_physical(conn, sk); else @@ -932,24 +914,18 @@ static void l2cap_conn_start(struct l2cap_conn *conn) } /* Find socket with fixed cid with given source and destination bdaddrs. - * Direction of the req/rsp must match. + * Returns closest match, locked. */ -struct sock *l2cap_find_sock_by_fixed_cid_and_dir(__le16 cid, bdaddr_t *src, - bdaddr_t *dst, int incoming) +static struct sock *l2cap_get_sock_by_fixed_scid(int state, + __le16 cid, bdaddr_t *src, bdaddr_t *dst) { struct sock *sk = NULL, *sk1 = NULL; struct hlist_node *node; - BT_DBG(" %d", incoming); - read_lock(&l2cap_sk_list.lock); sk_for_each(sk, node, &l2cap_sk_list.head) { - - if (incoming && !l2cap_pi(sk)->incoming) - continue; - - if (!incoming && l2cap_pi(sk)->incoming) + if (state && sk->sk_state != state) continue; if (l2cap_pi(sk)->scid == cid && !bacmp(&bt_sk(sk)->dst, dst)) { @@ -1030,12 +1006,13 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) l2cap_sock_init(sk, parent); bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); - l2cap_pi(sk)->incoming = 1; bt_accept_enqueue(parent, sk); __l2cap_chan_add(conn, sk); + l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + sk->sk_state = BT_CONNECTED; parent->sk_data_ready(parent, 0); @@ -1068,10 +1045,10 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) if (pending_sec > sec_level) sec_level = pending_sec; - if (smp_conn_security(conn, sec_level)) + if (smp_conn_security(conn, sec_level)) { l2cap_chan_ready(sk); - - hci_conn_put(conn->hcon); + hci_conn_put(conn->hcon); + } } else if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM) { @@ -1088,9 +1065,6 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) } read_unlock(&l->lock); - - if (conn->hcon->out && conn->hcon->type == LE_LINK) - l2cap_le_conn_ready(conn); } /* Notify sockets that we cannot guaranty reliability anymore */ @@ -1162,7 +1136,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) return conn; } -static void l2cap_conn_del(struct hci_conn *hcon, int err, u8 is_process) +static void l2cap_conn_del(struct hci_conn *hcon, int err) { struct l2cap_conn *conn = hcon->l2cap_data; struct sock *sk; @@ -1183,15 +1157,9 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err, u8 is_process) BT_DBG("ampcon %p", l2cap_pi(sk)->ampcon); if ((conn->hcon == hcon) || (l2cap_pi(sk)->ampcon == hcon)) { next = l2cap_pi(sk)->next_c; - if (is_process) - lock_sock(sk); - else - bh_lock_sock(sk); + bh_lock_sock(sk); l2cap_chan_del(sk, err); - if (is_process) - release_sock(sk); - else - bh_unlock_sock(sk); + bh_unlock_sock(sk); l2cap_sock_kill(sk); sk = next; } else @@ -1256,7 +1224,7 @@ int l2cap_do_connect(struct sock *sk) struct hci_conn *hcon; struct hci_dev *hdev; __u8 auth_type; - int err; + int err = 0; BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm); @@ -1280,9 +1248,8 @@ int l2cap_do_connect(struct sock *sk) conn = hcon->l2cap_data; } else { if (l2cap_pi(sk)->dcid == L2CAP_CID_LE_DATA) - hcon = hci_le_connect(hdev, 0, dst, - l2cap_pi(sk)->sec_level, auth_type, - &bt_sk(sk)->le_params); + hcon = hci_connect(hdev, LE_LINK, 0, dst, + l2cap_pi(sk)->sec_level, auth_type); else hcon = hci_connect(hdev, ACL_LINK, 0, dst, l2cap_pi(sk)->sec_level, auth_type); @@ -1312,17 +1279,7 @@ int l2cap_do_connect(struct sock *sk) sk->sk_state_change(sk); } else { sk->sk_state = BT_CONNECT; - /* If we have valid LE Params, let timeout override default */ - if (l2cap_pi(sk)->dcid == L2CAP_CID_LE_DATA && - l2cap_sock_le_params_valid(&bt_sk(sk)->le_params)) { - u16 timeout = bt_sk(sk)->le_params.conn_timeout; - - if (timeout) - l2cap_sock_set_timer(sk, - msecs_to_jiffies(timeout*1000)); - } else - l2cap_sock_set_timer(sk, sk->sk_sndtimeo); - + l2cap_sock_set_timer(sk, sk->sk_sndtimeo); sk->sk_state_change(sk); if (hcon->state == BT_CONNECTED) { @@ -1423,11 +1380,6 @@ void l2cap_do_send(struct sock *sk, struct sk_buff *skb) } else { u16 flags; - if (!(pi->conn)) { - kfree_skb(skb); - return; - } - bt_cb(skb)->force_active = pi->force_active; BT_DBG("Sending on BR/EDR connection %p", pi->conn->hcon); @@ -1501,6 +1453,10 @@ int l2cap_ertm_send(struct sock *sk) tx_skb->destructor = l2cap_skb_destructor; atomic_inc(&pi->ertm_queued); + l2cap_do_send(sk, tx_skb); + + BT_DBG("Sent txseq %d", (int)control->txseq); + l2cap_ertm_start_retrans_timer(pi); pi->next_tx_seq = __next_seq(pi->next_tx_seq, pi); @@ -1512,9 +1468,6 @@ int l2cap_ertm_send(struct sock *sk) sk->sk_send_head = NULL; else sk->sk_send_head = skb_queue_next(TX_QUEUE(sk), skb); - - l2cap_do_send(sk, tx_skb); - BT_DBG("Sent txseq %d", (int)control->txseq); } BT_DBG("Sent %d, %d unacked, %d in ERTM queue, %d in HCI queue", sent, @@ -1606,7 +1559,7 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sk_buff **frag; struct sk_buff *final; - int err, sent = 0; + int err = 0, sent = 0; BT_DBG("sk %p, msg %p, len %d, count %d, skb %p", sk, msg, (int)len, (int)count, skb); @@ -1708,7 +1661,7 @@ struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, s { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sk_buff *skb; - int err, count, hlen = L2CAP_HDR_SIZE + 2; + int err = 0, count = 0, hlen = L2CAP_HDR_SIZE + 2; struct l2cap_hdr *lh; BT_DBG("sk %p len %d", sk, (int)len); @@ -1737,7 +1690,7 @@ struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sk_buff *skb; - int err, count, hlen = L2CAP_HDR_SIZE; + int err = 0, count = 0, hlen = L2CAP_HDR_SIZE; struct l2cap_hdr *lh; BT_DBG("sk %p len %d", sk, (int)len); @@ -1766,7 +1719,7 @@ struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, u16 sdulen, int reseg) { struct sk_buff *skb; - int err, count, hlen; + int err = 0, count = 0, hlen = 0; int reserve = 0; struct l2cap_hdr *lh; u8 fcs = l2cap_pi(sk)->fcs; @@ -1998,10 +1951,10 @@ static void l2cap_ertm_send_ack(struct sock *sk) frames_to_ack = 0; } - /* Ack now if the window is 3/4ths full. + /* Ack now if the tx window is 3/4ths full. * Calculate without mul or div */ - threshold = pi->ack_win; + threshold = pi->tx_win; threshold += threshold << 1; threshold >>= 2; @@ -2731,7 +2684,7 @@ void l2cap_amp_move_init(struct sock *sk) if (!l2cap_pi(sk)->conn) return; - if (!(l2cap_pi(sk)->conn->fc_mask & L2CAP_FC_A2MP) || !enable_hs) + if (!(l2cap_pi(sk)->conn->fc_mask & L2CAP_FC_A2MP)) return; if (l2cap_pi(sk)->amp_id == 0) { @@ -3067,8 +3020,7 @@ void l2cap_ertm_recv_done(struct sock *sk) { lock_sock(sk); - if (l2cap_pi(sk)->mode != L2CAP_MODE_ERTM || - sk->sk_state != BT_CONNECTED) { + if (l2cap_pi(sk)->mode != L2CAP_MODE_ERTM) { release_sock(sk); return; } @@ -3110,7 +3062,6 @@ static void l2cap_setup_txwin(struct l2cap_pinfo *pi) pi->tx_win_max = L2CAP_TX_WIN_MAX_ENHANCED; pi->extended_control = 0; } - pi->ack_win = pi->tx_win; } static void l2cap_aggregate_fs(struct hci_ext_fs *cur, @@ -3206,9 +3157,8 @@ static int l2cap_deaggregate(struct hci_chan *chan, struct l2cap_pinfo *pi) return 1; } -static struct hci_chan *l2cap_chan_admit(u8 amp_id, struct sock *sk) +static struct hci_chan *l2cap_chan_admit(u8 amp_id, struct l2cap_pinfo *pi) { - struct l2cap_pinfo *pi = l2cap_pi(sk); struct hci_dev *hdev; struct hci_conn *hcon; struct hci_chan *chan; @@ -3228,67 +3178,26 @@ static struct hci_chan *l2cap_chan_admit(u8 amp_id, struct sock *sk) chan = hci_chan_list_lookup_id(hdev, hcon->handle); if (chan) { l2cap_aggregate(chan, pi); - sock_hold(sk); - chan->l2cap_sk = sk; hci_chan_hold(chan); - pi->ampchan = chan; goto done; } - chan = hci_chan_add(hdev); - if (chan) { - chan->conn = hcon; - sock_hold(sk); - chan->l2cap_sk = sk; - hci_chan_hold(chan); - pi->ampchan = chan; - hci_chan_create(chan, - (struct hci_ext_fs *) &pi->local_fs, - (struct hci_ext_fs *) &pi->remote_fs); + if (bt_sk(pi)->parent) { + /* Incoming connection */ + chan = hci_chan_accept(hcon, + (struct hci_ext_fs *) &pi->local_fs, + (struct hci_ext_fs *) &pi->remote_fs); + } else { + /* Outgoing connection */ + chan = hci_chan_create(hcon, + (struct hci_ext_fs *) &pi->local_fs, + (struct hci_ext_fs *) &pi->remote_fs); } done: hci_dev_put(hdev); return chan; } -static void l2cap_get_ertm_timeouts(struct l2cap_conf_rfc *rfc, - struct l2cap_pinfo *pi) -{ - if (pi->amp_id && pi->ampcon) { - u64 ertm_to = pi->ampcon->hdev->amp_be_flush_to; - - /* Class 1 devices have must have ERTM timeouts - * exceeding the Link Supervision Timeout. The - * default Link Supervision Timeout for AMP - * controllers is 10 seconds. - * - * Class 1 devices use 0xffffffff for their - * best-effort flush timeout, so the clamping logic - * will result in a timeout that meets the above - * requirement. ERTM timeouts are 16-bit values, so - * the maximum timeout is 65.535 seconds. - */ - - /* Convert timeout to milliseconds and round */ - ertm_to = div_u64(ertm_to + 999, 1000); - - /* This is the recommended formula for class 2 devices - * that start ERTM timers when packets are sent to the - * controller. - */ - ertm_to = 3 * ertm_to + 500; - - if (ertm_to > 0xffff) - ertm_to = 0xffff; - - rfc->retrans_timeout = cpu_to_le16((u16) ertm_to); - rfc->monitor_timeout = rfc->retrans_timeout; - } else { - rfc->retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO); - rfc->monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO); - } -} - int l2cap_build_conf_req(struct sock *sk, void *data) { struct l2cap_pinfo *pi = l2cap_pi(sk); @@ -3296,7 +3205,7 @@ int l2cap_build_conf_req(struct sock *sk, void *data) struct l2cap_conf_rfc rfc = { .mode = pi->mode }; void *ptr = req->data; - BT_DBG("sk %p mode %d", sk, pi->mode); + BT_DBG("sk %p", sk); if (pi->num_conf_req || pi->num_conf_rsp) goto done; @@ -3322,6 +3231,7 @@ int l2cap_build_conf_req(struct sock *sk, void *data) if (!(pi->conn->feat_mask & L2CAP_FEAT_ERTM) && !(pi->conn->feat_mask & L2CAP_FEAT_STREAMING)) break; + rfc.txwin_size = 0; rfc.max_transmit = 0; rfc.retrans_timeout = 0; @@ -3338,10 +3248,10 @@ int l2cap_build_conf_req(struct sock *sk, void *data) rfc.txwin_size = L2CAP_TX_WIN_MAX_ENHANCED; else rfc.txwin_size = pi->tx_win; - rfc.max_transmit = pi->max_tx; - rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); - l2cap_get_ertm_timeouts(&rfc, pi); - + rfc.max_transmit = pi->max_tx; + rfc.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO); + rfc.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO); + rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->imtu) rfc.max_pdu_size = cpu_to_le16(pi->imtu); @@ -3373,7 +3283,6 @@ int l2cap_build_conf_req(struct sock *sk, void *data) break; case L2CAP_MODE_STREAMING: - l2cap_setup_txwin(pi); rfc.txwin_size = 0; rfc.max_transmit = 0; rfc.retrans_timeout = 0; @@ -3414,16 +3323,30 @@ static int l2cap_build_amp_reconf_req(struct sock *sk, void *data) struct l2cap_conf_req *req = data; struct l2cap_conf_rfc rfc = { .mode = pi->mode }; void *ptr = req->data; + u32 be_flush_to; BT_DBG("sk %p", sk); + /* convert to milliseconds, round up */ + be_flush_to = (pi->conn->hcon->hdev->amp_be_flush_to + 999) / 1000; + switch (pi->mode) { case L2CAP_MODE_ERTM: rfc.mode = L2CAP_MODE_ERTM; rfc.txwin_size = pi->tx_win; rfc.max_transmit = pi->max_tx; + if (pi->amp_move_id) { + rfc.retrans_timeout = + cpu_to_le16((3 * be_flush_to) + 500); + rfc.monitor_timeout = + cpu_to_le16((3 * be_flush_to) + 500); + } else { + rfc.retrans_timeout = + cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO); + rfc.monitor_timeout = + cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO); + } rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); - l2cap_get_ertm_timeouts(&rfc, pi); if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->imtu) rfc.max_pdu_size = cpu_to_le16(pi->imtu); @@ -3437,16 +3360,17 @@ static int l2cap_build_amp_reconf_req(struct sock *sk, void *data) (unsigned long) &rfc); if (pi->conn->feat_mask & L2CAP_FEAT_FCS) { + /* TODO assign fcs for br/edr based on socket config option */ - /* FCS is not used with AMP because it is redundant - lower - * layers already include a checksum. */ - if (pi->amp_id) + if (pi->amp_move_id) pi->local_conf.fcs = L2CAP_FCS_NONE; else pi->local_conf.fcs = L2CAP_FCS_CRC16; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->local_conf.fcs); - pi->fcs = pi->local_conf.fcs | pi->remote_conf.fcs; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, + pi->local_conf.fcs); + + pi->fcs = pi->local_conf.fcs | pi->remote_conf.fcs; } req->dcid = cpu_to_le16(pi->dcid); @@ -3471,9 +3395,6 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) BT_DBG("sk %p", sk); - if (pi->omtu > mtu) - mtu = pi->omtu; - while (len >= L2CAP_CONF_OPT_SIZE) { len -= l2cap_get_conf_opt(&req, &type, &olen, &val); @@ -3575,8 +3496,6 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) if (pi->mode != rfc.mode) { result = L2CAP_CONF_UNACCEPT; rfc.mode = pi->mode; - if (mtu > L2CAP_DEFAULT_MTU) - pi->omtu = mtu; if (pi->num_conf_rsp == 1) return -ECONNREFUSED; @@ -3613,9 +3532,15 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) case L2CAP_MODE_ERTM: if (!(pi->conf_state & L2CAP_CONF_EXT_WIN_RECV)) pi->remote_tx_win = rfc.txwin_size; + pi->remote_max_tx = rfc.max_transmit; + pi->remote_mps = le16_to_cpu(rfc.max_pdu_size); - l2cap_get_ertm_timeouts(&rfc, pi); + + rfc.retrans_timeout = + cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO); + rfc.monitor_timeout = + cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO); pi->conf_state |= L2CAP_CONF_MODE_DONE; @@ -3655,10 +3580,11 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) struct hci_chan *chan; /* Trigger logical link creation only on AMP */ - chan = l2cap_chan_admit(pi->amp_id, sk); + chan = l2cap_chan_admit(pi->amp_id, pi); if (!chan) return -ECONNREFUSED; + chan->l2cap_sk = sk; if (chan->state == BT_CONNECTED) l2cap_create_cfm(chan, 0); } @@ -3717,6 +3643,9 @@ static int l2cap_parse_amp_move_reconf_req(struct sock *sk, void *data) case L2CAP_CONF_RFC: if (olen == sizeof(rfc)) memcpy(&rfc, (void *) val, olen); + if (pi->mode != rfc.mode || + rfc.mode == L2CAP_MODE_BASIC) + result = L2CAP_CONF_UNACCEPT; break; case L2CAP_CONF_FCS: @@ -3752,9 +3681,6 @@ static int l2cap_parse_amp_move_reconf_req(struct sock *sk, void *data) BT_DBG("result 0x%2.2x cur mode 0x%2.2x req mode 0x%2.2x", result, pi->mode, rfc.mode); - if (pi->mode != rfc.mode || rfc.mode == L2CAP_MODE_BASIC) - result = L2CAP_CONF_UNACCEPT; - if (result == L2CAP_CONF_SUCCESS) { /* Configure output options and let the other side know * which ones we don't like. */ @@ -3774,26 +3700,38 @@ static int l2cap_parse_amp_move_reconf_req(struct sock *sk, void *data) pi->remote_tx_win); } - pi->remote_mps = rfc.max_pdu_size; - if (rfc.mode == L2CAP_MODE_ERTM) { - l2cap_get_ertm_timeouts(&rfc, pi); - } else { - rfc.retrans_timeout = 0; - rfc.monitor_timeout = 0; - } + pi->remote_conf.retrans_timeout = + le16_to_cpu(rfc.retrans_timeout); + pi->remote_conf.monitor_timeout = + le16_to_cpu(rfc.monitor_timeout); - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, + BT_DBG("remote conf monitor timeout %d", + pi->remote_conf.monitor_timeout); + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); + } + } if (result != L2CAP_CONF_SUCCESS) goto done; - pi->fcs = pi->remote_conf.fcs | pi->local_conf.fcs; + pi->fcs = pi->remote_conf.fcs | pi->local_conf.fcs ; - if (pi->rx_state == L2CAP_ERTM_RX_STATE_WAIT_F_FLAG) + if (pi->rx_state == L2CAP_ERTM_RX_STATE_WAIT_F_FLAG) { pi->flush_to = pi->remote_conf.flush_to; + pi->retrans_timeout = pi->remote_conf.retrans_timeout; + + if (pi->amp_move_id) + pi->monitor_timeout = pi->remote_conf.monitor_timeout; + else + pi->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO; + BT_DBG("mode %d monitor timeout %d", + pi->mode, pi->monitor_timeout); + + } done: rsp->scid = cpu_to_le16(pi->dcid); @@ -3854,7 +3792,10 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, break; case L2CAP_CONF_EXT_WINDOW: - pi->ack_win = min_t(u16, val, pi->ack_win); + pi->tx_win = val; + + if (pi->tx_win > L2CAP_TX_WIN_MAX_ENHANCED) + pi->tx_win = L2CAP_TX_WIN_MAX_ENHANCED; l2cap_add_conf_opt(&ptr, L2CAP_CONF_EXT_WINDOW, 2, pi->tx_win); @@ -3876,10 +3817,6 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, pi->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); pi->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); pi->mps = le16_to_cpu(rfc.max_pdu_size); - if (!pi->extended_control) { - pi->ack_win = min_t(u16, pi->ack_win, - rfc.txwin_size); - } break; case L2CAP_MODE_STREAMING: pi->mps = le16_to_cpu(rfc.max_pdu_size); @@ -3912,7 +3849,6 @@ static void l2cap_conf_rfc_get(struct sock *sk, void *rsp, int len) int type, olen; unsigned long val; struct l2cap_conf_rfc rfc; - u16 txwin_ext = pi->ack_win; BT_DBG("sk %p, rsp %p, len %d", sk, rsp, len); @@ -3921,7 +3857,6 @@ static void l2cap_conf_rfc_get(struct sock *sk, void *rsp, int len) rfc.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO); rfc.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO); rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); - rfc.txwin_size = min_t(u16, pi->ack_win, L2CAP_DEFAULT_TX_WINDOW); if ((pi->mode != L2CAP_MODE_ERTM) && (pi->mode != L2CAP_MODE_STREAMING)) return; @@ -3933,22 +3868,16 @@ static void l2cap_conf_rfc_get(struct sock *sk, void *rsp, int len) case L2CAP_CONF_RFC: if (olen == sizeof(rfc)) memcpy(&rfc, (void *)val, olen); - break; - case L2CAP_CONF_EXT_WINDOW: - txwin_ext = val; - break; + goto done; } } +done: switch (rfc.mode) { case L2CAP_MODE_ERTM: pi->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); pi->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); pi->mps = le16_to_cpu(rfc.max_pdu_size); - if (pi->extended_control) - pi->ack_win = min_t(u16, pi->ack_win, txwin_ext); - else - pi->ack_win = min_t(u16, pi->ack_win, rfc.txwin_size); break; case L2CAP_MODE_STREAMING: pi->mps = le16_to_cpu(rfc.max_pdu_size); @@ -4026,41 +3955,34 @@ static int l2cap_amp_move_reconf_rsp(struct sock *sk, void *rsp, int len, if (type == L2CAP_CONF_RFC) { if (olen == sizeof(rfc)) memcpy(&rfc, (void *)val, olen); - - if (rfc.mode != pi->mode) { - l2cap_send_disconn_req(pi->conn, sk, - ECONNRESET); - return -ECONNRESET; + if (rfc.mode != pi->mode && + rfc.mode != L2CAP_MODE_ERTM) { + err = -ECONNREFUSED; + goto done; } - - goto done; + break; } } } - BT_ERR("Expected RFC option was missing, using existing values"); - - rfc.mode = pi->mode; - rfc.retrans_timeout = cpu_to_le16(pi->retrans_timeout); - rfc.monitor_timeout = cpu_to_le16(pi->monitor_timeout); - done: l2cap_ertm_stop_ack_timer(pi); l2cap_ertm_stop_retrans_timer(pi); l2cap_ertm_stop_monitor_timer(pi); - pi->mps = le16_to_cpu(rfc.max_pdu_size); - if (pi->mode == L2CAP_MODE_ERTM) { - pi->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); - pi->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); - } - if (l2cap_pi(sk)->reconf_state == L2CAP_RECONF_ACC) { l2cap_pi(sk)->reconf_state = L2CAP_RECONF_NONE; /* Respond to poll */ err = l2cap_answer_move_poll(sk); + } else if (l2cap_pi(sk)->reconf_state == L2CAP_RECONF_INT) { + + /* If moving to BR/EDR, use default timeout defined by + * the spec */ + if (pi->amp_move_id == 0) + pi->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO; + if (pi->mode == L2CAP_MODE_ERTM) { l2cap_ertm_tx(sk, NULL, NULL, L2CAP_ERTM_EVENT_EXPLICIT_POLL); @@ -4501,13 +4423,14 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr /* Already sent a 'pending' response, so set up * the logical link now */ - chan = l2cap_chan_admit(pi->amp_id, sk); + chan = l2cap_chan_admit(pi->amp_id, pi); if (!chan) { l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); goto done; } + chan->l2cap_sk = sk; if (chan->state == BT_CONNECTED) l2cap_create_cfm(chan, 0); } @@ -4593,7 +4516,6 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd if (sk->sk_state != BT_DISCONN) { sk->sk_shutdown = SHUTDOWN_MASK; - sk->sk_send_head = NULL; skb_queue_purge(TX_QUEUE(sk)); if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) { @@ -4676,14 +4598,10 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm L2CAP_INFO_RSP, sizeof(buf), buf); } else if (type == L2CAP_IT_FIXED_CHAN) { u8 buf[12]; - u8 fc_mask = l2cap_fc_mask; struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); - if (enable_hs) - fc_mask |= L2CAP_FC_A2MP; - memset(rsp->data, 0, 8); - rsp->data[0] = fc_mask; + memcpy(buf + 4, l2cap_fixed_chan, 8); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), buf); } else { @@ -5002,16 +4920,15 @@ static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn, */ pi->amp_move_state = L2CAP_AMP_STATE_WAIT_LOGICAL_CONFIRM; - } else if (pi->amp_move_state == + } else if (result == L2CAP_MOVE_CHAN_SUCCESS && + pi->amp_move_state == L2CAP_AMP_STATE_WAIT_MOVE_RSP_SUCCESS) { - if (result == L2CAP_MOVE_CHAN_PENDING) { - break; - } else if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { + /* Logical link is up or moving to BR/EDR, + * proceed with move */ + if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { pi->amp_move_state = L2CAP_AMP_STATE_WAIT_LOCAL_BUSY; } else { - /* Logical link is up or moving to BR/EDR, - * proceed with move */ pi->amp_move_state = L2CAP_AMP_STATE_WAIT_MOVE_CONFIRM_RSP; l2cap_send_move_chan_cfm(conn, pi, pi->scid, @@ -5039,16 +4956,16 @@ static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn, } pi->remote_fs = default_fs; pi->local_fs = default_fs; - chan = l2cap_chan_admit(pi->amp_move_id, sk); + chan = l2cap_chan_admit(pi->amp_move_id, pi); if (!chan) { /* Logical link not available */ l2cap_send_move_chan_cfm(conn, pi, pi->scid, L2CAP_MOVE_CHAN_UNCONFIRMED); break; } - if (chan->state == BT_CONNECTED) { /* Logical link is already ready to go */ + pi->ampchan = chan; pi->ampcon = chan->conn; pi->ampcon->l2cap_data = pi->conn; if (result == L2CAP_MOVE_CHAN_SUCCESS) { @@ -5063,15 +4980,10 @@ static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn, pi->amp_move_state = L2CAP_AMP_STATE_WAIT_MOVE_RSP_SUCCESS; } - - l2cap_create_cfm(chan, 0); - } + } else + chan->l2cap_sk = sk; } else { /* Any other amp move state means the move failed. */ - pi->amp_move_id = pi->amp_id; - pi->amp_move_state = L2CAP_AMP_STATE_STABLE; - l2cap_amp_move_revert(sk); - pi->amp_move_role = L2CAP_AMP_MOVE_NONE; l2cap_send_move_chan_cfm(conn, pi, pi->scid, L2CAP_MOVE_CHAN_UNCONFIRMED); l2cap_sock_set_timer(sk, L2CAP_MOVE_TIMEOUT); @@ -5124,7 +5036,6 @@ static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn, { struct l2cap_move_chan_cfm *cfm = (struct l2cap_move_chan_cfm *) data; struct sock *sk; - struct l2cap_pinfo *pi; u16 icid, result; icid = le16_to_cpu(cfm->icid); @@ -5142,32 +5053,29 @@ static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn, } lock_sock(sk); - pi = l2cap_pi(sk); - if (pi->amp_move_state == L2CAP_AMP_STATE_WAIT_MOVE_CONFIRM) { - pi->amp_move_state = L2CAP_AMP_STATE_STABLE; + if (l2cap_pi(sk)->amp_move_state == L2CAP_AMP_STATE_WAIT_MOVE_CONFIRM) { + l2cap_pi(sk)->amp_move_state = L2CAP_AMP_STATE_STABLE; if (result == L2CAP_MOVE_CHAN_CONFIRMED) { - pi->amp_id = pi->amp_move_id; - if (!pi->amp_id && pi->ampchan) { - struct hci_chan *ampchan = pi->ampchan; - struct hci_conn *ampcon = pi->ampcon; + l2cap_pi(sk)->amp_id = l2cap_pi(sk)->amp_move_id; + if ((!l2cap_pi(sk)->amp_id) && + (l2cap_pi(sk)->ampchan)) { /* Have moved off of AMP, free the channel */ - pi->ampchan = NULL; - pi->ampcon = NULL; - if (hci_chan_put(ampchan)) - ampcon->l2cap_data = NULL; - else - l2cap_deaggregate(ampchan, pi); + if (!hci_chan_put(l2cap_pi(sk)->ampchan)) + l2cap_deaggregate(l2cap_pi(sk)->ampchan, + l2cap_pi(sk)); + l2cap_pi(sk)->ampchan = NULL; + l2cap_pi(sk)->ampcon = NULL; } l2cap_amp_move_success(sk); } else { - pi->amp_move_id = pi->amp_id; + l2cap_pi(sk)->amp_move_id = l2cap_pi(sk)->amp_id; l2cap_amp_move_revert(sk); } - pi->amp_move_role = L2CAP_AMP_MOVE_NONE; - } else if (pi->amp_move_state == + l2cap_pi(sk)->amp_move_role = L2CAP_AMP_MOVE_NONE; + } else if (l2cap_pi(sk)->amp_move_state == L2CAP_AMP_STATE_WAIT_LOGICAL_CONFIRM) { - BT_DBG("Bad AMP_MOVE_STATE (%d)", pi->amp_move_state); + BT_DBG("Bad AMP_MOVE_STATE (%d)", l2cap_pi(sk)->amp_move_state); } send_move_confirm_response: @@ -5185,8 +5093,6 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, struct l2cap_move_chan_cfm_rsp *rsp = (struct l2cap_move_chan_cfm_rsp *) data; struct sock *sk; - struct l2cap_pinfo *pi; - u16 icid; icid = le16_to_cpu(rsp->icid); @@ -5201,30 +5107,28 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, return 0; lock_sock(sk); - pi = l2cap_pi(sk); l2cap_sock_clear_timer(sk); - if (pi->amp_move_state == + if (l2cap_pi(sk)->amp_move_state == L2CAP_AMP_STATE_WAIT_MOVE_CONFIRM_RSP) { - pi->amp_move_state = L2CAP_AMP_STATE_STABLE; - pi->amp_id = pi->amp_move_id; + l2cap_pi(sk)->amp_move_state = L2CAP_AMP_STATE_STABLE; + l2cap_pi(sk)->amp_id = l2cap_pi(sk)->amp_move_id; - if (!pi->amp_id && pi->ampchan) { - struct hci_chan *ampchan = pi->ampchan; - struct hci_conn *ampcon = pi->ampcon; + if (!l2cap_pi(sk)->amp_id) { /* Have moved off of AMP, free the channel */ - pi->ampchan = NULL; - pi->ampcon = NULL; - if (hci_chan_put(ampchan)) - ampcon->l2cap_data = NULL; - else - l2cap_deaggregate(ampchan, pi); + l2cap_pi(sk)->ampcon = NULL; + if (l2cap_pi(sk)->ampchan) { + if (!hci_chan_put(l2cap_pi(sk)->ampchan)) + l2cap_deaggregate(l2cap_pi(sk)->ampchan, + l2cap_pi(sk)); + } + l2cap_pi(sk)->ampchan = NULL; } l2cap_amp_move_success(sk); - pi->amp_move_role = L2CAP_AMP_MOVE_NONE; + l2cap_pi(sk)->amp_move_role = L2CAP_AMP_MOVE_NONE; } release_sock(sk); @@ -5354,10 +5258,11 @@ void l2cap_amp_physical_complete(int result, u8 local_id, u8 remote_id, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; pi->remote_fs = default_fs; pi->local_fs = default_fs; - chan = l2cap_chan_admit(local_id, sk); + chan = l2cap_chan_admit(local_id, pi); if (chan) { if (chan->state == BT_CONNECTED) { /* Logical link is ready to go */ + pi->ampchan = chan; pi->ampcon = chan->conn; pi->ampcon->l2cap_data = pi->conn; pi->amp_move_state = @@ -5365,10 +5270,9 @@ void l2cap_amp_physical_complete(int result, u8 local_id, u8 remote_id, l2cap_send_move_chan_rsp(pi->conn, pi->amp_move_cmd_ident, pi->dcid, L2CAP_MOVE_CHAN_SUCCESS); - - l2cap_create_cfm(chan, 0); } else { /* Wait for logical link to be ready */ + chan->l2cap_sk = sk; pi->amp_move_state = L2CAP_AMP_STATE_WAIT_LOGICAL_CONFIRM; } @@ -5409,17 +5313,14 @@ void l2cap_amp_physical_complete(int result, u8 local_id, u8 remote_id, release_sock(sk); } -static void l2cap_logical_link_complete(struct hci_chan *chan, u8 status) +int l2cap_logical_link_complete(struct hci_chan *chan, u8 status) { struct l2cap_pinfo *pi; struct sock *sk; - struct hci_chan *ampchan; - struct hci_conn *ampcon; BT_DBG("status %d, chan %p, conn %p", (int) status, chan, chan->conn); sk = chan->l2cap_sk; - chan->l2cap_sk = NULL; BT_DBG("sk %p", sk); @@ -5427,17 +5328,16 @@ static void l2cap_logical_link_complete(struct hci_chan *chan, u8 status) if (sk->sk_state != BT_CONNECTED && !l2cap_pi(sk)->amp_id) { release_sock(sk); - return; + return 0; } pi = l2cap_pi(sk); if ((!status) && (chan != NULL)) { + pi->ampchan = chan; pi->ampcon = chan->conn; pi->ampcon->l2cap_data = pi->conn; - BT_DBG("amp_move_state %d", pi->amp_move_state); - if (sk->sk_state != BT_CONNECTED) { struct l2cap_conf_rsp rsp; @@ -5489,24 +5389,13 @@ static void l2cap_logical_link_complete(struct hci_chan *chan, u8 status) pi->amp_move_cmd_ident, pi->dcid, L2CAP_MOVE_CHAN_SUCCESS); } - } else if ((pi->amp_move_state != - L2CAP_AMP_STATE_WAIT_MOVE_RSP_SUCCESS) && - (pi->amp_move_state != - L2CAP_AMP_STATE_WAIT_MOVE_CONFIRM) && - (pi->amp_move_state != - L2CAP_AMP_STATE_WAIT_MOVE_CONFIRM_RSP)) { - /* Move was not in expected state, free the channel */ - ampchan = pi->ampchan; - ampcon = pi->ampcon; - pi->ampchan = NULL; + } else { + /* Move was not in expected state, free the + * logical link + */ + hci_chan_put(pi->ampchan); pi->ampcon = NULL; - if (ampchan) { - if (hci_chan_put(ampchan)) - ampcon->l2cap_data = NULL; - else - l2cap_deaggregate(ampchan, pi); - } - pi->amp_move_state = L2CAP_AMP_STATE_STABLE; + pi->ampchan = NULL; } } else { /* Logical link setup failed. */ @@ -5541,33 +5430,21 @@ static void l2cap_logical_link_complete(struct hci_chan *chan, u8 status) L2CAP_MOVE_CHAN_UNCONFIRMED); l2cap_sock_set_timer(sk, L2CAP_MOVE_TIMEOUT); } - ampchan = pi->ampchan; - ampcon = pi->ampcon; - pi->ampchan = NULL; + pi->ampcon = NULL; - if (ampchan) { - if (hci_chan_put(ampchan)) - ampcon->l2cap_data = NULL; - else - l2cap_deaggregate(ampchan, pi); - } + pi->ampchan = NULL; } release_sock(sk); + return 0; } static void l2cap_logical_link_worker(struct work_struct *work) { struct l2cap_logical_link_work *log_link_work = container_of(work, struct l2cap_logical_link_work, work); - struct sock *sk = log_link_work->chan->l2cap_sk; - if (sk) { - l2cap_logical_link_complete(log_link_work->chan, - log_link_work->status); - sock_put(sk); - } - hci_chan_put(log_link_work->chan); + l2cap_logical_link_complete(log_link_work->chan, log_link_work->status); kfree(log_link_work); } @@ -5575,27 +5452,15 @@ static int l2cap_create_cfm(struct hci_chan *chan, u8 status) { struct l2cap_logical_link_work *amp_work; - if (!chan->l2cap_sk) { - BT_ERR("Expected l2cap_sk to point to connecting socket"); - return -EFAULT; - } - amp_work = kzalloc(sizeof(*amp_work), GFP_ATOMIC); - if (!amp_work) { - sock_put(chan->l2cap_sk); + if (!amp_work) return -ENOMEM; - } INIT_WORK(&_work->work, l2cap_logical_link_worker); amp_work->chan = chan; amp_work->status = status; - - hci_chan_hold(chan); - if (!queue_work(_l2cap_wq, &_work->work)) { kfree(amp_work); - sock_put(chan->l2cap_sk); - hci_chan_put(chan); return -ENOMEM; } @@ -5631,14 +5496,8 @@ int l2cap_destroy_cfm(struct hci_chan *chan, u8 reason) bh_lock_sock(sk); /* TODO MM/PK - What to do if connection is LOCAL_BUSY? */ if (l2cap_pi(sk)->ampchan == chan) { - struct hci_conn *ampcon = l2cap_pi(sk)->ampcon; l2cap_pi(sk)->ampchan = NULL; l2cap_pi(sk)->ampcon = NULL; - if (hci_chan_put(chan)) - ampcon->l2cap_data = NULL; - else - l2cap_deaggregate(chan, l2cap_pi(sk)); - l2cap_amp_move_init(sk); } bh_unlock_sock(sk); @@ -5706,8 +5565,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, struct hci_conn *hcon = conn->hcon; struct l2cap_conn_param_update_req *req; struct l2cap_conn_param_update_rsp rsp; - struct sock *sk; - u16 min, max, latency, timeout, cmd_len; + u16 min, max, latency, to_multiplier, cmd_len; int err; if (!(hcon->link_mode & HCI_LM_MASTER)) @@ -5717,32 +5575,28 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, if (cmd_len != sizeof(struct l2cap_conn_param_update_req)) return -EPROTO; - memset(&rsp, 0, sizeof(rsp)); - rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED); + req = (struct l2cap_conn_param_update_req *) data; + min = __le16_to_cpu(req->min); + max = __le16_to_cpu(req->max); + latency = __le16_to_cpu(req->latency); + to_multiplier = __le16_to_cpu(req->to_multiplier); - sk = l2cap_find_sock_by_fixed_cid_and_dir(4, conn->src, conn->dst, 0); + BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x Timeout: 0x%4.4x", + min, max, latency, to_multiplier); - if (sk && !bt_sk(sk)->le_params.prohibit_remote_chg) { - req = (struct l2cap_conn_param_update_req *) data; - min = __le16_to_cpu(req->min); - max = __le16_to_cpu(req->max); - latency = __le16_to_cpu(req->latency); - timeout = __le16_to_cpu(req->to_multiplier); + memset(&rsp, 0, sizeof(rsp)); - err = l2cap_check_conn_param(min, max, latency, timeout); - if (!err) { - rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED); - hci_le_conn_update(hcon, min, max, latency, timeout); - bt_sk(sk)->le_params.interval_min = min; - bt_sk(sk)->le_params.interval_max = max; - bt_sk(sk)->le_params.latency = latency; - bt_sk(sk)->le_params.supervision_timeout = timeout; - } - } + err = l2cap_check_conn_param(min, max, latency, to_multiplier); + if (err) + rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED); + else + rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED); l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP, sizeof(rsp), &rsp); + if (!err) + hci_le_conn_update(hcon, min, max, latency, to_multiplier); return 0; } @@ -7265,41 +7119,15 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str return 0; } -static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, - struct sk_buff *skb) +static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct sk_buff *skb) { - struct sock *sk = NULL; + struct sock *sk; struct sk_buff *skb_rsp; struct l2cap_hdr *lh; - int dir; u8 err_rsp[] = {L2CAP_ATT_ERROR, 0x00, 0x00, 0x00, L2CAP_ATT_NOT_SUPPORTED}; - if (skb->data[0] == L2CAP_ATT_MTU_REQ) { - u8 mtu_rsp[] = {L2CAP_ATT_MTU_RSP, 23, 0}; - - skb_rsp = bt_skb_alloc(sizeof(mtu_rsp) + L2CAP_HDR_SIZE, - GFP_ATOMIC); - if (!skb_rsp) - goto drop; - - lh = (struct l2cap_hdr *) skb_put(skb_rsp, L2CAP_HDR_SIZE); - lh->len = cpu_to_le16(sizeof(mtu_rsp)); - lh->cid = cpu_to_le16(L2CAP_CID_LE_DATA); - memcpy(skb_put(skb_rsp, sizeof(mtu_rsp)), mtu_rsp, - sizeof(mtu_rsp)); - hci_send_acl(conn->hcon, NULL, skb_rsp, 0); - - goto free_skb; - } - - dir = (skb->data[0] & L2CAP_ATT_RESPONSE_BIT) ? 0 : 1; - - sk = l2cap_find_sock_by_fixed_cid_and_dir(cid, conn->src, - conn->dst, dir); - - BT_DBG("sk %p, dir:%d", sk, dir); - + sk = l2cap_get_sock_by_fixed_scid(0, cid, conn->src, conn->dst); if (!sk) goto drop; @@ -7317,26 +7145,8 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, goto done; drop: - if (skb->data[0] != L2CAP_ATT_INDICATE) - goto not_indicate; - - /* If this is an incoming Indication, we are required to confirm */ - - skb_rsp = bt_skb_alloc(sizeof(u8) + L2CAP_HDR_SIZE, GFP_ATOMIC); - if (!skb_rsp) - goto free_skb; - - lh = (struct l2cap_hdr *) skb_put(skb_rsp, L2CAP_HDR_SIZE); - lh->len = cpu_to_le16(sizeof(u8)); - lh->cid = cpu_to_le16(L2CAP_CID_LE_DATA); - err_rsp[0] = L2CAP_ATT_CONFIRM; - memcpy(skb_put(skb_rsp, sizeof(u8)), err_rsp, sizeof(u8)); - hci_send_acl(conn->hcon, NULL, skb_rsp, 0); - goto free_skb; - -not_indicate: - if (skb->data[0] & L2CAP_ATT_RESPONSE_BIT || - skb->data[0] == L2CAP_ATT_CONFIRM) + if (skb->data[0] & L2CAP_ATT_RESPONSE_BIT && + skb->data[0] != L2CAP_ATT_INDICATE) goto free_skb; /* If this is an incoming PDU that requires a response, respond with @@ -7398,7 +7208,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) case L2CAP_CID_SMP: if (smp_sig_channel(conn, skb)) - l2cap_conn_del(conn->hcon, EACCES, 0); + l2cap_conn_del(conn->hcon, EACCES); break; default: @@ -7412,9 +7222,9 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) l2cap_data_channel(sk, skb); bh_unlock_sock(sk); - } else if ((cid == L2CAP_CID_A2MP) && enable_hs) { + } else if (cid == L2CAP_CID_A2MP) { BT_DBG("A2MP"); - amp_conn_ind(conn->hcon, skb); + amp_conn_ind(conn, skb); } else { BT_DBG("unknown cid 0x%4.4x", cid); kfree_skb(skb); @@ -7433,7 +7243,7 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) struct hlist_node *node; if (type != ACL_LINK) - return 0; + return -EINVAL; BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); @@ -7473,7 +7283,7 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status) if (conn) l2cap_conn_ready(conn); } else - l2cap_conn_del(hcon, bt_err(status), 0); + l2cap_conn_del(hcon, bt_err(status)); return 0; } @@ -7490,14 +7300,14 @@ static int l2cap_disconn_ind(struct hci_conn *hcon) return conn->disc_reason; } -static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason, u8 is_process) +static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK)) return -EINVAL; - l2cap_conn_del(hcon, bt_err(reason), is_process); + l2cap_conn_del(hcon, bt_err(reason)); return 0; } @@ -7524,7 +7334,6 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) struct l2cap_chan_list *l; struct l2cap_conn *conn = hcon->l2cap_data; struct sock *sk; - int smp = 0; if (!conn) return 0; @@ -7541,12 +7350,13 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) BT_DBG("sk->scid %d", l2cap_pi(sk)->scid); if (l2cap_pi(sk)->scid == L2CAP_CID_LE_DATA) { - if (!status && encrypt) { + if (!status && encrypt) l2cap_pi(sk)->sec_level = hcon->sec_level; - l2cap_chan_ready(sk); - } - smp = 1; + del_timer(&hcon->smp_timer); + l2cap_chan_ready(sk); + smp_link_encrypt_cmplt(conn, status, encrypt); + bh_unlock_sock(sk); continue; } @@ -7567,9 +7377,8 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) if (!status) { l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; - if ((l2cap_pi(sk)->amp_pref == - BT_AMP_POLICY_PREFER_AMP) && - enable_hs) { + if (l2cap_pi(sk)->amp_pref == + BT_AMP_POLICY_PREFER_AMP) { amp_create_physical(l2cap_pi(sk)->conn, sk); } else @@ -7604,17 +7413,6 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); l2cap_send_cmd(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); - - if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) && - result == L2CAP_CR_SUCCESS) { - char buf[128]; - l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; - l2cap_send_cmd(conn, l2cap_get_ident(conn), - L2CAP_CONF_REQ, - l2cap_build_conf_req(sk, buf), - buf); - l2cap_pi(sk)->num_conf_req++; - } } bh_unlock_sock(sk); @@ -7622,11 +7420,6 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) read_unlock(&l->lock); - if (smp) { - del_timer(&hcon->smp_timer); - smp_link_encrypt_cmplt(conn, status, encrypt); - } - return 0; } @@ -7733,33 +7526,6 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl return 0; } -static void l2cap_set_acl_flushto(struct hci_conn *hcon, u16 flush_to) -{ - struct hci_cp_write_automatic_flush_timeout flush_tm; - if (hcon && hcon->hdev) { - flush_tm.handle = hcon->handle; - if (flush_to == L2CAP_DEFAULT_FLUSH_TO) - flush_to = 0; - flush_tm.timeout = (flush_to < L2CAP_MAX_FLUSH_TO) ? - flush_to : L2CAP_MAX_FLUSH_TO; - hci_send_cmd(hcon->hdev, - HCI_OP_WRITE_AUTOMATIC_FLUSH_TIMEOUT, - 4, &(flush_tm)); - } -} - -static u16 l2cap_get_smallest_flushto(struct l2cap_chan_list *l) -{ - int ret_flush_to = L2CAP_DEFAULT_FLUSH_TO; - struct sock *s; - for (s = l->head; s; s = l2cap_pi(s)->next_c) { - if (l2cap_pi(s)->flush_to > 0 && - l2cap_pi(s)->flush_to < ret_flush_to) - ret_flush_to = l2cap_pi(s)->flush_to; - } - return ret_flush_to; -} - static int l2cap_debugfs_show(struct seq_file *f, void *p) { struct sock *sk; @@ -7871,8 +7637,5 @@ void l2cap_exit(void) module_param(disable_ertm, bool, 0644); MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode"); -module_param(enable_hs, bool, 0644); -MODULE_PARM_DESC(enable_hs, "Enable A2MP protocol"); - module_param(enable_reconfig, bool, 0644); MODULE_PARM_DESC(enable_reconfig, "Enable reconfig after initiating AMP move"); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 4557ddad..11e46ba0 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2011-2012 The Linux Foundation. All rights reserved. + Copyright (C) 2000-2001 Qualcomm Incorporated Copyright (C) 2009-2010 Gustavo F. Padovan Copyright (C) 2010 Google Inc. @@ -25,8 +25,7 @@ */ /* Bluetooth L2CAP sockets. */ - -#include +// rollback to original BlueZ #include #include @@ -81,23 +80,6 @@ void l2cap_sock_clear_timer(struct sock *sk) sk_stop_timer(sk, &sk->sk_timer); } -int l2cap_sock_le_params_valid(struct bt_le_params *le_params) -{ - if (!le_params || le_params->latency > BT_LE_LATENCY_MAX || - le_params->scan_window > BT_LE_SCAN_WINDOW_MAX || - le_params->scan_interval < BT_LE_SCAN_INTERVAL_MIN || - le_params->scan_window > le_params->scan_interval || - le_params->interval_min < BT_LE_CONN_INTERVAL_MIN || - le_params->interval_max > BT_LE_CONN_INTERVAL_MAX || - le_params->interval_min > le_params->interval_max || - le_params->supervision_timeout < BT_LE_SUP_TO_MIN || - le_params->supervision_timeout > BT_LE_SUP_TO_MAX) { - return 0; - } - - return 1; -} - static struct sock *__l2cap_get_sock_by_addr(__le16 psm, bdaddr_t *src) { struct sock *sk; @@ -390,6 +372,7 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l BT_DBG("sock %p, sk %p", sock, sk); + memset(la, 0, sizeof(struct sockaddr_l2)); addr->sa_family = AF_BLUETOOTH; *len = sizeof(struct sockaddr_l2); @@ -525,10 +508,8 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch memset(&sec, 0, sizeof(sec)); sec.level = l2cap_pi(sk)->sec_level; - if (sk->sk_state == BT_CONNECTED) { + if (sk->sk_state == BT_CONNECTED) sec.key_size = l2cap_pi(sk)->conn->hcon->enc_key_size; - sec.level = l2cap_pi(sk)->conn->hcon->sec_level; - } len = min_t(unsigned int, len, sizeof(sec)); if (copy_to_user(optval, (char *) &sec, len)) @@ -567,17 +548,6 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch err = -EFAULT; break; - case BT_LE_PARAMS: - if (l2cap_pi(sk)->scid != L2CAP_CID_LE_DATA) { - err = -EINVAL; - break; - } - - if (copy_to_user(optval, (char *) &bt_sk(sk)->le_params, - sizeof(bt_sk(sk)->le_params))) - err = -EFAULT; - break; - default: err = -ENOPROTOOPT; break; @@ -644,16 +614,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us case L2CAP_MODE_BASIC: l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_STATE2_DEVICE; break; - case L2CAP_MODE_STREAMING: - if (!disable_ertm) { - /* No fallback to ERTM or Basic mode */ - l2cap_pi(sk)->conf_state |= - L2CAP_CONF_STATE2_DEVICE; - break; - } - err = -EINVAL; - break; case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: if (!disable_ertm) break; /* fall through */ @@ -667,7 +629,6 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us l2cap_pi(sk)->fcs = opts.fcs; l2cap_pi(sk)->max_tx = opts.max_tx; l2cap_pi(sk)->tx_win = opts.txwin_size; - l2cap_pi(sk)->flush_to = opts.flush_to; break; case L2CAP_LM: @@ -702,7 +663,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch struct sock *sk = sock->sk; struct bt_security sec; struct bt_power pwr; - struct bt_le_params le_params; struct l2cap_conn *conn; int len, err = 0; u32 opt; @@ -809,50 +769,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch break; - case BT_FLUSHABLE: - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - l2cap_pi(sk)->flushable = opt; - - break; - - case BT_LE_PARAMS: - if (l2cap_pi(sk)->scid != L2CAP_CID_LE_DATA) { - err = -EINVAL; - break; - } - - if (copy_from_user((char *) &le_params, optval, - sizeof(struct bt_le_params))) { - err = -EFAULT; - break; - } - - conn = l2cap_pi(sk)->conn; - if (!conn || !conn->hcon || - l2cap_pi(sk)->scid != L2CAP_CID_LE_DATA) { - memcpy(&bt_sk(sk)->le_params, &le_params, - sizeof(le_params)); - break; - } - - if (!conn->hcon->out || - !l2cap_sock_le_params_valid(&le_params)) { - err = -EINVAL; - break; - } - - memcpy(&bt_sk(sk)->le_params, &le_params, sizeof(le_params)); - - hci_le_conn_update(conn->hcon, - le_params.interval_min, - le_params.interval_max, - le_params.latency, - le_params.supervision_timeout); - break; - default: err = -ENOPROTOOPT; break; @@ -1034,8 +950,7 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms else err = bt_sock_recvmsg(iocb, sock, msg, len, flags); - if (err >= 0) - l2cap_ertm_recv_done(sk); + l2cap_ertm_recv_done(sk); return err; } @@ -1174,7 +1089,6 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) static int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; - struct sock *sk2 = NULL; int err; BT_DBG("sock %p, sk %p", sock, sk); @@ -1182,17 +1096,6 @@ static int l2cap_sock_release(struct socket *sock) if (!sk) return 0; - /* If this is an ATT socket, find it's matching server/client */ - if (l2cap_pi(sk)->scid == L2CAP_CID_LE_DATA) - sk2 = l2cap_find_sock_by_fixed_cid_and_dir(L2CAP_CID_LE_DATA, - &bt_sk(sk)->src, &bt_sk(sk)->dst, - l2cap_pi(sk)->incoming ? 0 : 1); - - /* If matching socket found, request tear down */ - BT_DBG("sock:%p companion:%p", sk, sk2); - if (sk2) - l2cap_sock_set_timer(sk2, 1); - err = l2cap_sock_shutdown(sock, 2); sock_orphan(sk); @@ -1213,6 +1116,8 @@ static void l2cap_sock_destruct(struct sock *sk) static void set_default_config(struct l2cap_conf_prm *conf_prm) { conf_prm->fcs = L2CAP_FCS_CRC16; + conf_prm->retrans_timeout = 0; + conf_prm->monitor_timeout = 0; conf_prm->flush_to = L2CAP_DEFAULT_FLUSH_TO; } @@ -1271,10 +1176,17 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent) pi->scid = 0; pi->dcid = 0; pi->tx_win_max = L2CAP_TX_WIN_MAX_ENHANCED; - pi->ack_win = pi->tx_win; pi->extended_control = 0; pi->local_conf.fcs = pi->fcs; + if (pi->mode == L2CAP_MODE_BASIC) { + pi->local_conf.retrans_timeout = 0; + pi->local_conf.monitor_timeout = 0; + } else { + pi->local_conf.retrans_timeout = L2CAP_DEFAULT_RETRANS_TO; + pi->local_conf.monitor_timeout = L2CAP_DEFAULT_MONITOR_TO; + } + pi->local_conf.flush_to = pi->flush_to; set_default_config(&pi->remote_conf); diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index 177e64f2..b826d1bf 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -1,6 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Technologies, Inc. + Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1c9d6d9a..28a5d9b2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1,7 +1,6 @@ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2010 Nokia Corporation - Copyright (c) 2011-2012 The Linux Foundation. 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 @@ -22,6 +21,7 @@ */ /* Bluetooth HCI Management interface */ +// rollback to original BlueZ #include #include @@ -37,9 +37,18 @@ #define MGMT_VERSION 0 #define MGMT_REVISION 1 -#define SCAN_IDLE 0x00 -#define SCAN_LE 0x01 -#define SCAN_BR 0x02 +enum scan_mode { + SCAN_IDLE, + SCAN_LE, + SCAN_BR +}; + +struct disco_interleave { + u16 index; + enum scan_mode mode; + int int_phase; + int int_count; +}; struct pending_cmd { struct list_head list; @@ -310,7 +319,7 @@ static void mgmt_pending_foreach(u16 opcode, int index, cmd = list_entry(p, struct pending_cmd, list); - if (opcode > 0 && cmd->opcode != opcode) + if (cmd->opcode != opcode) continue; if (index >= 0 && cmd->index != index) @@ -418,7 +427,6 @@ static u8 get_service_classes(struct hci_dev *hdev) static int update_class(struct hci_dev *hdev) { u8 cod[3]; - int err = 0; BT_DBG("%s", hdev->name); @@ -432,12 +440,7 @@ static int update_class(struct hci_dev *hdev) if (memcmp(cod, hdev->dev_class, 3) == 0) return 0; - err = hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); - - if (err == 0) - memcpy(hdev->dev_class, cod, 3); - - return err; + return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); } static int set_limited_discoverable(struct sock *sk, u16 index, @@ -770,7 +773,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data) struct list_head *p; size_t name_len; - name_len = strnlen(hdev->dev_name, HCI_MAX_EIR_LENGTH); + name_len = strlen(hdev->dev_name); if (name_len > 0) { /* EIR Data type */ @@ -898,17 +901,13 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) list_add(&uuid->list, &hdev->uuids); - if (test_bit(HCI_UP, &hdev->flags)) { - - err = update_class(hdev); - if (err < 0) - goto failed; + err = update_class(hdev); + if (err < 0) + goto failed; - err = update_eir(hdev); - if (err < 0) - goto failed; - } else - err = 0; + err = update_eir(hdev); + if (err < 0) + goto failed; err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0); @@ -962,16 +961,13 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) goto unlock; } - if (test_bit(HCI_UP, &hdev->flags)) { - err = update_class(hdev); - if (err < 0) - goto unlock; + err = update_class(hdev); + if (err < 0) + goto unlock; - err = update_eir(hdev); - if (err < 0) - goto unlock; - } else - err = 0; + err = update_eir(hdev); + if (err < 0) + goto unlock; err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0); @@ -1006,12 +1002,9 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data, hdev->major_class |= cp->major & MGMT_MAJOR_CLASS_MASK; hdev->minor_class = cp->minor; - if (test_bit(HCI_UP, &hdev->flags)) { - err = update_class(hdev); - if (err == 0) - err = cmd_complete(sk, index, - MGMT_OP_SET_DEV_CLASS, hdev->dev_class, sizeof(u8)*3); - } else + err = update_class(hdev); + + if (err == 0) err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0); hci_dev_unlock_bh(hdev); @@ -1045,12 +1038,9 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data, err = 0; } else { clear_bit(HCI_SERVICE_CACHE, &hdev->flags); - if (test_bit(HCI_UP, &hdev->flags)) { - err = update_class(hdev); - if (err == 0) - err = update_eir(hdev); - } else - err = 0; + err = update_class(hdev); + if (err == 0) + err = update_eir(hdev); } if (err == 0) @@ -1361,23 +1351,20 @@ static int encrypt_link(struct sock *sk, u16 index, unsigned char *data, if (!hdev) return cmd_status(sk, index, MGMT_OP_ENCRYPT_LINK, ENODEV); - hci_dev_lock_bh(hdev); + hci_dev_lock(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { err = cmd_status(sk, index, MGMT_OP_ENCRYPT_LINK, ENETDOWN); - goto done; + goto failed; } - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - if (!conn) { - err = cmd_status(sk, index, MGMT_OP_ENCRYPT_LINK, ENOTCONN); - goto done; - } + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, + &cp->bdaddr); + if (!conn) + return cmd_status(sk, index, MGMT_OP_ENCRYPT_LINK, ENOTCONN); - if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { - err = cmd_status(sk, index, MGMT_OP_ENCRYPT_LINK, EINPROGRESS); - goto done; - } + if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) + return cmd_status(sk, index, MGMT_OP_ENCRYPT_LINK, EINPROGRESS); if (conn->link_mode & HCI_LM_AUTH) { enc.handle = cpu_to_le16(conn->handle); @@ -1394,8 +1381,8 @@ static int encrypt_link(struct sock *sk, u16 index, unsigned char *data, } } -done: - hci_dev_unlock_bh(hdev); +failed: + hci_dev_unlock(hdev); hci_dev_put(hdev); return err; @@ -1538,7 +1525,6 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status) } pairing_complete(cmd, status); - hci_conn_put(conn); } static void pairing_security_complete_cb(struct hci_conn *conn, u8 status) @@ -1588,8 +1574,8 @@ static void discovery_terminated(struct pending_cmd *cmd, void *data) if (!hdev) goto not_found; - del_timer(&hdev->disco_le_timer); - del_timer(&hdev->disco_timer); + del_timer_sync(&hdev->disc_le_timer); + del_timer_sync(&hdev->disc_timer); hci_dev_put(hdev); not_found: @@ -1624,22 +1610,26 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) hci_dev_lock_bh(hdev); + BT_DBG("SSP Cap is %d", cp->ssp_cap); io_cap = cp->io_cap; - - sec_level = BT_SECURITY_MEDIUM; - auth_type = HCI_AT_DEDICATED_BONDING; + if ((cp->ssp_cap == 0) || (io_cap == 0x03)) { + sec_level = BT_SECURITY_MEDIUM; + auth_type = HCI_AT_DEDICATED_BONDING; + } else { + sec_level = BT_SECURITY_HIGH; + auth_type = HCI_AT_DEDICATED_BONDING_MITM; + } entry = hci_find_adv_entry(hdev, &cp->bdaddr); if (entry && entry->flags & 0x04) { - conn = hci_le_connect(hdev, 0, &cp->bdaddr, sec_level, - auth_type, NULL); + conn = hci_connect(hdev, LE_LINK, 0, &cp->bdaddr, sec_level, + auth_type); } else { /* ACL-SSP does not support io_cap 0x04 (KeyboadDisplay) */ if (io_cap == 0x04) io_cap = 0x01; conn = hci_connect(hdev, ACL_LINK, 0, &cp->bdaddr, sec_level, auth_type); - conn->auth_initiator = 1; } if (IS_ERR(conn)) { @@ -1817,81 +1807,6 @@ static int set_connection_params(struct sock *sk, u16 index, return err; } -static int set_rssi_reporter(struct sock *sk, u16 index, - unsigned char *data, u16 len) -{ - struct mgmt_cp_set_rssi_reporter *cp = (void *) data; - struct hci_dev *hdev; - struct hci_conn *conn; - int err = 0; - - if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_SET_RSSI_REPORTER, - EINVAL); - - hdev = hci_dev_get(index); - if (!hdev) - return cmd_status(sk, index, MGMT_OP_SET_RSSI_REPORTER, - ENODEV); - - hci_dev_lock_bh(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr); - - if (!conn) { - err = cmd_status(sk, index, MGMT_OP_SET_RSSI_REPORTER, - ENOTCONN); - goto failed; - } - - BT_DBG("updateOnThreshExceed %d ", cp->updateOnThreshExceed); - hci_conn_set_rssi_reporter(conn, cp->rssi_threshold, - __le16_to_cpu(cp->interval), cp->updateOnThreshExceed); - -failed: - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - - return err; -} - -static int unset_rssi_reporter(struct sock *sk, u16 index, - unsigned char *data, u16 len) -{ - struct mgmt_cp_unset_rssi_reporter *cp = (void *) data; - struct hci_dev *hdev; - struct hci_conn *conn; - int err = 0; - - if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_UNSET_RSSI_REPORTER, - EINVAL); - - hdev = hci_dev_get(index); - - if (!hdev) - return cmd_status(sk, index, MGMT_OP_UNSET_RSSI_REPORTER, - ENODEV); - - hci_dev_lock_bh(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr); - - if (!conn) { - err = cmd_status(sk, index, MGMT_OP_UNSET_RSSI_REPORTER, - ENOTCONN); - goto failed; - } - - hci_conn_unset_rssi_reporter(conn); - -failed: - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - - return err; -} - static int set_local_name(struct sock *sk, u16 index, unsigned char *data, u16 len) { @@ -1946,8 +1861,8 @@ static void discovery_rsp(struct pending_cmd *cmd, void *data) if (cmd->opcode == MGMT_OP_STOP_DISCOVERY) { struct hci_dev *hdev = hci_dev_get(cmd->index); if (hdev) { - del_timer(&hdev->disco_le_timer); - del_timer(&hdev->disco_timer); + del_timer_sync(&hdev->disc_le_timer); + del_timer_sync(&hdev->disc_timer); hci_dev_put(hdev); } } @@ -1971,7 +1886,7 @@ void mgmt_inquiry_complete_evt(u16 index, u8 status) { struct hci_dev *hdev; struct hci_cp_le_set_scan_enable le_cp = {1, 0}; - struct mgmt_mode cp = {0}; + struct pending_cmd *cmd; int err = -1; BT_DBG(""); @@ -1979,6 +1894,7 @@ void mgmt_inquiry_complete_evt(u16 index, u8 status) hdev = hci_dev_get(index); if (!hdev || !lmp_le_capable(hdev)) { + struct mgmt_mode cp = {0}; mgmt_pending_foreach(MGMT_OP_STOP_DISCOVERY, index, discovery_terminated, NULL); @@ -1991,20 +1907,20 @@ void mgmt_inquiry_complete_evt(u16 index, u8 status) return; } - if (hdev->disco_state != SCAN_IDLE) { + cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index); + if (cmd && cmd->param) { + struct disco_interleave *ilp = cmd->param; + err = hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(le_cp), &le_cp); - if (err >= 0) { - mod_timer(&hdev->disco_le_timer, jiffies + - msecs_to_jiffies(hdev->disco_int_phase * 1000)); - hdev->disco_state = SCAN_LE; + if (err >= 0 && hdev) { + mod_timer(&hdev->disc_le_timer, jiffies + + msecs_to_jiffies(ilp->int_phase * 1000)); + ilp->mode = SCAN_LE; } else - hdev->disco_state = SCAN_IDLE; + ilp->mode = SCAN_IDLE; } - if (hdev->disco_state == SCAN_IDLE) - mgmt_event(MGMT_EV_DISCOVERING, index, &cp, sizeof(cp), NULL); - if (err < 0) mgmt_pending_foreach(MGMT_OP_STOP_DISCOVERY, index, discovery_terminated, NULL); @@ -2013,78 +1929,82 @@ void mgmt_inquiry_complete_evt(u16 index, u8 status) hci_dev_put(hdev); } -void mgmt_disco_timeout(unsigned long data) +static void disco_to(unsigned long data) { - struct hci_dev *hdev = (void *) data; + struct disco_interleave *ilp = (void *)data; + struct hci_dev *hdev; struct pending_cmd *cmd; - struct mgmt_mode cp = {0}; - BT_DBG("hci%d", hdev->id); + BT_DBG("hci%d", ilp->index); - hdev = hci_dev_get(hdev->id); + hdev = hci_dev_get(ilp->index); - if (!hdev) - return; + if (hdev) { + hci_dev_lock_bh(hdev); + del_timer_sync(&hdev->disc_le_timer); - hci_dev_lock_bh(hdev); - del_timer(&hdev->disco_le_timer); + cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, ilp->index); - if (hdev->disco_state != SCAN_IDLE) { - struct hci_cp_le_set_scan_enable le_cp = {0, 0}; + if (ilp->mode != SCAN_IDLE) { + struct hci_cp_le_set_scan_enable le_cp = {0, 0}; - if (test_bit(HCI_UP, &hdev->flags)) { - if (hdev->disco_state == SCAN_LE) + if (ilp->mode == SCAN_LE) hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(le_cp), &le_cp); else - hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, - NULL); + hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, + 0, NULL); + + ilp->mode = SCAN_IDLE; } - hdev->disco_state = SCAN_IDLE; - } - mgmt_event(MGMT_EV_DISCOVERING, hdev->id, &cp, sizeof(cp), NULL); + if (cmd) { + struct mgmt_mode cp = {0}; - cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev->id); - if (cmd) - mgmt_pending_remove(cmd); + mgmt_event(MGMT_EV_DISCOVERING, ilp->index, &cp, + sizeof(cp), NULL); + mgmt_pending_remove(cmd); + } - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + } } -void mgmt_disco_le_timeout(unsigned long data) +static void disco_le_to(unsigned long data) { - struct hci_dev *hdev = (void *)data; + struct disco_interleave *ilp = (void *)data; + struct hci_dev *hdev; + struct pending_cmd *cmd; struct hci_cp_le_set_scan_enable le_cp = {0, 0}; - BT_DBG("hci%d", hdev->id); + BT_DBG("hci%d", ilp->index); - hdev = hci_dev_get(hdev->id); + hdev = hci_dev_get(ilp->index); - if (!hdev) - return; + if (hdev) { + hci_dev_lock_bh(hdev); - hci_dev_lock_bh(hdev); + cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, ilp->index); - if (test_bit(HCI_UP, &hdev->flags)) { - if (hdev->disco_state == SCAN_LE) + if (ilp->mode == SCAN_LE) hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, - sizeof(le_cp), &le_cp); + sizeof(le_cp), &le_cp); - /* re-start BR scan */ - if (hdev->disco_state != SCAN_IDLE) { + /* re-start BR scan */ + if (cmd) { struct hci_cp_inquiry cp = {{0x33, 0x8b, 0x9e}, 4, 0}; - hdev->disco_int_phase *= 2; - hdev->disco_int_count = 0; - cp.num_rsp = (u8) hdev->disco_int_phase; + ilp->int_phase *= 2; + ilp->int_count = 0; + cp.num_rsp = (u8) ilp->int_phase; hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); - hdev->disco_state = SCAN_BR; - } - } + ilp->mode = SCAN_BR; + } else + ilp->mode = SCAN_IDLE; - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + } } static int start_discovery(struct sock *sk, u16 index) @@ -2102,11 +2022,6 @@ static int start_discovery(struct sock *sk, u16 index) hci_dev_lock_bh(hdev); - if (hdev->disco_state && timer_pending(&hdev->disco_timer)) { - err = -EBUSY; - goto failed; - } - cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, index, NULL, 0); if (!cmd) { err = -ENOMEM; @@ -2137,31 +2052,33 @@ static int start_discovery(struct sock *sk, u16 index) err = hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); - if (err < 0) { + if (err < 0) mgmt_pending_remove(cmd); - hdev->disco_state = SCAN_IDLE; - } else if (lmp_le_capable(hdev)) { + else if (lmp_le_capable(hdev)) { + struct disco_interleave il, *ilp; + + il.int_phase = 1; + il.int_count = 0; + il.index = index; + il.mode = SCAN_BR; + mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, &il, + sizeof(struct disco_interleave)); cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index); - if (!cmd) - mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, - NULL, 0); - hdev->disco_int_phase = 1; - hdev->disco_int_count = 0; - hdev->disco_state = SCAN_BR; - del_timer(&hdev->disco_le_timer); - del_timer(&hdev->disco_timer); - mod_timer(&hdev->disco_timer, - jiffies + msecs_to_jiffies(20000)); - } else - hdev->disco_state = SCAN_BR; + if (cmd) { + ilp = cmd->param; + setup_timer(&hdev->disc_le_timer, disco_le_to, + (unsigned long) ilp); + setup_timer(&hdev->disc_timer, disco_to, + (unsigned long) ilp); + mod_timer(&hdev->disc_timer, + jiffies + msecs_to_jiffies(20000)); + } + } failed: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); - if (err < 0) - return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, -err); - return err; } @@ -2169,10 +2086,10 @@ static int stop_discovery(struct sock *sk, u16 index) { struct hci_cp_le_set_scan_enable le_cp = {0, 0}; struct mgmt_mode mode_cp = {0}; + struct disco_interleave *ilp = NULL; struct hci_dev *hdev; struct pending_cmd *cmd = NULL; int err = -EPERM; - u8 state; BT_DBG(""); @@ -2182,30 +2099,38 @@ static int stop_discovery(struct sock *sk, u16 index) hci_dev_lock_bh(hdev); - state = hdev->disco_state; - hdev->disco_state = SCAN_IDLE; - del_timer(&hdev->disco_le_timer); - del_timer(&hdev->disco_timer); + if (lmp_le_capable(hdev)) { + cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + + ilp = cmd->param; + } - if (state == SCAN_LE) { + if (lmp_le_capable(hdev) && ilp && (ilp->mode == SCAN_LE)) err = hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(le_cp), &le_cp); - if (err >= 0) { - mgmt_pending_foreach(MGMT_OP_STOP_DISCOVERY, index, - discovery_terminated, NULL); - err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY, - NULL, 0); - } - } else if (state == SCAN_BR) - err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL); + if (err < 0) { + if (!ilp || (ilp->mode == SCAN_BR)) + err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, + 0, NULL); + } + + if (ilp) { + ilp->mode = SCAN_IDLE; + del_timer_sync(&hdev->disc_le_timer); + del_timer_sync(&hdev->disc_timer); + } - cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index); if (err < 0 && cmd) mgmt_pending_remove(cmd); mgmt_event(MGMT_EV_DISCOVERING, index, &mode_cp, sizeof(mode_cp), NULL); +failed: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); @@ -2447,12 +2372,6 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_SET_CONNECTION_PARAMS: err = set_connection_params(sk, index, buf + sizeof(*hdr), len); break; - case MGMT_OP_SET_RSSI_REPORTER: - err = set_rssi_reporter(sk, index, buf + sizeof(*hdr), len); - break; - case MGMT_OP_UNSET_RSSI_REPORTER: - err = unset_rssi_reporter(sk, index, buf + sizeof(*hdr), len); - break; case MGMT_OP_READ_LOCAL_OOB_DATA: err = read_local_oob_data(sk, index); break; @@ -2483,14 +2402,6 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) return err; } -static void cmd_status_rsp(struct pending_cmd *cmd, void *data) -{ - u8 *status = data; - - cmd_status(cmd->sk, cmd->index, cmd->opcode, *status); - mgmt_pending_remove(cmd); -} - int mgmt_index_added(u16 index) { BT_DBG("%d", index); @@ -2499,12 +2410,7 @@ int mgmt_index_added(u16 index) int mgmt_index_removed(u16 index) { - u8 status = ENODEV; - BT_DBG("%d", index); - - mgmt_pending_foreach(0, index, cmd_status_rsp, &status); - return mgmt_event(MGMT_EV_INDEX_REMOVED, index, NULL, 0, NULL); } @@ -2543,11 +2449,6 @@ int mgmt_powered(u16 index, u8 powered) mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match); - if (!powered) { - u8 status = ENETDOWN; - mgmt_pending_foreach(0, index, cmd_status_rsp, &status); - } - ev.val = powered; ret = mgmt_event(MGMT_EV_POWERED, index, &ev, sizeof(ev), match.sk); @@ -2792,11 +2693,8 @@ int mgmt_user_confirm_request(u16 index, u8 event, if ((conn->auth_type & HCI_AT_DEDICATED_BONDING) && conn->auth_initiator && rem_cap == 0x03) ev.auto_confirm = 1; - else if (loc_cap == 0x01 && (rem_cap == 0x00 || rem_cap == 0x03)) { - if (!loc_mitm && !rem_mitm) - value = 0; + else if (loc_cap == 0x01 && (rem_cap == 0x00 || rem_cap == 0x03)) goto no_auto_confirm; - } if ((!loc_mitm || rem_cap == 0x03) && (!rem_mitm || loc_cap == 0x03)) @@ -2937,62 +2835,11 @@ int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer, return err; } -void mgmt_read_rssi_complete(u16 index, s8 rssi, bdaddr_t *bdaddr, - u16 handle, u8 status) -{ - struct mgmt_ev_rssi_update ev; - struct hci_conn *conn; - struct hci_dev *hdev; - - if (status) - return; - - hdev = hci_dev_get(index); - conn = hci_conn_hash_lookup_handle(hdev, handle); - - if (!conn) - return; - - BT_DBG("rssi_update_thresh_exceed : %d ", - conn->rssi_update_thresh_exceed); - BT_DBG("RSSI Threshold : %d , recvd RSSI : %d ", - conn->rssi_threshold, rssi); - - if (conn->rssi_update_thresh_exceed == 1) { - BT_DBG("rssi_update_thresh_exceed == 1"); - if (rssi > conn->rssi_threshold) { - memset(&ev, 0, sizeof(ev)); - bacpy(&ev.bdaddr, bdaddr); - ev.rssi = rssi; - mgmt_event(MGMT_EV_RSSI_UPDATE, index, &ev, - sizeof(ev), NULL); - } else { - hci_conn_set_rssi_reporter(conn, conn->rssi_threshold, - conn->rssi_update_interval, - conn->rssi_update_thresh_exceed); - } - } else { - BT_DBG("rssi_update_thresh_exceed == 0"); - if (rssi < conn->rssi_threshold) { - memset(&ev, 0, sizeof(ev)); - bacpy(&ev.bdaddr, bdaddr); - ev.rssi = rssi; - mgmt_event(MGMT_EV_RSSI_UPDATE, index, &ev, - sizeof(ev), NULL); - } else { - hci_conn_set_rssi_reporter(conn, conn->rssi_threshold, - conn->rssi_update_interval, - conn->rssi_update_thresh_exceed); - } - } -} - - int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le, u8 *dev_class, s8 rssi, u8 eir_len, u8 *eir) { struct mgmt_ev_device_found ev; - struct hci_dev *hdev; + struct pending_cmd *cmd; int err; BT_DBG("le: %d", le); @@ -3015,38 +2862,36 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le, if (err < 0) return err; - hdev = hci_dev_get(index); - - if (!hdev) - return 0; - - if (hdev->disco_state == SCAN_IDLE) - goto done; - - hdev->disco_int_count++; + cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index); + if (cmd) { + struct disco_interleave *ilp = cmd->param; + struct hci_dev *hdev = hci_dev_get(index); - if (hdev->disco_int_count >= hdev->disco_int_phase) { - /* Inquiry scan for General Discovery LAP */ - struct hci_cp_inquiry cp = {{0x33, 0x8b, 0x9e}, 4, 0}; - struct hci_cp_le_set_scan_enable le_cp = {0, 0}; + ilp->int_count++; + if (hdev && ilp->int_count >= ilp->int_phase) { + /* Inquiry scan for General Discovery LAP */ + struct hci_cp_inquiry cp = {{0x33, 0x8b, 0x9e}, 4, 0}; + struct hci_cp_le_set_scan_enable le_cp = {0, 0}; - hdev->disco_int_phase *= 2; - hdev->disco_int_count = 0; - if (hdev->disco_state == SCAN_LE) { - /* cancel LE scan */ - hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, - sizeof(le_cp), &le_cp); - /* start BR scan */ - cp.num_rsp = (u8) hdev->disco_int_phase; - hci_send_cmd(hdev, HCI_OP_INQUIRY, - sizeof(cp), &cp); - hdev->disco_state = SCAN_BR; - del_timer_sync(&hdev->disco_le_timer); + ilp->int_phase *= 2; + ilp->int_count = 0; + if (ilp->mode == SCAN_LE) { + /* cancel LE scan */ + hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, + sizeof(le_cp), &le_cp); + /* start BR scan */ + cp.num_rsp = (u8) ilp->int_phase; + hci_send_cmd(hdev, HCI_OP_INQUIRY, + sizeof(cp), &cp); + ilp->mode = SCAN_BR; + del_timer_sync(&hdev->disc_le_timer); + } } + + if (hdev) + hci_dev_put(hdev); } -done: - hci_dev_put(hdev); return 0; } @@ -3077,42 +2922,3 @@ int mgmt_encrypt_change(u16 index, bdaddr_t *bdaddr, u8 status) NULL); } -int mgmt_remote_class(u16 index, bdaddr_t *bdaddr, u8 dev_class[3]) -{ - struct mgmt_ev_remote_class ev; - - memset(&ev, 0, sizeof(ev)); - - bacpy(&ev.bdaddr, bdaddr); - memcpy(ev.dev_class, dev_class, 3); - - return mgmt_event(MGMT_EV_REMOTE_CLASS, index, &ev, sizeof(ev), NULL); -} - -int mgmt_remote_version(u16 index, bdaddr_t *bdaddr, u8 ver, u16 mnf, - u16 sub_ver) -{ - struct mgmt_ev_remote_version ev; - - memset(&ev, 0, sizeof(ev)); - - bacpy(&ev.bdaddr, bdaddr); - ev.lmp_ver = ver; - ev.manufacturer = mnf; - ev.lmp_subver = sub_ver; - - return mgmt_event(MGMT_EV_REMOTE_VERSION, index, &ev, sizeof(ev), NULL); -} - -int mgmt_remote_features(u16 index, bdaddr_t *bdaddr, u8 features[8]) -{ - struct mgmt_ev_remote_features ev; - - memset(&ev, 0, sizeof(ev)); - - bacpy(&ev.bdaddr, bdaddr); - memcpy(ev.features, features, sizeof(ev.features)); - - return mgmt_event(MGMT_EV_REMOTE_FEATURES, index, &ev, sizeof(ev), - NULL); -} diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c old mode 100644 new mode 100755 index 66cc1f0c..459fc0f9 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -544,6 +544,7 @@ static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int * BT_DBG("sock %p, sk %p", sock, sk); + memset(sa, 0, sizeof(*sa)); sa->rc_family = AF_BLUETOOTH; sa->rc_channel = rfcomm_pi(sk)->channel; if (peer) @@ -832,6 +833,7 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c } sec.level = rfcomm_pi(sk)->sec_level; + sec.key_size = 0; len = min_t(unsigned int, len, sizeof(sec)); if (copy_to_user(optval, (char *) &sec, len)) diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index e18781ce..435af03a 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -472,7 +472,7 @@ static int rfcomm_get_dev_list(void __user *arg) size = sizeof(*dl) + dev_num * sizeof(*di); - dl = kmalloc(size, GFP_KERNEL); + dl = kzalloc(size, GFP_KERNEL); if (!dl) return -ENOMEM; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 5a4303d2..7990b4cd 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -1,7 +1,7 @@ /* BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Technologies, Inc. - Copyright (c) 2011, The Linux Foundation. All rights reserved. + Copyright (C) 2000-2001 Qualcomm Incorporated + Copyright (c) 2011, Code Aurora Forum. All rights reserved. Written 2000,2001 by Maxim Krasnyansky @@ -24,6 +24,7 @@ */ /* Bluetooth SCO sockets. */ +// rollback to original BlueZ #include #include @@ -63,7 +64,7 @@ static struct bt_sock_list sco_sk_list = { static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent); static void sco_chan_del(struct sock *sk, int err); -static int sco_conn_del(struct hci_conn *conn, int err, u8 is_process); +static int sco_conn_del(struct hci_conn *conn, int err); static void sco_sock_close(struct sock *sk); static void sco_sock_kill(struct sock *sk); @@ -136,7 +137,7 @@ static inline struct sock *sco_chan_get(struct sco_conn *conn) return sk; } -static int sco_conn_del(struct hci_conn *hcon, int err, u8 is_process) +static int sco_conn_del(struct hci_conn *hcon, int err) { struct sco_conn *conn = hcon->sco_data; struct sock *sk; @@ -149,16 +150,10 @@ static int sco_conn_del(struct hci_conn *hcon, int err, u8 is_process) /* Kill socket */ sk = sco_chan_get(conn); if (sk) { - if (is_process) - lock_sock(sk); - else - bh_lock_sock(sk); + bh_lock_sock(sk); sco_sock_clear_timer(sk); sco_chan_del(sk, err); - if (is_process) - release_sock(sk); - else - bh_unlock_sock(sk); + bh_unlock_sock(sk); sco_sock_kill(sk); } @@ -403,6 +398,7 @@ static void __sco_sock_close(struct sock *sk) sco_chan_del(sk, ECONNRESET); break; + case BT_CONNECT2: case BT_CONNECT: case BT_DISCONN: sco_chan_del(sk, ECONNRESET); @@ -935,7 +931,7 @@ static int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type) int lm = 0; if (type != SCO_LINK && type != ESCO_LINK) - return 0; + return -EINVAL; BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); @@ -970,19 +966,19 @@ static int sco_connect_cfm(struct hci_conn *hcon, __u8 status) if (conn) sco_conn_ready(conn); } else - sco_conn_del(hcon, bt_err(status), 0); + sco_conn_del(hcon, bt_err(status)); return 0; } -static int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason, __u8 is_process) +static int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK) return -EINVAL; - sco_conn_del(hcon, bt_err(reason), is_process); + sco_conn_del(hcon, bt_err(reason)); return 0; } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 276ff715..f881b5b6 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -19,6 +19,7 @@ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ +// rollback to original BlueZ #include #include @@ -655,7 +656,6 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) static int smp_encrypt_link(struct hci_conn *hcon, struct link_key *key) { struct key_master_id *master; - u8 sec_level; u8 zerobuf[8]; if (!hcon || !key || !key->data) @@ -670,17 +670,6 @@ static int smp_encrypt_link(struct hci_conn *hcon, struct link_key *key) hcon->enc_key_size = key->pin_len; hcon->sec_req = TRUE; - sec_level = authreq_to_seclevel(key->auth); - - BT_DBG("cur %d, req: %d", hcon->sec_level, sec_level); - - if (sec_level > hcon->sec_level) - hcon->pending_sec_level = sec_level; - - - if (!(hcon->link_mode & HCI_LM_ENCRYPT)) - hci_conn_hold(hcon); - hci_le_start_enc(hcon, master->ediv, master->rand, key->val); return 0; @@ -705,6 +694,11 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) if (smp_encrypt_link(hcon, key) < 0) goto invalid_key; + hcon->sec_level = authreq_to_seclevel(key->auth); + + if (!(hcon->link_mode & HCI_LM_ENCRYPT)) + hci_conn_hold(hcon); + return 0; } @@ -716,7 +710,6 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) memset(&cp, 0, sizeof(cp)); build_pairing_cmd(conn, &cp, NULL, rp->auth_req); - hcon->pending_sec_level = authreq_to_seclevel(rp->auth_req); hcon->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&hcon->preq[1], &cp, sizeof(cp)); @@ -736,26 +729,33 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) struct hci_conn *hcon = conn->hcon; __u8 authreq; - BT_DBG("conn %p hcon %p %d req: %d", - conn, hcon, hcon->sec_level, sec_level); + BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level); - if (IS_ERR(hcon->hdev->tfm)) + if (IS_ERR(hcon->hdev->tfm)) { + BT_DBG("IS_ERR"); return 1; + } - if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) + if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) { + BT_DBG("HCI_CONN_ENCRYPT_PEND"); return -EINPROGRESS; + } - if (sec_level == BT_SECURITY_LOW) + if (sec_level == BT_SECURITY_LOW) { + BT_DBG("BT_SECURITY_LOW"); return 1; + } - - if (hcon->sec_level >= sec_level) + if (hcon->sec_level > sec_level) { + BT_DBG("hcon->sec_level > sec_level"); return 1; + } authreq = seclevel_to_authreq(sec_level); + BT_ERR("conn = %p, sec: %d", conn, sec_level); hcon->smp_conn = conn; - hcon->pending_sec_level = sec_level; + hcon->sec_level = sec_level; if ((hcon->link_mode & HCI_LM_MASTER) && !hcon->sec_req) { struct link_key *key; @@ -780,7 +780,6 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) msecs_to_jiffies(SMP_TIMEOUT)); smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); - hci_conn_hold(hcon); } else { struct smp_cmd_security_req cp; cp.auth_req = authreq; @@ -788,6 +787,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) } done: + hcon->pending_sec_level = sec_level; set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend); return 0; @@ -1040,17 +1040,15 @@ int smp_link_encrypt_cmplt(struct l2cap_conn *conn, u8 status, u8 encrypt) clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend); - if (!status && encrypt && hcon->sec_level < hcon->pending_sec_level) - hcon->sec_level = hcon->pending_sec_level; - if (!status && encrypt && !hcon->sec_req) - return smp_distribute_keys(conn, 0); + smp_distribute_keys(conn, 0); /* Fall back to Pairing request if failed a Link Security request */ else if (hcon->sec_req && (status || !encrypt)) - smp_conn_security(conn, hcon->pending_sec_level); + smp_conn_security(conn, hcon->sec_level); - hci_conn_put(hcon); + else + hci_conn_put(hcon); return 0; } diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 6af54f2d..02208f49 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -17,6 +17,12 @@ #include #include +#define INVALID_UID ((uid_t) -1) +#define uid_valid(uid) ((uid) != -1) +#define uid_lte(a, b) ((a) <= (b)) +#define uid_eq(a, b) ((a) == (b)) +#define uid_gte(a, b) ((a) >= (b)) + int fib_default_rule_add(struct fib_rules_ops *ops, u32 pref, u32 table, u32 flags) { @@ -31,6 +37,8 @@ int fib_default_rule_add(struct fib_rules_ops *ops, r->pref = pref; r->table = table; r->flags = flags; + r->uid_start = INVALID_UID; + r->uid_end = INVALID_UID; r->fr_net = hold_net(ops->fro_net); /* The lock is not required here, the list in unreacheable @@ -177,6 +185,23 @@ void fib_rules_unregister(struct fib_rules_ops *ops) } EXPORT_SYMBOL_GPL(fib_rules_unregister); +static inline uid_t fib_nl_uid(struct nlattr *nla) +{ + return nla_get_u32(nla); +} + +static int nla_put_uid(struct sk_buff *skb, int idx, uid_t uid) +{ + return nla_put_u32(skb, idx, uid); +} + +static int fib_uid_range_match(struct flowi *fl, struct fib_rule *rule) +{ + return (!uid_valid(rule->uid_start) && !uid_valid(rule->uid_end)) || + (uid_gte(fl->flowi_uid, rule->uid_start) && + uid_lte(fl->flowi_uid, rule->uid_end)); +} + static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, struct flowi *fl, int flags) { @@ -191,6 +216,9 @@ static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, if ((rule->mark ^ fl->flowi_mark) & rule->mark_mask) goto out; + if (!fib_uid_range_match(fl, rule)) + goto out; + ret = ops->match(rule, fl, flags); out: return (rule->flags & FIB_RULE_INVERT) ? !ret : ret; @@ -361,6 +389,19 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) } else if (rule->action == FR_ACT_GOTO) goto errout_free; + /* UID start and end must either both be valid or both unspecified. */ + rule->uid_start = rule->uid_end = INVALID_UID; + if (tb[FRA_UID_START] || tb[FRA_UID_END]) { + if (tb[FRA_UID_START] && tb[FRA_UID_END]) { + rule->uid_start = fib_nl_uid(tb[FRA_UID_START]); + rule->uid_end = fib_nl_uid(tb[FRA_UID_END]); + } + if (!uid_valid(rule->uid_start) || + !uid_valid(rule->uid_end) || + !uid_lte(rule->uid_start, rule->uid_end)) + goto errout_free; + } + err = ops->configure(rule, skb, frh, tb); if (err < 0) goto errout_free; @@ -467,6 +508,14 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) (rule->mark_mask != nla_get_u32(tb[FRA_FWMASK]))) continue; + if (tb[FRA_UID_START] && + !uid_eq(rule->uid_start, fib_nl_uid(tb[FRA_UID_START]))) + continue; + + if (tb[FRA_UID_END] && + !uid_eq(rule->uid_end, fib_nl_uid(tb[FRA_UID_END]))) + continue; + if (!ops->compare(rule, frh, tb)) continue; @@ -521,7 +570,9 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, + nla_total_size(4) /* FRA_PRIORITY */ + nla_total_size(4) /* FRA_TABLE */ + nla_total_size(4) /* FRA_FWMARK */ - + nla_total_size(4); /* FRA_FWMASK */ + + nla_total_size(4) /* FRA_FWMASK */ + + nla_total_size(4) /* FRA_UID_START */ + + nla_total_size(4); /* FRA_UID_END */ if (ops->nlmsg_payload) payload += ops->nlmsg_payload(rule); @@ -579,6 +630,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, if (rule->target) NLA_PUT_U32(skb, FRA_GOTO, rule->target); + if (uid_valid(rule->uid_start)) + nla_put_uid(skb, FRA_UID_START, rule->uid_start); + + if (uid_valid(rule->uid_end)) + nla_put_uid(skb, FRA_UID_END, rule->uid_end); + if (ops->fill(rule, skb, frh) < 0) goto nla_put_failure; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index cbe3a685..0a24199f 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -481,6 +481,7 @@ const struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = { [RTA_METRICS] = { .type = NLA_NESTED }, [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, [RTA_FLOW] = { .type = NLA_U32 }, + [RTA_UID] = { .type = NLA_U32 }, }; static int rtm_to_fib_config(struct net *net, struct sk_buff *skb, diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 3f404325..c4784c32 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -365,7 +365,8 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk), (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr, - ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); + ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport, + sock_i_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) @@ -398,7 +399,8 @@ struct dst_entry *inet_csk_route_child_sock(struct sock *sk, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk), (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr, - ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); + ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport, + sock_i_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 330be870..1a939d8b 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1504,7 +1504,8 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, __be32 daddr, RT_SCOPE_UNIVERSE, sk->sk_protocol, ip_reply_arg_flowi_flags(arg), daddr, rt->rt_spec_dst, - tcp_hdr(skb)->source, tcp_hdr(skb)->dest); + tcp_hdr(skb)->source, tcp_hdr(skb)->dest, + arg->uid); security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(sock_net(sk), &fl4); if (IS_ERR(rt)) diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 9f471c32..5e788f0f 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -568,7 +568,8 @@ static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, - inet_sk_flowi_flags(sk), faddr, saddr, 0, 0); + inet_sk_flowi_flags(sk), faddr, saddr, 0, 0, + sock_i_uid(sk)); security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); rt = ip_route_output_flow(net, &fl4, sk); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index c1b3970c..a8f60180 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -568,8 +568,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, - inet_sk_flowi_flags(sk) | FLOWI_FLAG_CAN_SLEEP, - daddr, saddr, 0, 0); + FLOWI_FLAG_CAN_SLEEP, daddr, saddr, 0, 0, + sock_i_uid(sk)); if (!inet->hdrincl) { err = raw_probe_proto_opt(&fl4, msg); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 6c34bc98..5c774c14 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -743,6 +743,7 @@ static inline int compare_keys(struct rtable *rt1, struct rtable *rt2) (rt1->rt_mark ^ rt2->rt_mark) | (rt1->rt_key_tos ^ rt2->rt_key_tos) | (rt1->rt_route_iif ^ rt2->rt_route_iif) | + (rt1->rt_uid ^ rt2->rt_uid) | (rt1->rt_oif ^ rt2->rt_oif)) == 0; } @@ -1913,6 +1914,7 @@ void ip_rt_get_source(u8 *addr, struct sk_buff *skb, struct rtable *rt) fl4.flowi4_oif = rt->dst.dev->ifindex; fl4.flowi4_iif = skb->dev->ifindex; fl4.flowi4_mark = skb->mark; + fl4.flowi4_uid = skb->sk ? sock_i_uid(skb->sk) : 0; rcu_read_lock(); if (fib_lookup(dev_net(rt->dst.dev), &fl4, &res) == 0) @@ -2096,6 +2098,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, rth->rt_iif = dev->ifindex; rth->rt_oif = 0; rth->rt_mark = skb->mark; + rth->rt_uid = 0; rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; rth->rt_peer_genid = 0; @@ -2225,6 +2228,7 @@ static int __mkroute_input(struct sk_buff *skb, rth->rt_iif = in_dev->dev->ifindex; rth->rt_oif = 0; rth->rt_mark = skb->mark; + rth->rt_uid = 0; rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; rth->rt_peer_genid = 0; @@ -2408,6 +2412,7 @@ out: return err; rth->rt_iif = dev->ifindex; rth->rt_oif = 0; rth->rt_mark = skb->mark; + rth->rt_uid = 0; rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; rth->rt_peer_genid = 0; @@ -2612,6 +2617,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res, rth->rt_iif = orig_oif ? : dev_out->ifindex; rth->rt_oif = orig_oif; rth->rt_mark = fl4->flowi4_mark; + rth->rt_uid = fl4->flowi4_uid; rth->rt_gateway = fl4->daddr; rth->rt_spec_dst= fl4->saddr; rth->rt_peer_genid = 0; @@ -2863,6 +2869,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *flp4) rt_is_output_route(rth) && rth->rt_oif == flp4->flowi4_oif && rth->rt_mark == flp4->flowi4_mark && + rth->rt_uid == flp4->flowi4_uid && !((rth->rt_key_tos ^ flp4->flowi4_tos) & (IPTOS_RT_MASK | RTO_ONLINK)) && net_eq(dev_net(rth->dst.dev), net) && @@ -2944,6 +2951,7 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or rt->rt_iif = ort->rt_iif; rt->rt_oif = ort->rt_oif; rt->rt_mark = ort->rt_mark; + rt->rt_uid = ort->rt_uid; rt->rt_genid = rt_genid(net); rt->rt_flags = ort->rt_flags; @@ -3039,6 +3047,9 @@ static int rt_fill_info(struct net *net, if (rt->rt_mark) NLA_PUT_BE32(skb, RTA_MARK, rt->rt_mark); + if (rt->rt_uid != (uid_t) -1) + NLA_PUT_BE32(skb, RTA_UID, rt->rt_uid); + error = rt->dst.error; if (peer) { inet_peer_refcheck(rt->peer); @@ -3157,6 +3168,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void .flowi4_tos = rtm->rtm_tos, .flowi4_oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0, .flowi4_mark = mark, + .flowi4_uid = tb[RTA_UID] ? nla_get_u32(tb[RTA_UID]) : current_uid(), }; rt = ip_route_output_key(net, &fl4); diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 550aa2a9..50fc86fc 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -351,7 +351,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP, inet_sk_flowi_flags(sk), (opt && opt->srr) ? opt->faddr : ireq->rmt_addr, - ireq->loc_addr, th->source, th->dest); + ireq->loc_addr, th->source, th->dest, + sock_i_uid(sk)) security_req_classify_flow(req, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(sock_net(sk), &fl4); if (IS_ERR(rt)) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 5f8c20b6..cf5f672b 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -932,7 +932,8 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk)|FLOWI_FLAG_CAN_SLEEP, - faddr, saddr, dport, inet->inet_sport); + faddr, saddr, dport, inet->inet_sport, + sock_i_uid(sk)); security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index a0b4c5da..e8ee4279 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -86,6 +86,7 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, xdst->u.rt.rt_iif = fl4->flowi4_iif; xdst->u.rt.rt_oif = fl4->flowi4_oif; xdst->u.rt.rt_mark = fl4->flowi4_mark; + xdst->u.rt.rt_uid = fl4->flowi4_uid; xdst->u.dst.dev = dev; dev_hold(dev); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 29625e9a..1f7ab56f 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -701,6 +701,7 @@ int inet6_sk_rebuild_header(struct sock *sk) fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); final_p = fl6_update_dst(&fl6, np->opt, &final); diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 94e5258f..a21a2ecb 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -160,6 +160,7 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); if (!fl6.flowi6_oif && (addr_type&IPV6_ADDR_MULTICAST)) fl6.flowi6_oif = np->mcast_oif; diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 02dd203d..21ee0027 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -72,6 +72,7 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet_rsk(req)->rmt_port; fl6.fl6_sport = inet_rsk(req)->loc_port; + fl6.flowi6_uid = sock_i_uid(sk); security_req_classify_flow(req, flowi6_to_flowi(&fl6)); dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false); @@ -223,6 +224,7 @@ int inet6_csk_xmit(struct sk_buff *skb, struct flowi *fl_unused) fl6.flowi6_mark = sk->sk_mark; fl6.fl6_sport = inet->inet_sport; fl6.fl6_dport = inet->inet_dport; + fl6.flowi6_uid = sock_i_uid(sk); security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); final_p = fl6_update_dst(&fl6, np->opt, &final); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index d6820d01..cf32b834 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -758,6 +758,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sock_i_uid(sk); if (sin6) { if (addr_len < SIN6_LEN_RFC2133) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 23b33048..cb1143d2 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2298,6 +2298,7 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { [RTA_IIF] = { .type = NLA_U32 }, [RTA_PRIORITY] = { .type = NLA_U32 }, [RTA_METRICS] = { .type = NLA_NESTED }, + [RTA_UID] = { .type = NLA_U32 }, }; static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -2610,6 +2611,11 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void if (tb[RTA_OIF]) oif = nla_get_u32(tb[RTA_OIF]); + if (tb[RTA_UID]) + fl6.flowi6_uid = nla_get_u32(tb[RTA_UID]); + else + fl6.flowi6_uid = (iif ? (uid_t) -1 : current_uid()); + if (iif) { struct net_device *dev; int flags = 0; diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 8e951d8d..83ee321c 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -244,6 +244,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet_rsk(req)->rmt_port; fl6.fl6_sport = inet_sk(sk)->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); security_req_classify_flow(req, flowi6_to_flowi(&fl6)); dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 8dd4210a..8c756628 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -251,6 +251,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = usin->sin6_port; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); final_p = fl6_update_dst(&fl6, np->opt, &final); @@ -404,6 +405,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); dst = ip6_dst_lookup_flow(sk, &fl6, NULL, false); @@ -496,6 +498,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req, fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet_rsk(req)->rmt_port; fl6.fl6_sport = inet_rsk(req)->loc_port; + fl6.flowi6_uid = sock_i_uid(sk); security_req_classify_flow(req, flowi6_to_flowi(&fl6)); opt = np->opt; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 2f99b12b..dedfd528 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1087,6 +1087,7 @@ int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sock_i_uid(sk); if (msg->msg_controllen) { opt = &opt_space; diff --git a/net/wireless_ath/Makefile b/net/wireless_ath/Makefile new file mode 100644 index 00000000..2af9470d --- /dev/null +++ b/net/wireless_ath/Makefile @@ -0,0 +1,23 @@ +NOSTDINC_FLAGS := -I$(srctree)/include/compat/ \ + -include $(srctree)/include/compat/linux/compat-2.6.h \ + $(CFLAGS) + +obj-$(CONFIG_CFG80211) += cfg80211.o + +obj-$(CONFIG_WEXT_CORE) += wext-core.o +obj-$(CONFIG_WEXT_PROC) += wext-proc.o +obj-$(CONFIG_WEXT_SPY) += wext-spy.o +obj-$(CONFIG_WEXT_PRIV) += wext-priv.o + +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o +cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o +cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o +cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o +cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o + +ccflags-y += -D__CHECK_ENDIAN__ + +$(obj)/regdb.c: $(srctree)/$(src)/db.txt $(srctree)/$(src)/genregdb.awk + @$(AWK) -f $(srctree)/$(src)/genregdb.awk < $< > $@ + +clean-files := regdb.c \ No newline at end of file diff --git a/net/wireless_ath/Makefile.bk b/net/wireless_ath/Makefile.bk new file mode 100644 index 00000000..9b12455b --- /dev/null +++ b/net/wireless_ath/Makefile.bk @@ -0,0 +1,18 @@ +obj-$(CONFIG_CFG80211) += cfg80211.o +obj-$(CONFIG_LIB80211) += lib80211.o +obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o +obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o +obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o + +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o +cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o +cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o +cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o +cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o + +ccflags-y += -D__CHECK_ENDIAN__ + +$(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk + @$(AWK) -f $(src)/genregdb.awk < $< > $@ + +clean-files := regdb.c diff --git a/net/wireless_ath/chan.c b/net/wireless_ath/chan.c new file mode 100644 index 00000000..17cd0c04 --- /dev/null +++ b/net/wireless_ath/chan.c @@ -0,0 +1,135 @@ +/* + * This file contains helper code to handle channel + * settings and keeping track of what is possible at + * any point in time. + * + * Copyright 2009 Johannes Berg + */ + +#include +#include "core.h" + +struct ieee80211_channel * +rdev_freq_to_chan(struct cfg80211_registered_device *rdev, + int freq, enum nl80211_channel_type channel_type) +{ + struct ieee80211_channel *chan; + struct ieee80211_sta_ht_cap *ht_cap; + + chan = ieee80211_get_channel(&rdev->wiphy, freq); + + /* Primary channel not allowed */ + if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) + return NULL; + + if (channel_type == NL80211_CHAN_HT40MINUS && + chan->flags & IEEE80211_CHAN_NO_HT40MINUS) + return NULL; + else if (channel_type == NL80211_CHAN_HT40PLUS && + chan->flags & IEEE80211_CHAN_NO_HT40PLUS) + return NULL; + + ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; + + if (channel_type != NL80211_CHAN_NO_HT) { + if (!ht_cap->ht_supported) + return NULL; + + if (channel_type != NL80211_CHAN_HT20 && + (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || + ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) + return NULL; + } + + return chan; +} + +static bool can_beacon_sec_chan(struct wiphy *wiphy, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + struct ieee80211_channel *sec_chan; + int diff; + + switch (channel_type) { + case NL80211_CHAN_HT40PLUS: + diff = 20; + break; + case NL80211_CHAN_HT40MINUS: + diff = -20; + break; + default: + return false; + } + + sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff); + if (!sec_chan) + return false; + + /* we'll need a DFS capability later */ + if (sec_chan->flags & (IEEE80211_CHAN_DISABLED | + IEEE80211_CHAN_PASSIVE_SCAN | + IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_RADAR)) + return false; + + return true; +} + +int cfg80211_set_freq(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, int freq, + enum nl80211_channel_type channel_type) +{ + struct ieee80211_channel *chan; + int result; + + if (wdev && wdev->iftype == NL80211_IFTYPE_MONITOR) + wdev = NULL; + + if (wdev) { + ASSERT_WDEV_LOCK(wdev); + + if (!netif_running(wdev->netdev)) + return -ENETDOWN; + } + + if (!rdev->ops->set_channel) + return -EOPNOTSUPP; + + chan = rdev_freq_to_chan(rdev, freq, channel_type); + if (!chan) + return -EINVAL; + + /* Both channels should be able to initiate communication */ + if (wdev && (wdev->iftype == NL80211_IFTYPE_ADHOC || + wdev->iftype == NL80211_IFTYPE_AP || + wdev->iftype == NL80211_IFTYPE_AP_VLAN || + wdev->iftype == NL80211_IFTYPE_MESH_POINT || + wdev->iftype == NL80211_IFTYPE_P2P_GO)) { + switch (channel_type) { + case NL80211_CHAN_HT40PLUS: + case NL80211_CHAN_HT40MINUS: + if (!can_beacon_sec_chan(&rdev->wiphy, chan, + channel_type)) { + printk(KERN_DEBUG + "cfg80211: Secondary channel not " + "allowed to initiate communication\n"); + return -EINVAL; + } + break; + default: + break; + } + } + + result = rdev->ops->set_channel(&rdev->wiphy, + wdev ? wdev->netdev : NULL, + chan, channel_type); + if (result) + return result; + + if (wdev) + wdev->channel = chan; + + return 0; +} diff --git a/net/wireless_ath/core.c b/net/wireless_ath/core.c new file mode 100644 index 00000000..4e843bbd --- /dev/null +++ b/net/wireless_ath/core.c @@ -0,0 +1,1104 @@ +/* + * This is the linux wireless configuration interface. + * + * Copyright 2006-2010 Johannes Berg + */ + +//#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nl80211.h" +#include "core.h" +#include "sysfs.h" +#include "debugfs.h" +#include "wext-compat.h" +#include "ethtool.h" + +/* name for sysfs, %d is appended */ +#define PHY_NAME "phy" + +MODULE_AUTHOR("Johannes Berg"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("wireless configuration support"); + +/* RCU-protected (and cfg80211_mutex for writers) */ +LIST_HEAD(cfg80211_rdev_list); +int cfg80211_rdev_list_generation; + +DEFINE_MUTEX(cfg80211_mutex); + +/* for debugfs */ +static struct dentry *ieee80211_debugfs_dir; + +/* for the cleanup, scan and event works */ +struct workqueue_struct *cfg80211_wq; + +static bool cfg80211_disable_40mhz_24ghz; +module_param(cfg80211_disable_40mhz_24ghz, bool, 0644); +MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz, + "Disable 40MHz support in the 2.4GHz band"); + +/* requires cfg80211_mutex to be held! */ +struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) +{ + struct cfg80211_registered_device *result = NULL, *rdev; + + if (!wiphy_idx_valid(wiphy_idx)) + return NULL; + + assert_cfg80211_lock(); + + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (rdev->wiphy_idx == wiphy_idx) { + result = rdev; + break; + } + } + + return result; +} + +int get_wiphy_idx(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *rdev; + if (!wiphy) + return WIPHY_IDX_STALE; + rdev = wiphy_to_dev(wiphy); + return rdev->wiphy_idx; +} + +/* requires cfg80211_rdev_mutex to be held! */ +struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) +{ + struct cfg80211_registered_device *rdev; + + if (!wiphy_idx_valid(wiphy_idx)) + return NULL; + + assert_cfg80211_lock(); + + rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); + if (!rdev) + return NULL; + return &rdev->wiphy; +} + +/* requires cfg80211_mutex to be held! */ +struct cfg80211_registered_device * +__cfg80211_rdev_from_info(struct genl_info *info) +{ + int ifindex; + struct cfg80211_registered_device *bywiphyidx = NULL, *byifidx = NULL; + struct net_device *dev; + int err = -EINVAL; + + assert_cfg80211_lock(); + + if (info->attrs[NL80211_ATTR_WIPHY]) { + bywiphyidx = cfg80211_rdev_by_wiphy_idx( + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY])); + err = -ENODEV; + } + + if (info->attrs[NL80211_ATTR_IFINDEX]) { + ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); + dev = dev_get_by_index(genl_info_net(info), ifindex); + if (dev) { + if (dev->ieee80211_ptr) + byifidx = + wiphy_to_dev(dev->ieee80211_ptr->wiphy); + dev_put(dev); + } + err = -ENODEV; + } + + if (bywiphyidx && byifidx) { + if (bywiphyidx != byifidx) + return ERR_PTR(-EINVAL); + else + return bywiphyidx; /* == byifidx */ + } + if (bywiphyidx) + return bywiphyidx; + + if (byifidx) + return byifidx; + + return ERR_PTR(err); +} + +struct cfg80211_registered_device * +cfg80211_get_dev_from_info(struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + + mutex_lock(&cfg80211_mutex); + rdev = __cfg80211_rdev_from_info(info); + + /* if it is not an error we grab the lock on + * it to assure it won't be going away while + * we operate on it */ + if (!IS_ERR(rdev)) + mutex_lock(&rdev->mtx); + + mutex_unlock(&cfg80211_mutex); + + return rdev; +} + +struct cfg80211_registered_device * +cfg80211_get_dev_from_ifindex(struct net *net, int ifindex) +{ + struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV); + struct net_device *dev; + + mutex_lock(&cfg80211_mutex); + dev = dev_get_by_index(net, ifindex); + if (!dev) + goto out; + if (dev->ieee80211_ptr) { + rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); + mutex_lock(&rdev->mtx); + } else + rdev = ERR_PTR(-ENODEV); + dev_put(dev); + out: + mutex_unlock(&cfg80211_mutex); + return rdev; +} + +/* requires cfg80211_mutex to be held */ +int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, + char *newname) +{ + struct cfg80211_registered_device *rdev2; + int wiphy_idx, taken = -1, result, digits; + + assert_cfg80211_lock(); + + /* prohibit calling the thing phy%d when %d is not its number */ + sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken); + if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) { + /* count number of places needed to print wiphy_idx */ + digits = 1; + while (wiphy_idx /= 10) + digits++; + /* + * deny the name if it is phy where is printed + * without leading zeroes. taken == strlen(newname) here + */ + if (taken == strlen(PHY_NAME) + digits) + return -EINVAL; + } + + + /* Ignore nop renames */ + if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0) + return 0; + + /* Ensure another device does not already have this name. */ + list_for_each_entry(rdev2, &cfg80211_rdev_list, list) + if (strcmp(newname, dev_name(&rdev2->wiphy.dev)) == 0) + return -EINVAL; + + result = device_rename(&rdev->wiphy.dev, newname); + if (result) + return result; + + if (rdev->wiphy.debugfsdir && + !debugfs_rename(rdev->wiphy.debugfsdir->d_parent, + rdev->wiphy.debugfsdir, + rdev->wiphy.debugfsdir->d_parent, + newname)) + pr_err("failed to rename debugfs dir to %s!\n", newname); + + nl80211_notify_dev_rename(rdev); + + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) +int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, + struct net *net) +{ + struct wireless_dev *wdev; + int err = 0; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK)) + return -EOPNOTSUPP; + + list_for_each_entry(wdev, &rdev->netdev_list, list) { + wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; + err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); + if (err) + break; + wdev->netdev->features |= NETIF_F_NETNS_LOCAL; + } + + if (err) { + /* failed -- clean up to old netns */ + net = wiphy_net(&rdev->wiphy); + + list_for_each_entry_continue_reverse(wdev, &rdev->netdev_list, + list) { + wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; + err = dev_change_net_namespace(wdev->netdev, net, + "wlan%d"); + WARN_ON(err); + wdev->netdev->features |= NETIF_F_NETNS_LOCAL; + } + + return err; + } + + wiphy_net_set(&rdev->wiphy, net); + + err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev)); + WARN_ON(err); + + return 0; +} +#endif + +static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) +{ + struct cfg80211_registered_device *rdev = data; + + rdev->ops->rfkill_poll(&rdev->wiphy); +} + +static int cfg80211_rfkill_set_block(void *data, bool blocked) +{ + struct cfg80211_registered_device *rdev = data; + struct wireless_dev *wdev; + + if (!blocked) + return 0; + + rtnl_lock(); + mutex_lock(&rdev->devlist_mtx); + + list_for_each_entry(wdev, &rdev->netdev_list, list) + dev_close(wdev->netdev); + + mutex_unlock(&rdev->devlist_mtx); + rtnl_unlock(); + + return 0; +} + +static void cfg80211_rfkill_sync_work(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(work, struct cfg80211_registered_device, rfkill_sync); + cfg80211_rfkill_set_block(rdev, rfkill_blocked(rdev->rfkill)); +} + +static void cfg80211_event_work(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(work, struct cfg80211_registered_device, + event_work); + + rtnl_lock(); + cfg80211_lock_rdev(rdev); + + cfg80211_process_rdev_events(rdev); + cfg80211_unlock_rdev(rdev); + rtnl_unlock(); +} + +/* exported functions */ + +struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) +{ + static int wiphy_counter; + + struct cfg80211_registered_device *rdev; + int alloc_size; + + /* + * Make sure the padding is >= the rest of the struct so that we + * always keep it large enough to pad out the entire original + * kernel's struct. We really only need to make sure it's larger + * than the kernel compat is compiled against, but since it'll + * only increase in size make sure it's larger than the current + * version of it. Subtract since it's included. + */ + BUILD_BUG_ON(WIPHY_COMPAT_PAD_SIZE < + sizeof(struct wiphy) - WIPHY_COMPAT_PAD_SIZE); + + WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key)); + WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc)); + WARN_ON(ops->connect && !ops->disconnect); + WARN_ON(ops->join_ibss && !ops->leave_ibss); + WARN_ON(ops->add_virtual_intf && !ops->del_virtual_intf); + WARN_ON(ops->add_station && !ops->del_station); + WARN_ON(ops->add_mpath && !ops->del_mpath); + WARN_ON(ops->join_mesh && !ops->leave_mesh); + + alloc_size = sizeof(*rdev) + sizeof_priv; + + rdev = kzalloc(alloc_size, GFP_KERNEL); + if (!rdev) + return NULL; + + rdev->ops = ops; + + mutex_lock(&cfg80211_mutex); + + rdev->wiphy_idx = wiphy_counter++; + + if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) { + wiphy_counter--; + mutex_unlock(&cfg80211_mutex); + /* ugh, wrapped! */ + kfree(rdev); + return NULL; + } + + mutex_unlock(&cfg80211_mutex); + + /* give it a proper name */ + dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); + + mutex_init(&rdev->mtx); + mutex_init(&rdev->devlist_mtx); + mutex_init(&rdev->sched_scan_mtx); + INIT_LIST_HEAD(&rdev->netdev_list); + spin_lock_init(&rdev->bss_lock); + INIT_LIST_HEAD(&rdev->bss_list); + INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); + INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); + device_initialize(&rdev->wiphy.dev); + rdev->wiphy.dev.class = &ieee80211_class; + rdev->wiphy.dev.platform_data = rdev; + +#ifdef CONFIG_CFG80211_DEFAULT_PS + rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + wiphy_net_set(&rdev->wiphy, &init_net); +#endif + + rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; + rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), + &rdev->wiphy.dev, RFKILL_TYPE_WLAN, + &rdev->rfkill_ops, rdev); + + if (!rdev->rfkill) { + kfree(rdev); + return NULL; + } + + INIT_WORK(&rdev->rfkill_sync, cfg80211_rfkill_sync_work); + INIT_WORK(&rdev->conn_work, cfg80211_conn_work); + INIT_WORK(&rdev->event_work, cfg80211_event_work); + + init_waitqueue_head(&rdev->dev_wait); + + /* + * Initialize wiphy parameters to IEEE 802.11 MIB default values. + * Fragmentation and RTS threshold are disabled by default with the + * special -1 value. + */ + rdev->wiphy.retry_short = 7; + rdev->wiphy.retry_long = 4; + rdev->wiphy.frag_threshold = (u32) -1; + rdev->wiphy.rts_threshold = (u32) -1; + rdev->wiphy.coverage_class = 0; + + return &rdev->wiphy; +} +EXPORT_SYMBOL(wiphy_new); + +static int wiphy_verify_combinations(struct wiphy *wiphy) +{ + const struct ieee80211_iface_combination *c; + int i, j; + + /* If we have combinations enforce them */ + if (wiphy->n_iface_combinations) + wiphy->flags |= WIPHY_FLAG_ENFORCE_COMBINATIONS; + + for (i = 0; i < wiphy->n_iface_combinations; i++) { + u32 cnt = 0; + u16 all_iftypes = 0; + + c = &wiphy->iface_combinations[i]; + + /* Combinations with just one interface aren't real */ + if (WARN_ON(c->max_interfaces < 2)) + return -EINVAL; + + /* Need at least one channel */ + if (WARN_ON(!c->num_different_channels)) + return -EINVAL; + + if (WARN_ON(!c->n_limits)) + return -EINVAL; + + for (j = 0; j < c->n_limits; j++) { + u16 types = c->limits[j].types; + + /* + * interface types shouldn't overlap, this is + * used in cfg80211_can_change_interface() + */ + if (WARN_ON(types & all_iftypes)) + return -EINVAL; + all_iftypes |= types; + + if (WARN_ON(!c->limits[j].max)) + return -EINVAL; + + /* Shouldn't list software iftypes in combinations! */ + if (WARN_ON(wiphy->software_iftypes & types)) + return -EINVAL; + + cnt += c->limits[j].max; + /* + * Don't advertise an unsupported type + * in a combination. + */ + if (WARN_ON((wiphy->interface_modes & types) != types)) + return -EINVAL; + } + + /* You can't even choose that many! */ + if (WARN_ON(cnt < c->max_interfaces)) + return -EINVAL; + } + + return 0; +} + +int wiphy_register(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + int res; + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + bool have_band = false; + int i; + u16 ifmodes = wiphy->interface_modes; + + if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && + !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) + return -EINVAL; + + if (WARN_ON(wiphy->ap_sme_capa && + !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) + return -EINVAL; + + if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) + return -EINVAL; + + if (WARN_ON(wiphy->addresses && + !is_zero_ether_addr(wiphy->perm_addr) && + memcmp(wiphy->perm_addr, wiphy->addresses[0].addr, + ETH_ALEN))) + return -EINVAL; + + if (wiphy->addresses) + memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); + + /* sanity check ifmodes */ + WARN_ON(!ifmodes); + ifmodes &= ((1 << NUM_NL80211_IFTYPES) - 1) & ~1; + if (WARN_ON(ifmodes != wiphy->interface_modes)) + wiphy->interface_modes = ifmodes; + + res = wiphy_verify_combinations(wiphy); + if (res) + return res; + + /* sanity check supported bands/channels */ + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + sband = wiphy->bands[band]; + if (!sband) + continue; + + sband->band = band; + + if (WARN_ON(!sband->n_channels || !sband->n_bitrates)) + return -EINVAL; + + /* + * Since cfg80211_disable_40mhz_24ghz is global, we can + * modify the sband's ht data even if the driver uses a + * global structure for that. + */ + if (cfg80211_disable_40mhz_24ghz && + band == IEEE80211_BAND_2GHZ && + sband->ht_cap.ht_supported) { + sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; + } + + /* + * Since we use a u32 for rate bitmaps in + * ieee80211_get_response_rate, we cannot + * have more than 32 legacy rates. + */ + if (WARN_ON(sband->n_bitrates > 32)) + return -EINVAL; + + for (i = 0; i < sband->n_channels; i++) { + sband->channels[i].orig_flags = + sband->channels[i].flags; + sband->channels[i].orig_mag = + sband->channels[i].max_antenna_gain; + sband->channels[i].orig_mpwr = + sband->channels[i].max_power; + sband->channels[i].band = band; + } + + have_band = true; + } + + if (!have_band) { + WARN_ON(1); + return -EINVAL; + } + + if (rdev->wiphy.wowlan.n_patterns) { + if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len || + rdev->wiphy.wowlan.pattern_min_len > + rdev->wiphy.wowlan.pattern_max_len)) + return -EINVAL; + } + + /* check and set up bitrates */ + ieee80211_set_bitrate_flags(wiphy); + + mutex_lock(&cfg80211_mutex); + + res = device_add(&rdev->wiphy.dev); + if (res) { + mutex_unlock(&cfg80211_mutex); + return res; + } + + /* set up regulatory info */ + regulatory_update(wiphy, NL80211_REGDOM_SET_BY_CORE); + + list_add_rcu(&rdev->list, &cfg80211_rdev_list); + cfg80211_rdev_list_generation++; + + /* add to debugfs */ + rdev->wiphy.debugfsdir = + debugfs_create_dir(wiphy_name(&rdev->wiphy), + ieee80211_debugfs_dir); + if (IS_ERR(rdev->wiphy.debugfsdir)) + rdev->wiphy.debugfsdir = NULL; + + if (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { + struct regulatory_request request; + + request.wiphy_idx = get_wiphy_idx(wiphy); + request.initiator = NL80211_REGDOM_SET_BY_DRIVER; + request.alpha2[0] = '9'; + request.alpha2[1] = '9'; + + nl80211_send_reg_change_event(&request); + } + + cfg80211_debugfs_rdev_add(rdev); + mutex_unlock(&cfg80211_mutex); + + /* + * due to a locking dependency this has to be outside of the + * cfg80211_mutex lock + */ + res = rfkill_register(rdev->rfkill); + if (res) + goto out_rm_dev; + + rtnl_lock(); + rdev->wiphy.registered = true; + rtnl_unlock(); + return 0; + +out_rm_dev: + device_del(&rdev->wiphy.dev); + return res; +} +EXPORT_SYMBOL(wiphy_register); + +void wiphy_rfkill_start_polling(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + if (!rdev->ops->rfkill_poll) + return; + rdev->rfkill_ops.poll = cfg80211_rfkill_poll; + rfkill_resume_polling(rdev->rfkill); +} +EXPORT_SYMBOL(wiphy_rfkill_start_polling); + +void wiphy_rfkill_stop_polling(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + rfkill_pause_polling(rdev->rfkill); +} +EXPORT_SYMBOL(wiphy_rfkill_stop_polling); + +void wiphy_unregister(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + rtnl_lock(); + rdev->wiphy.registered = false; + rtnl_unlock(); + + rfkill_unregister(rdev->rfkill); + + /* protect the device list */ + mutex_lock(&cfg80211_mutex); + + wait_event(rdev->dev_wait, ({ + int __count; + mutex_lock(&rdev->devlist_mtx); + __count = rdev->opencount; + mutex_unlock(&rdev->devlist_mtx); + __count == 0;})); + + mutex_lock(&rdev->devlist_mtx); + BUG_ON(!list_empty(&rdev->netdev_list)); + mutex_unlock(&rdev->devlist_mtx); + + /* + * First remove the hardware from everywhere, this makes + * it impossible to find from userspace. + */ + debugfs_remove_recursive(rdev->wiphy.debugfsdir); + list_del_rcu(&rdev->list); + synchronize_rcu(); + + /* + * Try to grab rdev->mtx. If a command is still in progress, + * hopefully the driver will refuse it since it's tearing + * down the device already. We wait for this command to complete + * before unlinking the item from the list. + * Note: as codified by the BUG_ON above we cannot get here if + * a virtual interface is still present. Hence, we can only get + * to lock contention here if userspace issues a command that + * identified the hardware by wiphy index. + */ + cfg80211_lock_rdev(rdev); + /* nothing */ + cfg80211_unlock_rdev(rdev); + + /* If this device got a regulatory hint tell core its + * free to listen now to a new shiny device regulatory hint */ + reg_device_remove(wiphy); + + cfg80211_rdev_list_generation++; + device_del(&rdev->wiphy.dev); + + mutex_unlock(&cfg80211_mutex); + + flush_work(&rdev->scan_done_wk); + cancel_work_sync(&rdev->conn_work); + flush_work(&rdev->event_work); +} +EXPORT_SYMBOL(wiphy_unregister); + +void cfg80211_dev_free(struct cfg80211_registered_device *rdev) +{ + struct cfg80211_internal_bss *scan, *tmp; + rfkill_destroy(rdev->rfkill); + mutex_destroy(&rdev->mtx); + mutex_destroy(&rdev->devlist_mtx); + mutex_destroy(&rdev->sched_scan_mtx); + list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) + cfg80211_put_bss(&scan->pub); + cfg80211_rdev_free_wowlan(rdev); + kfree(rdev); +} + +void wiphy_free(struct wiphy *wiphy) +{ + put_device(&wiphy->dev); +} +EXPORT_SYMBOL(wiphy_free); + +void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + if (rfkill_set_hw_state(rdev->rfkill, blocked)) + schedule_work(&rdev->rfkill_sync); +} +EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); + +static void wdev_cleanup_work(struct work_struct *work) +{ + struct wireless_dev *wdev; + struct cfg80211_registered_device *rdev; + + wdev = container_of(work, struct wireless_dev, cleanup_work); + rdev = wiphy_to_dev(wdev->wiphy); + + cfg80211_lock_rdev(rdev); + + if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == wdev->netdev)) { + rdev->scan_req->aborted = true; + ___cfg80211_scan_done(rdev, true); + } + + cfg80211_unlock_rdev(rdev); + + mutex_lock(&rdev->sched_scan_mtx); + + if (WARN_ON(rdev->sched_scan_req && + rdev->sched_scan_req->dev == wdev->netdev)) { + __cfg80211_stop_sched_scan(rdev, false); + } + + mutex_unlock(&rdev->sched_scan_mtx); + + mutex_lock(&rdev->devlist_mtx); + rdev->opencount--; + mutex_unlock(&rdev->devlist_mtx); + wake_up(&rdev->dev_wait); + + dev_put(wdev->netdev); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) +static struct device_type wiphy_type = { + .name = "wlan", +}; +#endif + +static int 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 cfg80211_registered_device *rdev; + int ret; + + if (!wdev) + return NOTIFY_DONE; + + rdev = wiphy_to_dev(wdev->wiphy); + + WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED); + + switch (state) { + case NETDEV_POST_INIT: + SET_NETDEV_DEVTYPE(dev, &wiphy_type); + break; + case NETDEV_REGISTER: + /* + * NB: cannot take rdev->mtx here because this may be + * called within code protected by it when interfaces + * are added with nl80211. + */ + mutex_init(&wdev->mtx); + INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); + INIT_LIST_HEAD(&wdev->event_list); + spin_lock_init(&wdev->event_lock); + INIT_LIST_HEAD(&wdev->mgmt_registrations); + spin_lock_init(&wdev->mgmt_registrations_lock); + + mutex_lock(&rdev->devlist_mtx); + list_add_rcu(&wdev->list, &rdev->netdev_list); + rdev->devlist_generation++; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + /* can only change netns with wiphy */ + dev->features |= NETIF_F_NETNS_LOCAL; +#endif + + if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, + "phy80211")) { + pr_err("failed to add phy80211 symlink to netdev!\n"); + } + wdev->netdev = dev; + wdev->sme_state = CFG80211_SME_IDLE; + mutex_unlock(&rdev->devlist_mtx); +#ifdef CONFIG_CFG80211_WEXT +#ifdef CONFIG_WIRELESS_EXT + if (!dev->wireless_handlers) + dev->wireless_handlers = &cfg80211_wext_handler; +#else + printk_once(KERN_WARNING "cfg80211: wext will not work because " + "kernel was compiled with CONFIG_WIRELESS_EXT=n. " + "Tools using wext interface, like iwconfig will " + "not work.\n"); +#endif + wdev->wext.default_key = -1; + wdev->wext.default_mgmt_key = -1; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; +#endif + + if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) + wdev->ps = true; + else + wdev->ps = false; + /* allow mac80211 to determine the timeout */ + wdev->ps_timeout = -1; + + if (!dev->ethtool_ops) + dev->ethtool_ops = &cfg80211_ethtool_ops; + + if ((wdev->iftype == NL80211_IFTYPE_STATION || + wdev->iftype == NL80211_IFTYPE_P2P_CLIENT || + wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr) + dev->priv_flags |= IFF_DONT_BRIDGE; + break; + case NETDEV_GOING_DOWN: + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + cfg80211_leave_ibss(rdev, dev, true); + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_STATION: + mutex_lock(&rdev->sched_scan_mtx); + __cfg80211_stop_sched_scan(rdev, false); + mutex_unlock(&rdev->sched_scan_mtx); + + wdev_lock(wdev); +#ifdef CONFIG_CFG80211_WEXT + kfree(wdev->wext.ie); + wdev->wext.ie = NULL; + wdev->wext.ie_len = 0; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; +#endif + __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, true); + cfg80211_mlme_down(rdev, dev); + wdev_unlock(wdev); + break; + case NL80211_IFTYPE_MESH_POINT: + cfg80211_leave_mesh(rdev, dev); + break; + default: + break; + } + wdev->beacon_interval = 0; + break; + case NETDEV_DOWN: + dev_hold(dev); + queue_work(cfg80211_wq, &wdev->cleanup_work); + break; + case NETDEV_UP: + /* + * If we have a really quick DOWN/UP succession we may + * have this work still pending ... cancel it and see + * if it was pending, in which case we need to account + * for some of the work it would have done. + */ + if (cancel_work_sync(&wdev->cleanup_work)) { + mutex_lock(&rdev->devlist_mtx); + rdev->opencount--; + mutex_unlock(&rdev->devlist_mtx); + dev_put(dev); + } + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); + wdev_lock(wdev); + switch (wdev->iftype) { +#ifdef CONFIG_CFG80211_WEXT + case NL80211_IFTYPE_ADHOC: + cfg80211_ibss_wext_join(rdev, wdev); + break; + case NL80211_IFTYPE_STATION: + cfg80211_mgd_wext_connect(rdev, wdev); + break; +#endif +#ifdef CONFIG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: + { + /* backward compat code... */ + struct mesh_setup setup; + memcpy(&setup, &default_mesh_setup, + sizeof(setup)); + /* back compat only needed for mesh_id */ + setup.mesh_id = wdev->ssid; + setup.mesh_id_len = wdev->mesh_id_up_len; + if (wdev->mesh_id_up_len) + __cfg80211_join_mesh(rdev, dev, + &setup, + &default_mesh_config); + break; + } +#endif + default: + break; + } + wdev_unlock(wdev); + rdev->opencount++; + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); + + /* + * Configure power management to the driver here so that its + * correctly set also after interface type changes etc. + */ + if ((wdev->iftype == NL80211_IFTYPE_STATION || + wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) && + rdev->ops->set_power_mgmt) + if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, + wdev->ps, + wdev->ps_timeout)) { + /* assume this means it's off */ + wdev->ps = false; + } + break; + case NETDEV_UNREGISTER: + /* + * NB: cannot take rdev->mtx here because this may be + * called within code protected by it when interfaces + * are removed with nl80211. + */ + mutex_lock(&rdev->devlist_mtx); + /* + * It is possible to get NETDEV_UNREGISTER + * multiple times. To detect that, check + * that the interface is still on the list + * of registered interfaces, and only then + * remove and clean it up. + */ + if (!list_empty(&wdev->list)) { + sysfs_remove_link(&dev->dev.kobj, "phy80211"); + list_del_rcu(&wdev->list); + rdev->devlist_generation++; + cfg80211_mlme_purge_registrations(wdev); +#ifdef CONFIG_CFG80211_WEXT + kfree(wdev->wext.keys); +#endif + } + mutex_unlock(&rdev->devlist_mtx); + /* + * synchronise (so that we won't find this netdev + * from other code any more) and then clear the list + * head so that the above code can safely check for + * !list_empty() to avoid double-cleanup. + */ + synchronize_rcu(); + INIT_LIST_HEAD(&wdev->list); + break; + case NETDEV_PRE_UP: + if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) + return notifier_from_errno(-EOPNOTSUPP); + if (rfkill_blocked(rdev->rfkill)) + return notifier_from_errno(-ERFKILL); + ret = cfg80211_can_add_interface(rdev, wdev->iftype); + if (ret) + return notifier_from_errno(ret); + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block cfg80211_netdev_notifier = { + .notifier_call = cfg80211_netdev_notifier_call, +}; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) +static void __net_exit cfg80211_pernet_exit(struct net *net) +{ + struct cfg80211_registered_device *rdev; + + rtnl_lock(); + mutex_lock(&cfg80211_mutex); + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (net_eq(wiphy_net(&rdev->wiphy), net)) + WARN_ON(cfg80211_switch_netns(rdev, &init_net)); + } + mutex_unlock(&cfg80211_mutex); + rtnl_unlock(); +} + +static struct pernet_operations cfg80211_pernet_ops = { + .exit = cfg80211_pernet_exit, +}; +#endif + +static int __init cfg80211_init(void) +{ + int err; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + err = register_pernet_device(&cfg80211_pernet_ops); + if (err) + goto out_fail_pernet; +#endif + + err = wiphy_sysfs_init(); + if (err) + goto out_fail_sysfs; + + err = register_netdevice_notifier(&cfg80211_netdev_notifier); + if (err) + goto out_fail_notifier; + + err = nl80211_init(); + if (err) + goto out_fail_nl80211; + + ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); + + err = regulatory_init(); + if (err) + goto out_fail_reg; + + cfg80211_wq = create_singlethread_workqueue("cfg80211"); + if (!cfg80211_wq) + goto out_fail_wq; + + return 0; + +out_fail_wq: + regulatory_exit(); +out_fail_reg: + debugfs_remove(ieee80211_debugfs_dir); +out_fail_nl80211: + unregister_netdevice_notifier(&cfg80211_netdev_notifier); +out_fail_notifier: + wiphy_sysfs_exit(); +out_fail_sysfs: +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + unregister_pernet_device(&cfg80211_pernet_ops); +out_fail_pernet: +#endif + return err; +} +subsys_initcall(cfg80211_init); + +static void __exit cfg80211_exit(void) +{ + debugfs_remove(ieee80211_debugfs_dir); + nl80211_exit(); + unregister_netdevice_notifier(&cfg80211_netdev_notifier); + wiphy_sysfs_exit(); + regulatory_exit(); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + unregister_pernet_device(&cfg80211_pernet_ops); +#endif + destroy_workqueue(cfg80211_wq); +} +module_exit(cfg80211_exit); diff --git a/net/wireless_ath/core.h b/net/wireless_ath/core.h new file mode 100644 index 00000000..11ff6bbd --- /dev/null +++ b/net/wireless_ath/core.h @@ -0,0 +1,468 @@ +/* + * Wireless configuration interface internals. + * + * Copyright 2006-2010 Johannes Berg + */ +#ifndef __NET_WIRELESS_CORE_H +#define __NET_WIRELESS_CORE_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "reg.h" + +struct cfg80211_registered_device { + const struct cfg80211_ops *ops; + struct list_head list; + /* we hold this mutex during any call so that + * we cannot do multiple calls at once, and also + * to avoid the deregister call to proceed while + * any call is in progress */ + struct mutex mtx; + + /* rfkill support */ + struct rfkill_ops rfkill_ops; + struct rfkill *rfkill; + struct work_struct rfkill_sync; + + /* ISO / IEC 3166 alpha2 for which this device is receiving + * country IEs on, this can help disregard country IEs from APs + * on the same alpha2 quickly. The alpha2 may differ from + * cfg80211_regdomain's alpha2 when an intersection has occurred. + * If the AP is reconfigured this can also be used to tell us if + * the country on the country IE changed. */ + char country_ie_alpha2[2]; + + /* If a Country IE has been received this tells us the environment + * which its telling us its in. This defaults to ENVIRON_ANY */ + enum environment_cap env; + + /* wiphy index, internal only */ + int wiphy_idx; + + /* associate netdev list */ + struct mutex devlist_mtx; + /* protected by devlist_mtx or RCU */ + struct list_head netdev_list; + int devlist_generation; + int opencount; /* also protected by devlist_mtx */ + wait_queue_head_t dev_wait; + + u32 ap_beacons_nlpid; + + /* BSSes/scanning */ + spinlock_t bss_lock; + struct list_head bss_list; + struct rb_root bss_tree; + u32 bss_generation; + struct cfg80211_scan_request *scan_req; /* protected by RTNL */ + struct cfg80211_sched_scan_request *sched_scan_req; + unsigned long suspend_at; + struct work_struct scan_done_wk; + struct work_struct sched_scan_results_wk; + + struct mutex sched_scan_mtx; + +#ifdef CONFIG_NL80211_TESTMODE + struct genl_info *testmode_info; +#endif + + struct work_struct conn_work; + struct work_struct event_work; + + struct cfg80211_wowlan *wowlan; + + /* must be last because of the way we do wiphy_priv(), + * and it should at least be aligned to NETDEV_ALIGN */ + struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); +}; + +static inline +struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy) +{ + BUG_ON(!wiphy); + return container_of(wiphy, struct cfg80211_registered_device, wiphy); +} + +/* Note 0 is valid, hence phy0 */ +static inline +bool wiphy_idx_valid(int wiphy_idx) +{ + return wiphy_idx >= 0; +} + +static inline void +cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) +{ + int i; + + if (!rdev->wowlan) + return; + for (i = 0; i < rdev->wowlan->n_patterns; i++) + kfree(rdev->wowlan->patterns[i].mask); + kfree(rdev->wowlan->patterns); + kfree(rdev->wowlan); +} + +extern struct workqueue_struct *cfg80211_wq; +extern struct mutex cfg80211_mutex; +extern struct list_head cfg80211_rdev_list; +extern int cfg80211_rdev_list_generation; + +static inline void assert_cfg80211_lock(void) +{ + lockdep_assert_held(&cfg80211_mutex); +} + +/* + * You can use this to mark a wiphy_idx as not having an associated wiphy. + * It guarantees cfg80211_rdev_by_wiphy_idx(wiphy_idx) will return NULL + */ +#define WIPHY_IDX_STALE -1 + +struct cfg80211_internal_bss { + struct list_head list; + struct rb_node rbn; + unsigned long ts; + struct kref ref; + atomic_t hold; + bool beacon_ies_allocated; + bool proberesp_ies_allocated; + + /* must be last because of priv member */ + struct cfg80211_bss pub; +}; + +static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pub) +{ + return container_of(pub, struct cfg80211_internal_bss, pub); +} + +static inline void cfg80211_ref_bss(struct cfg80211_internal_bss *bss) +{ + kref_get(&bss->ref); +} + +static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss) +{ + atomic_inc(&bss->hold); +} + +static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss) +{ + int r = atomic_dec_return(&bss->hold); + WARN_ON(r < 0); +} + + +struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx); +int get_wiphy_idx(struct wiphy *wiphy); + +struct cfg80211_registered_device * +__cfg80211_rdev_from_info(struct genl_info *info); + +/* + * This function returns a pointer to the driver + * that the genl_info item that is passed refers to. + * If successful, it returns non-NULL and also locks + * the driver's mutex! + * + * This means that you need to call cfg80211_unlock_rdev() + * before being allowed to acquire &cfg80211_mutex! + * + * This is necessary because we need to lock the global + * mutex to get an item off the list safely, and then + * we lock the rdev mutex so it doesn't go away under us. + * + * We don't want to keep cfg80211_mutex locked + * for all the time in order to allow requests on + * other interfaces to go through at the same time. + * + * The result of this can be a PTR_ERR and hence must + * be checked with IS_ERR() for errors. + */ +extern struct cfg80211_registered_device * +cfg80211_get_dev_from_info(struct genl_info *info); + +/* requires cfg80211_rdev_mutex to be held! */ +struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); + +/* identical to cfg80211_get_dev_from_info but only operate on ifindex */ +extern struct cfg80211_registered_device * +cfg80211_get_dev_from_ifindex(struct net *net, int ifindex); + +int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, + struct net *net); + +static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *rdev) +{ + mutex_lock(&rdev->mtx); +} + +static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *rdev) +{ + BUG_ON(IS_ERR(rdev) || !rdev); + mutex_unlock(&rdev->mtx); +} + +static inline void wdev_lock(struct wireless_dev *wdev) + __acquires(wdev) +{ + mutex_lock(&wdev->mtx); + __acquire(wdev->mtx); +} + +static inline void wdev_unlock(struct wireless_dev *wdev) + __releases(wdev) +{ + __release(wdev->mtx); + mutex_unlock(&wdev->mtx); +} + +#define ASSERT_RDEV_LOCK(rdev) lockdep_assert_held(&(rdev)->mtx) +#define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx) + +enum cfg80211_event_type { + EVENT_CONNECT_RESULT, + EVENT_ROAMED, + EVENT_DISCONNECTED, + EVENT_IBSS_JOINED, +}; + +struct cfg80211_event { + struct list_head list; + enum cfg80211_event_type type; + + union { + struct { + u8 bssid[ETH_ALEN]; + const u8 *req_ie; + const u8 *resp_ie; + size_t req_ie_len; + size_t resp_ie_len; + u16 status; + } cr; + struct { + const u8 *req_ie; + const u8 *resp_ie; + size_t req_ie_len; + size_t resp_ie_len; + struct cfg80211_bss *bss; + } rm; + struct { + const u8 *ie; + size_t ie_len; + u16 reason; + } dc; + struct { + u8 bssid[ETH_ALEN]; + } ij; + }; +}; + +struct cfg80211_cached_keys { + struct key_params params[6]; + u8 data[6][WLAN_MAX_KEY_LEN]; + int def, defmgmt; +}; + + +/* free object */ +extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); + +extern int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, + char *newname); + +void ieee80211_set_bitrate_flags(struct wiphy *wiphy); + +void cfg80211_bss_expire(struct cfg80211_registered_device *dev); +void cfg80211_bss_age(struct cfg80211_registered_device *dev, + unsigned long age_secs); + +/* IBSS */ +int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys); +int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys); +void cfg80211_clear_ibss(struct net_device *dev, bool nowext); +int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool nowext); +int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool nowext); +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid); +int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); + +/* mesh */ +extern const struct mesh_config default_mesh_config; +extern const struct mesh_setup default_mesh_setup; +int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev, + const struct mesh_setup *setup, + const struct mesh_config *conf); +int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev, + const struct mesh_setup *setup, + const struct mesh_config *conf); +int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev); + +/* MLME */ +int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, + const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx, + bool local_state_change); +int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx, + bool local_state_change); +int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt); +int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt); +int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason, + bool local_state_change); +int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason, + bool local_state_change); +int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason, + bool local_state_change); +void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, + struct net_device *dev); +void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, bool wextev, + struct cfg80211_bss *bss); +int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, + u16 frame_type, const u8 *match_data, + int match_len); +void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid); +void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); +int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, + const u8 *buf, size_t len, bool no_cck, + bool dont_wait_for_ack, u64 *cookie); + +/* SME */ +int __cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys, + const u8 *prev_bssid); +int cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys); +int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason, + bool wextev); +int cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason, + bool wextev); +void __cfg80211_roamed(struct wireless_dev *wdev, + struct cfg80211_bss *bss, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len); +int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); + +void cfg80211_conn_work(struct work_struct *work); +void cfg80211_sme_failed_assoc(struct wireless_dev *wdev); +bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev); + +/* internal helpers */ +bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher); +int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, + struct key_params *params, int key_idx, + bool pairwise, const u8 *mac_addr); +void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, + size_t ie_len, u16 reason, bool from_ap); +void cfg80211_sme_scan_done(struct net_device *dev); +void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); +void cfg80211_sme_disassoc(struct net_device *dev, int idx); +void __cfg80211_scan_done(struct work_struct *wk); +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); +void __cfg80211_sched_scan_results(struct work_struct *wk); +int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, + bool driver_initiated); +void cfg80211_upload_connect_keys(struct wireless_dev *wdev); +int cfg80211_change_iface(struct cfg80211_registered_device *rdev, + struct net_device *dev, enum nl80211_iftype ntype, + u32 *flags, struct vif_params *params); +void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); + +int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + enum nl80211_iftype iftype); + +static inline int +cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, + enum nl80211_iftype iftype) +{ + return cfg80211_can_change_interface(rdev, NULL, iftype); +} + +struct ieee80211_channel * +rdev_freq_to_chan(struct cfg80211_registered_device *rdev, + int freq, enum nl80211_channel_type channel_type); +int cfg80211_set_freq(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, int freq, + enum nl80211_channel_type channel_type); + +u16 cfg80211_calculate_bitrate(struct rate_info *rate); + +int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, + const u8 *rates, unsigned int n_rates, + u32 *mask); + +int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, + u32 beacon_int); + +#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS +#define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) +#else +/* + * Trick to enable using it as a condition, + * and also not give a warning when it's + * not used that way. + */ +#define CFG80211_DEV_WARN_ON(cond) ({bool __r = (cond); __r; }) +#endif + +#endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless_ath/db.txt b/net/wireless_ath/db.txt new file mode 100644 index 00000000..d0f56304 --- /dev/null +++ b/net/wireless_ath/db.txt @@ -0,0 +1,697 @@ +# This is the world regulatory domain +country 00: + (2402 - 2472 @ 40), (3, 20) + # Channel 12 - 13. No HT40 channel fits here + (2457 - 2482 @ 20), (3, 20), PASSIVE-SCAN, NO-IBSS + # Channel 14. Only JP enables this and for 802.11b only + (2474 - 2494 @ 20), (3, 20), PASSIVE-SCAN, NO-IBSS, NO-OFDM + # Channel 36 - 48 + (5140 - 5360 @ 40), (0, 30), PASSIVE-SCAN, NO-IBSS + # NB: 5260 MHz - 5700 MHz requies DFS + # Channel 149 - 165 + (5460 - 5860 @ 40), (0, 30), PASSIVE-SCAN, NO-IBSS + + +country AE: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country AL: + (2402 - 2482 @ 20), (N/A, 20) + +country AM: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 20), (N/A, 18) + (5250 - 5330 @ 20), (N/A, 18), DFS + +country AN: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country AR: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 20), DFS + (5490 - 5710 @ 40), (3, 20), DFS + (5735 - 5835 @ 40), (3, 30) + +country AT: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country AU: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (3, 23) + (5250 - 5330 @ 40), (3, 23), DFS + (5735 - 5835 @ 40), (3, 30) + +country AW: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country AZ: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 18) + (5250 - 5330 @ 40), (N/A, 18), DFS + +country BA: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country BB: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (3, 23) + (5250 - 5330 @ 40), (3, 23), DFS + (5735 - 5835 @ 40), (3, 30) + +country BD: + (2402 - 2482 @ 40), (N/A, 20) + +country BE: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country BG: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 23) + (5250 - 5290 @ 40), (N/A, 23), DFS + (5490 - 5710 @ 40), (N/A, 30), DFS + +country BH: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 20), (N/A, 20) + (5250 - 5330 @ 20), (N/A, 20), DFS + (5735 - 5835 @ 20), (N/A, 20) + +country BL: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 18) + (5250 - 5330 @ 40), (N/A, 18), DFS + +country BN: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5735 - 5835 @ 40), (N/A, 30) + +country BO: + (2402 - 2482 @ 40), (N/A, 30) + (5735 - 5835 @ 40), (N/A, 30) + +country BR: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 20), DFS + (5490 - 5710 @ 40), (3, 20), DFS + (5735 - 5835 @ 40), (3, 30) + +country BY: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country BZ: + (2402 - 2482 @ 40), (N/A, 30) + (5735 - 5835 @ 40), (N/A, 30) + +country CA: + (2402 - 2472 @ 40), (3, 27) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 20), DFS + (5490 - 5710 @ 40), (3, 20), DFS + (5735 - 5835 @ 40), (3, 30) + +country CH: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country CL: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5735 - 5835 @ 40), (N/A, 20) + +country CN: + (2402 - 2482 @ 40), (N/A, 20) + (5735 - 5835 @ 40), (N/A, 30) + +country CO: + (2402 - 2472 @ 40), (3, 27) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 23), DFS + (5735 - 5835 @ 40), (3, 30) + +country CR: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 20), (3, 17) + (5250 - 5330 @ 20), (3, 23), DFS + (5735 - 5835 @ 20), (3, 30) + +country CS: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country CY: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +# Data from http://www.ctu.eu/164/download/VOR/VOR-12-08-2005-34.pdf +# and http://www.ctu.eu/164/download/VOR/VOR-12-05-2007-6-AN.pdf +# Power at 5250 - 5350 MHz and 5470 - 5725 MHz can be doubled if TPC is +# implemented. +country CZ: DFS-ETSI + (2400 - 2483.5 @ 40), (N/A, 100 mW) + (5150 - 5250 @ 40), (N/A, 200 mW), NO-OUTDOOR + (5250 - 5350 @ 40), (N/A, 100 mW), NO-OUTDOOR, DFS + (5470 - 5725 @ 40), (N/A, 500 mW), DFS + +# Data from "Frequenznutzungsplan" (as published in April 2008), downloaded from +# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38448/publicationFile/2659/Frequenznutzungsplan2008_Id17448pdf.pdf +# For the 5GHz range also see +# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38216/publicationFile/6579/WLAN5GHzVfg7_2010_28042010pdf.pdf +# The values have been reduced by a factor of 2 (3db) for non TPC devices +# (in other words: devices with TPC can use twice the tx power of this table). +# Note that the docs do not require TPC for 5150--5250; the reduction to +# 100mW thus is not strictly required -- however the conservative 100mW +# limit is used here as the non-interference with radar and satellite +# apps relies on the attenuation by the building walls only in the +# absence of DFS; the neighbour countries have 100mW limit here as well. + +country DE: DFS-ETSI + # entries 279004 and 280006 + (2400 - 2483.5 @ 40), (N/A, 100 mW) + # entry 303005 + (5150 - 5250 @ 40), (N/A, 100 mW), NO-OUTDOOR + # entries 304002 and 305002 + (5250 - 5350 @ 40), (N/A, 100 mW), NO-OUTDOOR, DFS + # entries 308002, 309001 and 310003 + (5470 - 5725 @ 40), (N/A, 500 mW), DFS + +country DK: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country DO: + (2402 - 2472 @ 40), (3, 27) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 23), DFS + (5735 - 5835 @ 40), (3, 30) + +country DZ: + (2402 - 2482 @ 40), (N/A, 20) + +country EC: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 20), (3, 17) + (5250 - 5330 @ 20), (3, 23), DFS + (5735 - 5835 @ 20), (3, 30) + +country EE: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country EG: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 20), (N/A, 20) + (5250 - 5330 @ 20), (N/A, 20), DFS + +country ES: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country FI: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country FR: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country GE: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 18) + (5250 - 5330 @ 40), (N/A, 18), DFS + +country GB: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country GD: + (2402 - 2472 @ 40), (3, 27) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 20), DFS + (5490 - 5710 @ 40), (3, 20), DFS + (5735 - 5835 @ 40), (3, 30) + +country GR: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country GL: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 20), (N/A, 20) + (5250 - 5330 @ 20), (N/A, 20), DFS + (5490 - 5710 @ 20), (N/A, 27), DFS + +country GT: + (2402 - 2472 @ 40), (3, 27) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 23), DFS + (5735 - 5835 @ 40), (3, 30) + +country GU: + (2402 - 2472 @ 40), (3, 27) + (5170 - 5250 @ 20), (3, 17) + (5250 - 5330 @ 20), (3, 23), DFS + (5735 - 5835 @ 20), (3, 30) + +country HN: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 20), DFS + (5490 - 5710 @ 40), (3, 20), DFS + (5735 - 5835 @ 40), (3, 30) + +country HK: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 20), DFS + (5490 - 5710 @ 40), (3, 20), DFS + (5735 - 5835 @ 40), (3, 30) + +country HR: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country HT: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country HU: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country ID: + (2402 - 2482 @ 40), (N/A, 20) + +country IE: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country IL: + (2402 - 2482 @ 40), (N/A, 20) + (5150 - 5250 @ 40), (N/A, 200 mW), NO-OUTDOOR + (5250 - 5350 @ 40), (N/A, 200 mW), NO-OUTDOOR, DFS + +country IN: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5735 - 5835 @ 40), (N/A, 20) + +country IS: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country IR: + (2402 - 2482 @ 40), (N/A, 20) + (5735 - 5835 @ 40), (N/A, 30) + +country IT: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country JM: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 20), DFS + (5490 - 5710 @ 40), (3, 20), DFS + (5735 - 5835 @ 40), (3, 30) + +country JP: + (2402 - 2472 @ 40), (N/A, 20) + (2457 - 2482 @ 20), (N/A, 20) + (2474 - 2494 @ 20), (N/A, 20), NO-OFDM + (4910 - 4930 @ 10), (N/A, 23) + (4910 - 4990 @ 40), (N/A, 23) + (4930 - 4950 @ 10), (N/A, 23) + (5030 - 5045 @ 10), (N/A, 23) + (5030 - 5090 @ 40), (N/A, 23) + (5050 - 5060 @ 10), (N/A, 23) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 23), DFS + +country JO: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 18) + +country KE: + (2402 - 2482 @ 40), (N/A, 20) + (5735 - 5835 @ 40), (N/A, 30) + +country KH: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country KP: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5330 @ 40), (3, 20) + (5160 - 5250 @ 40), (3, 20), DFS + (5490 - 5630 @ 40), (3, 30), DFS + (5735 - 5815 @ 40), (3, 30) + +country KR: + (2402 - 2482 @ 20), (N/A, 20) + (5170 - 5250 @ 20), (3, 20) + (5250 - 5330 @ 20), (3, 20), DFS + (5490 - 5630 @ 20), (3, 30), DFS + (5735 - 5815 @ 20), (3, 30) + +country KW: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + +country KZ: + (2402 - 2482 @ 40), (N/A, 20) + +country LB: + (2402 - 2482 @ 40), (N/A, 20) + (5735 - 5835 @ 40), (N/A, 30) + +country LI: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country LK: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 20), (3, 17) + (5250 - 5330 @ 20), (3, 20), DFS + (5490 - 5710 @ 20), (3, 20), DFS + (5735 - 5835 @ 20), (3, 30) + +country LT: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country LU: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country LV: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country MC: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 18) + (5250 - 5330 @ 40), (N/A, 18), DFS + +country MA: + (2402 - 2482 @ 40), (N/A, 20) + +country MO: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (3, 23) + (5250 - 5330 @ 40), (3, 23), DFS + (5735 - 5835 @ 40), (3, 30) + +country MK: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country MT: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country MY: + (2402 - 2482 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 30), DFS + (5735 - 5835 @ 40), (N/A, 30) + +country MX: + (2402 - 2472 @ 40), (3, 27) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 23), DFS + (5735 - 5835 @ 40), (3, 30) + +country NL: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20), NO-OUTDOOR + (5250 - 5330 @ 40), (N/A, 20), NO-OUTDOOR, DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country NO: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country NP: + (2402 - 2482 @ 40), (N/A, 20) + (5735 - 5835 @ 40), (N/A, 30) + +country NZ: + (2402 - 2482 @ 40), (N/A, 30) + (5170 - 5250 @ 20), (3, 23) + (5250 - 5330 @ 20), (3, 23), DFS + (5735 - 5835 @ 20), (3, 30) + +country OM: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 20), DFS + (5490 - 5710 @ 40), (3, 20), DFS + (5735 - 5835 @ 40), (3, 30) + +country PA: + (2402 - 2472 @ 40), (3, 27) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 23), DFS + (5735 - 5835 @ 40), (3, 30) + +country PE: + (2402 - 2482 @ 40), (N/A, 20) + (5735 - 5835 @ 40), (N/A, 30) + +country PG: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 23), DFS + (5735 - 5835 @ 40), (3, 30) + +country PH: + (2402 - 2482 @ 40), (N/A, 20) + (5735 - 5835 @ 40), (N/A, 30) + +country PK: + (2402 - 2482 @ 40), (N/A, 20) + (5735 - 5835 @ 40), (N/A, 30) + +country PL: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country PT: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country PR: + (2402 - 2472 @ 40), (3, 27) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 23), DFS + (5735 - 5835 @ 40), (3, 30) + +country QA: + (2402 - 2482 @ 40), (N/A, 20) + (5735 - 5835 @ 40), (N/A, 30) + +country RO: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country RU: + (2402 - 2482 @ 40), (N/A, 20) + (5735 - 5835 @ 20), (N/A, 30) + +country SA: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 20), (3, 23) + (5250 - 5330 @ 20), (3, 23), DFS + (5735 - 5835 @ 20), (3, 30) + +country SE: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country SG: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5735 - 5835 @ 40), (N/A, 20) + +country SI: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country SK: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + (5490 - 5710 @ 40), (N/A, 27), DFS + +country SV: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 20), (3, 17) + (5250 - 5330 @ 20), (3, 23), DFS + (5735 - 5835 @ 20), (3, 30) + +country SY: + (2402 - 2482 @ 40), (N/A, 20) + +country TW: + (2402 - 2472 @ 40), (3, 27) + (5270 - 5330 @ 40), (3, 17), DFS + (5735 - 5815 @ 40), (3, 30) + +country TH: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 20), DFS + (5490 - 5710 @ 40), (3, 20), DFS + (5735 - 5835 @ 40), (3, 30) + +country TT: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 20), DFS + (5490 - 5710 @ 40), (3, 20), DFS + (5735 - 5835 @ 40), (3, 30) + +country TN: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 20), (N/A, 20) + (5250 - 5330 @ 20), (N/A, 20), DFS + +country TR: DFS-ETSI + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 20), (N/A, 20) + (5250 - 5330 @ 20), (N/A, 20), DFS + +country UA: + (2402 - 2482 @ 40), (N/A, 20) + +country US: DFS-FCC + (2402 - 2472 @ 40), (3, 27) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 20), DFS + (5490 - 5600 @ 40), (3, 20), DFS + (5650 - 5710 @ 40), (3, 20), DFS + (5735 - 5835 @ 40), (3, 30) + +country UY: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 20), DFS + (5490 - 5710 @ 40), (3, 20), DFS + (5735 - 5835 @ 40), (3, 30) + +country UZ: + (2402 - 2472 @ 40), (3, 27) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 20), DFS + (5490 - 5710 @ 40), (3, 20), DFS + (5735 - 5835 @ 40), (3, 30) + +country VE: + (2402 - 2482 @ 40), (N/A, 20) + (5735 - 5815 @ 40), (N/A, 23) + +country VN: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (N/A, 20) + (5250 - 5330 @ 40), (N/A, 20), DFS + +country YE: + (2402 - 2482 @ 40), (N/A, 20) + +country ZA: + (2402 - 2482 @ 40), (N/A, 20) + (5170 - 5250 @ 40), (3, 17) + (5250 - 5330 @ 40), (3, 20), DFS + (5490 - 5710 @ 40), (3, 20), DFS + (5735 - 5835 @ 40), (3, 30) + +country ZW: + (2402 - 2482 @ 40), (N/A, 20) + diff --git a/net/wireless_ath/debugfs.c b/net/wireless_ath/debugfs.c new file mode 100644 index 00000000..39765bcf --- /dev/null +++ b/net/wireless_ath/debugfs.c @@ -0,0 +1,121 @@ +/* + * cfg80211 debugfs + * + * Copyright 2009 Luis R. Rodriguez + * Copyright 2007 Johannes Berg + * + * 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 "core.h" +#include "debugfs.h" + +static int cfg80211_open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +#define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \ +static ssize_t name## _read(struct file *file, char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + struct wiphy *wiphy= file->private_data; \ + char buf[buflen]; \ + int res; \ + \ + res = scnprintf(buf, buflen, fmt "\n", ##value); \ + return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ +} \ + \ +static const struct file_operations name## _ops = { \ + .read = name## _read, \ + .open = cfg80211_open_file_generic, \ + .llseek = generic_file_llseek, \ +}; + +DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d", + wiphy->rts_threshold) +DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d", + wiphy->frag_threshold); +DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d", + wiphy->retry_short) +DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d", + wiphy->retry_long); + +static int ht_print_chan(struct ieee80211_channel *chan, + char *buf, int buf_size, int offset) +{ + if (WARN_ON(offset > buf_size)) + return 0; + + if (chan->flags & IEEE80211_CHAN_DISABLED) + return snprintf(buf + offset, + buf_size - offset, + "%d Disabled\n", + chan->center_freq); + + return snprintf(buf + offset, + buf_size - offset, + "%d HT40 %c%c\n", + chan->center_freq, + (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? ' ' : '-', + (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? ' ' : '+'); +} + +static ssize_t ht40allow_map_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wiphy *wiphy = file->private_data; + char *buf; + unsigned int offset = 0, buf_size = PAGE_SIZE, i, r; + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&cfg80211_mutex); + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + sband = wiphy->bands[band]; + if (!sband) + continue; + for (i = 0; i < sband->n_channels; i++) + offset += ht_print_chan(&sband->channels[i], + buf, buf_size, offset); + } + + mutex_unlock(&cfg80211_mutex); + + r = simple_read_from_buffer(user_buf, count, ppos, buf, offset); + + kfree(buf); + + return r; +} + +static const struct file_operations ht40allow_map_ops = { + .read = ht40allow_map_read, + .open = cfg80211_open_file_generic, + .llseek = default_llseek, +}; + +#define DEBUGFS_ADD(name) \ + debugfs_create_file(#name, S_IRUGO, phyd, &rdev->wiphy, &name## _ops); + +void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) +{ + struct dentry *phyd = rdev->wiphy.debugfsdir; + + DEBUGFS_ADD(rts_threshold); + DEBUGFS_ADD(fragmentation_threshold); + DEBUGFS_ADD(short_retry_limit); + DEBUGFS_ADD(long_retry_limit); + DEBUGFS_ADD(ht40allow_map); +} diff --git a/net/wireless_ath/debugfs.h b/net/wireless_ath/debugfs.h new file mode 100644 index 00000000..74fdd381 --- /dev/null +++ b/net/wireless_ath/debugfs.h @@ -0,0 +1,11 @@ +#ifndef __CFG80211_DEBUGFS_H +#define __CFG80211_DEBUGFS_H + +#ifdef CONFIG_CFG80211_DEBUGFS +void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev); +#else +static inline +void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) {} +#endif + +#endif /* __CFG80211_DEBUGFS_H */ diff --git a/net/wireless_ath/ethtool.c b/net/wireless_ath/ethtool.c new file mode 100644 index 00000000..9bde4d1d --- /dev/null +++ b/net/wireless_ath/ethtool.c @@ -0,0 +1,78 @@ +#include +#include +#include "core.h" +#include "ethtool.h" + +static void cfg80211_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + strlcpy(info->driver, wiphy_dev(wdev->wiphy)->driver->name, + sizeof(info->driver)); + + strlcpy(info->version, init_utsname()->release, sizeof(info->version)); + + if (wdev->wiphy->fw_version[0]) + strncpy(info->fw_version, wdev->wiphy->fw_version, + sizeof(info->fw_version)); + else + strncpy(info->fw_version, "N/A", sizeof(info->fw_version)); + + strlcpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)), + sizeof(info->bus_info)); +} + +static int cfg80211_get_regs_len(struct net_device *dev) +{ + /* For now, return 0... */ + return 0; +} + +static void cfg80211_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *data) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + regs->version = wdev->wiphy->hw_version; + regs->len = 0; +} + +static void cfg80211_get_ringparam(struct net_device *dev, + struct ethtool_ringparam *rp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + + memset(rp, 0, sizeof(*rp)); + + if (rdev->ops->get_ringparam) + rdev->ops->get_ringparam(wdev->wiphy, + &rp->tx_pending, &rp->tx_max_pending, + &rp->rx_pending, &rp->rx_max_pending); +} + +static int cfg80211_set_ringparam(struct net_device *dev, + struct ethtool_ringparam *rp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + + if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0) + return -EINVAL; + + if (rdev->ops->set_ringparam) + return rdev->ops->set_ringparam(wdev->wiphy, + rp->tx_pending, rp->rx_pending); + + return -ENOTSUPP; +} + +const struct ethtool_ops cfg80211_ethtool_ops = { + .get_drvinfo = cfg80211_get_drvinfo, + .get_regs_len = cfg80211_get_regs_len, + .get_regs = cfg80211_get_regs, + .get_link = ethtool_op_get_link, + .get_ringparam = cfg80211_get_ringparam, + .set_ringparam = cfg80211_set_ringparam, +}; diff --git a/net/wireless_ath/ethtool.h b/net/wireless_ath/ethtool.h new file mode 100644 index 00000000..695ecad2 --- /dev/null +++ b/net/wireless_ath/ethtool.h @@ -0,0 +1,6 @@ +#ifndef __CFG80211_ETHTOOL__ +#define __CFG80211_ETHTOOL__ + +extern const struct ethtool_ops cfg80211_ethtool_ops; + +#endif /* __CFG80211_ETHTOOL__ */ diff --git a/net/wireless_ath/genregdb.awk b/net/wireless_ath/genregdb.awk new file mode 100644 index 00000000..53c143f5 --- /dev/null +++ b/net/wireless_ath/genregdb.awk @@ -0,0 +1,119 @@ +#!/usr/bin/awk -f +# +# genregdb.awk -- generate regdb.c from db.txt +# +# Actually, it reads from stdin (presumed to be db.txt) and writes +# to stdout (presumed to be regdb.c), but close enough... +# +# Copyright 2009 John W. Linville +# +# 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. +# + +BEGIN { + active = 0 + rules = 0; + print "/*" + print " * DO NOT EDIT -- file generated from data in db.txt" + print " */" + print "" + print "#include " + print "#include " + print "#include \"regdb.h\"" + print "" + regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n" +} + +/^[ \t]*#/ { + # Ignore +} + +!active && /^[ \t]*$/ { + # Ignore +} + +!active && /country/ { + country=$2 + sub(/:/, "", country) + printf "static const struct ieee80211_regdomain regdom_%s = {\n", country + printf "\t.alpha2 = \"%s\",\n", country + printf "\t.reg_rules = {\n" + active = 1 + regdb = regdb "\t®dom_" country ",\n" +} + +active && /^[ \t]*\(/ { + start = $1 + sub(/\(/, "", start) + end = $3 + bw = $5 + sub(/\),/, "", bw) + gain = $6 + sub(/\(/, "", gain) + sub(/,/, "", gain) + power = $7 + sub(/\)/, "", power) + sub(/,/, "", power) + # power might be in mW... + units = $8 + sub(/\)/, "", units) + sub(/,/, "", units) + if (units == "mW") { + if (power == 100) { + power = 20 + } else if (power == 200) { + power = 23 + } else if (power == 500) { + power = 27 + } else if (power == 1000) { + power = 30 + } else { + print "Unknown power value in database!" + } + } + flagstr = "" + for (i=8; i<=NF; i++) + flagstr = flagstr $i + split(flagstr, flagarray, ",") + flags = "" + for (arg in flagarray) { + if (flagarray[arg] == "NO-OFDM") { + flags = flags "\n\t\t\tNL80211_RRF_NO_OFDM | " + } else if (flagarray[arg] == "NO-CCK") { + flags = flags "\n\t\t\tNL80211_RRF_NO_CCK | " + } else if (flagarray[arg] == "NO-INDOOR") { + flags = flags "\n\t\t\tNL80211_RRF_NO_INDOOR | " + } else if (flagarray[arg] == "NO-OUTDOOR") { + flags = flags "\n\t\t\tNL80211_RRF_NO_OUTDOOR | " + } else if (flagarray[arg] == "DFS") { + flags = flags "\n\t\t\tNL80211_RRF_DFS | " + } else if (flagarray[arg] == "PTP-ONLY") { + flags = flags "\n\t\t\tNL80211_RRF_PTP_ONLY | " + } else if (flagarray[arg] == "PTMP-ONLY") { + flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | " + } else if (flagarray[arg] == "PASSIVE-SCAN") { + flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | " + } else if (flagarray[arg] == "NO-IBSS") { + flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | " + } + } + flags = flags "0" + printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags + rules++ +} + +active && /^[ \t]*$/ { + active = 0 + printf "\t},\n" + printf "\t.n_reg_rules = %d\n", rules + printf "};\n\n" + rules = 0; +} + +END { + print regdb "};" + print "" + print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);" +} diff --git a/net/wireless_ath/ibss.c b/net/wireless_ath/ibss.c new file mode 100644 index 00000000..30f20fe4 --- /dev/null +++ b/net/wireless_ath/ibss.c @@ -0,0 +1,527 @@ +/* + * Some IBSS support code for cfg80211. + * + * Copyright 2009 Johannes Berg + */ + +#include +#include +#include +#include +#include +#include "wext-compat.h" +#include "nl80211.h" + + +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_bss *bss; +#ifdef CONFIG_CFG80211_WEXT + union iwreq_data wrqu; +#endif + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) + return; + + if (!wdev->ssid_len) + return; + + bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + wdev->ssid, wdev->ssid_len, + WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); + + if (WARN_ON(!bss)) + return; + + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + } + + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); + + cfg80211_upload_connect_keys(wdev); + + nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, + GFP_KERNEL); +#ifdef CONFIG_CFG80211_WEXT + memset(&wrqu, 0, sizeof(wrqu)); + memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); +#endif +} + +void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + CFG80211_DEV_WARN_ON(!wdev->ssid_len); + + ev = kzalloc(sizeof(*ev), gfp); + if (!ev) + return; + + ev->type = EVENT_IBSS_JOINED; + memcpy(ev->cr.bssid, bssid, ETH_ALEN); + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + queue_work(cfg80211_wq, &rdev->event_work); +} +EXPORT_SYMBOL(cfg80211_ibss_joined); + +int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (wdev->ssid_len) + return -EALREADY; + + if (!params->basic_rates) { + /* + * If no rates were explicitly configured, + * use the mandatory rate set for 11b or + * 11a for maximum compatibility. + */ + struct ieee80211_supported_band *sband = + rdev->wiphy.bands[params->channel->band]; + int j; + u32 flag = params->channel->band == IEEE80211_BAND_5GHZ ? + IEEE80211_RATE_MANDATORY_A : + IEEE80211_RATE_MANDATORY_B; + + for (j = 0; j < sband->n_bitrates; j++) { + if (sband->bitrates[j].flags & flag) + params->basic_rates |= BIT(j); + } + } + + if (WARN_ON(wdev->connect_keys)) + kfree(wdev->connect_keys); + wdev->connect_keys = connkeys; + +#ifdef CONFIG_CFG80211_WEXT + wdev->wext.ibss.channel = params->channel; +#endif + err = rdev->ops->join_ibss(&rdev->wiphy, dev, params); + if (err) { + wdev->connect_keys = NULL; + return err; + } + + memcpy(wdev->ssid, params->ssid, params->ssid_len); + wdev->ssid_len = params->ssid_len; + + return 0; +} + +int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + mutex_lock(&rdev->devlist_mtx); + wdev_lock(wdev); + err = __cfg80211_join_ibss(rdev, dev, params, connkeys); + wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); + + return err; +} + +static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + int i; + + ASSERT_WDEV_LOCK(wdev); + + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + + /* + * Delete all the keys ... pairwise keys can't really + * exist any more anyway, but default keys might. + */ + if (rdev->ops->del_key) + for (i = 0; i < 6; i++) + rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL); + + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + } + + wdev->current_bss = NULL; + wdev->ssid_len = 0; +#ifdef CONFIG_CFG80211_WEXT + if (!nowext) + wdev->wext.ibss.ssid_len = 0; +#endif +} + +void cfg80211_clear_ibss(struct net_device *dev, bool nowext) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + wdev_lock(wdev); + __cfg80211_clear_ibss(dev, nowext); + wdev_unlock(wdev); +} + +int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool nowext) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (!wdev->ssid_len) + return -ENOLINK; + + err = rdev->ops->leave_ibss(&rdev->wiphy, dev); + + if (err) + return err; + + __cfg80211_clear_ibss(dev, nowext); + + return 0; +} + +int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool nowext) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_leave_ibss(rdev, dev, nowext); + wdev_unlock(wdev); + + return err; +} + +#ifdef CONFIG_CFG80211_WEXT +int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + struct cfg80211_cached_keys *ck = NULL; + enum ieee80211_band band; + int i, err; + + ASSERT_WDEV_LOCK(wdev); + + if (!wdev->wext.ibss.beacon_interval) + wdev->wext.ibss.beacon_interval = 100; + + /* try to find an IBSS channel if none requested ... */ + if (!wdev->wext.ibss.channel) { + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + + sband = rdev->wiphy.bands[band]; + if (!sband) + continue; + + for (i = 0; i < sband->n_channels; i++) { + chan = &sband->channels[i]; + if (chan->flags & IEEE80211_CHAN_NO_IBSS) + continue; + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + wdev->wext.ibss.channel = chan; + break; + } + + if (wdev->wext.ibss.channel) + break; + } + + if (!wdev->wext.ibss.channel) + return -EINVAL; + } + + /* don't join -- SSID is not there */ + if (!wdev->wext.ibss.ssid_len) + return 0; + + if (!netif_running(wdev->netdev)) + return 0; + + if (wdev->wext.keys) { + wdev->wext.keys->def = wdev->wext.default_key; + wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key; + } + + wdev->wext.ibss.privacy = wdev->wext.default_key != -1; + + if (wdev->wext.keys) { + ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); + if (!ck) + return -ENOMEM; + for (i = 0; i < 6; i++) + ck->params[i].key = ck->data[i]; + } + err = __cfg80211_join_ibss(rdev, wdev->netdev, + &wdev->wext.ibss, ck); + if (err) + kfree(ck); + + return err; +} + +int cfg80211_ibss_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *wextfreq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct ieee80211_channel *chan = NULL; + int err, freq; + + /* call only for ibss! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) + return -EINVAL; + + if (!rdev->ops->join_ibss) + return -EOPNOTSUPP; + + freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); + if (freq < 0) + return freq; + + if (freq) { + chan = ieee80211_get_channel(wdev->wiphy, freq); + if (!chan) + return -EINVAL; + if (chan->flags & IEEE80211_CHAN_NO_IBSS || + chan->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; + } + + if (wdev->wext.ibss.channel == chan) + return 0; + + wdev_lock(wdev); + err = 0; + if (wdev->ssid_len) + err = __cfg80211_leave_ibss(rdev, dev, true); + wdev_unlock(wdev); + + if (err) + return err; + + if (chan) { + wdev->wext.ibss.channel = chan; + wdev->wext.ibss.channel_fixed = true; + } else { + /* cfg80211_ibss_wext_join will pick one if needed */ + wdev->wext.ibss.channel_fixed = false; + } + + mutex_lock(&rdev->devlist_mtx); + wdev_lock(wdev); + err = cfg80211_ibss_wext_join(rdev, wdev); + wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); + + return err; +} + +int cfg80211_ibss_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct ieee80211_channel *chan = NULL; + + /* call only for ibss! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) + return -EINVAL; + + wdev_lock(wdev); + if (wdev->current_bss) + chan = wdev->current_bss->pub.channel; + else if (wdev->wext.ibss.channel) + chan = wdev->wext.ibss.channel; + wdev_unlock(wdev); + + if (chan) { + freq->m = chan->center_freq; + freq->e = 6; + return 0; + } + + /* no channel if not joining */ + return -EINVAL; +} + +int cfg80211_ibss_wext_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + size_t len = data->length; + int err; + + /* call only for ibss! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) + return -EINVAL; + + if (!rdev->ops->join_ibss) + return -EOPNOTSUPP; + + wdev_lock(wdev); + err = 0; + if (wdev->ssid_len) + err = __cfg80211_leave_ibss(rdev, dev, true); + wdev_unlock(wdev); + + if (err) + return err; + + /* iwconfig uses nul termination in SSID.. */ + if (len > 0 && ssid[len - 1] == '\0') + len--; + + wdev->wext.ibss.ssid = wdev->ssid; + memcpy(wdev->wext.ibss.ssid, ssid, len); + wdev->wext.ibss.ssid_len = len; + + mutex_lock(&rdev->devlist_mtx); + wdev_lock(wdev); + err = cfg80211_ibss_wext_join(rdev, wdev); + wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); + + return err; +} + +int cfg80211_ibss_wext_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + /* call only for ibss! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) + return -EINVAL; + + data->flags = 0; + + wdev_lock(wdev); + if (wdev->ssid_len) { + data->flags = 1; + data->length = wdev->ssid_len; + memcpy(ssid, wdev->ssid, data->length); + } else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) { + data->flags = 1; + data->length = wdev->wext.ibss.ssid_len; + memcpy(ssid, wdev->wext.ibss.ssid, data->length); + } + wdev_unlock(wdev); + + return 0; +} + +int cfg80211_ibss_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + u8 *bssid = ap_addr->sa_data; + int err; + + /* call only for ibss! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) + return -EINVAL; + + if (!rdev->ops->join_ibss) + return -EOPNOTSUPP; + + if (ap_addr->sa_family != ARPHRD_ETHER) + return -EINVAL; + + /* automatic mode */ + if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) + bssid = NULL; + + /* both automatic */ + if (!bssid && !wdev->wext.ibss.bssid) + return 0; + + /* fixed already - and no change */ + if (wdev->wext.ibss.bssid && bssid && + compare_ether_addr(bssid, wdev->wext.ibss.bssid) == 0) + return 0; + + wdev_lock(wdev); + err = 0; + if (wdev->ssid_len) + err = __cfg80211_leave_ibss(rdev, dev, true); + wdev_unlock(wdev); + + if (err) + return err; + + if (bssid) { + memcpy(wdev->wext.bssid, bssid, ETH_ALEN); + wdev->wext.ibss.bssid = wdev->wext.bssid; + } else + wdev->wext.ibss.bssid = NULL; + + mutex_lock(&rdev->devlist_mtx); + wdev_lock(wdev); + err = cfg80211_ibss_wext_join(rdev, wdev); + wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); + + return err; +} + +int cfg80211_ibss_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + /* call only for ibss! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) + return -EINVAL; + + ap_addr->sa_family = ARPHRD_ETHER; + + wdev_lock(wdev); + if (wdev->current_bss) + memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); + else if (wdev->wext.ibss.bssid) + memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN); + else + memset(ap_addr->sa_data, 0, ETH_ALEN); + + wdev_unlock(wdev); + + return 0; +} +#endif diff --git a/net/wireless_ath/lib80211.c b/net/wireless_ath/lib80211.c new file mode 100644 index 00000000..a55c27b7 --- /dev/null +++ b/net/wireless_ath/lib80211.c @@ -0,0 +1,289 @@ +/* + * lib80211 -- common bits for IEEE802.11 drivers + * + * Copyright(c) 2008 John W. Linville + * + * Portions copied from old ieee80211 component, w/ original copyright + * notices below: + * + * Host AP crypto routines + * + * Copyright (c) 2002-2003, Jouni Malinen + * Portions Copyright (C) 2004, Intel Corporation + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRV_NAME "lib80211" + +#define DRV_DESCRIPTION "common routines for IEEE802.11 drivers" + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_AUTHOR("John W. Linville "); +MODULE_LICENSE("GPL"); + +struct lib80211_crypto_alg { + struct list_head list; + struct lib80211_crypto_ops *ops; +}; + +static LIST_HEAD(lib80211_crypto_algs); +static DEFINE_SPINLOCK(lib80211_crypto_lock); + +static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, + int force); +static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info); +static void lib80211_crypt_deinit_handler(unsigned long data); + +const char *print_ssid(char *buf, const char *ssid, u8 ssid_len) +{ + const char *s = ssid; + char *d = buf; + + ssid_len = min_t(u8, ssid_len, IEEE80211_MAX_SSID_LEN); + while (ssid_len--) { + if (isprint(*s)) { + *d++ = *s++; + continue; + } + + *d++ = '\\'; + if (*s == '\0') + *d++ = '0'; + else if (*s == '\n') + *d++ = 'n'; + else if (*s == '\r') + *d++ = 'r'; + else if (*s == '\t') + *d++ = 't'; + else if (*s == '\\') + *d++ = '\\'; + else + d += snprintf(d, 3, "%03o", *s); + s++; + } + *d = '\0'; + return buf; +} +EXPORT_SYMBOL(print_ssid); + +int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name, + spinlock_t *lock) +{ + memset(info, 0, sizeof(*info)); + + info->name = name; + info->lock = lock; + + INIT_LIST_HEAD(&info->crypt_deinit_list); + setup_timer(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler, + (unsigned long)info); + + return 0; +} +EXPORT_SYMBOL(lib80211_crypt_info_init); + +void lib80211_crypt_info_free(struct lib80211_crypt_info *info) +{ + int i; + + lib80211_crypt_quiescing(info); + del_timer_sync(&info->crypt_deinit_timer); + lib80211_crypt_deinit_entries(info, 1); + + for (i = 0; i < NUM_WEP_KEYS; i++) { + struct lib80211_crypt_data *crypt = info->crypt[i]; + if (crypt) { + if (crypt->ops) { + crypt->ops->deinit(crypt->priv); + module_put(crypt->ops->owner); + } + kfree(crypt); + info->crypt[i] = NULL; + } + } +} +EXPORT_SYMBOL(lib80211_crypt_info_free); + +static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, + int force) +{ + struct lib80211_crypt_data *entry, *next; + unsigned long flags; + + spin_lock_irqsave(info->lock, flags); + list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) { + if (atomic_read(&entry->refcnt) != 0 && !force) + continue; + + list_del(&entry->list); + + if (entry->ops) { + entry->ops->deinit(entry->priv); + module_put(entry->ops->owner); + } + kfree(entry); + } + spin_unlock_irqrestore(info->lock, flags); +} + +/* After this, crypt_deinit_list won't accept new members */ +static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info) +{ + unsigned long flags; + + spin_lock_irqsave(info->lock, flags); + info->crypt_quiesced = 1; + spin_unlock_irqrestore(info->lock, flags); +} + +static void lib80211_crypt_deinit_handler(unsigned long data) +{ + struct lib80211_crypt_info *info = (struct lib80211_crypt_info *)data; + unsigned long flags; + + lib80211_crypt_deinit_entries(info, 0); + + spin_lock_irqsave(info->lock, flags); + if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) { + printk(KERN_DEBUG "%s: entries remaining in delayed crypt " + "deletion list\n", info->name); + info->crypt_deinit_timer.expires = jiffies + HZ; + add_timer(&info->crypt_deinit_timer); + } + spin_unlock_irqrestore(info->lock, flags); +} + +void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info, + struct lib80211_crypt_data **crypt) +{ + struct lib80211_crypt_data *tmp; + unsigned long flags; + + if (*crypt == NULL) + return; + + tmp = *crypt; + *crypt = NULL; + + /* must not run ops->deinit() while there may be pending encrypt or + * decrypt operations. Use a list of delayed deinits to avoid needing + * locking. */ + + spin_lock_irqsave(info->lock, flags); + if (!info->crypt_quiesced) { + list_add(&tmp->list, &info->crypt_deinit_list); + if (!timer_pending(&info->crypt_deinit_timer)) { + info->crypt_deinit_timer.expires = jiffies + HZ; + add_timer(&info->crypt_deinit_timer); + } + } + spin_unlock_irqrestore(info->lock, flags); +} +EXPORT_SYMBOL(lib80211_crypt_delayed_deinit); + +int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops) +{ + unsigned long flags; + struct lib80211_crypto_alg *alg; + + alg = kzalloc(sizeof(*alg), GFP_KERNEL); + if (alg == NULL) + return -ENOMEM; + + alg->ops = ops; + + spin_lock_irqsave(&lib80211_crypto_lock, flags); + list_add(&alg->list, &lib80211_crypto_algs); + spin_unlock_irqrestore(&lib80211_crypto_lock, flags); + + printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'\n", + ops->name); + + return 0; +} +EXPORT_SYMBOL(lib80211_register_crypto_ops); + +int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops) +{ + struct lib80211_crypto_alg *alg; + unsigned long flags; + + spin_lock_irqsave(&lib80211_crypto_lock, flags); + list_for_each_entry(alg, &lib80211_crypto_algs, list) { + if (alg->ops == ops) + goto found; + } + spin_unlock_irqrestore(&lib80211_crypto_lock, flags); + return -EINVAL; + + found: + printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm '%s'\n", + ops->name); + list_del(&alg->list); + spin_unlock_irqrestore(&lib80211_crypto_lock, flags); + kfree(alg); + return 0; +} +EXPORT_SYMBOL(lib80211_unregister_crypto_ops); + +struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name) +{ + struct lib80211_crypto_alg *alg; + unsigned long flags; + + spin_lock_irqsave(&lib80211_crypto_lock, flags); + list_for_each_entry(alg, &lib80211_crypto_algs, list) { + if (strcmp(alg->ops->name, name) == 0) + goto found; + } + spin_unlock_irqrestore(&lib80211_crypto_lock, flags); + return NULL; + + found: + spin_unlock_irqrestore(&lib80211_crypto_lock, flags); + return alg->ops; +} +EXPORT_SYMBOL(lib80211_get_crypto_ops); + +static void *lib80211_crypt_null_init(int keyidx) +{ + return (void *)1; +} + +static void lib80211_crypt_null_deinit(void *priv) +{ +} + +static struct lib80211_crypto_ops lib80211_crypt_null = { + .name = "NULL", + .init = lib80211_crypt_null_init, + .deinit = lib80211_crypt_null_deinit, + .owner = THIS_MODULE, +}; + +static int __init lib80211_init(void) +{ + pr_info(DRV_DESCRIPTION "\n"); + return lib80211_register_crypto_ops(&lib80211_crypt_null); +} + +static void __exit lib80211_exit(void) +{ + lib80211_unregister_crypto_ops(&lib80211_crypt_null); + BUG_ON(!list_empty(&lib80211_crypto_algs)); +} + +module_init(lib80211_init); +module_exit(lib80211_exit); diff --git a/net/wireless_ath/lib80211_crypt_ccmp.c b/net/wireless_ath/lib80211_crypt_ccmp.c new file mode 100644 index 00000000..755738d2 --- /dev/null +++ b/net/wireless_ath/lib80211_crypt_ccmp.c @@ -0,0 +1,491 @@ +/* + * lib80211 crypt: host-based CCMP encryption implementation for lib80211 + * + * Copyright (c) 2003-2004, Jouni Malinen + * Copyright (c) 2008, John W. Linville + * + * 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. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypt: CCMP"); +MODULE_LICENSE("GPL"); + +#define AES_BLOCK_LEN 16 +#define CCMP_HDR_LEN 8 +#define CCMP_MIC_LEN 8 +#define CCMP_TK_LEN 16 +#define CCMP_PN_LEN 6 + +struct lib80211_ccmp_data { + u8 key[CCMP_TK_LEN]; + int key_set; + + u8 tx_pn[CCMP_PN_LEN]; + u8 rx_pn[CCMP_PN_LEN]; + + u32 dot11RSNAStatsCCMPFormatErrors; + u32 dot11RSNAStatsCCMPReplays; + u32 dot11RSNAStatsCCMPDecryptErrors; + + int key_idx; + + struct crypto_cipher *tfm; + + /* scratch buffers for virt_to_page() (crypto API) */ + u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN], + tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN]; + u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN]; +}; + +static inline void lib80211_ccmp_aes_encrypt(struct crypto_cipher *tfm, + const u8 pt[16], u8 ct[16]) +{ + crypto_cipher_encrypt_one(tfm, ct, pt); +} + +static void *lib80211_ccmp_init(int key_idx) +{ + struct lib80211_ccmp_data *priv; + + priv = kzalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + priv->key_idx = key_idx; + + priv->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(priv->tfm)) { + priv->tfm = NULL; + goto fail; + } + + return priv; + + fail: + if (priv) { + if (priv->tfm) + crypto_free_cipher(priv->tfm); + kfree(priv); + } + + return NULL; +} + +static void lib80211_ccmp_deinit(void *priv) +{ + struct lib80211_ccmp_data *_priv = priv; + if (_priv && _priv->tfm) + crypto_free_cipher(_priv->tfm); + kfree(priv); +} + +static inline void xor_block(u8 * b, u8 * a, size_t len) +{ + int i; + for (i = 0; i < len; i++) + b[i] ^= a[i]; +} + +static void ccmp_init_blocks(struct crypto_cipher *tfm, + struct ieee80211_hdr *hdr, + u8 * pn, size_t dlen, u8 * b0, u8 * auth, u8 * s0) +{ + u8 *pos, qc = 0; + size_t aad_len; + int a4_included, qc_included; + u8 aad[2 * AES_BLOCK_LEN]; + + a4_included = ieee80211_has_a4(hdr->frame_control); + qc_included = ieee80211_is_data_qos(hdr->frame_control); + + aad_len = 22; + if (a4_included) + aad_len += 6; + if (qc_included) { + pos = (u8 *) & hdr->addr4; + if (a4_included) + pos += 6; + qc = *pos & 0x0f; + aad_len += 2; + } + + /* CCM Initial Block: + * Flag (Include authentication header, M=3 (8-octet MIC), + * L=1 (2-octet Dlen)) + * Nonce: 0x00 | A2 | PN + * Dlen */ + b0[0] = 0x59; + b0[1] = qc; + memcpy(b0 + 2, hdr->addr2, ETH_ALEN); + memcpy(b0 + 8, pn, CCMP_PN_LEN); + b0[14] = (dlen >> 8) & 0xff; + b0[15] = dlen & 0xff; + + /* AAD: + * FC with bits 4..6 and 11..13 masked to zero; 14 is always one + * A1 | A2 | A3 + * SC with bits 4..15 (seq#) masked to zero + * A4 (if present) + * QC (if present) + */ + pos = (u8 *) hdr; + aad[0] = 0; /* aad_len >> 8 */ + aad[1] = aad_len & 0xff; + aad[2] = pos[0] & 0x8f; + aad[3] = pos[1] & 0xc7; + memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN); + pos = (u8 *) & hdr->seq_ctrl; + aad[22] = pos[0] & 0x0f; + aad[23] = 0; /* all bits masked */ + memset(aad + 24, 0, 8); + if (a4_included) + memcpy(aad + 24, hdr->addr4, ETH_ALEN); + if (qc_included) { + aad[a4_included ? 30 : 24] = qc; + /* rest of QC masked */ + } + + /* Start with the first block and AAD */ + lib80211_ccmp_aes_encrypt(tfm, b0, auth); + xor_block(auth, aad, AES_BLOCK_LEN); + lib80211_ccmp_aes_encrypt(tfm, auth, auth); + xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); + lib80211_ccmp_aes_encrypt(tfm, auth, auth); + b0[0] &= 0x07; + b0[14] = b0[15] = 0; + lib80211_ccmp_aes_encrypt(tfm, b0, s0); +} + +static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len, + u8 *aeskey, int keylen, void *priv) +{ + struct lib80211_ccmp_data *key = priv; + int i; + u8 *pos; + + if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len) + return -1; + + if (aeskey != NULL && keylen >= CCMP_TK_LEN) + memcpy(aeskey, key->key, CCMP_TK_LEN); + + pos = skb_push(skb, CCMP_HDR_LEN); + memmove(pos, pos + CCMP_HDR_LEN, hdr_len); + pos += hdr_len; + + i = CCMP_PN_LEN - 1; + while (i >= 0) { + key->tx_pn[i]++; + if (key->tx_pn[i] != 0) + break; + i--; + } + + *pos++ = key->tx_pn[5]; + *pos++ = key->tx_pn[4]; + *pos++ = 0; + *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */ ; + *pos++ = key->tx_pn[3]; + *pos++ = key->tx_pn[2]; + *pos++ = key->tx_pn[1]; + *pos++ = key->tx_pn[0]; + + return CCMP_HDR_LEN; +} + +static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct lib80211_ccmp_data *key = priv; + int data_len, i, blocks, last, len; + u8 *pos, *mic; + struct ieee80211_hdr *hdr; + u8 *b0 = key->tx_b0; + u8 *b = key->tx_b; + u8 *e = key->tx_e; + u8 *s0 = key->tx_s0; + + if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len) + return -1; + + data_len = skb->len - hdr_len; + len = lib80211_ccmp_hdr(skb, hdr_len, NULL, 0, priv); + if (len < 0) + return -1; + + pos = skb->data + hdr_len + CCMP_HDR_LEN; + hdr = (struct ieee80211_hdr *)skb->data; + ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); + + blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); + last = data_len % AES_BLOCK_LEN; + + for (i = 1; i <= blocks; i++) { + len = (i == blocks && last) ? last : AES_BLOCK_LEN; + /* Authentication */ + xor_block(b, pos, len); + lib80211_ccmp_aes_encrypt(key->tfm, b, b); + /* Encryption, with counter */ + b0[14] = (i >> 8) & 0xff; + b0[15] = i & 0xff; + lib80211_ccmp_aes_encrypt(key->tfm, b0, e); + xor_block(pos, e, len); + pos += len; + } + + mic = skb_put(skb, CCMP_MIC_LEN); + for (i = 0; i < CCMP_MIC_LEN; i++) + mic[i] = b[i] ^ s0[i]; + + return 0; +} + +/* + * deal with seq counter wrapping correctly. + * refer to timer_after() for jiffies wrapping handling + */ +static inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o) +{ + u32 iv32_n, iv16_n; + u32 iv32_o, iv16_o; + + iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3]; + iv16_n = (pn_n[4] << 8) | pn_n[5]; + + iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3]; + iv16_o = (pn_o[4] << 8) | pn_o[5]; + + if ((s32)iv32_n - (s32)iv32_o < 0 || + (iv32_n == iv32_o && iv16_n <= iv16_o)) + return 1; + return 0; +} + +static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct lib80211_ccmp_data *key = priv; + u8 keyidx, *pos; + struct ieee80211_hdr *hdr; + u8 *b0 = key->rx_b0; + u8 *b = key->rx_b; + u8 *a = key->rx_a; + u8 pn[6]; + int i, blocks, last, len; + size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN; + u8 *mic = skb->data + skb->len - CCMP_MIC_LEN; + + if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) { + key->dot11RSNAStatsCCMPFormatErrors++; + return -1; + } + + hdr = (struct ieee80211_hdr *)skb->data; + pos = skb->data + hdr_len; + keyidx = pos[3]; + if (!(keyidx & (1 << 5))) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: received packet without ExtIV" + " flag from %pM\n", hdr->addr2); + } + key->dot11RSNAStatsCCMPFormatErrors++; + return -2; + } + keyidx >>= 6; + if (key->key_idx != keyidx) { + printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame " + "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv); + return -6; + } + if (!key->key_set) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: received packet from %pM" + " with keyid=%d that does not have a configured" + " key\n", hdr->addr2, keyidx); + } + return -3; + } + + pn[0] = pos[7]; + pn[1] = pos[6]; + pn[2] = pos[5]; + pn[3] = pos[4]; + pn[4] = pos[1]; + pn[5] = pos[0]; + pos += 8; + + if (ccmp_replay_check(pn, key->rx_pn)) { +#ifdef CONFIG_LIB80211_DEBUG + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: replay detected: STA=%pM " + "previous PN %02x%02x%02x%02x%02x%02x " + "received PN %02x%02x%02x%02x%02x%02x\n", + hdr->addr2, + key->rx_pn[0], key->rx_pn[1], key->rx_pn[2], + key->rx_pn[3], key->rx_pn[4], key->rx_pn[5], + pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]); + } +#endif + key->dot11RSNAStatsCCMPReplays++; + return -4; + } + + ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b); + xor_block(mic, b, CCMP_MIC_LEN); + + blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); + last = data_len % AES_BLOCK_LEN; + + for (i = 1; i <= blocks; i++) { + len = (i == blocks && last) ? last : AES_BLOCK_LEN; + /* Decrypt, with counter */ + b0[14] = (i >> 8) & 0xff; + b0[15] = i & 0xff; + lib80211_ccmp_aes_encrypt(key->tfm, b0, b); + xor_block(pos, b, len); + /* Authentication */ + xor_block(a, pos, len); + lib80211_ccmp_aes_encrypt(key->tfm, a, a); + pos += len; + } + + if (memcmp(mic, a, CCMP_MIC_LEN) != 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: decrypt failed: STA=" + "%pM\n", hdr->addr2); + } + key->dot11RSNAStatsCCMPDecryptErrors++; + return -5; + } + + memcpy(key->rx_pn, pn, CCMP_PN_LEN); + + /* Remove hdr and MIC */ + memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len); + skb_pull(skb, CCMP_HDR_LEN); + skb_trim(skb, skb->len - CCMP_MIC_LEN); + + return keyidx; +} + +static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv) +{ + struct lib80211_ccmp_data *data = priv; + int keyidx; + struct crypto_cipher *tfm = data->tfm; + + keyidx = data->key_idx; + memset(data, 0, sizeof(*data)); + data->key_idx = keyidx; + data->tfm = tfm; + if (len == CCMP_TK_LEN) { + memcpy(data->key, key, CCMP_TK_LEN); + data->key_set = 1; + if (seq) { + data->rx_pn[0] = seq[5]; + data->rx_pn[1] = seq[4]; + data->rx_pn[2] = seq[3]; + data->rx_pn[3] = seq[2]; + data->rx_pn[4] = seq[1]; + data->rx_pn[5] = seq[0]; + } + crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN); + } else if (len == 0) + data->key_set = 0; + else + return -1; + + return 0; +} + +static int lib80211_ccmp_get_key(void *key, int len, u8 * seq, void *priv) +{ + struct lib80211_ccmp_data *data = priv; + + if (len < CCMP_TK_LEN) + return -1; + + if (!data->key_set) + return 0; + memcpy(key, data->key, CCMP_TK_LEN); + + if (seq) { + seq[0] = data->tx_pn[5]; + seq[1] = data->tx_pn[4]; + seq[2] = data->tx_pn[3]; + seq[3] = data->tx_pn[2]; + seq[4] = data->tx_pn[1]; + seq[5] = data->tx_pn[0]; + } + + return CCMP_TK_LEN; +} + +static char *lib80211_ccmp_print_stats(char *p, void *priv) +{ + struct lib80211_ccmp_data *ccmp = priv; + + p += sprintf(p, "key[%d] alg=CCMP key_set=%d " + "tx_pn=%02x%02x%02x%02x%02x%02x " + "rx_pn=%02x%02x%02x%02x%02x%02x " + "format_errors=%d replays=%d decrypt_errors=%d\n", + ccmp->key_idx, ccmp->key_set, + ccmp->tx_pn[0], ccmp->tx_pn[1], ccmp->tx_pn[2], + ccmp->tx_pn[3], ccmp->tx_pn[4], ccmp->tx_pn[5], + ccmp->rx_pn[0], ccmp->rx_pn[1], ccmp->rx_pn[2], + ccmp->rx_pn[3], ccmp->rx_pn[4], ccmp->rx_pn[5], + ccmp->dot11RSNAStatsCCMPFormatErrors, + ccmp->dot11RSNAStatsCCMPReplays, + ccmp->dot11RSNAStatsCCMPDecryptErrors); + + return p; +} + +static struct lib80211_crypto_ops lib80211_crypt_ccmp = { + .name = "CCMP", + .init = lib80211_ccmp_init, + .deinit = lib80211_ccmp_deinit, + .encrypt_mpdu = lib80211_ccmp_encrypt, + .decrypt_mpdu = lib80211_ccmp_decrypt, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = lib80211_ccmp_set_key, + .get_key = lib80211_ccmp_get_key, + .print_stats = lib80211_ccmp_print_stats, + .extra_mpdu_prefix_len = CCMP_HDR_LEN, + .extra_mpdu_postfix_len = CCMP_MIC_LEN, + .owner = THIS_MODULE, +}; + +static int __init lib80211_crypto_ccmp_init(void) +{ + return lib80211_register_crypto_ops(&lib80211_crypt_ccmp); +} + +static void __exit lib80211_crypto_ccmp_exit(void) +{ + lib80211_unregister_crypto_ops(&lib80211_crypt_ccmp); +} + +module_init(lib80211_crypto_ccmp_init); +module_exit(lib80211_crypto_ccmp_exit); diff --git a/net/wireless_ath/lib80211_crypt_tkip.c b/net/wireless_ath/lib80211_crypt_tkip.c new file mode 100644 index 00000000..38734846 --- /dev/null +++ b/net/wireless_ath/lib80211_crypt_tkip.c @@ -0,0 +1,780 @@ +/* + * lib80211 crypt: host-based TKIP encryption implementation for lib80211 + * + * Copyright (c) 2003-2004, Jouni Malinen + * Copyright (c) 2008, John W. Linville + * + * 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. See README and COPYING for + * more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("lib80211 crypt: TKIP"); +MODULE_LICENSE("GPL"); + +#define TKIP_HDR_LEN 8 + +struct lib80211_tkip_data { +#define TKIP_KEY_LEN 32 + u8 key[TKIP_KEY_LEN]; + int key_set; + + u32 tx_iv32; + u16 tx_iv16; + u16 tx_ttak[5]; + int tx_phase1_done; + + u32 rx_iv32; + u16 rx_iv16; + u16 rx_ttak[5]; + int rx_phase1_done; + u32 rx_iv32_new; + u16 rx_iv16_new; + + u32 dot11RSNAStatsTKIPReplays; + u32 dot11RSNAStatsTKIPICVErrors; + u32 dot11RSNAStatsTKIPLocalMICFailures; + + int key_idx; + + struct crypto_blkcipher *rx_tfm_arc4; + struct crypto_hash *rx_tfm_michael; + struct crypto_blkcipher *tx_tfm_arc4; + struct crypto_hash *tx_tfm_michael; + + /* scratch buffers for virt_to_page() (crypto API) */ + u8 rx_hdr[16], tx_hdr[16]; + + unsigned long flags; +}; + +static unsigned long lib80211_tkip_set_flags(unsigned long flags, void *priv) +{ + struct lib80211_tkip_data *_priv = priv; + unsigned long old_flags = _priv->flags; + _priv->flags = flags; + return old_flags; +} + +static unsigned long lib80211_tkip_get_flags(void *priv) +{ + struct lib80211_tkip_data *_priv = priv; + return _priv->flags; +} + +static void *lib80211_tkip_init(int key_idx) +{ + struct lib80211_tkip_data *priv; + + priv = kzalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + + priv->key_idx = key_idx; + + priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, + CRYPTO_ALG_ASYNC); + if (IS_ERR(priv->tx_tfm_arc4)) { + priv->tx_tfm_arc4 = NULL; + goto fail; + } + + priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0, + CRYPTO_ALG_ASYNC); + if (IS_ERR(priv->tx_tfm_michael)) { + priv->tx_tfm_michael = NULL; + goto fail; + } + + priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, + CRYPTO_ALG_ASYNC); + if (IS_ERR(priv->rx_tfm_arc4)) { + priv->rx_tfm_arc4 = NULL; + goto fail; + } + + priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0, + CRYPTO_ALG_ASYNC); + if (IS_ERR(priv->rx_tfm_michael)) { + priv->rx_tfm_michael = NULL; + goto fail; + } + + return priv; + + fail: + if (priv) { + if (priv->tx_tfm_michael) + crypto_free_hash(priv->tx_tfm_michael); + if (priv->tx_tfm_arc4) + crypto_free_blkcipher(priv->tx_tfm_arc4); + if (priv->rx_tfm_michael) + crypto_free_hash(priv->rx_tfm_michael); + if (priv->rx_tfm_arc4) + crypto_free_blkcipher(priv->rx_tfm_arc4); + kfree(priv); + } + + return NULL; +} + +static void lib80211_tkip_deinit(void *priv) +{ + struct lib80211_tkip_data *_priv = priv; + if (_priv) { + if (_priv->tx_tfm_michael) + crypto_free_hash(_priv->tx_tfm_michael); + if (_priv->tx_tfm_arc4) + crypto_free_blkcipher(_priv->tx_tfm_arc4); + if (_priv->rx_tfm_michael) + crypto_free_hash(_priv->rx_tfm_michael); + if (_priv->rx_tfm_arc4) + crypto_free_blkcipher(_priv->rx_tfm_arc4); + } + kfree(priv); +} + +static inline u16 RotR1(u16 val) +{ + return (val >> 1) | (val << 15); +} + +static inline u8 Lo8(u16 val) +{ + return val & 0xff; +} + +static inline u8 Hi8(u16 val) +{ + return val >> 8; +} + +static inline u16 Lo16(u32 val) +{ + return val & 0xffff; +} + +static inline u16 Hi16(u32 val) +{ + return val >> 16; +} + +static inline u16 Mk16(u8 hi, u8 lo) +{ + return lo | (((u16) hi) << 8); +} + +static inline u16 Mk16_le(__le16 * v) +{ + return le16_to_cpu(*v); +} + +static const u16 Sbox[256] = { + 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, + 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, + 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, + 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, + 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, + 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, + 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, + 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, + 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, + 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, + 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, + 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, + 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, + 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, + 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, + 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, + 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, + 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, + 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, + 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, + 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, + 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, + 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, + 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, + 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, + 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, + 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, + 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, + 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, + 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, + 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, + 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, +}; + +static inline u16 _S_(u16 v) +{ + u16 t = Sbox[Hi8(v)]; + return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8)); +} + +#define PHASE1_LOOP_COUNT 8 + +static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA, + u32 IV32) +{ + int i, j; + + /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */ + TTAK[0] = Lo16(IV32); + TTAK[1] = Hi16(IV32); + TTAK[2] = Mk16(TA[1], TA[0]); + TTAK[3] = Mk16(TA[3], TA[2]); + TTAK[4] = Mk16(TA[5], TA[4]); + + for (i = 0; i < PHASE1_LOOP_COUNT; i++) { + j = 2 * (i & 1); + TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j])); + TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j])); + TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j])); + TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j])); + TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i; + } +} + +static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK, + u16 IV16) +{ + /* Make temporary area overlap WEP seed so that the final copy can be + * avoided on little endian hosts. */ + u16 *PPK = (u16 *) & WEPSeed[4]; + + /* Step 1 - make copy of TTAK and bring in TSC */ + PPK[0] = TTAK[0]; + PPK[1] = TTAK[1]; + PPK[2] = TTAK[2]; + PPK[3] = TTAK[3]; + PPK[4] = TTAK[4]; + PPK[5] = TTAK[4] + IV16; + + /* Step 2 - 96-bit bijective mixing using S-box */ + PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0])); + PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2])); + PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4])); + PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6])); + PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8])); + PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10])); + + PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12])); + PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14])); + PPK[2] += RotR1(PPK[1]); + PPK[3] += RotR1(PPK[2]); + PPK[4] += RotR1(PPK[3]); + PPK[5] += RotR1(PPK[4]); + + /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value + * WEPSeed[0..2] is transmitted as WEP IV */ + WEPSeed[0] = Hi8(IV16); + WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F; + WEPSeed[2] = Lo8(IV16); + WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1); + +#ifdef __BIG_ENDIAN + { + int i; + for (i = 0; i < 6; i++) + PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8); + } +#endif +} + +static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len, + u8 * rc4key, int keylen, void *priv) +{ + struct lib80211_tkip_data *tkey = priv; + u8 *pos; + struct ieee80211_hdr *hdr; + + hdr = (struct ieee80211_hdr *)skb->data; + + if (skb_headroom(skb) < TKIP_HDR_LEN || skb->len < hdr_len) + return -1; + + if (rc4key == NULL || keylen < 16) + return -1; + + if (!tkey->tx_phase1_done) { + tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2, + tkey->tx_iv32); + tkey->tx_phase1_done = 1; + } + tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16); + + pos = skb_push(skb, TKIP_HDR_LEN); + memmove(pos, pos + TKIP_HDR_LEN, hdr_len); + pos += hdr_len; + + *pos++ = *rc4key; + *pos++ = *(rc4key + 1); + *pos++ = *(rc4key + 2); + *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ; + *pos++ = tkey->tx_iv32 & 0xff; + *pos++ = (tkey->tx_iv32 >> 8) & 0xff; + *pos++ = (tkey->tx_iv32 >> 16) & 0xff; + *pos++ = (tkey->tx_iv32 >> 24) & 0xff; + + tkey->tx_iv16++; + if (tkey->tx_iv16 == 0) { + tkey->tx_phase1_done = 0; + tkey->tx_iv32++; + } + + return TKIP_HDR_LEN; +} + +static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct lib80211_tkip_data *tkey = priv; + struct blkcipher_desc desc = { .tfm = tkey->tx_tfm_arc4 }; + int len; + u8 rc4key[16], *pos, *icv; + u32 crc; + struct scatterlist sg; + + if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) { + if (net_ratelimit()) { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *)skb->data; + printk(KERN_DEBUG ": TKIP countermeasures: dropped " + "TX packet to %pM\n", hdr->addr1); + } + return -1; + } + + if (skb_tailroom(skb) < 4 || skb->len < hdr_len) + return -1; + + len = skb->len - hdr_len; + pos = skb->data + hdr_len; + + if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0) + return -1; + + crc = ~crc32_le(~0, pos, len); + icv = skb_put(skb, 4); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + + crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16); + sg_init_one(&sg, pos, len + 4); + return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4); +} + +/* + * deal with seq counter wrapping correctly. + * refer to timer_after() for jiffies wrapping handling + */ +static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n, + u32 iv32_o, u16 iv16_o) +{ + if ((s32)iv32_n - (s32)iv32_o < 0 || + (iv32_n == iv32_o && iv16_n <= iv16_o)) + return 1; + return 0; +} + +static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct lib80211_tkip_data *tkey = priv; + struct blkcipher_desc desc = { .tfm = tkey->rx_tfm_arc4 }; + u8 rc4key[16]; + u8 keyidx, *pos; + u32 iv32; + u16 iv16; + struct ieee80211_hdr *hdr; + u8 icv[4]; + u32 crc; + struct scatterlist sg; + int plen; + + hdr = (struct ieee80211_hdr *)skb->data; + + if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) { + if (net_ratelimit()) { + printk(KERN_DEBUG ": TKIP countermeasures: dropped " + "received packet from %pM\n", hdr->addr2); + } + return -1; + } + + if (skb->len < hdr_len + TKIP_HDR_LEN + 4) + return -1; + + pos = skb->data + hdr_len; + keyidx = pos[3]; + if (!(keyidx & (1 << 5))) { + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: received packet without ExtIV" + " flag from %pM\n", hdr->addr2); + } + return -2; + } + keyidx >>= 6; + if (tkey->key_idx != keyidx) { + printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame " + "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv); + return -6; + } + if (!tkey->key_set) { + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: received packet from %pM" + " with keyid=%d that does not have a configured" + " key\n", hdr->addr2, keyidx); + } + return -3; + } + iv16 = (pos[0] << 8) | pos[2]; + iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); + pos += TKIP_HDR_LEN; + + if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) { +#ifdef CONFIG_LIB80211_DEBUG + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: replay detected: STA=%pM" + " previous TSC %08x%04x received TSC " + "%08x%04x\n", hdr->addr2, + tkey->rx_iv32, tkey->rx_iv16, iv32, iv16); + } +#endif + tkey->dot11RSNAStatsTKIPReplays++; + return -4; + } + + if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) { + tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32); + tkey->rx_phase1_done = 1; + } + tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16); + + plen = skb->len - hdr_len - 12; + + crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16); + sg_init_one(&sg, pos, plen + 4); + if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) { + if (net_ratelimit()) { + printk(KERN_DEBUG ": TKIP: failed to decrypt " + "received packet from %pM\n", + hdr->addr2); + } + return -7; + } + + crc = ~crc32_le(~0, pos, plen); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + if (memcmp(icv, pos + plen, 4) != 0) { + if (iv32 != tkey->rx_iv32) { + /* Previously cached Phase1 result was already lost, so + * it needs to be recalculated for the next packet. */ + tkey->rx_phase1_done = 0; + } +#ifdef CONFIG_LIB80211_DEBUG + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: ICV error detected: STA=" + "%pM\n", hdr->addr2); + } +#endif + tkey->dot11RSNAStatsTKIPICVErrors++; + return -5; + } + + /* Update real counters only after Michael MIC verification has + * completed */ + tkey->rx_iv32_new = iv32; + tkey->rx_iv16_new = iv16; + + /* Remove IV and ICV */ + memmove(skb->data + TKIP_HDR_LEN, skb->data, hdr_len); + skb_pull(skb, TKIP_HDR_LEN); + skb_trim(skb, skb->len - 4); + + return keyidx; +} + +static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr, + u8 * data, size_t data_len, u8 * mic) +{ + struct hash_desc desc; + struct scatterlist sg[2]; + + if (tfm_michael == NULL) { + pr_warn("%s(): tfm_michael == NULL\n", __func__); + return -1; + } + sg_init_table(sg, 2); + sg_set_buf(&sg[0], hdr, 16); + sg_set_buf(&sg[1], data, data_len); + + if (crypto_hash_setkey(tfm_michael, key, 8)) + return -1; + + desc.tfm = tfm_michael; + desc.flags = 0; + return crypto_hash_digest(&desc, sg, data_len + 16, mic); +} + +static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr) +{ + struct ieee80211_hdr *hdr11; + + hdr11 = (struct ieee80211_hdr *)skb->data; + + switch (le16_to_cpu(hdr11->frame_control) & + (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { + case IEEE80211_FCTL_TODS: + memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ + break; + case IEEE80211_FCTL_FROMDS: + memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */ + break; + case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: + memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */ + break; + case 0: + memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ + break; + } + + if (ieee80211_is_data_qos(hdr11->frame_control)) { + hdr[12] = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(hdr11))) + & IEEE80211_QOS_CTL_TID_MASK; + } else + hdr[12] = 0; /* priority */ + + hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ +} + +static int lib80211_michael_mic_add(struct sk_buff *skb, int hdr_len, + void *priv) +{ + struct lib80211_tkip_data *tkey = priv; + u8 *pos; + + if (skb_tailroom(skb) < 8 || skb->len < hdr_len) { + printk(KERN_DEBUG "Invalid packet for Michael MIC add " + "(tailroom=%d hdr_len=%d skb->len=%d)\n", + skb_tailroom(skb), hdr_len, skb->len); + return -1; + } + + michael_mic_hdr(skb, tkey->tx_hdr); + pos = skb_put(skb, 8); + if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) + return -1; + + return 0; +} + +static void lib80211_michael_mic_failure(struct net_device *dev, + struct ieee80211_hdr *hdr, + int keyidx) +{ + union iwreq_data wrqu; + struct iw_michaelmicfailure ev; + + /* TODO: needed parameters: count, keyid, key type, TSC */ + memset(&ev, 0, sizeof(ev)); + ev.flags = keyidx & IW_MICFAILURE_KEY_ID; + if (hdr->addr1[0] & 0x01) + ev.flags |= IW_MICFAILURE_GROUP; + else + ev.flags |= IW_MICFAILURE_PAIRWISE; + ev.src_addr.sa_family = ARPHRD_ETHER; + memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = sizeof(ev); + wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev); +} + +static int lib80211_michael_mic_verify(struct sk_buff *skb, int keyidx, + int hdr_len, void *priv) +{ + struct lib80211_tkip_data *tkey = priv; + u8 mic[8]; + + if (!tkey->key_set) + return -1; + + michael_mic_hdr(skb, tkey->rx_hdr); + if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, mic)) + return -1; + if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) { + struct ieee80211_hdr *hdr; + hdr = (struct ieee80211_hdr *)skb->data; + printk(KERN_DEBUG "%s: Michael MIC verification failed for " + "MSDU from %pM keyidx=%d\n", + skb->dev ? skb->dev->name : "N/A", hdr->addr2, + keyidx); + if (skb->dev) + lib80211_michael_mic_failure(skb->dev, hdr, keyidx); + tkey->dot11RSNAStatsTKIPLocalMICFailures++; + return -1; + } + + /* Update TSC counters for RX now that the packet verification has + * completed. */ + tkey->rx_iv32 = tkey->rx_iv32_new; + tkey->rx_iv16 = tkey->rx_iv16_new; + + skb_trim(skb, skb->len - 8); + + return 0; +} + +static int lib80211_tkip_set_key(void *key, int len, u8 * seq, void *priv) +{ + struct lib80211_tkip_data *tkey = priv; + int keyidx; + struct crypto_hash *tfm = tkey->tx_tfm_michael; + struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4; + struct crypto_hash *tfm3 = tkey->rx_tfm_michael; + struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4; + + keyidx = tkey->key_idx; + memset(tkey, 0, sizeof(*tkey)); + tkey->key_idx = keyidx; + tkey->tx_tfm_michael = tfm; + tkey->tx_tfm_arc4 = tfm2; + tkey->rx_tfm_michael = tfm3; + tkey->rx_tfm_arc4 = tfm4; + if (len == TKIP_KEY_LEN) { + memcpy(tkey->key, key, TKIP_KEY_LEN); + tkey->key_set = 1; + tkey->tx_iv16 = 1; /* TSC is initialized to 1 */ + if (seq) { + tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) | + (seq[3] << 8) | seq[2]; + tkey->rx_iv16 = (seq[1] << 8) | seq[0]; + } + } else if (len == 0) + tkey->key_set = 0; + else + return -1; + + return 0; +} + +static int lib80211_tkip_get_key(void *key, int len, u8 * seq, void *priv) +{ + struct lib80211_tkip_data *tkey = priv; + + if (len < TKIP_KEY_LEN) + return -1; + + if (!tkey->key_set) + return 0; + memcpy(key, tkey->key, TKIP_KEY_LEN); + + if (seq) { + /* Return the sequence number of the last transmitted frame. */ + u16 iv16 = tkey->tx_iv16; + u32 iv32 = tkey->tx_iv32; + if (iv16 == 0) + iv32--; + iv16--; + seq[0] = tkey->tx_iv16; + seq[1] = tkey->tx_iv16 >> 8; + seq[2] = tkey->tx_iv32; + seq[3] = tkey->tx_iv32 >> 8; + seq[4] = tkey->tx_iv32 >> 16; + seq[5] = tkey->tx_iv32 >> 24; + } + + return TKIP_KEY_LEN; +} + +static char *lib80211_tkip_print_stats(char *p, void *priv) +{ + struct lib80211_tkip_data *tkip = priv; + p += sprintf(p, "key[%d] alg=TKIP key_set=%d " + "tx_pn=%02x%02x%02x%02x%02x%02x " + "rx_pn=%02x%02x%02x%02x%02x%02x " + "replays=%d icv_errors=%d local_mic_failures=%d\n", + tkip->key_idx, tkip->key_set, + (tkip->tx_iv32 >> 24) & 0xff, + (tkip->tx_iv32 >> 16) & 0xff, + (tkip->tx_iv32 >> 8) & 0xff, + tkip->tx_iv32 & 0xff, + (tkip->tx_iv16 >> 8) & 0xff, + tkip->tx_iv16 & 0xff, + (tkip->rx_iv32 >> 24) & 0xff, + (tkip->rx_iv32 >> 16) & 0xff, + (tkip->rx_iv32 >> 8) & 0xff, + tkip->rx_iv32 & 0xff, + (tkip->rx_iv16 >> 8) & 0xff, + tkip->rx_iv16 & 0xff, + tkip->dot11RSNAStatsTKIPReplays, + tkip->dot11RSNAStatsTKIPICVErrors, + tkip->dot11RSNAStatsTKIPLocalMICFailures); + return p; +} + +static struct lib80211_crypto_ops lib80211_crypt_tkip = { + .name = "TKIP", + .init = lib80211_tkip_init, + .deinit = lib80211_tkip_deinit, + .encrypt_mpdu = lib80211_tkip_encrypt, + .decrypt_mpdu = lib80211_tkip_decrypt, + .encrypt_msdu = lib80211_michael_mic_add, + .decrypt_msdu = lib80211_michael_mic_verify, + .set_key = lib80211_tkip_set_key, + .get_key = lib80211_tkip_get_key, + .print_stats = lib80211_tkip_print_stats, + .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */ + .extra_mpdu_postfix_len = 4, /* ICV */ + .extra_msdu_postfix_len = 8, /* MIC */ + .get_flags = lib80211_tkip_get_flags, + .set_flags = lib80211_tkip_set_flags, + .owner = THIS_MODULE, +}; + +static int __init lib80211_crypto_tkip_init(void) +{ + return lib80211_register_crypto_ops(&lib80211_crypt_tkip); +} + +static void __exit lib80211_crypto_tkip_exit(void) +{ + lib80211_unregister_crypto_ops(&lib80211_crypt_tkip); +} + +module_init(lib80211_crypto_tkip_init); +module_exit(lib80211_crypto_tkip_exit); diff --git a/net/wireless_ath/lib80211_crypt_wep.c b/net/wireless_ath/lib80211_crypt_wep.c new file mode 100644 index 00000000..c1304018 --- /dev/null +++ b/net/wireless_ath/lib80211_crypt_wep.c @@ -0,0 +1,290 @@ +/* + * lib80211 crypt: host-based WEP encryption implementation for lib80211 + * + * Copyright (c) 2002-2004, Jouni Malinen + * Copyright (c) 2008, John W. Linville + * + * 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. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("lib80211 crypt: WEP"); +MODULE_LICENSE("GPL"); + +struct lib80211_wep_data { + u32 iv; +#define WEP_KEY_LEN 13 + u8 key[WEP_KEY_LEN + 1]; + u8 key_len; + u8 key_idx; + struct crypto_blkcipher *tx_tfm; + struct crypto_blkcipher *rx_tfm; +}; + +static void *lib80211_wep_init(int keyidx) +{ + struct lib80211_wep_data *priv; + + priv = kzalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + priv->key_idx = keyidx; + + priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(priv->tx_tfm)) { + priv->tx_tfm = NULL; + goto fail; + } + + priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(priv->rx_tfm)) { + priv->rx_tfm = NULL; + goto fail; + } + /* start WEP IV from a random value */ + get_random_bytes(&priv->iv, 4); + + return priv; + + fail: + if (priv) { + if (priv->tx_tfm) + crypto_free_blkcipher(priv->tx_tfm); + if (priv->rx_tfm) + crypto_free_blkcipher(priv->rx_tfm); + kfree(priv); + } + return NULL; +} + +static void lib80211_wep_deinit(void *priv) +{ + struct lib80211_wep_data *_priv = priv; + if (_priv) { + if (_priv->tx_tfm) + crypto_free_blkcipher(_priv->tx_tfm); + if (_priv->rx_tfm) + crypto_free_blkcipher(_priv->rx_tfm); + } + kfree(priv); +} + +/* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */ +static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len, + u8 *key, int keylen, void *priv) +{ + struct lib80211_wep_data *wep = priv; + u32 klen; + u8 *pos; + + if (skb_headroom(skb) < 4 || skb->len < hdr_len) + return -1; + + pos = skb_push(skb, 4); + memmove(pos, pos + 4, hdr_len); + pos += hdr_len; + + klen = 3 + wep->key_len; + + wep->iv++; + + /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key + * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N) + * can be used to speedup attacks, so avoid using them. */ + if ((wep->iv & 0xff00) == 0xff00) { + u8 B = (wep->iv >> 16) & 0xff; + if (B >= 3 && B < klen) + wep->iv += 0x0100; + } + + /* Prepend 24-bit IV to RC4 key and TX frame */ + *pos++ = (wep->iv >> 16) & 0xff; + *pos++ = (wep->iv >> 8) & 0xff; + *pos++ = wep->iv & 0xff; + *pos++ = wep->key_idx << 6; + + return 0; +} + +/* Perform WEP encryption on given skb that has at least 4 bytes of headroom + * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted, + * so the payload length increases with 8 bytes. + * + * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) + */ +static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct lib80211_wep_data *wep = priv; + struct blkcipher_desc desc = { .tfm = wep->tx_tfm }; + u32 crc, klen, len; + u8 *pos, *icv; + struct scatterlist sg; + u8 key[WEP_KEY_LEN + 3]; + + /* other checks are in lib80211_wep_build_iv */ + if (skb_tailroom(skb) < 4) + return -1; + + /* add the IV to the frame */ + if (lib80211_wep_build_iv(skb, hdr_len, NULL, 0, priv)) + return -1; + + /* Copy the IV into the first 3 bytes of the key */ + skb_copy_from_linear_data_offset(skb, hdr_len, key, 3); + + /* Copy rest of the WEP key (the secret part) */ + memcpy(key + 3, wep->key, wep->key_len); + + len = skb->len - hdr_len - 4; + pos = skb->data + hdr_len + 4; + klen = 3 + wep->key_len; + + /* Append little-endian CRC32 over only the data and encrypt it to produce ICV */ + crc = ~crc32_le(~0, pos, len); + icv = skb_put(skb, 4); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + + crypto_blkcipher_setkey(wep->tx_tfm, key, klen); + sg_init_one(&sg, pos, len + 4); + return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4); +} + +/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of + * the frame: IV (4 bytes), encrypted payload (including SNAP header), + * ICV (4 bytes). len includes both IV and ICV. + * + * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on + * failure. If frame is OK, IV and ICV will be removed. + */ +static int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct lib80211_wep_data *wep = priv; + struct blkcipher_desc desc = { .tfm = wep->rx_tfm }; + u32 crc, klen, plen; + u8 key[WEP_KEY_LEN + 3]; + u8 keyidx, *pos, icv[4]; + struct scatterlist sg; + + if (skb->len < hdr_len + 8) + return -1; + + pos = skb->data + hdr_len; + key[0] = *pos++; + key[1] = *pos++; + key[2] = *pos++; + keyidx = *pos++ >> 6; + if (keyidx != wep->key_idx) + return -1; + + klen = 3 + wep->key_len; + + /* Copy rest of the WEP key (the secret part) */ + memcpy(key + 3, wep->key, wep->key_len); + + /* Apply RC4 to data and compute CRC32 over decrypted data */ + plen = skb->len - hdr_len - 8; + + crypto_blkcipher_setkey(wep->rx_tfm, key, klen); + sg_init_one(&sg, pos, plen + 4); + if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) + return -7; + + crc = ~crc32_le(~0, pos, plen); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + if (memcmp(icv, pos + plen, 4) != 0) { + /* ICV mismatch - drop frame */ + return -2; + } + + /* Remove IV and ICV */ + memmove(skb->data + 4, skb->data, hdr_len); + skb_pull(skb, 4); + skb_trim(skb, skb->len - 4); + + return 0; +} + +static int lib80211_wep_set_key(void *key, int len, u8 * seq, void *priv) +{ + struct lib80211_wep_data *wep = priv; + + if (len < 0 || len > WEP_KEY_LEN) + return -1; + + memcpy(wep->key, key, len); + wep->key_len = len; + + return 0; +} + +static int lib80211_wep_get_key(void *key, int len, u8 * seq, void *priv) +{ + struct lib80211_wep_data *wep = priv; + + if (len < wep->key_len) + return -1; + + memcpy(key, wep->key, wep->key_len); + + return wep->key_len; +} + +static char *lib80211_wep_print_stats(char *p, void *priv) +{ + struct lib80211_wep_data *wep = priv; + p += sprintf(p, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len); + return p; +} + +static struct lib80211_crypto_ops lib80211_crypt_wep = { + .name = "WEP", + .init = lib80211_wep_init, + .deinit = lib80211_wep_deinit, + .encrypt_mpdu = lib80211_wep_encrypt, + .decrypt_mpdu = lib80211_wep_decrypt, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = lib80211_wep_set_key, + .get_key = lib80211_wep_get_key, + .print_stats = lib80211_wep_print_stats, + .extra_mpdu_prefix_len = 4, /* IV */ + .extra_mpdu_postfix_len = 4, /* ICV */ + .owner = THIS_MODULE, +}; + +static int __init lib80211_crypto_wep_init(void) +{ + return lib80211_register_crypto_ops(&lib80211_crypt_wep); +} + +static void __exit lib80211_crypto_wep_exit(void) +{ + lib80211_unregister_crypto_ops(&lib80211_crypt_wep); +} + +module_init(lib80211_crypto_wep_init); +module_exit(lib80211_crypto_wep_exit); diff --git a/net/wireless_ath/mesh.c b/net/wireless_ath/mesh.c new file mode 100644 index 00000000..b7b7868f --- /dev/null +++ b/net/wireless_ath/mesh.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include "nl80211.h" +#include "core.h" + +/* Default values, timeouts in ms */ +#define MESH_TTL 31 +#define MESH_DEFAULT_ELEMENT_TTL 31 +#define MESH_MAX_RETR 3 +#define MESH_RET_T 100 +#define MESH_CONF_T 100 +#define MESH_HOLD_T 100 + +#define MESH_PATH_TIMEOUT 5000 +#define MESH_RANN_INTERVAL 5000 + +/* + * Minimum interval between two consecutive PREQs originated by the same + * interface + */ +#define MESH_PREQ_MIN_INT 10 +#define MESH_DIAM_TRAVERSAL_TIME 50 + +/* + * A path will be refreshed if it is used PATH_REFRESH_TIME milliseconds + * before timing out. This way it will remain ACTIVE and no data frames + * will be unnecessarily held in the pending queue. + */ +#define MESH_PATH_REFRESH_TIME 1000 +#define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME) + +/* Default maximum number of established plinks per interface */ +#define MESH_MAX_ESTAB_PLINKS 32 + +#define MESH_MAX_PREQ_RETRIES 4 + + +const struct mesh_config default_mesh_config = { + .dot11MeshRetryTimeout = MESH_RET_T, + .dot11MeshConfirmTimeout = MESH_CONF_T, + .dot11MeshHoldingTimeout = MESH_HOLD_T, + .dot11MeshMaxRetries = MESH_MAX_RETR, + .dot11MeshTTL = MESH_TTL, + .element_ttl = MESH_DEFAULT_ELEMENT_TTL, + .auto_open_plinks = true, + .dot11MeshMaxPeerLinks = MESH_MAX_ESTAB_PLINKS, + .dot11MeshHWMPactivePathTimeout = MESH_PATH_TIMEOUT, + .dot11MeshHWMPpreqMinInterval = MESH_PREQ_MIN_INT, + .dot11MeshHWMPnetDiameterTraversalTime = MESH_DIAM_TRAVERSAL_TIME, + .dot11MeshHWMPmaxPREQretries = MESH_MAX_PREQ_RETRIES, + .path_refresh_time = MESH_PATH_REFRESH_TIME, + .min_discovery_timeout = MESH_MIN_DISCOVERY_TIMEOUT, + .dot11MeshHWMPRannInterval = MESH_RANN_INTERVAL, + .dot11MeshGateAnnouncementProtocol = false, +}; + +const struct mesh_setup default_mesh_setup = { + .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, + .path_metric = IEEE80211_PATH_METRIC_AIRTIME, + .ie = NULL, + .ie_len = 0, + .is_secure = false, +}; + +int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev, + const struct mesh_setup *setup, + const struct mesh_config *conf) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); + + ASSERT_WDEV_LOCK(wdev); + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) && + setup->is_secure) + return -EOPNOTSUPP; + + if (wdev->mesh_id_len) + return -EALREADY; + + if (!setup->mesh_id_len) + return -EINVAL; + + if (!rdev->ops->join_mesh) + return -EOPNOTSUPP; + + err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup); + if (!err) { + memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); + wdev->mesh_id_len = setup->mesh_id_len; + } + + return err; +} + +int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev, + const struct mesh_setup *setup, + const struct mesh_config *conf) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_join_mesh(rdev, dev, setup, conf); + wdev_unlock(wdev); + + return err; +} + +void cfg80211_notify_new_peer_candidate(struct net_device *dev, + const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT)) + return; + + nl80211_send_new_peer_candidate(wiphy_to_dev(wdev->wiphy), dev, + macaddr, ie, ie_len, gfp); +} +EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate); + +static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + if (!rdev->ops->leave_mesh) + return -EOPNOTSUPP; + + if (!wdev->mesh_id_len) + return -ENOTCONN; + + err = rdev->ops->leave_mesh(&rdev->wiphy, dev); + if (!err) + wdev->mesh_id_len = 0; + return err; +} + +int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_leave_mesh(rdev, dev); + wdev_unlock(wdev); + + return err; +} diff --git a/net/wireless_ath/mlme.c b/net/wireless_ath/mlme.c new file mode 100644 index 00000000..4d74154d --- /dev/null +++ b/net/wireless_ath/mlme.c @@ -0,0 +1,1191 @@ +/* + * cfg80211 MLME SAP interface + * + * Copyright (c) 2009, Jouni Malinen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "nl80211.h" + +void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + u8 *bssid = mgmt->bssid; + int i; + u16 status = le16_to_cpu(mgmt->u.auth.status_code); + bool done = false; + + wdev_lock(wdev); + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] && + memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, + ETH_ALEN) == 0) { + if (status == WLAN_STATUS_SUCCESS) { + wdev->auth_bsses[i] = wdev->authtry_bsses[i]; + } else { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + } + wdev->authtry_bsses[i] = NULL; + done = true; + break; + } + } + + if (done) { + nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); + cfg80211_sme_rx_auth(dev, buf, len); + } + + wdev_unlock(wdev); +} +EXPORT_SYMBOL(cfg80211_send_rx_auth); + +void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) +{ + u16 status_code; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + u8 *ie = mgmt->u.assoc_resp.variable; + int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); + struct cfg80211_internal_bss *bss = NULL; + + wdev_lock(wdev); + + status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); + + /* + * This is a bit of a hack, we don't notify userspace of + * a (re-)association reply if we tried to send a reassoc + * and got a reject -- we only try again with an assoc + * frame instead of reassoc. + */ + if (status_code != WLAN_STATUS_SUCCESS && wdev->conn && + cfg80211_sme_failed_reassoc(wdev)) + goto out; + + nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); + + if (status_code == WLAN_STATUS_SUCCESS) { + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (!wdev->auth_bsses[i]) + continue; + if (memcmp(wdev->auth_bsses[i]->pub.bssid, mgmt->bssid, + ETH_ALEN) == 0) { + bss = wdev->auth_bsses[i]; + wdev->auth_bsses[i] = NULL; + /* additional reference to drop hold */ + cfg80211_ref_bss(bss); + break; + } + } + + /* + * We might be coming here because the driver reported + * a successful association at the same time as the + * user requested a deauth. In that case, we will have + * removed the BSS from the auth_bsses list due to the + * deauth request when the assoc response makes it. If + * the two code paths acquire the lock the other way + * around, that's just the standard situation of a + * deauth being requested while connected. + */ + if (!bss) + goto out; + } else if (wdev->conn) { + cfg80211_sme_failed_assoc(wdev); + /* + * do not call connect_result() now because the + * sme will schedule work that does it later. + */ + goto out; + } + + if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) { + /* + * This is for the userspace SME, the CONNECTING + * state will be changed to CONNECTED by + * __cfg80211_connect_result() below. + */ + wdev->sme_state = CFG80211_SME_CONNECTING; + } + + /* this consumes one bss reference (unless bss is NULL) */ + __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, + status_code, + status_code == WLAN_STATUS_SUCCESS, + bss ? &bss->pub : NULL); + /* drop hold now, and also reference acquired above */ + if (bss) { + cfg80211_unhold_bss(bss); + cfg80211_put_bss(&bss->pub); + } + + out: + wdev_unlock(wdev); +} +EXPORT_SYMBOL(cfg80211_send_rx_assoc); + +void __cfg80211_send_deauth(struct net_device *dev, + const u8 *buf, size_t len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + const u8 *bssid = mgmt->bssid; + int i; + bool found = false, was_current = false; + + ASSERT_WDEV_LOCK(wdev); + + if (wdev->current_bss && + memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; + found = true; + was_current = true; + } else for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] && + memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + found = true; + break; + } + if (wdev->authtry_bsses[i] && + memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, + ETH_ALEN) == 0 && + memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + wdev->authtry_bsses[i] = NULL; + found = true; + break; + } + } + + if (!found) + return; + + nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); + + if (wdev->sme_state == CFG80211_SME_CONNECTED && was_current) { + u16 reason_code; + bool from_ap; + + reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); + + from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0; + __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); + } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { + __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false, NULL); + } +} +EXPORT_SYMBOL(__cfg80211_send_deauth); + +void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + wdev_lock(wdev); + __cfg80211_send_deauth(dev, buf, len); + wdev_unlock(wdev); +} +EXPORT_SYMBOL(cfg80211_send_deauth); + +void __cfg80211_send_disassoc(struct net_device *dev, + const u8 *buf, size_t len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + const u8 *bssid = mgmt->bssid; + int i; + u16 reason_code; + bool from_ap; + bool done = false; + + ASSERT_WDEV_LOCK(wdev); + + nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); + + if (wdev->sme_state != CFG80211_SME_CONNECTED) + return; + + if (wdev->current_bss && + memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] || wdev->auth_bsses[i]) + continue; + wdev->auth_bsses[i] = wdev->current_bss; + wdev->current_bss = NULL; + done = true; + cfg80211_sme_disassoc(dev, i); + break; + } + WARN_ON(!done); + } else + WARN_ON(1); + + + reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); + + from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0; + __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); +} +EXPORT_SYMBOL(__cfg80211_send_disassoc); + +void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + wdev_lock(wdev); + __cfg80211_send_disassoc(dev, buf, len); + wdev_unlock(wdev); +} +EXPORT_SYMBOL(cfg80211_send_disassoc); + +void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf, + size_t len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_send_unprot_deauth(rdev, dev, buf, len, GFP_ATOMIC); +} +EXPORT_SYMBOL(cfg80211_send_unprot_deauth); + +void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf, + size_t len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_send_unprot_disassoc(rdev, dev, buf, len, GFP_ATOMIC); +} +EXPORT_SYMBOL(cfg80211_send_unprot_disassoc); + +static void __cfg80211_auth_remove(struct wireless_dev *wdev, const u8 *addr) +{ + int i; + bool done = false; + + ASSERT_WDEV_LOCK(wdev); + + for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] && + memcmp(wdev->authtry_bsses[i]->pub.bssid, + addr, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + wdev->authtry_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); +} + +void __cfg80211_auth_canceled(struct net_device *dev, const u8 *addr) +{ + __cfg80211_auth_remove(dev->ieee80211_ptr, addr); +} +EXPORT_SYMBOL(__cfg80211_auth_canceled); + +void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + wdev_lock(wdev); + + nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); + if (wdev->sme_state == CFG80211_SME_CONNECTING) + __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false, NULL); + + __cfg80211_auth_remove(wdev, addr); + + wdev_unlock(wdev); +} +EXPORT_SYMBOL(cfg80211_send_auth_timeout); + +void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + int i; + bool done = false; + + wdev_lock(wdev); + + nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); + if (wdev->sme_state == CFG80211_SME_CONNECTING) + __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false, NULL); + + for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] && + memcmp(wdev->auth_bsses[i]->pub.bssid, + addr, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); + + wdev_unlock(wdev); +} +EXPORT_SYMBOL(cfg80211_send_assoc_timeout); + +void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, + enum nl80211_key_type key_type, int key_id, + const u8 *tsc, gfp_t gfp) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +#ifdef CONFIG_CFG80211_WEXT + union iwreq_data wrqu; + char *buf = kmalloc(128, gfp); + + if (buf) { + sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" + "keyid=%d %scast addr=%pM)", key_id, + key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni", + addr); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); + kfree(buf); + } +#endif + + nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp); +} +EXPORT_SYMBOL(cfg80211_michael_mic_failure); + +/* some MLME handling for userspace SME */ +int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, + const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx, + bool local_state_change) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_auth_request req; + struct cfg80211_internal_bss *bss; + int i, err, slot = -1, nfree = 0; + + ASSERT_WDEV_LOCK(wdev); + + if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) + if (!key || !key_len || key_idx < 0 || key_idx > 4) + return -EINVAL; + + if (wdev->current_bss && + memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0) + return -EALREADY; + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] && + memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, + ETH_ALEN) == 0) + return -EALREADY; + if (wdev->auth_bsses[i] && + memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, + ETH_ALEN) == 0) + return -EALREADY; + } + + memset(&req, 0, sizeof(req)); + + req.local_state_change = local_state_change; + req.ie = ie; + req.ie_len = ie_len; + req.auth_type = auth_type; + req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + req.key = key; + req.key_len = key_len; + req.key_idx = key_idx; + if (!req.bss) + return -ENOENT; + + bss = bss_from_pub(req.bss); + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) { + slot = i; + nfree++; + } + } + + /* we need one free slot for disassoc and one for this auth */ + if (nfree < 2) { + err = -ENOSPC; + goto out; + } + + if (local_state_change) + wdev->auth_bsses[slot] = bss; + else + wdev->authtry_bsses[slot] = bss; + cfg80211_hold_bss(bss); + + err = rdev->ops->auth(&rdev->wiphy, dev, &req); + if (err) { + if (local_state_change) + wdev->auth_bsses[slot] = NULL; + else + wdev->authtry_bsses[slot] = NULL; + cfg80211_unhold_bss(bss); + } + + out: + if (err) + cfg80211_put_bss(req.bss); + return err; +} + +int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx, + bool local_state_change) +{ + int err; + + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, + ssid, ssid_len, ie, ie_len, + key, key_len, key_idx, local_state_change); + wdev_unlock(dev->ieee80211_ptr); + + return err; +} + +int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_assoc_request req; + struct cfg80211_internal_bss *bss; + int i, err, slot = -1; + bool was_connected = false; + + ASSERT_WDEV_LOCK(wdev); + + memset(&req, 0, sizeof(req)); + + if (wdev->current_bss && prev_bssid && + memcmp(wdev->current_bss->pub.bssid, prev_bssid, ETH_ALEN) == 0) { + /* + * Trying to reassociate: Allow this to proceed and let the old + * association to be dropped when the new one is completed. + */ + if (wdev->sme_state == CFG80211_SME_CONNECTED) { + was_connected = true; + wdev->sme_state = CFG80211_SME_CONNECTING; + } + } else if (wdev->current_bss) + return -EALREADY; + + req.ie = ie; + req.ie_len = ie_len; + memcpy(&req.crypto, crypt, sizeof(req.crypto)); + req.use_mfp = use_mfp; + req.prev_bssid = prev_bssid; + req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + if (!req.bss) { + if (was_connected) + wdev->sme_state = CFG80211_SME_CONNECTED; + return -ENOENT; + } + + bss = bss_from_pub(req.bss); + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (bss == wdev->auth_bsses[i]) { + slot = i; + break; + } + } + + if (slot < 0) { + err = -ENOTCONN; + goto out; + } + + err = rdev->ops->assoc(&rdev->wiphy, dev, &req); + out: + if (err && was_connected) + wdev->sme_state = CFG80211_SME_CONNECTED; + /* still a reference in wdev->auth_bsses[slot] */ + cfg80211_put_bss(req.bss); + return err; +} + +int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, + ssid, ssid_len, ie, ie_len, use_mfp, crypt); + wdev_unlock(wdev); + + return err; +} + +int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason, + bool local_state_change) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_deauth_request req; + int i; + + ASSERT_WDEV_LOCK(wdev); + + memset(&req, 0, sizeof(req)); + req.reason_code = reason; + req.local_state_change = local_state_change; + req.ie = ie; + req.ie_len = ie_len; + if (wdev->current_bss && + memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { + req.bss = &wdev->current_bss->pub; + } else for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] && + memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) { + req.bss = &wdev->auth_bsses[i]->pub; + break; + } + if (wdev->authtry_bsses[i] && + memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) { + req.bss = &wdev->authtry_bsses[i]->pub; + break; + } + } + + if (!req.bss) + return -ENOTCONN; + + return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); +} + +int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason, + bool local_state_change) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason, + local_state_change); + wdev_unlock(wdev); + + return err; +} + +static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason, + bool local_state_change) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_disassoc_request req; + + ASSERT_WDEV_LOCK(wdev); + + if (wdev->sme_state != CFG80211_SME_CONNECTED) + return -ENOTCONN; + + if (WARN_ON(!wdev->current_bss)) + return -ENOTCONN; + + memset(&req, 0, sizeof(req)); + req.reason_code = reason; + req.local_state_change = local_state_change; + req.ie = ie; + req.ie_len = ie_len; + if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) + req.bss = &wdev->current_bss->pub; + else + return -ENOTCONN; + + return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev); +} + +int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason, + bool local_state_change) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason, + local_state_change); + wdev_unlock(wdev); + + return err; +} + +void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_deauth_request req; + int i; + + ASSERT_WDEV_LOCK(wdev); + + if (!rdev->ops->deauth) + return; + + memset(&req, 0, sizeof(req)); + req.reason_code = WLAN_REASON_DEAUTH_LEAVING; + req.ie = NULL; + req.ie_len = 0; + + if (wdev->current_bss) { + req.bss = &wdev->current_bss->pub; + rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; + } + } + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i]) { + req.bss = &wdev->auth_bsses[i]->pub; + rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); + if (wdev->auth_bsses[i]) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + } + } + if (wdev->authtry_bsses[i]) { + req.bss = &wdev->authtry_bsses[i]->pub; + rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); + if (wdev->authtry_bsses[i]) { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + wdev->authtry_bsses[i] = NULL; + } + } + } +} + +void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int duration, gfp_t gfp) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_send_remain_on_channel(rdev, dev, cookie, chan, channel_type, + duration, gfp); +} +EXPORT_SYMBOL(cfg80211_ready_on_channel); + +void cfg80211_remain_on_channel_expired(struct net_device *dev, + u64 cookie, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + gfp_t gfp) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_send_remain_on_channel_cancel(rdev, dev, cookie, chan, + channel_type, gfp); +} +EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); + +void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, + struct station_info *sinfo, gfp_t gfp) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp); +} +EXPORT_SYMBOL(cfg80211_new_sta); + +void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp); +} +EXPORT_SYMBOL(cfg80211_del_sta); + +void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr, + enum nl80211_connect_failed_reason reason, + gfp_t gfp) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_send_conn_failed_event(rdev, dev, mac_addr, reason, gfp); +} +EXPORT_SYMBOL(cfg80211_conn_failed); + +struct cfg80211_mgmt_registration { + struct list_head list; + + u32 nlpid; + + int match_len; + + __le16 frame_type; + + u8 match[]; +}; + +int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, + u16 frame_type, const u8 *match_data, + int match_len) +{ + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct cfg80211_mgmt_registration *reg, *nreg; + int err = 0; + u16 mgmt_type; + + if (!wdev->wiphy->mgmt_stypes) + return -EOPNOTSUPP; + + if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT) + return -EINVAL; + + if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) + return -EINVAL; + + mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; + if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type))) + return -EINVAL; + + nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL); + if (!nreg) + return -ENOMEM; + + spin_lock_bh(&wdev->mgmt_registrations_lock); + + list_for_each_entry(reg, &wdev->mgmt_registrations, list) { + int mlen = min(match_len, reg->match_len); + + if (frame_type != le16_to_cpu(reg->frame_type)) + continue; + + if (memcmp(reg->match, match_data, mlen) == 0) { + err = -EALREADY; + break; + } + } + + if (err) { + kfree(nreg); + goto out; + } + + memcpy(nreg->match, match_data, match_len); + nreg->match_len = match_len; + nreg->nlpid = snd_pid; + nreg->frame_type = cpu_to_le16(frame_type); + list_add(&nreg->list, &wdev->mgmt_registrations); + + if (rdev->ops->mgmt_frame_register) + rdev->ops->mgmt_frame_register(wiphy, wdev->netdev, + frame_type, true); + + out: + spin_unlock_bh(&wdev->mgmt_registrations_lock); + + return err; +} + +void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid) +{ + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct cfg80211_mgmt_registration *reg, *tmp; + + spin_lock_bh(&wdev->mgmt_registrations_lock); + + list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { + if (reg->nlpid != nlpid) + continue; + + if (rdev->ops->mgmt_frame_register) { + u16 frame_type = le16_to_cpu(reg->frame_type); + + rdev->ops->mgmt_frame_register(wiphy, wdev->netdev, + frame_type, false); + } + + list_del(®->list); + kfree(reg); + } + + spin_unlock_bh(&wdev->mgmt_registrations_lock); + + if (nlpid == wdev->ap_unexpected_nlpid) + wdev->ap_unexpected_nlpid = 0; +} + +void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) +{ + struct cfg80211_mgmt_registration *reg, *tmp; + + spin_lock_bh(&wdev->mgmt_registrations_lock); + + list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { + list_del(®->list); + kfree(reg); + } + + spin_unlock_bh(&wdev->mgmt_registrations_lock); +} + +int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, + const u8 *buf, size_t len, bool no_cck, + bool dont_wait_for_ack, u64 *cookie) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + const struct ieee80211_mgmt *mgmt; + u16 stype; + + if (!wdev->wiphy->mgmt_stypes) + return -EOPNOTSUPP; + + if (!rdev->ops->mgmt_tx) + return -EOPNOTSUPP; + + if (len < 24 + 1) + return -EINVAL; + + mgmt = (const struct ieee80211_mgmt *) buf; + + if (!ieee80211_is_mgmt(mgmt->frame_control)) + return -EINVAL; + + stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; + if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4))) + return -EINVAL; + + if (ieee80211_is_action(mgmt->frame_control) && + mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) { + int err = 0; + + wdev_lock(wdev); + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + if (!wdev->current_bss) { + err = -ENOTCONN; + break; + } + + if (memcmp(wdev->current_bss->pub.bssid, + mgmt->bssid, ETH_ALEN)) { + err = -ENOTCONN; + break; + } + + /* + * check for IBSS DA must be done by driver as + * cfg80211 doesn't track the stations + */ + if (wdev->iftype == NL80211_IFTYPE_ADHOC) + break; + + /* for station, check that DA is the AP */ + if (memcmp(wdev->current_bss->pub.bssid, + mgmt->da, ETH_ALEN)) { + err = -ENOTCONN; + break; + } + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_AP_VLAN: + if (memcmp(mgmt->bssid, dev->dev_addr, ETH_ALEN)) + err = -EINVAL; + break; + case NL80211_IFTYPE_MESH_POINT: + if (memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN)) { + err = -EINVAL; + break; + } + /* + * check for mesh DA must be done by driver as + * cfg80211 doesn't track the stations + */ + break; + default: + err = -EOPNOTSUPP; + break; + } + wdev_unlock(wdev); + + if (err) + return err; + } + + if (memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0) + return -EINVAL; + + /* Transmit the Action frame as requested by user space */ + return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, offchan, + channel_type, channel_type_valid, + wait, buf, len, no_cck, dont_wait_for_ack, + cookie); +} + +bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, + size_t len, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct cfg80211_mgmt_registration *reg; + const struct ieee80211_txrx_stypes *stypes = + &wiphy->mgmt_stypes[wdev->iftype]; + struct ieee80211_mgmt *mgmt = (void *)buf; + const u8 *data; + int data_len; + bool result = false; + __le16 ftype = mgmt->frame_control & + cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE); + u16 stype; + + stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4; + + if (!(stypes->rx & BIT(stype))) + return false; + + data = buf + ieee80211_hdrlen(mgmt->frame_control); + data_len = len - ieee80211_hdrlen(mgmt->frame_control); + + spin_lock_bh(&wdev->mgmt_registrations_lock); + + list_for_each_entry(reg, &wdev->mgmt_registrations, list) { + if (reg->frame_type != ftype) + continue; + + if (reg->match_len > data_len) + continue; + + if (memcmp(reg->match, data, reg->match_len)) + continue; + + /* found match! */ + + /* Indicate the received Action frame to user space */ + if (nl80211_send_mgmt(rdev, dev, reg->nlpid, freq, + buf, len, gfp)) + continue; + + result = true; + break; + } + + spin_unlock_bh(&wdev->mgmt_registrations_lock); + + return result; +} +EXPORT_SYMBOL(cfg80211_rx_mgmt); + +void cfg80211_mgmt_tx_status(struct net_device *dev, u64 cookie, + const u8 *buf, size_t len, bool ack, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + /* Indicate TX status of the Action frame to user space */ + nl80211_send_mgmt_tx_status(rdev, dev, cookie, buf, len, ack, gfp); +} +EXPORT_SYMBOL(cfg80211_mgmt_tx_status); + +void cfg80211_cqm_rssi_notify(struct net_device *dev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + /* Indicate roaming trigger event to user space */ + nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp); +} +EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); + +void cfg80211_cqm_pktloss_notify(struct net_device *dev, + const u8 *peer, u32 num_packets, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + /* Indicate roaming trigger event to user space */ + nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp); +} +EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); + +void cfg80211_cqm_txe_notify(struct net_device *dev, + const u8 *peer, u32 num_packets, + u32 rate, u32 intvl, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_send_cqm_txe_notify(rdev, dev, peer, num_packets, + rate, intvl, gfp); +} +EXPORT_SYMBOL(cfg80211_cqm_txe_notify); + +void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, + const u8 *replay_ctr, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp); +} +EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); + +void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, + const u8 *bssid, bool preauth, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); +} +EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); + +void cfg80211_ch_switch_notify(struct net_device *dev, int freq, + enum nl80211_channel_type type) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_channel *chan; + + wdev_lock(wdev); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO)) + goto out; + + chan = rdev_freq_to_chan(rdev, freq, type); + if (WARN_ON(!chan)) + goto out; + + wdev->channel = chan; + + nl80211_ch_switch_notify(rdev, dev, freq, type, GFP_KERNEL); +out: + wdev_unlock(wdev); + return; +} +EXPORT_SYMBOL(cfg80211_ch_switch_notify); + +bool cfg80211_rx_spurious_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO)) + return false; + + return nl80211_unexpected_frame(dev, addr, gfp); +} +EXPORT_SYMBOL(cfg80211_rx_spurious_frame); + +bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO && + wdev->iftype != NL80211_IFTYPE_AP_VLAN)) + return false; + + return nl80211_unexpected_4addr_frame(dev, addr, gfp); +} +EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); diff --git a/net/wireless_ath/nl80211.c b/net/wireless_ath/nl80211.c new file mode 100644 index 00000000..5d93939d --- /dev/null +++ b/net/wireless_ath/nl80211.c @@ -0,0 +1,8154 @@ +/* + * This is the new netlink-based wireless configuration interface. + * + * Copyright 2006-2010 Johannes Berg + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "nl80211.h" +#include "reg.h" + +static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type); +static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, + struct genl_info *info, + struct cfg80211_crypto_settings *settings, + int cipher_limit); + +static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, + struct genl_info *info); +static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb, + struct genl_info *info); + +/* the netlink family */ +static struct genl_family nl80211_fam = { + .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */ + .name = "nl80211", /* have users key off the name instead */ + .hdrsize = 0, /* no private header */ + .version = 1, /* no particular meaning now */ + .maxattr = NL80211_ATTR_MAX, + .netnsok = true, + .pre_doit = nl80211_pre_doit, + .post_doit = nl80211_post_doit, +}; + +/* internal helper: get rdev and dev */ +static int get_rdev_dev_by_info_ifindex(struct genl_info *info, + struct cfg80211_registered_device **rdev, + struct net_device **dev) +{ + struct nlattr **attrs = info->attrs; + int ifindex; + + if (!attrs[NL80211_ATTR_IFINDEX]) + return -EINVAL; + + ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); + *dev = dev_get_by_index(genl_info_net(info), ifindex); + if (!*dev) + return -ENODEV; + + *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex); + if (IS_ERR(*rdev)) { + dev_put(*dev); + return PTR_ERR(*rdev); + } + + return 0; +} + +/* policy for the attributes */ +static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { + [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, + .len = 20-1 }, + [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, + [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 }, + [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, + [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 }, + + [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, + [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, + [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, + + [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN }, + [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN }, + + [NL80211_ATTR_KEY] = { .type = NLA_NESTED, }, + [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, + .len = WLAN_MAX_KEY_LEN }, + [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 }, + [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 }, + [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG }, + [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, + [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 }, + + [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 }, + [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 }, + [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_STA_AID] = { .type = NLA_U16 }, + [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED }, + [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 }, + [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY, + .len = NL80211_MAX_SUPP_RATES }, + [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 }, + [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, + [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ }, + [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_MESH_ID_LEN }, + [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, + + [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 }, + [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED }, + + [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, + [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, + [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, + [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY, + .len = NL80211_MAX_SUPP_RATES }, + [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 }, + + [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED }, + [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG }, + + [NL80211_ATTR_HT_CAPABILITY] = { .len = NL80211_HT_CAPABILITY_LEN }, + + [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, + [NL80211_ATTR_IE] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED }, + [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED }, + + [NL80211_ATTR_SSID] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_SSID_LEN }, + [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 }, + [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 }, + [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG }, + [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG }, + [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 }, + [NL80211_ATTR_STA_FLAGS2] = { + .len = sizeof(struct nl80211_sta_flag_update), + }, + [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG }, + [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 }, + [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG }, + [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, + [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, + [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, + [NL80211_ATTR_PID] = { .type = NLA_U32 }, + [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, + [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, + .len = WLAN_PMKID_LEN }, + [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, + [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, + [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, + [NL80211_ATTR_FRAME] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, + [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, + [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG }, + [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 }, + [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 }, + [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 }, + [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 }, + [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, + [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG }, + [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, + [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED }, + [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 }, + [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 }, + [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED }, + [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED }, + [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 }, + [NL80211_ATTR_IE_PROBE_RESP] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG }, + [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED }, + [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG }, + [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 }, + [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 }, + [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 }, + [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG }, + [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG }, + [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG }, + [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 }, + [NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG }, + [NL80211_ATTR_HT_CAPABILITY_MASK] = { + .len = NL80211_HT_CAPABILITY_LEN + }, + [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 }, + [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 }, + [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, + [NL80211_ATTR_BTCOEX_DATA] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_STA_CAP_REQ] = { .type = NLA_U8 }, + [NL80211_ATTR_ACS] = { .type = NLA_U8 }, + [NL80211_ATTR_MAC_ACL] = { .type = NLA_U8 }, + [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED }, + [NL80211_ATTR_ACL_POLICY] = { .type = NLA_U8 }, +}; + +/* policy for the key attributes */ +static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { + [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, + [NL80211_KEY_IDX] = { .type = NLA_U8 }, + [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, + [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, + [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG }, + [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, + [NL80211_KEY_TYPE] = { .type = NLA_U32 }, + [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, +}; + +/* policy for the key default flags */ +static const struct nla_policy +nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = { + [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG }, + [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG }, +}; + +/* policy for WoWLAN attributes */ +static const struct nla_policy +nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { + [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED }, + [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, +}; + +/* policy for GTK rekey offload attributes */ +static const struct nla_policy +nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { + [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN }, + [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN }, + [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN }, +}; + +static const struct nla_policy +nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = { + [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_SSID_LEN }, + [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 }, +}; + +/* ifidx get helper */ +static int nl80211_get_ifidx(struct netlink_callback *cb) +{ + int res; + + res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, + nl80211_fam.attrbuf, nl80211_fam.maxattr, + nl80211_policy); + if (res) + return res; + + if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) + return -EINVAL; + + res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); + if (!res) + return -EINVAL; + return res; +} + +static int nl80211_prepare_netdev_dump(struct sk_buff *skb, + struct netlink_callback *cb, + struct cfg80211_registered_device **rdev, + struct net_device **dev) +{ + int ifidx = cb->args[0]; + int err; + + if (!ifidx) + ifidx = nl80211_get_ifidx(cb); + if (ifidx < 0) + return ifidx; + + cb->args[0] = ifidx; + + rtnl_lock(); + + *dev = __dev_get_by_index(sock_net(skb->sk), ifidx); + if (!*dev) { + err = -ENODEV; + goto out_rtnl; + } + + *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); + if (IS_ERR(*rdev)) { + err = PTR_ERR(*rdev); + goto out_rtnl; + } + + return 0; + out_rtnl: + rtnl_unlock(); + return err; +} + +static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev) +{ + cfg80211_unlock_rdev(rdev); + rtnl_unlock(); +} + +/* IE validation */ +static bool is_valid_ie_attr(const struct nlattr *attr) +{ + const u8 *pos; + int len; + + if (!attr) + return true; + + pos = nla_data(attr); + len = nla_len(attr); + + while (len) { + u8 elemlen; + + if (len < 2) + return false; + len -= 2; + + elemlen = pos[1]; + if (elemlen > len) + return false; + + len -= elemlen; + pos += 2 + elemlen; + } + + return true; +} + +/* message building helper */ +static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq, + int flags, u8 cmd) +{ + /* since there is no private header just add the generic one */ + return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd); +} + +static int nl80211_msg_put_channel(struct sk_buff *msg, + struct ieee80211_channel *chan) +{ + NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ, + chan->center_freq); + + if (chan->flags & IEEE80211_CHAN_DISABLED) + NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED); + if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) + NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN); + if (chan->flags & IEEE80211_CHAN_NO_IBSS) + NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS); + if (chan->flags & IEEE80211_CHAN_RADAR) + NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR); + + NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, + DBM_TO_MBM(chan->max_power)); + + return 0; + + nla_put_failure: + return -ENOBUFS; +} + +/* netlink command implementations */ + +struct key_parse { + struct key_params p; + int idx; + int type; + bool def, defmgmt; + bool def_uni, def_multi; +}; + +static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k) +{ + struct nlattr *tb[NL80211_KEY_MAX + 1]; + int err = nla_parse_nested(tb, NL80211_KEY_MAX, key, + nl80211_key_policy); + if (err) + return err; + + k->def = !!tb[NL80211_KEY_DEFAULT]; + k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT]; + + if (k->def) { + k->def_uni = true; + k->def_multi = true; + } + if (k->defmgmt) + k->def_multi = true; + + if (tb[NL80211_KEY_IDX]) + k->idx = nla_get_u8(tb[NL80211_KEY_IDX]); + + if (tb[NL80211_KEY_DATA]) { + k->p.key = nla_data(tb[NL80211_KEY_DATA]); + k->p.key_len = nla_len(tb[NL80211_KEY_DATA]); + } + + if (tb[NL80211_KEY_SEQ]) { + k->p.seq = nla_data(tb[NL80211_KEY_SEQ]); + k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]); + } + + if (tb[NL80211_KEY_CIPHER]) + k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]); + + if (tb[NL80211_KEY_TYPE]) { + k->type = nla_get_u32(tb[NL80211_KEY_TYPE]); + if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES) + return -EINVAL; + } + + if (tb[NL80211_KEY_DEFAULT_TYPES]) { + struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; + int err = nla_parse_nested(kdt, + NUM_NL80211_KEY_DEFAULT_TYPES - 1, + tb[NL80211_KEY_DEFAULT_TYPES], + nl80211_key_default_policy); + if (err) + return err; + + k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST]; + k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST]; + } + + return 0; +} + +static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k) +{ + if (info->attrs[NL80211_ATTR_KEY_DATA]) { + k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]); + k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]); + } + + if (info->attrs[NL80211_ATTR_KEY_SEQ]) { + k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]); + k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]); + } + + if (info->attrs[NL80211_ATTR_KEY_IDX]) + k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); + + if (info->attrs[NL80211_ATTR_KEY_CIPHER]) + k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]); + + k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT]; + k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]; + + if (k->def) { + k->def_uni = true; + k->def_multi = true; + } + if (k->defmgmt) + k->def_multi = true; + + if (info->attrs[NL80211_ATTR_KEY_TYPE]) { + k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]); + if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) { + struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; + int err = nla_parse_nested( + kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1, + info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES], + nl80211_key_default_policy); + if (err) + return err; + + k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST]; + k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST]; + } + + return 0; +} + +static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) +{ + int err; + + memset(k, 0, sizeof(*k)); + k->idx = -1; + k->type = -1; + + if (info->attrs[NL80211_ATTR_KEY]) + err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k); + else + err = nl80211_parse_key_old(info, k); + + if (err) + return err; + + if (k->def && k->defmgmt) + return -EINVAL; + + if (k->defmgmt) { + if (k->def_uni || !k->def_multi) + return -EINVAL; + } + + if (k->idx != -1) { + if (k->defmgmt) { + if (k->idx < 4 || k->idx > 5) + return -EINVAL; + } else if (k->def) { + if (k->idx < 0 || k->idx > 3) + return -EINVAL; + } else { + if (k->idx < 0 || k->idx > 5) + return -EINVAL; + } + } + + return 0; +} + +static struct cfg80211_cached_keys * +nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, + struct nlattr *keys) +{ + struct key_parse parse; + struct nlattr *key; + struct cfg80211_cached_keys *result; + int rem, err, def = 0; + + result = kzalloc(sizeof(*result), GFP_KERNEL); + if (!result) + return ERR_PTR(-ENOMEM); + + result->def = -1; + result->defmgmt = -1; + + nla_for_each_nested(key, keys, rem) { + memset(&parse, 0, sizeof(parse)); + parse.idx = -1; + + err = nl80211_parse_key_new(key, &parse); + if (err) + goto error; + err = -EINVAL; + if (!parse.p.key) + goto error; + if (parse.idx < 0 || parse.idx > 4) + goto error; + if (parse.def) { + if (def) + goto error; + def = 1; + result->def = parse.idx; + if (!parse.def_uni || !parse.def_multi) + goto error; + } else if (parse.defmgmt) + goto error; + err = cfg80211_validate_key_settings(rdev, &parse.p, + parse.idx, false, NULL); + if (err) + goto error; + result->params[parse.idx].cipher = parse.p.cipher; + result->params[parse.idx].key_len = parse.p.key_len; + result->params[parse.idx].key = result->data[parse.idx]; + memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len); + } + + return result; + error: + kfree(result); + return ERR_PTR(err); +} + +static int nl80211_key_allowed(struct wireless_dev *wdev) +{ + ASSERT_WDEV_LOCK(wdev); + + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_MESH_POINT: + break; + case NL80211_IFTYPE_ADHOC: + if (!wdev->current_bss) + return -ENOLINK; + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + if (wdev->sme_state != CFG80211_SME_CONNECTED) + return -ENOLINK; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes) +{ + struct nlattr *nl_modes = nla_nest_start(msg, attr); + int i; + + if (!nl_modes) + goto nla_put_failure; + + i = 0; + while (ifmodes) { + if (ifmodes & 1) + NLA_PUT_FLAG(msg, i); + ifmodes >>= 1; + i++; + } + + nla_nest_end(msg, nl_modes); + return 0; + +nla_put_failure: + return -ENOBUFS; +} + +static int nl80211_put_iface_combinations(struct wiphy *wiphy, + struct sk_buff *msg) +{ + struct nlattr *nl_combis; + int i, j; + + nl_combis = nla_nest_start(msg, + NL80211_ATTR_INTERFACE_COMBINATIONS); + if (!nl_combis) + goto nla_put_failure; + + for (i = 0; i < wiphy->n_iface_combinations; i++) { + const struct ieee80211_iface_combination *c; + struct nlattr *nl_combi, *nl_limits; + + c = &wiphy->iface_combinations[i]; + + nl_combi = nla_nest_start(msg, i + 1); + if (!nl_combi) + goto nla_put_failure; + + nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS); + if (!nl_limits) + goto nla_put_failure; + + for (j = 0; j < c->n_limits; j++) { + struct nlattr *nl_limit; + + nl_limit = nla_nest_start(msg, j + 1); + if (!nl_limit) + goto nla_put_failure; + NLA_PUT_U32(msg, NL80211_IFACE_LIMIT_MAX, + c->limits[j].max); + if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES, + c->limits[j].types)) + goto nla_put_failure; + nla_nest_end(msg, nl_limit); + } + + nla_nest_end(msg, nl_limits); + + if (c->beacon_int_infra_match) + NLA_PUT_FLAG(msg, + NL80211_IFACE_COMB_STA_AP_BI_MATCH); + NLA_PUT_U32(msg, NL80211_IFACE_COMB_NUM_CHANNELS, + c->num_different_channels); + NLA_PUT_U32(msg, NL80211_IFACE_COMB_MAXNUM, + c->max_interfaces); + + nla_nest_end(msg, nl_combi); + } + + nla_nest_end(msg, nl_combis); + + return 0; +nla_put_failure: + return -ENOBUFS; +} + +static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, + struct cfg80211_registered_device *dev) +{ + void *hdr; + struct nlattr *nl_bands, *nl_band; + struct nlattr *nl_freqs, *nl_freq; + struct nlattr *nl_rates, *nl_rate; + struct nlattr *nl_cmds; + enum ieee80211_band band; + struct ieee80211_channel *chan; + struct ieee80211_rate *rate; + int i; + const struct ieee80211_txrx_stypes *mgmt_stypes = + dev->wiphy.mgmt_stypes; + + hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY); + if (!hdr) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx); + NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); + + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, + cfg80211_rdev_list_generation); + + NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, + dev->wiphy.retry_short); + NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, + dev->wiphy.retry_long); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + dev->wiphy.frag_threshold); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, + dev->wiphy.rts_threshold); + NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, + dev->wiphy.coverage_class); + NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + dev->wiphy.max_scan_ssids); + NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, + dev->wiphy.max_sched_scan_ssids); + NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, + dev->wiphy.max_scan_ie_len); + NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, + dev->wiphy.max_sched_scan_ie_len); + NLA_PUT_U8(msg, NL80211_ATTR_MAX_MATCH_SETS, + dev->wiphy.max_match_sets); + + if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) + NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN); + if (dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) + NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH); + if (dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) + NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_AP_UAPSD); + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) + NLA_PUT_FLAG(msg, NL80211_ATTR_ROAM_SUPPORT); + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) + NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_SUPPORT); + if (dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) + NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP); + + NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES, + sizeof(u32) * dev->wiphy.n_cipher_suites, + dev->wiphy.cipher_suites); + + NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS, + dev->wiphy.max_num_pmkids); + + if (dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) + NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE); + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, + dev->wiphy.available_antennas_tx); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, + dev->wiphy.available_antennas_rx); + + if (dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) + NLA_PUT_U32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD, + dev->wiphy.probe_resp_offload); + + if ((dev->wiphy.available_antennas_tx || + dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) { + u32 tx_ant = 0, rx_ant = 0; + int res; + res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant); + if (!res) { + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant); + } + } + + if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES, + dev->wiphy.interface_modes)) + goto nla_put_failure; + + nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); + if (!nl_bands) + goto nla_put_failure; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!dev->wiphy.bands[band]) + continue; + + nl_band = nla_nest_start(msg, band); + if (!nl_band) + goto nla_put_failure; + + /* add HT info */ + if (dev->wiphy.bands[band]->ht_cap.ht_supported) { + NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET, + sizeof(dev->wiphy.bands[band]->ht_cap.mcs), + &dev->wiphy.bands[band]->ht_cap.mcs); + NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA, + dev->wiphy.bands[band]->ht_cap.cap); + NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR, + dev->wiphy.bands[band]->ht_cap.ampdu_factor); + NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY, + dev->wiphy.bands[band]->ht_cap.ampdu_density); + } + + /* add frequencies */ + nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS); + if (!nl_freqs) + goto nla_put_failure; + + for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) { + nl_freq = nla_nest_start(msg, i); + if (!nl_freq) + goto nla_put_failure; + + chan = &dev->wiphy.bands[band]->channels[i]; + + if (nl80211_msg_put_channel(msg, chan)) + goto nla_put_failure; + + nla_nest_end(msg, nl_freq); + } + + nla_nest_end(msg, nl_freqs); + + /* add bitrates */ + nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES); + if (!nl_rates) + goto nla_put_failure; + + for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) { + nl_rate = nla_nest_start(msg, i); + if (!nl_rate) + goto nla_put_failure; + + rate = &dev->wiphy.bands[band]->bitrates[i]; + NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE, + rate->bitrate); + if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) + NLA_PUT_FLAG(msg, + NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE); + + nla_nest_end(msg, nl_rate); + } + + nla_nest_end(msg, nl_rates); + + nla_nest_end(msg, nl_band); + } + nla_nest_end(msg, nl_bands); + + nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS); + if (!nl_cmds) + goto nla_put_failure; + + i = 0; +#define CMD(op, n) \ + do { \ + if (dev->ops->op) { \ + i++; \ + NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \ + } \ + } while (0) + + CMD(add_virtual_intf, NEW_INTERFACE); + CMD(change_virtual_intf, SET_INTERFACE); + CMD(add_key, NEW_KEY); + CMD(add_beacon, NEW_BEACON); + CMD(add_station, NEW_STATION); + CMD(add_mpath, NEW_MPATH); + CMD(update_mesh_config, SET_MESH_CONFIG); + CMD(change_bss, SET_BSS); + CMD(auth, AUTHENTICATE); + CMD(assoc, ASSOCIATE); + CMD(deauth, DEAUTHENTICATE); + CMD(disassoc, DISASSOCIATE); + CMD(join_ibss, JOIN_IBSS); + CMD(join_mesh, JOIN_MESH); + CMD(set_pmksa, SET_PMKSA); + CMD(del_pmksa, DEL_PMKSA); + CMD(flush_pmksa, FLUSH_PMKSA); + CMD(remain_on_channel, REMAIN_ON_CHANNEL); + CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); + CMD(mgmt_tx, FRAME); + CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL); + if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); + } + CMD(set_channel, SET_CHANNEL); + CMD(set_wds_peer, SET_WDS_PEER); + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { + CMD(tdls_mgmt, TDLS_MGMT); + CMD(tdls_oper, TDLS_OPER); + } + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) + CMD(sched_scan_start, START_SCHED_SCAN); + CMD(probe_client, PROBE_CLIENT); + if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_REGISTER_BEACONS); + } + +#undef CMD + + if (dev->ops->connect || dev->ops->auth) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT); + } + + if (dev->ops->disconnect || dev->ops->deauth) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT); + } + + nla_nest_end(msg, nl_cmds); + + if (dev->ops->remain_on_channel) + NLA_PUT_U32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, + dev->wiphy.max_remain_on_channel_duration); + + if (dev->ops->mgmt_tx_cancel_wait) + NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK); + + if (mgmt_stypes) { + u16 stypes; + struct nlattr *nl_ftypes, *nl_ifs; + enum nl80211_iftype ift; + + nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES); + if (!nl_ifs) + goto nla_put_failure; + + for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { + nl_ftypes = nla_nest_start(msg, ift); + if (!nl_ftypes) + goto nla_put_failure; + i = 0; + stypes = mgmt_stypes[ift].tx; + while (stypes) { + if (stypes & 1) + NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, + (i << 4) | IEEE80211_FTYPE_MGMT); + stypes >>= 1; + i++; + } + nla_nest_end(msg, nl_ftypes); + } + + nla_nest_end(msg, nl_ifs); + + nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES); + if (!nl_ifs) + goto nla_put_failure; + + for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { + nl_ftypes = nla_nest_start(msg, ift); + if (!nl_ftypes) + goto nla_put_failure; + i = 0; + stypes = mgmt_stypes[ift].rx; + while (stypes) { + if (stypes & 1) + NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, + (i << 4) | IEEE80211_FTYPE_MGMT); + stypes >>= 1; + i++; + } + nla_nest_end(msg, nl_ftypes); + } + nla_nest_end(msg, nl_ifs); + } + + if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) { + struct nlattr *nl_wowlan; + + nl_wowlan = nla_nest_start(msg, + NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED); + if (!nl_wowlan) + goto nla_put_failure; + + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); + if (dev->wiphy.wowlan.n_patterns) { + struct nl80211_wowlan_pattern_support pat = { + .max_patterns = dev->wiphy.wowlan.n_patterns, + .min_pattern_len = + dev->wiphy.wowlan.pattern_min_len, + .max_pattern_len = + dev->wiphy.wowlan.pattern_max_len, + }; + NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, + sizeof(pat), &pat); + } + + nla_nest_end(msg, nl_wowlan); + } + + if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, + dev->wiphy.software_iftypes)) + goto nla_put_failure; + + if (nl80211_put_iface_combinations(&dev->wiphy, msg)) + goto nla_put_failure; + + if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) + NLA_PUT_U32(msg, NL80211_ATTR_DEVICE_AP_SME, + dev->wiphy.ap_sme_capa); + + NLA_PUT_U32(msg, NL80211_ATTR_FEATURE_FLAGS, dev->wiphy.features); + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) +{ + int idx = 0; + int start = cb->args[0]; + struct cfg80211_registered_device *dev; + + mutex_lock(&cfg80211_mutex); + list_for_each_entry(dev, &cfg80211_rdev_list, list) { + if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) + continue; + if (++idx <= start) + continue; + if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + dev) < 0) { + idx--; + break; + } + } + mutex_unlock(&cfg80211_mutex); + + cb->args[0] = idx; + + return skb->len; +} + +static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *msg; + struct cfg80211_registered_device *dev = info->user_ptr[0]; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) { + nlmsg_free(msg); + return -ENOBUFS; + } + + return genlmsg_reply(msg, info); +} + +static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = { + [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 }, + [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 }, + [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 }, + [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 }, + [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 }, +}; + +static int parse_txq_params(struct nlattr *tb[], + struct ieee80211_txq_params *txq_params) +{ + if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] || + !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] || + !tb[NL80211_TXQ_ATTR_AIFS]) + return -EINVAL; + + txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]); + txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]); + txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]); + txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]); + txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]); + + return 0; +} + +static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) +{ + /* + * You can only set the channel explicitly for AP, mesh + * and WDS type interfaces; all others have their channel + * managed via their respective "establish a connection" + * command (connect, join, ...) + * + * Monitors are special as they are normally slaved to + * whatever else is going on, so they behave as though + * you tried setting the wiphy channel itself. + */ + return !wdev || + wdev->iftype == NL80211_IFTYPE_AP || + wdev->iftype == NL80211_IFTYPE_WDS || + wdev->iftype == NL80211_IFTYPE_MESH_POINT || + wdev->iftype == NL80211_IFTYPE_MONITOR || + wdev->iftype == NL80211_IFTYPE_P2P_GO; +} + +static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct genl_info *info) +{ + enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; + u32 freq; + int result; + + if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) + return -EINVAL; + + if (!nl80211_can_set_dev_channel(wdev)) + return -EOPNOTSUPP; + + if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + channel_type = nla_get_u32(info->attrs[ + NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + if (channel_type != NL80211_CHAN_NO_HT && + channel_type != NL80211_CHAN_HT20 && + channel_type != NL80211_CHAN_HT40PLUS && + channel_type != NL80211_CHAN_HT40MINUS) + return -EINVAL; + } + + freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); + + mutex_lock(&rdev->devlist_mtx); + if (wdev) { + wdev_lock(wdev); + result = cfg80211_set_freq(rdev, wdev, freq, channel_type); + wdev_unlock(wdev); + } else { + result = cfg80211_set_freq(rdev, NULL, freq, channel_type); + } + mutex_unlock(&rdev->devlist_mtx); + + return result; +} + +static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *netdev = info->user_ptr[1]; + + return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info); +} + +static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + const u8 *bssid; + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + if (netif_running(dev)) + return -EBUSY; + + if (!rdev->ops->set_wds_peer) + return -EOPNOTSUPP; + + if (wdev->iftype != NL80211_IFTYPE_WDS) + return -EOPNOTSUPP; + + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid); +} + + +static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + struct net_device *netdev = NULL; + struct wireless_dev *wdev; + int result = 0, rem_txq_params = 0; + struct nlattr *nl_txq_params; + u32 changed; + u8 retry_short = 0, retry_long = 0; + u32 frag_threshold = 0, rts_threshold = 0; + u8 coverage_class = 0; + + /* + * Try to find the wiphy and netdev. Normally this + * function shouldn't need the netdev, but this is + * done for backward compatibility -- previously + * setting the channel was done per wiphy, but now + * it is per netdev. Previous userland like hostapd + * also passed a netdev to set_wiphy, so that it is + * possible to let that go to the right netdev! + */ + mutex_lock(&cfg80211_mutex); + + if (info->attrs[NL80211_ATTR_IFINDEX]) { + int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); + + netdev = dev_get_by_index(genl_info_net(info), ifindex); + if (netdev && netdev->ieee80211_ptr) { + rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy); + mutex_lock(&rdev->mtx); + } else + netdev = NULL; + } + + if (!netdev) { + rdev = __cfg80211_rdev_from_info(info); + if (IS_ERR(rdev)) { + mutex_unlock(&cfg80211_mutex); + return PTR_ERR(rdev); + } + wdev = NULL; + netdev = NULL; + result = 0; + + mutex_lock(&rdev->mtx); + } else if (netif_running(netdev) && + nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) + wdev = netdev->ieee80211_ptr; + else + wdev = NULL; + + /* + * end workaround code, by now the rdev is available + * and locked, and wdev may or may not be NULL. + */ + + if (info->attrs[NL80211_ATTR_WIPHY_NAME]) + result = cfg80211_dev_rename( + rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); + + mutex_unlock(&cfg80211_mutex); + + if (result) + goto bad_res; + + if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) { + struct ieee80211_txq_params txq_params; + struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1]; + + if (!rdev->ops->set_txq_params) { + result = -EOPNOTSUPP; + goto bad_res; + } + + if (!netdev) { + result = -EINVAL; + goto bad_res; + } + + if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) { + result = -EINVAL; + goto bad_res; + } + + nla_for_each_nested(nl_txq_params, + info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], + rem_txq_params) { + nla_parse(tb, NL80211_TXQ_ATTR_MAX, + nla_data(nl_txq_params), + nla_len(nl_txq_params), + txq_params_policy); + result = parse_txq_params(tb, &txq_params); + if (result) + goto bad_res; + + result = rdev->ops->set_txq_params(&rdev->wiphy, + netdev, + &txq_params); + if (result) + goto bad_res; + } + } + + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { + result = __nl80211_set_channel(rdev, wdev, info); + if (result) + goto bad_res; + } + + if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) { + enum nl80211_tx_power_setting type; + int idx, mbm = 0; + + if (!rdev->ops->set_tx_power) { + result = -EOPNOTSUPP; + goto bad_res; + } + + idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING; + type = nla_get_u32(info->attrs[idx]); + + if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] && + (type != NL80211_TX_POWER_AUTOMATIC)) { + result = -EINVAL; + goto bad_res; + } + + if (type != NL80211_TX_POWER_AUTOMATIC) { + idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL; + mbm = nla_get_u32(info->attrs[idx]); + } + + result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm); + if (result) + goto bad_res; + } + + if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] && + info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) { + u32 tx_ant, rx_ant; + if ((!rdev->wiphy.available_antennas_tx && + !rdev->wiphy.available_antennas_rx) || + !rdev->ops->set_antenna) { + result = -EOPNOTSUPP; + goto bad_res; + } + + tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]); + rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]); + + /* reject antenna configurations which don't match the + * available antenna masks, except for the "all" mask */ + if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) || + (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) { + result = -EINVAL; + goto bad_res; + } + + tx_ant = tx_ant & rdev->wiphy.available_antennas_tx; + rx_ant = rx_ant & rdev->wiphy.available_antennas_rx; + + result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant); + if (result) + goto bad_res; + } + + changed = 0; + + if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) { + retry_short = nla_get_u8( + info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]); + if (retry_short == 0) { + result = -EINVAL; + goto bad_res; + } + changed |= WIPHY_PARAM_RETRY_SHORT; + } + + if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) { + retry_long = nla_get_u8( + info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]); + if (retry_long == 0) { + result = -EINVAL; + goto bad_res; + } + changed |= WIPHY_PARAM_RETRY_LONG; + } + + if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) { + frag_threshold = nla_get_u32( + info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]); + if (frag_threshold < 256) { + result = -EINVAL; + goto bad_res; + } + if (frag_threshold != (u32) -1) { + /* + * Fragments (apart from the last one) are required to + * have even length. Make the fragmentation code + * simpler by stripping LSB should someone try to use + * odd threshold value. + */ + frag_threshold &= ~0x1; + } + changed |= WIPHY_PARAM_FRAG_THRESHOLD; + } + + if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) { + rts_threshold = nla_get_u32( + info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]); + changed |= WIPHY_PARAM_RTS_THRESHOLD; + } + + if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) { + coverage_class = nla_get_u8( + info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]); + changed |= WIPHY_PARAM_COVERAGE_CLASS; + } + + if (changed) { + u8 old_retry_short, old_retry_long; + u32 old_frag_threshold, old_rts_threshold; + u8 old_coverage_class; + + if (!rdev->ops->set_wiphy_params) { + result = -EOPNOTSUPP; + goto bad_res; + } + + old_retry_short = rdev->wiphy.retry_short; + old_retry_long = rdev->wiphy.retry_long; + old_frag_threshold = rdev->wiphy.frag_threshold; + old_rts_threshold = rdev->wiphy.rts_threshold; + old_coverage_class = rdev->wiphy.coverage_class; + + if (changed & WIPHY_PARAM_RETRY_SHORT) + rdev->wiphy.retry_short = retry_short; + if (changed & WIPHY_PARAM_RETRY_LONG) + rdev->wiphy.retry_long = retry_long; + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) + rdev->wiphy.frag_threshold = frag_threshold; + if (changed & WIPHY_PARAM_RTS_THRESHOLD) + rdev->wiphy.rts_threshold = rts_threshold; + if (changed & WIPHY_PARAM_COVERAGE_CLASS) + rdev->wiphy.coverage_class = coverage_class; + + result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); + if (result) { + rdev->wiphy.retry_short = old_retry_short; + rdev->wiphy.retry_long = old_retry_long; + rdev->wiphy.frag_threshold = old_frag_threshold; + rdev->wiphy.rts_threshold = old_rts_threshold; + rdev->wiphy.coverage_class = old_coverage_class; + } + } + + bad_res: + mutex_unlock(&rdev->mtx); + if (netdev) + dev_put(netdev); + return result; +} + + +static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, + struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + void *hdr; + + hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE); + if (!hdr) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype); + + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, + rdev->devlist_generation ^ + (cfg80211_rdev_list_generation << 2)); + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb) +{ + int wp_idx = 0; + int if_idx = 0; + int wp_start = cb->args[0]; + int if_start = cb->args[1]; + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + + mutex_lock(&cfg80211_mutex); + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk))) + continue; + if (wp_idx < wp_start) { + wp_idx++; + continue; + } + if_idx = 0; + + mutex_lock(&rdev->devlist_mtx); + list_for_each_entry(wdev, &rdev->netdev_list, list) { + if (if_idx < if_start) { + if_idx++; + continue; + } + if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + rdev, wdev->netdev) < 0) { + mutex_unlock(&rdev->devlist_mtx); + goto out; + } + if_idx++; + } + mutex_unlock(&rdev->devlist_mtx); + + wp_idx++; + } + out: + mutex_unlock(&cfg80211_mutex); + + cb->args[0] = wp_idx; + cb->args[1] = if_idx; + + return skb->len; +} + +static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *msg; + struct cfg80211_registered_device *dev = info->user_ptr[0]; + struct net_device *netdev = info->user_ptr[1]; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, + dev, netdev) < 0) { + nlmsg_free(msg); + return -ENOBUFS; + } + + return genlmsg_reply(msg, info); +} + +static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = { + [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG }, + [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG }, + [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG }, + [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG }, + [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG }, +}; + +static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) +{ + struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1]; + int flag; + + *mntrflags = 0; + + if (!nla) + return -EINVAL; + + if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX, + nla, mntr_flags_policy)) + return -EINVAL; + + for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++) + if (flags[flag]) + *mntrflags |= (1<wiphy.flags & WIPHY_FLAG_4ADDR_AP) + return 0; + break; + case NL80211_IFTYPE_STATION: + if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION) + return 0; + break; + default: + break; + } + + return -EOPNOTSUPP; +} + +static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct vif_params params; + int err; + enum nl80211_iftype otype, ntype; + struct net_device *dev = info->user_ptr[1]; + u32 _flags, *flags = NULL; + bool change = false; + + memset(¶ms, 0, sizeof(params)); + + otype = ntype = dev->ieee80211_ptr->iftype; + + if (info->attrs[NL80211_ATTR_IFTYPE]) { + ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); + if (otype != ntype) + change = true; + if (ntype > NL80211_IFTYPE_MAX) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_MESH_ID]) { + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (ntype != NL80211_IFTYPE_MESH_POINT) + return -EINVAL; + if (netif_running(dev)) + return -EBUSY; + + wdev_lock(wdev); + BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != + IEEE80211_MAX_MESH_ID_LEN); + wdev->mesh_id_up_len = + nla_len(info->attrs[NL80211_ATTR_MESH_ID]); + memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]), + wdev->mesh_id_up_len); + wdev_unlock(wdev); + } + + if (info->attrs[NL80211_ATTR_4ADDR]) { + params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); + change = true; + err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype); + if (err) + return err; + } else { + params.use_4addr = -1; + } + + if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { + if (ntype != NL80211_IFTYPE_MONITOR) + return -EINVAL; + err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], + &_flags); + if (err) + return err; + + flags = &_flags; + change = true; + } + + if (change) + err = cfg80211_change_iface(rdev, dev, ntype, flags, ¶ms); + else + err = 0; + + if (!err && params.use_4addr != -1) + dev->ieee80211_ptr->use_4addr = params.use_4addr; + + return err; +} + +static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct vif_params params; + struct net_device *dev; + int err; + enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; + u32 flags; + + memset(¶ms, 0, sizeof(params)); + + if (!info->attrs[NL80211_ATTR_IFNAME]) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_IFTYPE]) { + type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); + if (type > NL80211_IFTYPE_MAX) + return -EINVAL; + } + + if (!rdev->ops->add_virtual_intf || + !(rdev->wiphy.interface_modes & (1 << type))) + return -EOPNOTSUPP; + + if (info->attrs[NL80211_ATTR_4ADDR]) { + params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); + err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type); + if (err) + return err; + } + + err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? + info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, + &flags); + dev = rdev->ops->add_virtual_intf(&rdev->wiphy, + nla_data(info->attrs[NL80211_ATTR_IFNAME]), + type, err ? NULL : &flags, ¶ms); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + if (type == NL80211_IFTYPE_MESH_POINT && + info->attrs[NL80211_ATTR_MESH_ID]) { + struct wireless_dev *wdev = dev->ieee80211_ptr; + + wdev_lock(wdev); + BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != + IEEE80211_MAX_MESH_ID_LEN); + wdev->mesh_id_up_len = + nla_len(info->attrs[NL80211_ATTR_MESH_ID]); + memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]), + wdev->mesh_id_up_len); + wdev_unlock(wdev); + } + + return 0; +} + +static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + + if (!rdev->ops->del_virtual_intf) + return -EOPNOTSUPP; + + return rdev->ops->del_virtual_intf(&rdev->wiphy, dev); +} + +struct get_key_cookie { + struct sk_buff *msg; + int error; + int idx; +}; + +static void get_key_callback(void *c, struct key_params *params) +{ + struct nlattr *key; + struct get_key_cookie *cookie = c; + + if (params->key) + NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA, + params->key_len, params->key); + + if (params->seq) + NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ, + params->seq_len, params->seq); + + if (params->cipher) + NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER, + params->cipher); + + key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY); + if (!key) + goto nla_put_failure; + + if (params->key) + NLA_PUT(cookie->msg, NL80211_KEY_DATA, + params->key_len, params->key); + + if (params->seq) + NLA_PUT(cookie->msg, NL80211_KEY_SEQ, + params->seq_len, params->seq); + + if (params->cipher) + NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER, + params->cipher); + + NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx); + + nla_nest_end(cookie->msg, key); + + return; + nla_put_failure: + cookie->error = 1; +} + +static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + int err; + struct net_device *dev = info->user_ptr[1]; + u8 key_idx = 0; + const u8 *mac_addr = NULL; + bool pairwise; + struct get_key_cookie cookie = { + .error = 0, + }; + void *hdr; + struct sk_buff *msg; + + if (info->attrs[NL80211_ATTR_KEY_IDX]) + key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); + + if (key_idx > 5) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_MAC]) + mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + pairwise = !!mac_addr; + if (info->attrs[NL80211_ATTR_KEY_TYPE]) { + u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]); + if (kt >= NUM_NL80211_KEYTYPES) + return -EINVAL; + if (kt != NL80211_KEYTYPE_GROUP && + kt != NL80211_KEYTYPE_PAIRWISE) + return -EINVAL; + pairwise = kt == NL80211_KEYTYPE_PAIRWISE; + } + + if (!rdev->ops->get_key) + return -EOPNOTSUPP; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_NEW_KEY); + if (IS_ERR(hdr)) + return PTR_ERR(hdr); + + cookie.msg = msg; + cookie.idx = key_idx; + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); + if (mac_addr) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); + + if (pairwise && mac_addr && + !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) + return -ENOENT; + + err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise, + mac_addr, &cookie, get_key_callback); + + if (err) + goto free_msg; + + if (cookie.error) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + return genlmsg_reply(msg, info); + + nla_put_failure: + err = -ENOBUFS; + free_msg: + nlmsg_free(msg); + return err; +} + +static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct key_parse key; + int err; + struct net_device *dev = info->user_ptr[1]; + + err = nl80211_parse_key(info, &key); + if (err) + return err; + + if (key.idx < 0) + return -EINVAL; + + /* only support setting default key */ + if (!key.def && !key.defmgmt) + return -EINVAL; + + wdev_lock(dev->ieee80211_ptr); + + if (key.def) { + if (!rdev->ops->set_default_key) { + err = -EOPNOTSUPP; + goto out; + } + + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (err) + goto out; + + err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx, + key.def_uni, key.def_multi); + + if (err) + goto out; + +#ifdef CONFIG_CFG80211_WEXT + dev->ieee80211_ptr->wext.default_key = key.idx; +#endif + } else { + if (key.def_uni || !key.def_multi) { + err = -EINVAL; + goto out; + } + + if (!rdev->ops->set_default_mgmt_key) { + err = -EOPNOTSUPP; + goto out; + } + + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (err) + goto out; + + err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, + dev, key.idx); + if (err) + goto out; + +#ifdef CONFIG_CFG80211_WEXT + dev->ieee80211_ptr->wext.default_mgmt_key = key.idx; +#endif + } + + out: + wdev_unlock(dev->ieee80211_ptr); + + return err; +} + +static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + int err; + struct net_device *dev = info->user_ptr[1]; + struct key_parse key; + const u8 *mac_addr = NULL; + + err = nl80211_parse_key(info, &key); + if (err) + return err; + + if (!key.p.key) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_MAC]) + mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (key.type == -1) { + if (mac_addr) + key.type = NL80211_KEYTYPE_PAIRWISE; + else + key.type = NL80211_KEYTYPE_GROUP; + } + + /* for now */ + if (key.type != NL80211_KEYTYPE_PAIRWISE && + key.type != NL80211_KEYTYPE_GROUP) + return -EINVAL; + + if (!rdev->ops->add_key) + return -EOPNOTSUPP; + + if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, + key.type == NL80211_KEYTYPE_PAIRWISE, + mac_addr)) + return -EINVAL; + + wdev_lock(dev->ieee80211_ptr); + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (!err) + err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, + key.type == NL80211_KEYTYPE_PAIRWISE, + mac_addr, &key.p); + wdev_unlock(dev->ieee80211_ptr); + + return err; +} + +static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + int err; + struct net_device *dev = info->user_ptr[1]; + u8 *mac_addr = NULL; + struct key_parse key; + + err = nl80211_parse_key(info, &key); + if (err) + return err; + + if (info->attrs[NL80211_ATTR_MAC]) + mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (key.type == -1) { + if (mac_addr) + key.type = NL80211_KEYTYPE_PAIRWISE; + else + key.type = NL80211_KEYTYPE_GROUP; + } + + /* for now */ + if (key.type != NL80211_KEYTYPE_PAIRWISE && + key.type != NL80211_KEYTYPE_GROUP) + return -EINVAL; + + if (!rdev->ops->del_key) + return -EOPNOTSUPP; + + wdev_lock(dev->ieee80211_ptr); + err = nl80211_key_allowed(dev->ieee80211_ptr); + + if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr && + !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) + err = -ENOENT; + + if (!err) + err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, + key.type == NL80211_KEYTYPE_PAIRWISE, + mac_addr); + +#ifdef CONFIG_CFG80211_WEXT + if (!err) { + if (key.idx == dev->ieee80211_ptr->wext.default_key) + dev->ieee80211_ptr->wext.default_key = -1; + else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key) + dev->ieee80211_ptr->wext.default_mgmt_key = -1; + } +#endif + wdev_unlock(dev->ieee80211_ptr); + + return err; +} + +static int validate_acl_mac_addrs(struct nlattr *nl_attr) +{ + struct nlattr *attr; + int n_entries = 0, tmp; + u8 mac_addr[ETH_ALEN]; + + nla_for_each_nested(attr, nl_attr, tmp) { + if (nla_len(attr) != ETH_ALEN) + return -1; + + memcpy(mac_addr, nla_data(attr), ETH_ALEN); + if (is_multicast_ether_addr(mac_addr)) + return -1; + + n_entries++; + } + + return n_entries; +} + +static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct cfg80211_acl_params *params; + struct nlattr *attr; + int n_mac_addrs = 0, i = 0, tmp, err; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + if (!(rdev->wiphy.features & NL80211_FEATURE_MAC_ACL)) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_ACL_POLICY] || + !info->attrs[NL80211_ATTR_MAC_ADDRS]) + return -EINVAL; + + n_mac_addrs = validate_acl_mac_addrs( + info->attrs[NL80211_ATTR_MAC_ADDRS]); + if (n_mac_addrs < 0) + return -EINVAL; + + if (n_mac_addrs > rdev->wiphy.max_acl_mac_addrs) + return -EOPNOTSUPP; + + params = kzalloc(sizeof(*params) + + (sizeof(*params->mac_addrs) * n_mac_addrs), + GFP_KERNEL); + + if (!params) + return -ENOMEM; + + params->acl_policy = nla_get_u8(info->attrs[NL80211_ATTR_ACL_POLICY]); + if (params->acl_policy != NL80211_ACL_POLICY_ACCEPT && + params->acl_policy != NL80211_ACL_POLICY_DENY) { + err = -EINVAL; + goto out_free; + } + + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) { + memcpy(¶ms->mac_addrs[i].addr, nla_data(attr), ETH_ALEN); + i++; + } + + params->n_acl_entries = n_mac_addrs; + + if (!rdev->ops->set_mac_acl) { + err = -EOPNOTSUPP; + goto out_free; + } + + err = rdev->ops->set_mac_acl(&rdev->wiphy, dev, params); + +out_free: + kfree(params); + + return err; +} + +static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) +{ + int (*call)(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *info); + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct beacon_parameters params; + int haveinfo = 0, err; + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) || + !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) || + !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) || + !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP])) + return -EINVAL; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + memset(¶ms, 0, sizeof(params)); + + switch (info->genlhdr->cmd) { + case NL80211_CMD_NEW_BEACON: + /* these are required for NEW_BEACON */ + if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] || + !info->attrs[NL80211_ATTR_DTIM_PERIOD] || + !info->attrs[NL80211_ATTR_BEACON_HEAD]) + return -EINVAL; + + params.interval = + nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); + params.dtim_period = + nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); + + err = cfg80211_validate_beacon_int(rdev, params.interval); + if (err) + return err; + + /* + * In theory, some of these attributes could be required for + * NEW_BEACON, but since they were not used when the command was + * originally added, keep them optional for old user space + * programs to work with drivers that do not need the additional + * information. + */ + if (info->attrs[NL80211_ATTR_SSID]) { + params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + params.ssid_len = + nla_len(info->attrs[NL80211_ATTR_SSID]); + if (params.ssid_len == 0 || + params.ssid_len > IEEE80211_MAX_SSID_LEN) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) { + params.hidden_ssid = nla_get_u32( + info->attrs[NL80211_ATTR_HIDDEN_SSID]); + if (params.hidden_ssid != + NL80211_HIDDEN_SSID_NOT_IN_USE && + params.hidden_ssid != + NL80211_HIDDEN_SSID_ZERO_LEN && + params.hidden_ssid != + NL80211_HIDDEN_SSID_ZERO_CONTENTS) + return -EINVAL; + } + + params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; + + if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { + params.auth_type = nla_get_u32( + info->attrs[NL80211_ATTR_AUTH_TYPE]); + if (!nl80211_valid_auth_type(params.auth_type)) + return -EINVAL; + } else + params.auth_type = NL80211_AUTHTYPE_AUTOMATIC; + + err = nl80211_crypto_settings(rdev, info, ¶ms.crypto, + NL80211_MAX_NR_CIPHER_SUITES); + if (err) + return err; + + if (info->attrs[NL80211_ATTR_STA_CAP_REQ]) { + params.sta_cap_req = nla_get_u8( + info->attrs[NL80211_ATTR_STA_CAP_REQ]); + if (params.sta_cap_req != NL80211_STA_CAP_REQ_11BONLY && + params.sta_cap_req != NL80211_STA_CAP_REQ_11GONLY) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_ACS]) { + if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_ACS) + params.auto_channel_select = nla_get_u8( + info->attrs[NL80211_ATTR_ACS]); + else + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_MAC_ACL]) { + if (!(rdev->wiphy.features & NL80211_FEATURE_MAC_ACL)) + return -EOPNOTSUPP; + params.acl_mac = + !!nla_get_u8(info->attrs[NL80211_ATTR_MAC_ACL]); + } + + call = rdev->ops->add_beacon; + break; + case NL80211_CMD_SET_BEACON: + call = rdev->ops->set_beacon; + break; + default: + WARN_ON(1); + return -EOPNOTSUPP; + } + + if (!call) + return -EOPNOTSUPP; + + if (info->attrs[NL80211_ATTR_BEACON_HEAD]) { + params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]); + params.head_len = + nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]); + haveinfo = 1; + } + + if (info->attrs[NL80211_ATTR_BEACON_TAIL]) { + params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]); + params.tail_len = + nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]); + haveinfo = 1; + } + + if (!haveinfo) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_IE]) { + params.beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]); + params.beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) { + params.proberesp_ies = + nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]); + params.proberesp_ies_len = + nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]); + } + + if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) { + params.assocresp_ies = + nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]); + params.assocresp_ies_len = + nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]); + } + + if (info->attrs[NL80211_ATTR_PROBE_RESP]) { + params.probe_resp = + nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]); + params.probe_resp_len = + nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]); + } + + err = call(&rdev->wiphy, dev, ¶ms); + if (!err && params.interval) + wdev->beacon_interval = params.interval; + return err; +} + +static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + if (!rdev->ops->del_beacon) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + err = rdev->ops->del_beacon(&rdev->wiphy, dev); + if (!err) + wdev->beacon_interval = 0; + return err; +} + +static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { + [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG }, + [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG }, + [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG }, + [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG }, + [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG }, +}; + +static int parse_station_flags(struct genl_info *info, + struct station_parameters *params) +{ + struct nlattr *flags[NL80211_STA_FLAG_MAX + 1]; + struct nlattr *nla; + int flag; + + /* + * Try parsing the new attribute first so userspace + * can specify both for older kernels. + */ + nla = info->attrs[NL80211_ATTR_STA_FLAGS2]; + if (nla) { + struct nl80211_sta_flag_update *sta_flags; + + sta_flags = nla_data(nla); + params->sta_flags_mask = sta_flags->mask; + params->sta_flags_set = sta_flags->set; + if ((params->sta_flags_mask | + params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID)) + return -EINVAL; + return 0; + } + + /* if present, parse the old attribute */ + + nla = info->attrs[NL80211_ATTR_STA_FLAGS]; + if (!nla) + return 0; + + if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX, + nla, sta_flags_policy)) + return -EINVAL; + + params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1; + params->sta_flags_mask &= ~1; + + for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++) + if (flags[flag]) + params->sta_flags_set |= (1<= 32 */ + bitrate = cfg80211_calculate_bitrate(info); + if (bitrate > 0) + NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); + + if (info->flags & RATE_INFO_FLAGS_MCS) + NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, info->mcs); + if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) + NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH); + if (info->flags & RATE_INFO_FLAGS_SHORT_GI) + NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI); + + nla_nest_end(msg, rate); + return true; + +nla_put_failure: + return false; +} + +static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, + int flags, struct net_device *dev, + const u8 *mac_addr, struct station_info *sinfo) +{ + void *hdr; + struct nlattr *sinfoattr, *bss_param; + + hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); + if (!hdr) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); + + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation); + + sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO); + if (!sinfoattr) + goto nla_put_failure; + if (sinfo->filled & STATION_INFO_CONNECTED_TIME) + NLA_PUT_U32(msg, NL80211_STA_INFO_CONNECTED_TIME, + sinfo->connected_time); + if (sinfo->filled & STATION_INFO_INACTIVE_TIME) + NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME, + sinfo->inactive_time); + if (sinfo->filled & STATION_INFO_RX_BYTES) + NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES, + sinfo->rx_bytes); + if (sinfo->filled & STATION_INFO_TX_BYTES) + NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES, + sinfo->tx_bytes); + if (sinfo->filled & STATION_INFO_LLID) + NLA_PUT_U16(msg, NL80211_STA_INFO_LLID, + sinfo->llid); + if (sinfo->filled & STATION_INFO_PLID) + NLA_PUT_U16(msg, NL80211_STA_INFO_PLID, + sinfo->plid); + if (sinfo->filled & STATION_INFO_PLINK_STATE) + NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE, + sinfo->plink_state); + if (sinfo->filled & STATION_INFO_SIGNAL) + NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL, + sinfo->signal); + if (sinfo->filled & STATION_INFO_SIGNAL_AVG) + NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG, + sinfo->signal_avg); + if (sinfo->filled & STATION_INFO_TX_BITRATE) { + if (!nl80211_put_sta_rate(msg, &sinfo->txrate, + NL80211_STA_INFO_TX_BITRATE)) + goto nla_put_failure; + } + if (sinfo->filled & STATION_INFO_RX_BITRATE) { + if (!nl80211_put_sta_rate(msg, &sinfo->rxrate, + NL80211_STA_INFO_RX_BITRATE)) + goto nla_put_failure; + } + if (sinfo->filled & STATION_INFO_RX_PACKETS) + NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS, + sinfo->rx_packets); + if (sinfo->filled & STATION_INFO_TX_PACKETS) + NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS, + sinfo->tx_packets); + if (sinfo->filled & STATION_INFO_TX_RETRIES) + NLA_PUT_U32(msg, NL80211_STA_INFO_TX_RETRIES, + sinfo->tx_retries); + if (sinfo->filled & STATION_INFO_TX_FAILED) + NLA_PUT_U32(msg, NL80211_STA_INFO_TX_FAILED, + sinfo->tx_failed); + if (sinfo->filled & STATION_INFO_BSS_PARAM) { + bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM); + if (!bss_param) + goto nla_put_failure; + + if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT) + NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_CTS_PROT); + if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE) + NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE); + if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME) + NLA_PUT_FLAG(msg, + NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME); + NLA_PUT_U8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD, + sinfo->bss_param.dtim_period); + NLA_PUT_U16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL, + sinfo->bss_param.beacon_interval); + + nla_nest_end(msg, bss_param); + } + if (sinfo->filled & STATION_INFO_STA_FLAGS) + NLA_PUT(msg, NL80211_STA_INFO_STA_FLAGS, + sizeof(struct nl80211_sta_flag_update), + &sinfo->sta_flags); + nla_nest_end(msg, sinfoattr); + + if (sinfo->filled & STATION_INFO_ASSOC_REQ_IES) + NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len, + sinfo->assoc_req_ies); + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int nl80211_dump_station(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct station_info sinfo; + struct cfg80211_registered_device *dev; + struct net_device *netdev; + u8 mac_addr[ETH_ALEN]; + int sta_idx = cb->args[1]; + int err; + + err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev); + if (err) + return err; + + if (!dev->ops->dump_station) { + err = -EOPNOTSUPP; + goto out_err; + } + + while (1) { + memset(&sinfo, 0, sizeof(sinfo)); + err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx, + mac_addr, &sinfo); + if (err == -ENOENT) + break; + if (err) + goto out_err; + + if (nl80211_send_station(skb, + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + netdev, mac_addr, + &sinfo) < 0) + goto out; + + sta_idx++; + } + + + out: + cb->args[1] = sta_idx; + err = skb->len; + out_err: + nl80211_finish_netdev_dump(dev); + + return err; +} + +static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct station_info sinfo; + struct sk_buff *msg; + u8 *mac_addr = NULL; + int err; + + memset(&sinfo, 0, sizeof(sinfo)); + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (!rdev->ops->get_station) + return -EOPNOTSUPP; + + err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo); + if (err) + return err; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0, + dev, mac_addr, &sinfo) < 0) { + nlmsg_free(msg); + return -ENOBUFS; + } + + return genlmsg_reply(msg, info); +} + +/* + * Get vlan interface making sure it is running and on the right wiphy. + */ +static int get_vlan(struct genl_info *info, + struct cfg80211_registered_device *rdev, + struct net_device **vlan) +{ + struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN]; + *vlan = NULL; + + if (vlanattr) { + *vlan = dev_get_by_index(genl_info_net(info), + nla_get_u32(vlanattr)); + if (!*vlan) + return -ENODEV; + if (!(*vlan)->ieee80211_ptr) + return -EINVAL; + if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy) + return -EINVAL; + if (!netif_running(*vlan)) + return -ENETDOWN; + } + return 0; +} + +static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + int err; + struct net_device *dev = info->user_ptr[1]; + struct station_parameters params; + u8 *mac_addr = NULL; + + memset(¶ms, 0, sizeof(params)); + + params.listen_interval = -1; + params.plink_state = -1; + + if (info->attrs[NL80211_ATTR_STA_AID]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) { + params.supported_rates = + nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); + params.supported_rates_len = + nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); + } + + if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) + params.listen_interval = + nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); + + if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) + params.ht_capa = + nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); + + if (parse_station_flags(info, ¶ms)) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) + params.plink_action = + nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); + + if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) + params.plink_state = + nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); + + err = get_vlan(info, rdev, ¶ms.vlan); + if (err) + goto out; + + /* validate settings */ + err = 0; + + switch (dev->ieee80211_ptr->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_P2P_GO: + /* disallow mesh-specific things */ + if (params.plink_action) + err = -EINVAL; + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_STATION: + /* disallow things sta doesn't support */ + if (params.plink_action) + err = -EINVAL; + if (params.vlan) + err = -EINVAL; + if (params.supported_rates && + !(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) + err = -EINVAL; + if (params.ht_capa) + err = -EINVAL; + if (params.listen_interval >= 0) + err = -EINVAL; + if (params.sta_flags_mask & + ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_TDLS_PEER))) + err = -EINVAL; + /* can't change the TDLS bit */ + if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && + (params.sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER))) + err = -EINVAL; + break; + case NL80211_IFTYPE_MESH_POINT: + /* disallow things mesh doesn't support */ + if (params.vlan) + err = -EINVAL; + if (params.ht_capa) + err = -EINVAL; + if (params.listen_interval >= 0) + err = -EINVAL; + if (params.sta_flags_mask & + ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_MFP) | + BIT(NL80211_STA_FLAG_AUTHORIZED))) + err = -EINVAL; + break; + default: + err = -EINVAL; + } + + if (err) + goto out; + + if (!rdev->ops->change_station) { + err = -EOPNOTSUPP; + goto out; + } + + err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, ¶ms); + + out: + if (params.vlan) + dev_put(params.vlan); + + return err; +} + +static struct nla_policy +nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = { + [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, + [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 }, +}; + +static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + int err; + struct net_device *dev = info->user_ptr[1]; + struct station_parameters params; + u8 *mac_addr = NULL; + + memset(¶ms, 0, sizeof(params)); + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_STA_AID]) + return -EINVAL; + + mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + params.supported_rates = + nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); + params.supported_rates_len = + nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); + params.listen_interval = + nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); + + params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); + if (!params.aid || params.aid > IEEE80211_MAX_AID) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) + params.ht_capa = + nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); + + if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) + params.plink_action = + nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); + + if (parse_station_flags(info, ¶ms)) + return -EINVAL; + + /* parse WME attributes if sta is WME capable */ + if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) && + (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) && + info->attrs[NL80211_ATTR_STA_WME]) { + struct nlattr *tb[NL80211_STA_WME_MAX + 1]; + struct nlattr *nla; + + nla = info->attrs[NL80211_ATTR_STA_WME]; + err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla, + nl80211_sta_wme_policy); + if (err) + return err; + + if (tb[NL80211_STA_WME_UAPSD_QUEUES]) + params.uapsd_queues = + nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]); + if (params.uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) + return -EINVAL; + + if (tb[NL80211_STA_WME_MAX_SP]) + params.max_sp = + nla_get_u8(tb[NL80211_STA_WME_MAX_SP]); + + if (params.max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) + return -EINVAL; + + params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD; + } + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) + return -EINVAL; + + /* + * Only managed stations can add TDLS peers, and only when the + * wiphy supports external TDLS setup. + */ + if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION && + !((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && + (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) && + (rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))) + return -EINVAL; + + err = get_vlan(info, rdev, ¶ms.vlan); + if (err) + goto out; + + /* validate settings */ + err = 0; + + if (!rdev->ops->add_station) { + err = -EOPNOTSUPP; + goto out; + } + + err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, ¶ms); + + out: + if (params.vlan) + dev_put(params.vlan); + return err; +} + +static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + u8 *mac_addr = NULL; + + if (info->attrs[NL80211_ATTR_MAC]) + mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EINVAL; + + if (!rdev->ops->del_station) + return -EOPNOTSUPP; + + return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr); +} + +static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq, + int flags, struct net_device *dev, + u8 *dst, u8 *next_hop, + struct mpath_info *pinfo) +{ + void *hdr; + struct nlattr *pinfoattr; + + hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); + if (!hdr) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); + NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop); + + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation); + + pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO); + if (!pinfoattr) + goto nla_put_failure; + if (pinfo->filled & MPATH_INFO_FRAME_QLEN) + NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN, + pinfo->frame_qlen); + if (pinfo->filled & MPATH_INFO_SN) + NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN, + pinfo->sn); + if (pinfo->filled & MPATH_INFO_METRIC) + NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC, + pinfo->metric); + if (pinfo->filled & MPATH_INFO_EXPTIME) + NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME, + pinfo->exptime); + if (pinfo->filled & MPATH_INFO_FLAGS) + NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS, + pinfo->flags); + if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT) + NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, + pinfo->discovery_timeout); + if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) + NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES, + pinfo->discovery_retries); + + nla_nest_end(msg, pinfoattr); + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int nl80211_dump_mpath(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct mpath_info pinfo; + struct cfg80211_registered_device *dev; + struct net_device *netdev; + u8 dst[ETH_ALEN]; + u8 next_hop[ETH_ALEN]; + int path_idx = cb->args[1]; + int err; + + err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev); + if (err) + return err; + + if (!dev->ops->dump_mpath) { + err = -EOPNOTSUPP; + goto out_err; + } + + if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { + err = -EOPNOTSUPP; + goto out_err; + } + + while (1) { + err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx, + dst, next_hop, &pinfo); + if (err == -ENOENT) + break; + if (err) + goto out_err; + + if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + netdev, dst, next_hop, + &pinfo) < 0) + goto out; + + path_idx++; + } + + + out: + cb->args[1] = path_idx; + err = skb->len; + out_err: + nl80211_finish_netdev_dump(dev); + return err; +} + +static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + int err; + struct net_device *dev = info->user_ptr[1]; + struct mpath_info pinfo; + struct sk_buff *msg; + u8 *dst = NULL; + u8 next_hop[ETH_ALEN]; + + memset(&pinfo, 0, sizeof(pinfo)); + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + dst = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (!rdev->ops->get_mpath) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo); + if (err) + return err; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0, + dev, dst, next_hop, &pinfo) < 0) { + nlmsg_free(msg); + return -ENOBUFS; + } + + return genlmsg_reply(msg, info); +} + +static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + u8 *dst = NULL; + u8 *next_hop = NULL; + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) + return -EINVAL; + + dst = nla_data(info->attrs[NL80211_ATTR_MAC]); + next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); + + if (!rdev->ops->change_mpath) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop); +} + +static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + u8 *dst = NULL; + u8 *next_hop = NULL; + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) + return -EINVAL; + + dst = nla_data(info->attrs[NL80211_ATTR_MAC]); + next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); + + if (!rdev->ops->add_mpath) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop); +} + +static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + u8 *dst = NULL; + + if (info->attrs[NL80211_ATTR_MAC]) + dst = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (!rdev->ops->del_mpath) + return -EOPNOTSUPP; + + return rdev->ops->del_mpath(&rdev->wiphy, dev, dst); +} + +static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct bss_parameters params; + + memset(¶ms, 0, sizeof(params)); + /* default to not changing parameters */ + params.use_cts_prot = -1; + params.use_short_preamble = -1; + params.use_short_slot_time = -1; + params.ap_isolate = -1; + params.ht_opmode = -1; + + if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) + params.use_cts_prot = + nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]); + if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]) + params.use_short_preamble = + nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]); + if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) + params.use_short_slot_time = + nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); + if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { + params.basic_rates = + nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); + params.basic_rates_len = + nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); + } + if (info->attrs[NL80211_ATTR_AP_ISOLATE]) + params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]); + if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE]) + params.ht_opmode = + nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]); + + if (!rdev->ops->change_bss) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + return rdev->ops->change_bss(&rdev->wiphy, dev, ¶ms); +} + +static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { + [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, + [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, + [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, +}; + +static int parse_reg_rule(struct nlattr *tb[], + struct ieee80211_reg_rule *reg_rule) +{ + struct ieee80211_freq_range *freq_range = ®_rule->freq_range; + struct ieee80211_power_rule *power_rule = ®_rule->power_rule; + + if (!tb[NL80211_ATTR_REG_RULE_FLAGS]) + return -EINVAL; + if (!tb[NL80211_ATTR_FREQ_RANGE_START]) + return -EINVAL; + if (!tb[NL80211_ATTR_FREQ_RANGE_END]) + return -EINVAL; + if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) + return -EINVAL; + if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]) + return -EINVAL; + + reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]); + + freq_range->start_freq_khz = + nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]); + freq_range->end_freq_khz = + nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]); + freq_range->max_bandwidth_khz = + nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]); + + power_rule->max_eirp = + nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]); + + if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]) + power_rule->max_antenna_gain = + nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]); + + return 0; +} + +static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) +{ + int r; + char *data = NULL; + + /* + * You should only get this when cfg80211 hasn't yet initialized + * completely when built-in to the kernel right between the time + * window between nl80211_init() and regulatory_init(), if that is + * even possible. + */ + mutex_lock(&cfg80211_mutex); + if (unlikely(!cfg80211_regdomain)) { + mutex_unlock(&cfg80211_mutex); + return -EINPROGRESS; + } + mutex_unlock(&cfg80211_mutex); + + if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) + return -EINVAL; + + data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); + + r = regulatory_hint_user(data); + + return r; +} + +static int nl80211_get_mesh_config(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct mesh_config cur_params; + int err = 0; + void *hdr; + struct nlattr *pinfoattr; + struct sk_buff *msg; + + if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + if (!rdev->ops->get_mesh_config) + return -EOPNOTSUPP; + + wdev_lock(wdev); + /* If not connected, get default parameters */ + if (!wdev->mesh_id_len) + memcpy(&cur_params, &default_mesh_config, sizeof(cur_params)); + else + err = rdev->ops->get_mesh_config(&rdev->wiphy, dev, + &cur_params); + wdev_unlock(wdev); + + if (err) + return err; + + /* Draw up a netlink message to send back */ + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_GET_MESH_CONFIG); + if (!hdr) + goto out; + pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG); + if (!pinfoattr) + goto nla_put_failure; + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT, + cur_params.dot11MeshRetryTimeout); + NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT, + cur_params.dot11MeshConfirmTimeout); + NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT, + cur_params.dot11MeshHoldingTimeout); + NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS, + cur_params.dot11MeshMaxPeerLinks); + NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES, + cur_params.dot11MeshMaxRetries); + NLA_PUT_U8(msg, NL80211_MESHCONF_TTL, + cur_params.dot11MeshTTL); + NLA_PUT_U8(msg, NL80211_MESHCONF_ELEMENT_TTL, + cur_params.element_ttl); + NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, + cur_params.auto_open_plinks); + NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, + cur_params.dot11MeshHWMPmaxPREQretries); + NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME, + cur_params.path_refresh_time); + NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, + cur_params.min_discovery_timeout); + NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, + cur_params.dot11MeshHWMPactivePathTimeout); + NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, + cur_params.dot11MeshHWMPpreqMinInterval); + NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, + cur_params.dot11MeshHWMPnetDiameterTraversalTime); + NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE, + cur_params.dot11MeshHWMPRootMode); + NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL, + cur_params.dot11MeshHWMPRannInterval); + NLA_PUT_U8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, + cur_params.dot11MeshGateAnnouncementProtocol); + nla_nest_end(msg, pinfoattr); + genlmsg_end(msg, hdr); + return genlmsg_reply(msg, info); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + out: + nlmsg_free(msg); + return -ENOBUFS; +} + +static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = { + [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, + [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, + [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 }, + [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 }, + [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 }, + [NL80211_MESHCONF_TTL] = { .type = NLA_U8 }, + [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 }, + [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 }, + + [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 }, + [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 }, + [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 }, + [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 }, + [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 }, + [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 }, + [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 }, + [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 }, + [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 }, +}; + +static const struct nla_policy + nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = { + [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 }, + [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 }, + [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG }, + [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG }, +}; + +static int nl80211_parse_mesh_config(struct genl_info *info, + struct mesh_config *cfg, + u32 *mask_out) +{ + struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; + u32 mask = 0; + +#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \ +do {\ + if (table[attr_num]) {\ + cfg->param = nla_fn(table[attr_num]); \ + mask |= (1 << (attr_num - 1)); \ + } \ +} while (0);\ + + + if (!info->attrs[NL80211_ATTR_MESH_CONFIG]) + return -EINVAL; + if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX, + info->attrs[NL80211_ATTR_MESH_CONFIG], + nl80211_meshconf_params_policy)) + return -EINVAL; + + /* This makes sure that there aren't more than 32 mesh config + * parameters (otherwise our bitfield scheme would not work.) */ + BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32); + + /* Fill in the params struct */ + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, + mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, + mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, + mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, + mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, + mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, + mask, NL80211_MESHCONF_TTL, nla_get_u8); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, + mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, + mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, + mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, + nla_get_u8); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, + mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, + mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, + nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, + mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, + nla_get_u32); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, + mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, + nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, + dot11MeshHWMPnetDiameterTraversalTime, + mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, + nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, + dot11MeshHWMPRootMode, mask, + NL80211_MESHCONF_HWMP_ROOTMODE, + nla_get_u8); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, + dot11MeshHWMPRannInterval, mask, + NL80211_MESHCONF_HWMP_RANN_INTERVAL, + nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, + dot11MeshGateAnnouncementProtocol, mask, + NL80211_MESHCONF_GATE_ANNOUNCEMENTS, + nla_get_u8); + if (mask_out) + *mask_out = mask; + + return 0; + +#undef FILL_IN_MESH_PARAM_IF_SET +} + +static int nl80211_parse_mesh_setup(struct genl_info *info, + struct mesh_setup *setup) +{ + struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1]; + + if (!info->attrs[NL80211_ATTR_MESH_SETUP]) + return -EINVAL; + if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX, + info->attrs[NL80211_ATTR_MESH_SETUP], + nl80211_mesh_setup_params_policy)) + return -EINVAL; + + if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL]) + setup->path_sel_proto = + (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ? + IEEE80211_PATH_PROTOCOL_VENDOR : + IEEE80211_PATH_PROTOCOL_HWMP; + + if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC]) + setup->path_metric = + (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ? + IEEE80211_PATH_METRIC_VENDOR : + IEEE80211_PATH_METRIC_AIRTIME; + + + if (tb[NL80211_MESH_SETUP_IE]) { + struct nlattr *ieattr = + tb[NL80211_MESH_SETUP_IE]; + if (!is_valid_ie_attr(ieattr)) + return -EINVAL; + setup->ie = nla_data(ieattr); + setup->ie_len = nla_len(ieattr); + } + setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]); + setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]); + + return 0; +} + +static int nl80211_update_mesh_config(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct mesh_config cfg; + u32 mask; + int err; + + if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + if (!rdev->ops->update_mesh_config) + return -EOPNOTSUPP; + + err = nl80211_parse_mesh_config(info, &cfg, &mask); + if (err) + return err; + + wdev_lock(wdev); + if (!wdev->mesh_id_len) + err = -ENOLINK; + + if (!err) + err = rdev->ops->update_mesh_config(&rdev->wiphy, dev, + mask, &cfg); + + wdev_unlock(wdev); + + return err; +} + +static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *msg; + void *hdr = NULL; + struct nlattr *nl_reg_rules; + unsigned int i; + int err = -EINVAL; + + mutex_lock(&cfg80211_mutex); + + if (!cfg80211_regdomain) + goto out; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) { + err = -ENOBUFS; + goto out; + } + + hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_GET_REG); + if (!hdr) + goto put_failure; + + NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, + cfg80211_regdomain->alpha2); + + nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); + if (!nl_reg_rules) + goto nla_put_failure; + + for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { + struct nlattr *nl_reg_rule; + const struct ieee80211_reg_rule *reg_rule; + const struct ieee80211_freq_range *freq_range; + const struct ieee80211_power_rule *power_rule; + + reg_rule = &cfg80211_regdomain->reg_rules[i]; + freq_range = ®_rule->freq_range; + power_rule = ®_rule->power_rule; + + nl_reg_rule = nla_nest_start(msg, i); + if (!nl_reg_rule) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS, + reg_rule->flags); + NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START, + freq_range->start_freq_khz); + NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END, + freq_range->end_freq_khz); + NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW, + freq_range->max_bandwidth_khz); + NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, + power_rule->max_antenna_gain); + NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP, + power_rule->max_eirp); + + nla_nest_end(msg, nl_reg_rule); + } + + nla_nest_end(msg, nl_reg_rules); + + genlmsg_end(msg, hdr); + err = genlmsg_reply(msg, info); + goto out; + +nla_put_failure: + genlmsg_cancel(msg, hdr); +put_failure: + nlmsg_free(msg); + err = -EMSGSIZE; +out: + mutex_unlock(&cfg80211_mutex); + return err; +} + +static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; + struct nlattr *nl_reg_rule; + char *alpha2 = NULL; + int rem_reg_rules = 0, r = 0; + u32 num_rules = 0, rule_idx = 0, size_of_regd; + struct ieee80211_regdomain *rd = NULL; + + if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_REG_RULES]) + return -EINVAL; + + alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); + + nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], + rem_reg_rules) { + num_rules++; + if (num_rules > NL80211_MAX_SUPP_REG_RULES) + return -EINVAL; + } + + mutex_lock(&cfg80211_mutex); + + if (!reg_is_valid_request(alpha2)) { + r = -EINVAL; + goto bad_reg; + } + + size_of_regd = sizeof(struct ieee80211_regdomain) + + (num_rules * sizeof(struct ieee80211_reg_rule)); + + rd = kzalloc(size_of_regd, GFP_KERNEL); + if (!rd) { + r = -ENOMEM; + goto bad_reg; + } + + rd->n_reg_rules = num_rules; + rd->alpha2[0] = alpha2[0]; + rd->alpha2[1] = alpha2[1]; + + nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], + rem_reg_rules) { + nla_parse(tb, NL80211_REG_RULE_ATTR_MAX, + nla_data(nl_reg_rule), nla_len(nl_reg_rule), + reg_rule_policy); + r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]); + if (r) + goto bad_reg; + + rule_idx++; + + if (rule_idx > NL80211_MAX_SUPP_REG_RULES) { + r = -EINVAL; + goto bad_reg; + } + } + + BUG_ON(rule_idx != num_rules); + + r = set_regdom(rd); + + mutex_unlock(&cfg80211_mutex); + + return r; + + bad_reg: + mutex_unlock(&cfg80211_mutex); + kfree(rd); + return r; +} + +static int validate_scan_freqs(struct nlattr *freqs) +{ + struct nlattr *attr1, *attr2; + int n_channels = 0, tmp1, tmp2; + + nla_for_each_nested(attr1, freqs, tmp1) { + n_channels++; + /* + * Some hardware has a limited channel list for + * scanning, and it is pretty much nonsensical + * to scan for a channel twice, so disallow that + * and don't require drivers to check that the + * channel list they get isn't longer than what + * they can scan, as long as they can scan all + * the channels they registered at once. + */ + nla_for_each_nested(attr2, freqs, tmp2) + if (attr1 != attr2 && + nla_get_u32(attr1) == nla_get_u32(attr2)) + return 0; + } + + return n_channels; +} + +static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct cfg80211_scan_request *request; + struct nlattr *attr; + struct wiphy *wiphy; + int err, tmp, n_ssids = 0, n_channels, i; + size_t ie_len; + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + wiphy = &rdev->wiphy; + + if (!rdev->ops->scan) + return -EOPNOTSUPP; + + if (rdev->scan_req) + return -EBUSY; + + if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + n_channels = validate_scan_freqs( + info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); + if (!n_channels) + return -EINVAL; + } else { + enum ieee80211_band band; + n_channels = 0; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + if (wiphy->bands[band]) + n_channels += wiphy->bands[band]->n_channels; + } + + if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) + n_ssids++; + + if (n_ssids > wiphy->max_scan_ssids) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_IE]) + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + else + ie_len = 0; + + if (ie_len > wiphy->max_scan_ie_len) + return -EINVAL; + + request = kzalloc(sizeof(*request) + + sizeof(*request->ssids) * n_ssids + + sizeof(*request->channels) * n_channels + + ie_len, GFP_KERNEL); + if (!request) + return -ENOMEM; + + if (n_ssids) + request->ssids = (void *)&request->channels[n_channels]; + request->n_ssids = n_ssids; + if (ie_len) { + if (request->ssids) + request->ie = (void *)(request->ssids + n_ssids); + else + request->ie = (void *)(request->channels + n_channels); + } + + i = 0; + if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + /* user specified, bail out if channel not found */ + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) { + struct ieee80211_channel *chan; + + chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); + + if (!chan) { + err = -EINVAL; + goto out_free; + } + + /* ignore disabled channels */ + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + request->channels[i] = chan; + i++; + } + } else { + enum ieee80211_band band; + + /* all channels */ + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + int j; + if (!wiphy->bands[band]) + continue; + for (j = 0; j < wiphy->bands[band]->n_channels; j++) { + struct ieee80211_channel *chan; + + chan = &wiphy->bands[band]->channels[j]; + + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + request->channels[i] = chan; + i++; + } + } + } + + if (!i) { + err = -EINVAL; + goto out_free; + } + + request->n_channels = i; + + i = 0; + if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { + if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out_free; + } + request->ssids[i].ssid_len = nla_len(attr); + memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr)); + i++; + } + } + + if (info->attrs[NL80211_ATTR_IE]) { + request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + memcpy((void *)request->ie, + nla_data(info->attrs[NL80211_ATTR_IE]), + request->ie_len); + } + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + if (wiphy->bands[i]) + request->rates[i] = + (1 << wiphy->bands[i]->n_bitrates) - 1; + + if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) { + nla_for_each_nested(attr, + info->attrs[NL80211_ATTR_SCAN_SUPP_RATES], + tmp) { + enum ieee80211_band band = nla_type(attr); + + if (band < 0 || band >= IEEE80211_NUM_BANDS) { + err = -EINVAL; + goto out_free; + } + err = ieee80211_get_ratemask(wiphy->bands[band], + nla_data(attr), + nla_len(attr), + &request->rates[band]); + if (err) + goto out_free; + } + } + + request->no_cck = + nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); + + request->dev = dev; + request->wiphy = &rdev->wiphy; + + rdev->scan_req = request; + err = rdev->ops->scan(&rdev->wiphy, dev, request); + + if (!err) { + nl80211_send_scan_start(rdev, dev); + dev_hold(dev); + } else { + out_free: + rdev->scan_req = NULL; + kfree(request); + } + + return err; +} + +static int nl80211_start_sched_scan(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_sched_scan_request *request; + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct nlattr *attr; + struct wiphy *wiphy; + int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i; + u32 interval; + enum ieee80211_band band; + size_t ie_len; + struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1]; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || + !rdev->ops->sched_scan_start) + return -EOPNOTSUPP; + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]) + return -EINVAL; + + interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]); + if (interval == 0) + return -EINVAL; + + wiphy = &rdev->wiphy; + + if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + n_channels = validate_scan_freqs( + info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); + if (!n_channels) + return -EINVAL; + } else { + n_channels = 0; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + if (wiphy->bands[band]) + n_channels += wiphy->bands[band]->n_channels; + } + + if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], + tmp) + n_ssids++; + + if (n_ssids > wiphy->max_sched_scan_ssids) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) + nla_for_each_nested(attr, + info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], + tmp) + n_match_sets++; + + if (n_match_sets > wiphy->max_match_sets) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_IE]) + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + else + ie_len = 0; + + if (ie_len > wiphy->max_sched_scan_ie_len) + return -EINVAL; + + mutex_lock(&rdev->sched_scan_mtx); + + if (rdev->sched_scan_req) { + err = -EINPROGRESS; + goto out; + } + + request = kzalloc(sizeof(*request) + + sizeof(*request->ssids) * n_ssids + + sizeof(*request->match_sets) * n_match_sets + + sizeof(*request->channels) * n_channels + + ie_len, GFP_KERNEL); + if (!request) { + err = -ENOMEM; + goto out; + } + + if (n_ssids) + request->ssids = (void *)&request->channels[n_channels]; + request->n_ssids = n_ssids; + if (ie_len) { + if (request->ssids) + request->ie = (void *)(request->ssids + n_ssids); + else + request->ie = (void *)(request->channels + n_channels); + } + + if (n_match_sets) { + if (request->ie) + request->match_sets = (void *)(request->ie + ie_len); + else if (request->ssids) + request->match_sets = + (void *)(request->ssids + n_ssids); + else + request->match_sets = + (void *)(request->channels + n_channels); + } + request->n_match_sets = n_match_sets; + + i = 0; + if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + /* user specified, bail out if channel not found */ + nla_for_each_nested(attr, + info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], + tmp) { + struct ieee80211_channel *chan; + + chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); + + if (!chan) { + err = -EINVAL; + goto out_free; + } + + /* ignore disabled channels */ + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + request->channels[i] = chan; + i++; + } + } else { + /* all channels */ + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + int j; + if (!wiphy->bands[band]) + continue; + for (j = 0; j < wiphy->bands[band]->n_channels; j++) { + struct ieee80211_channel *chan; + + chan = &wiphy->bands[band]->channels[j]; + + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + request->channels[i] = chan; + i++; + } + } + } + + if (!i) { + err = -EINVAL; + goto out_free; + } + + request->n_channels = i; + + i = 0; + if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], + tmp) { + if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out_free; + } + request->ssids[i].ssid_len = nla_len(attr); + memcpy(request->ssids[i].ssid, nla_data(attr), + nla_len(attr)); + i++; + } + } + + i = 0; + if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) { + nla_for_each_nested(attr, + info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], + tmp) { + struct nlattr *ssid, *rssi; + + nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, + nla_data(attr), nla_len(attr), + nl80211_match_policy); + ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]; + if (ssid) { + if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out_free; + } + memcpy(request->match_sets[i].ssid.ssid, + nla_data(ssid), nla_len(ssid)); + request->match_sets[i].ssid.ssid_len = + nla_len(ssid); + } + rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI]; + if (rssi) + request->rssi_thold = nla_get_u32(rssi); + else + request->rssi_thold = + NL80211_SCAN_RSSI_THOLD_OFF; + i++; + } + } + + if (info->attrs[NL80211_ATTR_IE]) { + request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + memcpy((void *)request->ie, + nla_data(info->attrs[NL80211_ATTR_IE]), + request->ie_len); + } + + request->dev = dev; + request->wiphy = &rdev->wiphy; + request->interval = interval; + + err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); + if (!err) { + rdev->sched_scan_req = request; + nl80211_send_sched_scan(rdev, dev, + NL80211_CMD_START_SCHED_SCAN); + goto out; + } + +out_free: + kfree(request); +out: + mutex_unlock(&rdev->sched_scan_mtx); + return err; +} + +static int nl80211_stop_sched_scan(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + int err; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || + !rdev->ops->sched_scan_stop) + return -EOPNOTSUPP; + + mutex_lock(&rdev->sched_scan_mtx); + err = __cfg80211_stop_sched_scan(rdev, false); + mutex_unlock(&rdev->sched_scan_mtx); + + return err; +} + +static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, + u32 seq, int flags, + struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_internal_bss *intbss) +{ + struct cfg80211_bss *res = &intbss->pub; + void *hdr; + struct nlattr *bss; + int i; + + ASSERT_WDEV_LOCK(wdev); + + hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags, + NL80211_CMD_NEW_SCAN_RESULTS); + if (!hdr) + return -1; + + genl_dump_check_consistent(cb, hdr, &nl80211_fam); + + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex); + + bss = nla_nest_start(msg, NL80211_ATTR_BSS); + if (!bss) + goto nla_put_failure; + if (!is_zero_ether_addr(res->bssid)) + NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid); + if (res->information_elements && res->len_information_elements) + NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, + res->len_information_elements, + res->information_elements); + if (res->beacon_ies && res->len_beacon_ies && + res->beacon_ies != res->information_elements) + NLA_PUT(msg, NL80211_BSS_BEACON_IES, + res->len_beacon_ies, res->beacon_ies); + if (res->tsf) + NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); + if (res->beacon_interval) + NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval); + NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability); + NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq); + NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO, + jiffies_to_msecs(jiffies - intbss->ts)); + + switch (rdev->wiphy.signal_type) { + case CFG80211_SIGNAL_TYPE_MBM: + NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal); + break; + case CFG80211_SIGNAL_TYPE_UNSPEC: + NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal); + break; + default: + break; + } + + switch (wdev->iftype) { + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_STATION: + if (intbss == wdev->current_bss) + NLA_PUT_U32(msg, NL80211_BSS_STATUS, + NL80211_BSS_STATUS_ASSOCIATED); + else for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (intbss != wdev->auth_bsses[i]) + continue; + NLA_PUT_U32(msg, NL80211_BSS_STATUS, + NL80211_BSS_STATUS_AUTHENTICATED); + break; + } + break; + case NL80211_IFTYPE_ADHOC: + if (intbss == wdev->current_bss) + NLA_PUT_U32(msg, NL80211_BSS_STATUS, + NL80211_BSS_STATUS_IBSS_JOINED); + break; + default: + break; + } + + nla_nest_end(msg, bss); + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int nl80211_dump_scan(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct cfg80211_registered_device *rdev; + struct net_device *dev; + struct cfg80211_internal_bss *scan; + struct wireless_dev *wdev; + int start = cb->args[1], idx = 0; + int err; + + err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev); + if (err) + return err; + + wdev = dev->ieee80211_ptr; + + wdev_lock(wdev); + spin_lock_bh(&rdev->bss_lock); + cfg80211_bss_expire(rdev); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)) + cb->seq = rdev->bss_generation; +#endif + + list_for_each_entry(scan, &rdev->bss_list, list) { + if (++idx <= start) + continue; + if (nl80211_send_bss(skb, cb, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + rdev, wdev, scan) < 0) { + idx--; + break; + } + } + + spin_unlock_bh(&rdev->bss_lock); + wdev_unlock(wdev); + + cb->args[1] = idx; + nl80211_finish_netdev_dump(rdev); + + return skb->len; +} + +static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq, + int flags, struct net_device *dev, + struct survey_info *survey) +{ + void *hdr; + struct nlattr *infoattr; + + hdr = nl80211hdr_put(msg, pid, seq, flags, + NL80211_CMD_NEW_SURVEY_RESULTS); + if (!hdr) + return -ENOMEM; + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + + infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO); + if (!infoattr) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY, + survey->channel->center_freq); + if (survey->filled & SURVEY_INFO_NOISE_DBM) + NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE, + survey->noise); + if (survey->filled & SURVEY_INFO_IN_USE) + NLA_PUT_FLAG(msg, NL80211_SURVEY_INFO_IN_USE); + if (survey->filled & SURVEY_INFO_CHANNEL_TIME) + NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME, + survey->channel_time); + if (survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) + NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, + survey->channel_time_busy); + if (survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) + NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, + survey->channel_time_ext_busy); + if (survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) + NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX, + survey->channel_time_rx); + if (survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) + NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX, + survey->channel_time_tx); + + nla_nest_end(msg, infoattr); + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int nl80211_dump_survey(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct survey_info survey; + struct cfg80211_registered_device *dev; + struct net_device *netdev; + int survey_idx = cb->args[1]; + int res; + + res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev); + if (res) + return res; + + if (!dev->ops->dump_survey) { + res = -EOPNOTSUPP; + goto out_err; + } + + while (1) { + struct ieee80211_channel *chan; + + res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx, + &survey); + if (res == -ENOENT) + break; + if (res) + goto out_err; + + /* Survey without a channel doesn't make sense */ + if (!survey.channel) { + res = -EINVAL; + goto out; + } + + chan = ieee80211_get_channel(&dev->wiphy, + survey.channel->center_freq); + if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) { + survey_idx++; + continue; + } + + if (nl80211_send_survey(skb, + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + netdev, + &survey) < 0) + goto out; + survey_idx++; + } + + out: + cb->args[1] = survey_idx; + res = skb->len; + out_err: + nl80211_finish_netdev_dump(dev); + return res; +} + +static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) +{ + return auth_type <= NL80211_AUTHTYPE_MAX; +} + +static bool nl80211_valid_wpa_versions(u32 wpa_versions) +{ + return !(wpa_versions & ~(NL80211_WPA_VERSION_1 | + NL80211_WPA_VERSION_2)); +} + +static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct ieee80211_channel *chan; + const u8 *bssid, *ssid, *ie = NULL; + int err, ssid_len, ie_len = 0; + enum nl80211_auth_type auth_type; + struct key_parse key; + bool local_state_change; + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_AUTH_TYPE]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_SSID]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) + return -EINVAL; + + err = nl80211_parse_key(info, &key); + if (err) + return err; + + if (key.idx >= 0) { + if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP) + return -EINVAL; + if (!key.p.key || !key.p.key_len) + return -EINVAL; + if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 || + key.p.key_len != WLAN_KEY_LEN_WEP40) && + (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 || + key.p.key_len != WLAN_KEY_LEN_WEP104)) + return -EINVAL; + if (key.idx > 4) + return -EINVAL; + } else { + key.p.key_len = 0; + key.p.key = NULL; + } + + if (key.idx >= 0) { + int i; + bool ok = false; + for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) { + if (key.p.cipher == rdev->wiphy.cipher_suites[i]) { + ok = true; + break; + } + } + if (!ok) + return -EINVAL; + } + + if (!rdev->ops->auth) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) + return -EOPNOTSUPP; + + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + chan = ieee80211_get_channel(&rdev->wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) + return -EINVAL; + + ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + + if (info->attrs[NL80211_ATTR_IE]) { + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); + if (!nl80211_valid_auth_type(auth_type)) + return -EINVAL; + + local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; + + return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, + ssid, ssid_len, ie, ie_len, + key.p.key, key.p.key_len, key.idx, + local_state_change); +} + +static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, + struct genl_info *info, + struct cfg80211_crypto_settings *settings, + int cipher_limit) +{ + memset(settings, 0, sizeof(*settings)); + + settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; + + if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) { + u16 proto; + proto = nla_get_u16( + info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]); + settings->control_port_ethertype = cpu_to_be16(proto); + if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) && + proto != ETH_P_PAE) + return -EINVAL; + if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]) + settings->control_port_no_encrypt = true; + } else + settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE); + + if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) { + void *data; + int len, i; + + data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); + len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); + settings->n_ciphers_pairwise = len / sizeof(u32); + + if (len % sizeof(u32)) + return -EINVAL; + + if (settings->n_ciphers_pairwise > cipher_limit) + return -EINVAL; + + memcpy(settings->ciphers_pairwise, data, len); + + for (i = 0; i < settings->n_ciphers_pairwise; i++) + if (!cfg80211_supported_cipher_suite( + &rdev->wiphy, + settings->ciphers_pairwise[i])) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) { + settings->cipher_group = + nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]); + if (!cfg80211_supported_cipher_suite(&rdev->wiphy, + settings->cipher_group)) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) { + settings->wpa_versions = + nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]); + if (!nl80211_valid_wpa_versions(settings->wpa_versions)) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_AKM_SUITES]) { + void *data; + int len; + + data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]); + len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]); + settings->n_akm_suites = len / sizeof(u32); + + if (len % sizeof(u32)) + return -EINVAL; + + if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES) + return -EINVAL; + + memcpy(settings->akm_suites, data, len); + } + + return 0; +} + +static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct cfg80211_crypto_settings crypto; + struct ieee80211_channel *chan; + const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; + int err, ssid_len, ie_len = 0; + bool use_mfp = false; + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_MAC] || + !info->attrs[NL80211_ATTR_SSID] || + !info->attrs[NL80211_ATTR_WIPHY_FREQ]) + return -EINVAL; + + if (!rdev->ops->assoc) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) + return -EOPNOTSUPP; + + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + + chan = ieee80211_get_channel(&rdev->wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) + return -EINVAL; + + ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + + if (info->attrs[NL80211_ATTR_IE]) { + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + if (info->attrs[NL80211_ATTR_USE_MFP]) { + enum nl80211_mfp mfp = + nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]); + if (mfp == NL80211_MFP_REQUIRED) + use_mfp = true; + else if (mfp != NL80211_MFP_NO) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_PREV_BSSID]) + prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]); + + err = nl80211_crypto_settings(rdev, info, &crypto, 1); + if (!err) + err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, + ssid, ssid_len, ie, ie_len, use_mfp, + &crypto); + + return err; +} + +static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + const u8 *ie = NULL, *bssid; + int ie_len = 0; + u16 reason_code; + bool local_state_change; + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_REASON_CODE]) + return -EINVAL; + + if (!rdev->ops->deauth) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) + return -EOPNOTSUPP; + + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + + reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + if (reason_code == 0) { + /* Reason Code 0 is reserved */ + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_IE]) { + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; + + return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code, + local_state_change); +} + +static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + const u8 *ie = NULL, *bssid; + int ie_len = 0; + u16 reason_code; + bool local_state_change; + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_REASON_CODE]) + return -EINVAL; + + if (!rdev->ops->disassoc) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) + return -EOPNOTSUPP; + + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + + reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + if (reason_code == 0) { + /* Reason Code 0 is reserved */ + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_IE]) { + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; + + return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code, + local_state_change); +} + +static bool +nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev, + int mcast_rate[IEEE80211_NUM_BANDS], + int rateval) +{ + struct wiphy *wiphy = &rdev->wiphy; + bool found = false; + int band, i; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + struct ieee80211_supported_band *sband; + + sband = wiphy->bands[band]; + if (!sband) + continue; + + for (i = 0; i < sband->n_bitrates; i++) { + if (sband->bitrates[i].bitrate == rateval) { + mcast_rate[band] = i + 1; + found = true; + break; + } + } + } + + return found; +} + +static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct cfg80211_ibss_params ibss; + struct wiphy *wiphy; + struct cfg80211_cached_keys *connkeys = NULL; + int err; + + memset(&ibss, 0, sizeof(ibss)); + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || + !info->attrs[NL80211_ATTR_SSID] || + !nla_len(info->attrs[NL80211_ATTR_SSID])) + return -EINVAL; + + ibss.beacon_interval = 100; + + if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) { + ibss.beacon_interval = + nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); + if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000) + return -EINVAL; + } + + if (!rdev->ops->join_ibss) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) + return -EOPNOTSUPP; + + wiphy = &rdev->wiphy; + + if (info->attrs[NL80211_ATTR_MAC]) { + ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (!is_valid_ether_addr(ibss.bssid)) + return -EINVAL; + } + ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + + if (info->attrs[NL80211_ATTR_IE]) { + ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + ibss.channel = ieee80211_get_channel(wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!ibss.channel || + ibss.channel->flags & IEEE80211_CHAN_NO_IBSS || + ibss.channel->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; + + ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; + ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; + + if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { + u8 *rates = + nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); + int n_rates = + nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); + struct ieee80211_supported_band *sband = + wiphy->bands[ibss.channel->band]; + int err; + + err = ieee80211_get_ratemask(sband, rates, n_rates, + &ibss.basic_rates); + if (err) + return err; + } + + if (info->attrs[NL80211_ATTR_MCAST_RATE] && + !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate, + nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) + return -EINVAL; + + if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { + connkeys = nl80211_parse_connkeys(rdev, + info->attrs[NL80211_ATTR_KEYS]); + if (IS_ERR(connkeys)) + return PTR_ERR(connkeys); + } + + err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); + if (err) + kfree(connkeys); + return err; +} + +static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + + if (!rdev->ops->leave_ibss) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) + return -EOPNOTSUPP; + + return cfg80211_leave_ibss(rdev, dev, false); +} + +#ifdef CONFIG_NL80211_TESTMODE +static struct genl_multicast_group nl80211_testmode_mcgrp = { + .name = "testmode", +}; + +static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + int err; + + if (!info->attrs[NL80211_ATTR_TESTDATA]) + return -EINVAL; + + err = -EOPNOTSUPP; + if (rdev->ops->testmode_cmd) { + rdev->testmode_info = info; + err = rdev->ops->testmode_cmd(&rdev->wiphy, + nla_data(info->attrs[NL80211_ATTR_TESTDATA]), + nla_len(info->attrs[NL80211_ATTR_TESTDATA])); + rdev->testmode_info = NULL; + } + + return err; +} + +static int nl80211_testmode_dump(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct cfg80211_registered_device *dev; + int err; + long phy_idx; + void *data = NULL; + int data_len = 0; + + if (cb->args[0]) { + /* + * 0 is a valid index, but not valid for args[0], + * so we need to offset by 1. + */ + phy_idx = cb->args[0] - 1; + } else { + err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, + nl80211_fam.attrbuf, nl80211_fam.maxattr, + nl80211_policy); + if (err) + return err; + if (!nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]) + return -EINVAL; + phy_idx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]); + if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]) + cb->args[1] = + (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]; + } + + if (cb->args[1]) { + data = nla_data((void *)cb->args[1]); + data_len = nla_len((void *)cb->args[1]); + } + + mutex_lock(&cfg80211_mutex); + dev = cfg80211_rdev_by_wiphy_idx(phy_idx); + if (!dev) { + mutex_unlock(&cfg80211_mutex); + return -ENOENT; + } + cfg80211_lock_rdev(dev); + mutex_unlock(&cfg80211_mutex); + + if (!dev->ops->testmode_dump) { + err = -EOPNOTSUPP; + goto out_err; + } + + while (1) { + void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + NL80211_CMD_TESTMODE); + struct nlattr *tmdata; + + if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) { + genlmsg_cancel(skb, hdr); + break; + } + + tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA); + if (!tmdata) { + genlmsg_cancel(skb, hdr); + break; + } + err = dev->ops->testmode_dump(&dev->wiphy, skb, cb, + data, data_len); + nla_nest_end(skb, tmdata); + + if (err == -ENOBUFS || err == -ENOENT) { + genlmsg_cancel(skb, hdr); + break; + } else if (err) { + genlmsg_cancel(skb, hdr); + goto out_err; + } + + genlmsg_end(skb, hdr); + } + + err = skb->len; + /* see above */ + cb->args[0] = phy_idx + 1; + out_err: + cfg80211_unlock_rdev(dev); + return err; +} + +static struct sk_buff * +__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, + int approxlen, u32 pid, u32 seq, gfp_t gfp) +{ + struct sk_buff *skb; + void *hdr; + struct nlattr *data; + + skb = nlmsg_new(approxlen + 100, gfp); + if (!skb) + return NULL; + + hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE); + if (!hdr) { + kfree_skb(skb); + return NULL; + } + + NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + data = nla_nest_start(skb, NL80211_ATTR_TESTDATA); + + ((void **)skb->cb)[0] = rdev; + ((void **)skb->cb)[1] = hdr; + ((void **)skb->cb)[2] = data; + + return skb; + + nla_put_failure: + kfree_skb(skb); + return NULL; +} + +struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, + int approxlen) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + if (WARN_ON(!rdev->testmode_info)) + return NULL; + + return __cfg80211_testmode_alloc_skb(rdev, approxlen, + rdev->testmode_info->snd_pid, + rdev->testmode_info->snd_seq, + GFP_KERNEL); +} +EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb); + +int cfg80211_testmode_reply(struct sk_buff *skb) +{ + struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; + void *hdr = ((void **)skb->cb)[1]; + struct nlattr *data = ((void **)skb->cb)[2]; + + if (WARN_ON(!rdev->testmode_info)) { + kfree_skb(skb); + return -EINVAL; + } + + nla_nest_end(skb, data); + genlmsg_end(skb, hdr); + return genlmsg_reply(skb, rdev->testmode_info); +} +EXPORT_SYMBOL(cfg80211_testmode_reply); + +struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, + int approxlen, gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp); +} +EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); + +void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) +{ + void *hdr = ((void **)skb->cb)[1]; + struct nlattr *data = ((void **)skb->cb)[2]; + + nla_nest_end(skb, data); + genlmsg_end(skb, hdr); + genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp); +} +EXPORT_SYMBOL(cfg80211_testmode_event); +#endif + +static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct cfg80211_connect_params connect; + struct wiphy *wiphy; + struct cfg80211_cached_keys *connkeys = NULL; + int err; + + memset(&connect, 0, sizeof(connect)); + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_SSID] || + !nla_len(info->attrs[NL80211_ATTR_SSID])) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { + connect.auth_type = + nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); + if (!nl80211_valid_auth_type(connect.auth_type)) + return -EINVAL; + } else + connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; + + connect.privacy = info->attrs[NL80211_ATTR_PRIVACY]; + + err = nl80211_crypto_settings(rdev, info, &connect.crypto, + NL80211_MAX_NR_CIPHER_SUITES); + if (err) + return err; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) + return -EOPNOTSUPP; + + wiphy = &rdev->wiphy; + + connect.bg_scan_period = -1; + if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] && + (wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) { + connect.bg_scan_period = + nla_get_u16(info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]); + } + + if (info->attrs[NL80211_ATTR_MAC]) + connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + + if (info->attrs[NL80211_ATTR_IE]) { + connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { + connect.channel = + ieee80211_get_channel(wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!connect.channel || + connect.channel->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; + } + + if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) { + connkeys = nl80211_parse_connkeys(rdev, + info->attrs[NL80211_ATTR_KEYS]); + if (IS_ERR(connkeys)) + return PTR_ERR(connkeys); + } + + err = cfg80211_connect(rdev, dev, &connect, connkeys); + if (err) + kfree(connkeys); + return err; +} + +static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + u16 reason; + + if (!info->attrs[NL80211_ATTR_REASON_CODE]) + reason = WLAN_REASON_DEAUTH_LEAVING; + else + reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + + if (reason == 0) + return -EINVAL; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) + return -EOPNOTSUPP; + + return cfg80211_disconnect(rdev, dev, reason, true); +} + +static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net *net; + int err; + u32 pid; + + if (!info->attrs[NL80211_ATTR_PID]) + return -EINVAL; + + pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]); + + net = get_net_ns_by_pid(pid); + if (IS_ERR(net)) + return PTR_ERR(net); + + err = 0; + + /* check if anything to do */ + if (!net_eq(wiphy_net(&rdev->wiphy), net)) + err = cfg80211_switch_netns(rdev, net); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + put_net(net); +#endif + return err; +} + +static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa) = NULL; + struct net_device *dev = info->user_ptr[1]; + struct cfg80211_pmksa pmksa; + + memset(&pmksa, 0, sizeof(struct cfg80211_pmksa)); + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_PMKID]) + return -EINVAL; + + pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]); + pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) + return -EOPNOTSUPP; + + switch (info->genlhdr->cmd) { + case NL80211_CMD_SET_PMKSA: + rdev_ops = rdev->ops->set_pmksa; + break; + case NL80211_CMD_DEL_PMKSA: + rdev_ops = rdev->ops->del_pmksa; + break; + default: + WARN_ON(1); + break; + } + + if (!rdev_ops) + return -EOPNOTSUPP; + + return rdev_ops(&rdev->wiphy, dev, &pmksa); +} + +static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) + return -EOPNOTSUPP; + + if (!rdev->ops->flush_pmksa) + return -EOPNOTSUPP; + + return rdev->ops->flush_pmksa(&rdev->wiphy, dev); +} + +static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + u8 action_code, dialog_token; + u16 status_code; + u8 *peer; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || + !rdev->ops->tdls_mgmt) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_TDLS_ACTION] || + !info->attrs[NL80211_ATTR_STATUS_CODE] || + !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] || + !info->attrs[NL80211_ATTR_IE] || + !info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + peer = nla_data(info->attrs[NL80211_ATTR_MAC]); + action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]); + status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]); + dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]); + + return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code, + dialog_token, status_code, + nla_data(info->attrs[NL80211_ATTR_IE]), + nla_len(info->attrs[NL80211_ATTR_IE])); +} + +static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + enum nl80211_tdls_operation operation; + u8 *peer; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || + !rdev->ops->tdls_oper) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] || + !info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]); + peer = nla_data(info->attrs[NL80211_ATTR_MAC]); + + return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation); +} + +static int nl80211_remain_on_channel(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct ieee80211_channel *chan; + struct sk_buff *msg; + void *hdr; + u64 cookie; + enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; + u32 freq, duration; + int err; + + if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || + !info->attrs[NL80211_ATTR_DURATION]) + return -EINVAL; + + duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); + + /* + * We should be on that channel for at least one jiffie, + * and more than 5 seconds seems excessive. + */ + if (!duration || !msecs_to_jiffies(duration) || + duration > rdev->wiphy.max_remain_on_channel_duration) + return -EINVAL; + + if (!rdev->ops->remain_on_channel) + return -EOPNOTSUPP; + + if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + channel_type = nla_get_u32( + info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + if (channel_type != NL80211_CHAN_NO_HT && + channel_type != NL80211_CHAN_HT20 && + channel_type != NL80211_CHAN_HT40PLUS && + channel_type != NL80211_CHAN_HT40MINUS) + return -EINVAL; + } + + freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); + chan = rdev_freq_to_chan(rdev, freq, channel_type); + if (chan == NULL) + return -EINVAL; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_REMAIN_ON_CHANNEL); + + if (IS_ERR(hdr)) { + err = PTR_ERR(hdr); + goto free_msg; + } + + err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan, + channel_type, duration, &cookie); + + if (err) + goto free_msg; + + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); + + genlmsg_end(msg, hdr); + + return genlmsg_reply(msg, info); + + nla_put_failure: + err = -ENOBUFS; + free_msg: + nlmsg_free(msg); + return err; +} + +static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + u64 cookie; + + if (!info->attrs[NL80211_ATTR_COOKIE]) + return -EINVAL; + + if (!rdev->ops->cancel_remain_on_channel) + return -EOPNOTSUPP; + + cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); + + return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie); +} + +static u32 rateset_to_mask(struct ieee80211_supported_band *sband, + u8 *rates, u8 rates_len) +{ + u8 i; + u32 mask = 0; + + for (i = 0; i < rates_len; i++) { + int rate = (rates[i] & 0x7f) * 5; + int ridx; + for (ridx = 0; ridx < sband->n_bitrates; ridx++) { + struct ieee80211_rate *srate = + &sband->bitrates[ridx]; + if (rate == srate->bitrate) { + mask |= 1 << ridx; + break; + } + } + if (ridx == sband->n_bitrates) + return 0; /* rate not found */ + } + + return mask; +} + +static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband, + u8 *rates, u8 rates_len, + u8 mcs[IEEE80211_HT_MCS_MASK_LEN]) +{ + u8 i; + + memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN); + + for (i = 0; i < rates_len; i++) { + int ridx, rbit; + + ridx = rates[i] / 8; + rbit = BIT(rates[i] % 8); + + /* check validity */ + if ((ridx < 0) || (ridx > IEEE80211_HT_MCS_MASK_LEN)) + return false; + + /* check availability */ + if (sband->ht_cap.mcs.rx_mask[ridx] & rbit) + mcs[ridx] |= rbit; + else + return false; + } + + return true; +} + +static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { + [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, + .len = NL80211_MAX_SUPP_RATES }, + [NL80211_TXRATE_MCS] = { .type = NLA_BINARY, + .len = NL80211_MAX_SUPP_HT_RATES }, +}; + +static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, + struct genl_info *info) +{ + struct nlattr *tb[NL80211_TXRATE_MAX + 1]; + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct cfg80211_bitrate_mask mask; + int rem, i; + struct net_device *dev = info->user_ptr[1]; + struct nlattr *tx_rates; + struct ieee80211_supported_band *sband; + + if (info->attrs[NL80211_ATTR_TX_RATES] == NULL) + return -EINVAL; + + if (!rdev->ops->set_bitrate_mask) + return -EOPNOTSUPP; + + memset(&mask, 0, sizeof(mask)); + /* Default to all rates enabled */ + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { + sband = rdev->wiphy.bands[i]; + mask.control[i].legacy = + sband ? (1 << sband->n_bitrates) - 1 : 0; + if (sband) + memcpy(mask.control[i].mcs, + sband->ht_cap.mcs.rx_mask, + sizeof(mask.control[i].mcs)); + else + memset(mask.control[i].mcs, 0, + sizeof(mask.control[i].mcs)); + } + + /* + * The nested attribute uses enum nl80211_band as the index. This maps + * directly to the enum ieee80211_band values used in cfg80211. + */ + BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8); + nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) + { + enum ieee80211_band band = nla_type(tx_rates); + if (band < 0 || band >= IEEE80211_NUM_BANDS) + return -EINVAL; + sband = rdev->wiphy.bands[band]; + if (sband == NULL) + return -EINVAL; + nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates), + nla_len(tx_rates), nl80211_txattr_policy); + if (tb[NL80211_TXRATE_LEGACY]) { + mask.control[band].legacy = rateset_to_mask( + sband, + nla_data(tb[NL80211_TXRATE_LEGACY]), + nla_len(tb[NL80211_TXRATE_LEGACY])); + } + if (tb[NL80211_TXRATE_MCS]) { + if (!ht_rateset_to_mask( + sband, + nla_data(tb[NL80211_TXRATE_MCS]), + nla_len(tb[NL80211_TXRATE_MCS]), + mask.control[band].mcs)) + return -EINVAL; + } + + if (mask.control[band].legacy == 0) { + /* don't allow empty legacy rates if HT + * is not even supported. */ + if (!rdev->wiphy.bands[band]->ht_cap.ht_supported) + return -EINVAL; + + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) + if (mask.control[band].mcs[i]) + break; + + /* legacy and mcs rates may not be both empty */ + if (i == IEEE80211_HT_MCS_MASK_LEN) + return -EINVAL; + } + } + + return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask); +} + +static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION; + + if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_FRAME_TYPE]) + frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]); + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + /* not much point in registering if we can't reply */ + if (!rdev->ops->mgmt_tx) + return -EOPNOTSUPP; + + return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid, + frame_type, + nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), + nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); +} + +static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct ieee80211_channel *chan; + enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; + bool channel_type_valid = false; + u32 freq; + int err; + void *hdr = NULL; + u64 cookie; + struct sk_buff *msg = NULL; + unsigned int wait = 0; + bool offchan, no_cck, dont_wait_for_ack; + + dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK]; + + if (!info->attrs[NL80211_ATTR_FRAME] || + !info->attrs[NL80211_ATTR_WIPHY_FREQ]) + return -EINVAL; + + if (!rdev->ops->mgmt_tx) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + if (info->attrs[NL80211_ATTR_DURATION]) { + if (!rdev->ops->mgmt_tx_cancel_wait) + return -EINVAL; + wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); + } + + if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + channel_type = nla_get_u32( + info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + if (channel_type != NL80211_CHAN_NO_HT && + channel_type != NL80211_CHAN_HT20 && + channel_type != NL80211_CHAN_HT40PLUS && + channel_type != NL80211_CHAN_HT40MINUS) + return -EINVAL; + channel_type_valid = true; + } + + offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; + + no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); + + freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); + chan = rdev_freq_to_chan(rdev, freq, channel_type); + if (chan == NULL) + return -EINVAL; + + if (!dont_wait_for_ack) { + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_FRAME); + + if (IS_ERR(hdr)) { + err = PTR_ERR(hdr); + goto free_msg; + } + } + + err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type, + channel_type_valid, wait, + nla_data(info->attrs[NL80211_ATTR_FRAME]), + nla_len(info->attrs[NL80211_ATTR_FRAME]), + no_cck, dont_wait_for_ack, &cookie); + if (err) + goto free_msg; + + if (msg) { + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); + + genlmsg_end(msg, hdr); + return genlmsg_reply(msg, info); + } + + return 0; + + nla_put_failure: + err = -ENOBUFS; + free_msg: + nlmsg_free(msg); + return err; +} + +static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + u64 cookie; + + if (!info->attrs[NL80211_ATTR_COOKIE]) + return -EINVAL; + + if (!rdev->ops->mgmt_tx_cancel_wait) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); + + return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie); +} + +static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev; + struct net_device *dev = info->user_ptr[1]; + u8 ps_state; + bool state; + int err; + + if (!info->attrs[NL80211_ATTR_PS_STATE]) + return -EINVAL; + + ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]); + + if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED) + return -EINVAL; + + wdev = dev->ieee80211_ptr; + + if (!rdev->ops->set_power_mgmt) + return -EOPNOTSUPP; + + state = (ps_state == NL80211_PS_ENABLED) ? true : false; + + if (state == wdev->ps) + return 0; + + err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state, + wdev->ps_timeout); + if (!err) + wdev->ps = state; + return err; +} + +static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + enum nl80211_ps_state ps_state; + struct wireless_dev *wdev; + struct net_device *dev = info->user_ptr[1]; + struct sk_buff *msg; + void *hdr; + int err; + + wdev = dev->ieee80211_ptr; + + if (!rdev->ops->set_power_mgmt) + return -EOPNOTSUPP; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_GET_POWER_SAVE); + if (!hdr) { + err = -ENOBUFS; + goto free_msg; + } + + if (wdev->ps) + ps_state = NL80211_PS_ENABLED; + else + ps_state = NL80211_PS_DISABLED; + + NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state); + + genlmsg_end(msg, hdr); + return genlmsg_reply(msg, info); + + nla_put_failure: + err = -ENOBUFS; + free_msg: + nlmsg_free(msg); + return err; +} + +static struct nla_policy +nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 }, +}; + +static int nl80211_set_cqm_txe(struct genl_info *info, + u32 rate, u32 pkts, u32 intvl) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev; + struct net_device *dev = info->user_ptr[1]; + + if ((rate < 0 || rate > 100) || + (intvl < 0 || intvl > NL80211_CQM_TXE_MAX_INTVL)) + return -EINVAL; + + wdev = dev->ieee80211_ptr; + + if (!rdev->ops->set_cqm_txe_config) + return -EOPNOTSUPP; + + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) + return -EOPNOTSUPP; + + return rdev->ops->set_cqm_txe_config(wdev->wiphy, dev, + rate, pkts, intvl); +} + +static int nl80211_set_cqm_rssi(struct genl_info *info, + s32 threshold, u32 hysteresis) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev; + struct net_device *dev = info->user_ptr[1]; + + if (threshold > 0) + return -EINVAL; + + wdev = dev->ieee80211_ptr; + + if (!rdev->ops->set_cqm_rssi_config) + return -EOPNOTSUPP; + + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) + return -EOPNOTSUPP; + + return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev, + threshold, hysteresis); +} + +static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1]; + struct nlattr *cqm; + int err; + + cqm = info->attrs[NL80211_ATTR_CQM]; + if (!cqm) { + err = -EINVAL; + goto out; + } + + err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm, + nl80211_attr_cqm_policy); + if (err) + goto out; + + if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] && + attrs[NL80211_ATTR_CQM_RSSI_HYST]) { + s32 threshold; + u32 hysteresis; + threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); + hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); + err = nl80211_set_cqm_rssi(info, threshold, hysteresis); + } else if (attrs[NL80211_ATTR_CQM_TXE_RATE] && + attrs[NL80211_ATTR_CQM_TXE_PKTS] && + attrs[NL80211_ATTR_CQM_TXE_INTVL]) { + u32 rate, pkts, intvl; + rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]); + pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]); + intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]); + err = nl80211_set_cqm_txe(info, rate, pkts, intvl); + } else + err = -EINVAL; + +out: + return err; +} + +static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct mesh_config cfg; + struct mesh_setup setup; + int err; + + /* start with default */ + memcpy(&cfg, &default_mesh_config, sizeof(cfg)); + memcpy(&setup, &default_mesh_setup, sizeof(setup)); + + if (info->attrs[NL80211_ATTR_MESH_CONFIG]) { + /* and parse parameters if given */ + err = nl80211_parse_mesh_config(info, &cfg, NULL); + if (err) + return err; + } + + if (!info->attrs[NL80211_ATTR_MESH_ID] || + !nla_len(info->attrs[NL80211_ATTR_MESH_ID])) + return -EINVAL; + + setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); + setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); + + if (info->attrs[NL80211_ATTR_MESH_SETUP]) { + /* parse additional setup parameters if given */ + err = nl80211_parse_mesh_setup(info, &setup); + if (err) + return err; + } + + return cfg80211_join_mesh(rdev, dev, &setup, &cfg); +} + +static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + + return cfg80211_leave_mesh(rdev, dev); +} + +static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct sk_buff *msg; + void *hdr; + + if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) + return -EOPNOTSUPP; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_GET_WOWLAN); + if (!hdr) + goto nla_put_failure; + + if (rdev->wowlan) { + struct nlattr *nl_wowlan; + + nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); + if (!nl_wowlan) + goto nla_put_failure; + + if (rdev->wowlan->any) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); + if (rdev->wowlan->disconnect) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); + if (rdev->wowlan->magic_pkt) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); + if (rdev->wowlan->gtk_rekey_failure) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); + if (rdev->wowlan->eap_identity_req) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); + if (rdev->wowlan->four_way_handshake) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); + if (rdev->wowlan->rfkill_release) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); + if (rdev->wowlan->n_patterns) { + struct nlattr *nl_pats, *nl_pat; + int i, pat_len; + + nl_pats = nla_nest_start(msg, + NL80211_WOWLAN_TRIG_PKT_PATTERN); + if (!nl_pats) + goto nla_put_failure; + + for (i = 0; i < rdev->wowlan->n_patterns; i++) { + nl_pat = nla_nest_start(msg, i + 1); + if (!nl_pat) + goto nla_put_failure; + pat_len = rdev->wowlan->patterns[i].pattern_len; + NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK, + DIV_ROUND_UP(pat_len, 8), + rdev->wowlan->patterns[i].mask); + NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN, + pat_len, + rdev->wowlan->patterns[i].pattern); + nla_nest_end(msg, nl_pat); + } + nla_nest_end(msg, nl_pats); + } + + nla_nest_end(msg, nl_wowlan); + } + + genlmsg_end(msg, hdr); + return genlmsg_reply(msg, info); + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + +static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG]; + struct cfg80211_wowlan no_triggers = {}; + struct cfg80211_wowlan new_triggers = {}; + struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan; + int err, i; + + if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) + goto no_triggers; + + err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG, + nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), + nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), + nl80211_wowlan_policy); + if (err) + return err; + + if (tb[NL80211_WOWLAN_TRIG_ANY]) { + if (!(wowlan->flags & WIPHY_WOWLAN_ANY)) + return -EINVAL; + new_triggers.any = true; + } + + if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) { + if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT)) + return -EINVAL; + new_triggers.disconnect = true; + } + + if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) { + if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT)) + return -EINVAL; + new_triggers.magic_pkt = true; + } + + if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED]) + return -EINVAL; + + if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) { + if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)) + return -EINVAL; + new_triggers.gtk_rekey_failure = true; + } + + if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) { + if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)) + return -EINVAL; + new_triggers.eap_identity_req = true; + } + + if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) { + if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)) + return -EINVAL; + new_triggers.four_way_handshake = true; + } + + if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) { + if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE)) + return -EINVAL; + new_triggers.rfkill_release = true; + } + + if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { + struct nlattr *pat; + int n_patterns = 0; + int rem, pat_len, mask_len; + struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT]; + + nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], + rem) + n_patterns++; + if (n_patterns > wowlan->n_patterns) + return -EINVAL; + + new_triggers.patterns = kcalloc(n_patterns, + sizeof(new_triggers.patterns[0]), + GFP_KERNEL); + if (!new_triggers.patterns) + return -ENOMEM; + + new_triggers.n_patterns = n_patterns; + i = 0; + + nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], + rem) { + nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT, + nla_data(pat), nla_len(pat), NULL); + err = -EINVAL; + if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] || + !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]) + goto error; + pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]); + mask_len = DIV_ROUND_UP(pat_len, 8); + if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) != + mask_len) + goto error; + if (pat_len > wowlan->pattern_max_len || + pat_len < wowlan->pattern_min_len) + goto error; + + new_triggers.patterns[i].mask = + kmalloc(mask_len + pat_len, GFP_KERNEL); + if (!new_triggers.patterns[i].mask) { + err = -ENOMEM; + goto error; + } + new_triggers.patterns[i].pattern = + new_triggers.patterns[i].mask + mask_len; + memcpy(new_triggers.patterns[i].mask, + nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]), + mask_len); + new_triggers.patterns[i].pattern_len = pat_len; + memcpy(new_triggers.patterns[i].pattern, + nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]), + pat_len); + i++; + } + } + + if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) { + struct cfg80211_wowlan *ntrig; + ntrig = kmemdup(&new_triggers, sizeof(new_triggers), + GFP_KERNEL); + if (!ntrig) { + err = -ENOMEM; + goto error; + } + cfg80211_rdev_free_wowlan(rdev); + rdev->wowlan = ntrig; + if (rdev->ops->set_wow_mode) + rdev->ops->set_wow_mode(&rdev->wiphy, ntrig); + } else { + no_triggers: + cfg80211_rdev_free_wowlan(rdev); + rdev->wowlan = NULL; + if (rdev->ops->clr_wow_mode) + rdev->ops->clr_wow_mode(&rdev->wiphy); + } + + return 0; + error: + for (i = 0; i < new_triggers.n_patterns; i++) + kfree(new_triggers.patterns[i].mask); + kfree(new_triggers.patterns); + return err; +} + +static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct nlattr *tb[NUM_NL80211_REKEY_DATA]; + struct cfg80211_gtk_rekey_data rekey_data; + int err; + + if (!info->attrs[NL80211_ATTR_REKEY_DATA]) + return -EINVAL; + + err = nla_parse(tb, MAX_NL80211_REKEY_DATA, + nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]), + nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]), + nl80211_rekey_policy); + if (err) + return err; + + if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN) + return -ERANGE; + if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN) + return -ERANGE; + if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN) + return -ERANGE; + + memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]), + NL80211_KEK_LEN); + memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]), + NL80211_KCK_LEN); + memcpy(rekey_data.replay_ctr, + nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]), + NL80211_REPLAY_CTR_LEN); + + wdev_lock(wdev); + if (!wdev->current_bss) { + err = -ENOTCONN; + goto out; + } + + if (!rdev->ops->set_rekey_data) { + err = -EOPNOTSUPP; + goto out; + } + + err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data); + out: + wdev_unlock(wdev); + return err; +} + +static int nl80211_register_unexpected_frame(struct sk_buff *skb, + struct genl_info *info) +{ + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO) + return -EINVAL; + + if (wdev->ap_unexpected_nlpid) + return -EBUSY; + + wdev->ap_unexpected_nlpid = info->snd_pid; + return 0; +} + +static int nl80211_probe_client(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct sk_buff *msg; + void *hdr; + const u8 *addr; + u64 cookie; + int err; + + if (wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + if (!rdev->ops->probe_client) + return -EOPNOTSUPP; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_PROBE_CLIENT); + + if (IS_ERR(hdr)) { + err = PTR_ERR(hdr); + goto free_msg; + } + + addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie); + if (err) + goto free_msg; + + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); + + genlmsg_end(msg, hdr); + + return genlmsg_reply(msg, info); + + nla_put_failure: + err = -ENOBUFS; + free_msg: + nlmsg_free(msg); + return err; +} + +static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS)) + return -EOPNOTSUPP; + + if (rdev->ap_beacons_nlpid) + return -EBUSY; + + rdev->ap_beacons_nlpid = info->snd_pid; + + return 0; +} + +static int nl80211_btcoex_notify(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *dev = info->user_ptr[0]; + u8 *buf; + int len; + + if (!dev) + return -ENODEV; + + if (!info->attrs[NL80211_ATTR_BTCOEX_DATA]) + return -EINVAL; + + if (!dev->ops->notify_btcoex) + return -EOPNOTSUPP; + + buf = (char *)nla_data(info->attrs[NL80211_ATTR_BTCOEX_DATA]); + len = nla_len(info->attrs[NL80211_ATTR_BTCOEX_DATA]); + + return dev->ops->notify_btcoex(&dev->wiphy, buf, len); +} + +#define NL80211_FLAG_NEED_WIPHY 0x01 +#define NL80211_FLAG_NEED_NETDEV 0x02 +#define NL80211_FLAG_NEED_RTNL 0x04 +#define NL80211_FLAG_CHECK_NETDEV_UP 0x08 +#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\ + NL80211_FLAG_CHECK_NETDEV_UP) + +static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + struct net_device *dev; + int err; + bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL; + + if (rtnl) + rtnl_lock(); + + if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) { + rdev = cfg80211_get_dev_from_info(info); + if (IS_ERR(rdev)) { + if (rtnl) + rtnl_unlock(); + return PTR_ERR(rdev); + } + info->user_ptr[0] = rdev; + } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) { + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); + if (err) { + if (rtnl) + rtnl_unlock(); + return err; + } + if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP && + !netif_running(dev)) { + cfg80211_unlock_rdev(rdev); + dev_put(dev); + if (rtnl) + rtnl_unlock(); + return -ENETDOWN; + } + info->user_ptr[0] = rdev; + info->user_ptr[1] = dev; + } + + return 0; +} + +static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb, + struct genl_info *info) +{ + if (info->user_ptr[0]) + cfg80211_unlock_rdev(info->user_ptr[0]); + if (info->user_ptr[1]) + dev_put(info->user_ptr[1]); + if (ops->internal_flags & NL80211_FLAG_NEED_RTNL) + rtnl_unlock(); +} + +static struct genl_ops nl80211_ops[] = { + { + .cmd = NL80211_CMD_GET_WIPHY, + .doit = nl80211_get_wiphy, + .dumpit = nl80211_dump_wiphy, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + .internal_flags = NL80211_FLAG_NEED_WIPHY, + }, + { + .cmd = NL80211_CMD_SET_WIPHY, + .doit = nl80211_set_wiphy, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_GET_INTERFACE, + .doit = nl80211_get_interface, + .dumpit = nl80211_dump_interface, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + .internal_flags = NL80211_FLAG_NEED_NETDEV, + }, + { + .cmd = NL80211_CMD_SET_INTERFACE, + .doit = nl80211_set_interface, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_NEW_INTERFACE, + .doit = nl80211_new_interface, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_DEL_INTERFACE, + .doit = nl80211_del_interface, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_GET_KEY, + .doit = nl80211_get_key, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_KEY, + .doit = nl80211_set_key, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_NEW_KEY, + .doit = nl80211_new_key, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_DEL_KEY, + .doit = nl80211_del_key, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_BEACON, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .doit = nl80211_addset_beacon, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_NEW_BEACON, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .doit = nl80211_addset_beacon, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_DEL_BEACON, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .doit = nl80211_del_beacon, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_GET_STATION, + .doit = nl80211_get_station, + .dumpit = nl80211_dump_station, + .policy = nl80211_policy, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_STATION, + .doit = nl80211_set_station, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_NEW_STATION, + .doit = nl80211_new_station, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_DEL_STATION, + .doit = nl80211_del_station, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_GET_MPATH, + .doit = nl80211_get_mpath, + .dumpit = nl80211_dump_mpath, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_MPATH, + .doit = nl80211_set_mpath, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_NEW_MPATH, + .doit = nl80211_new_mpath, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_DEL_MPATH, + .doit = nl80211_del_mpath, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_BSS, + .doit = nl80211_set_bss, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_GET_REG, + .doit = nl80211_get_reg, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + }, + { + .cmd = NL80211_CMD_SET_REG, + .doit = nl80211_set_reg, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_REQ_SET_REG, + .doit = nl80211_req_set_reg, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_GET_MESH_CONFIG, + .doit = nl80211_get_mesh_config, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_MESH_CONFIG, + .doit = nl80211_update_mesh_config, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_TRIGGER_SCAN, + .doit = nl80211_trigger_scan, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_GET_SCAN, + .policy = nl80211_policy, + .dumpit = nl80211_dump_scan, + }, + { + .cmd = NL80211_CMD_START_SCHED_SCAN, + .doit = nl80211_start_sched_scan, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_STOP_SCHED_SCAN, + .doit = nl80211_stop_sched_scan, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_AUTHENTICATE, + .doit = nl80211_authenticate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_ASSOCIATE, + .doit = nl80211_associate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_DEAUTHENTICATE, + .doit = nl80211_deauthenticate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_DISASSOCIATE, + .doit = nl80211_disassociate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_JOIN_IBSS, + .doit = nl80211_join_ibss, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_LEAVE_IBSS, + .doit = nl80211_leave_ibss, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, +#ifdef CONFIG_NL80211_TESTMODE + { + .cmd = NL80211_CMD_TESTMODE, + .doit = nl80211_testmode_do, + .dumpit = nl80211_testmode_dump, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, +#endif + { + .cmd = NL80211_CMD_CONNECT, + .doit = nl80211_connect, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_DISCONNECT, + .doit = nl80211_disconnect, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_WIPHY_NETNS, + .doit = nl80211_wiphy_netns, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_GET_SURVEY, + .policy = nl80211_policy, + .dumpit = nl80211_dump_survey, + }, + { + .cmd = NL80211_CMD_SET_PMKSA, + .doit = nl80211_setdel_pmksa, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_DEL_PMKSA, + .doit = nl80211_setdel_pmksa, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_FLUSH_PMKSA, + .doit = nl80211_flush_pmksa, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_REMAIN_ON_CHANNEL, + .doit = nl80211_remain_on_channel, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, + .doit = nl80211_cancel_remain_on_channel, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_TX_BITRATE_MASK, + .doit = nl80211_set_tx_bitrate_mask, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_REGISTER_FRAME, + .doit = nl80211_register_mgmt, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_FRAME, + .doit = nl80211_tx_mgmt, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_FRAME_WAIT_CANCEL, + .doit = nl80211_tx_mgmt_cancel_wait, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_POWER_SAVE, + .doit = nl80211_set_power_save, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_GET_POWER_SAVE, + .doit = nl80211_get_power_save, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_CQM, + .doit = nl80211_set_cqm, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_CHANNEL, + .doit = nl80211_set_channel, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_WDS_PEER, + .doit = nl80211_set_wds_peer, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_JOIN_MESH, + .doit = nl80211_join_mesh, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_LEAVE_MESH, + .doit = nl80211_leave_mesh, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_GET_WOWLAN, + .doit = nl80211_get_wowlan, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_WOWLAN, + .doit = nl80211_set_wowlan, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_REKEY_OFFLOAD, + .doit = nl80211_set_rekey_data, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_TDLS_MGMT, + .doit = nl80211_tdls_mgmt, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_TDLS_OPER, + .doit = nl80211_tdls_oper, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_UNEXPECTED_FRAME, + .doit = nl80211_register_unexpected_frame, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_PROBE_CLIENT, + .doit = nl80211_probe_client, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_REGISTER_BEACONS, + .doit = nl80211_register_beacons, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_BTCOEX, + .doit = nl80211_btcoex_notify, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_MAC_ACL, + .doit = nl80211_set_mac_acl, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, +}; + +static struct genl_multicast_group nl80211_mlme_mcgrp = { + .name = "mlme", +}; + +/* multicast groups */ +static struct genl_multicast_group nl80211_config_mcgrp = { + .name = "config", +}; +static struct genl_multicast_group nl80211_scan_mcgrp = { + .name = "scan", +}; +static struct genl_multicast_group nl80211_regulatory_mcgrp = { + .name = "regulatory", +}; + +/* notification functions */ + +void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_config_mcgrp.id, GFP_KERNEL); +} + +static int nl80211_add_scan_req(struct sk_buff *msg, + struct cfg80211_registered_device *rdev) +{ + struct cfg80211_scan_request *req = rdev->scan_req; + struct nlattr *nest; + int i; + + ASSERT_RDEV_LOCK(rdev); + + if (WARN_ON(!req)) + return 0; + + nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS); + if (!nest) + goto nla_put_failure; + for (i = 0; i < req->n_ssids; i++) + NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid); + nla_nest_end(msg, nest); + + nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES); + if (!nest) + goto nla_put_failure; + for (i = 0; i < req->n_channels; i++) + NLA_PUT_U32(msg, i, req->channels[i]->center_freq); + nla_nest_end(msg, nest); + + if (req->ie) + NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie); + + return 0; + nla_put_failure: + return -ENOBUFS; +} + +static int nl80211_send_scan_msg(struct sk_buff *msg, + struct cfg80211_registered_device *rdev, + struct net_device *netdev, + u32 pid, u32 seq, int flags, + u32 cmd) +{ + void *hdr; + + hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); + if (!hdr) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + /* ignore errors and send incomplete event anyway */ + nl80211_add_scan_req(msg, rdev); + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int +nl80211_send_sched_scan_msg(struct sk_buff *msg, + struct cfg80211_registered_device *rdev, + struct net_device *netdev, + u32 pid, u32 seq, int flags, u32 cmd) +{ + void *hdr; + + hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); + if (!hdr) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_TRIGGER_SCAN) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); +} + +void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_NEW_SCAN_RESULTS) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); +} + +void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_SCAN_ABORTED) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); +} + +void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_SCHED_SCAN_RESULTS) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); +} + +void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u32 cmd) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); +} + +/* + * This can happen on global regulatory changes or device specific settings + * based on custom world regulatory domains. + */ +void nl80211_send_reg_change_event(struct regulatory_request *request) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE); + if (!hdr) { + nlmsg_free(msg); + return; + } + + /* Userspace can always count this one always being set */ + NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator); + + if (request->alpha2[0] == '0' && request->alpha2[1] == '0') + NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, + NL80211_REGDOM_TYPE_WORLD); + else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') + NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, + NL80211_REGDOM_TYPE_CUSTOM_WORLD); + else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') || + request->intersect) + NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, + NL80211_REGDOM_TYPE_INTERSECTION); + else { + NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, + NL80211_REGDOM_TYPE_COUNTRY); + NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2); + } + + if (wiphy_idx_valid(request->wiphy_idx)) + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx); + + genlmsg_end(msg, hdr); + + rcu_read_lock(); + genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, + GFP_ATOMIC); + rcu_read_unlock(); + + return; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, + enum nl80211_commands cmd, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len, gfp_t gfp) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_AUTHENTICATE, gfp); +} + +void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len, gfp_t gfp) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_ASSOCIATE, gfp); +} + +void nl80211_send_deauth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len, gfp_t gfp) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_DEAUTHENTICATE, gfp); +} + +void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len, gfp_t gfp) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_DISASSOCIATE, gfp); +} + +void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len, gfp_t gfp) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp); +} + +void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len, gfp_t gfp) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_UNPROT_DISASSOCIATE, gfp); +} + +static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, + struct net_device *netdev, int cmd, + const u8 *addr, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *addr, + gfp_t gfp) +{ + nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE, + addr, gfp); +} + +void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *addr, + gfp_t gfp) +{ + nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE, + addr, gfp); +} + +void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + if (bssid) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status); + if (req_ie) + NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie); + if (resp_ie) + NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + +} + +void nl80211_send_roamed(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + if (req_ie) + NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie); + if (resp_ie) + NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + +} + +void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u16 reason, + const u8 *ie, size_t ie_len, bool from_ap) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + if (from_ap && reason) + NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason); + if (from_ap) + NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP); + if (ie) + NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, GFP_KERNEL); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + +} + +void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *macaddr, const u8* ie, u8 ie_len, + gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr); + if (ie_len && ie) + NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *addr, + enum nl80211_key_type key_type, int key_id, + const u8 *tsc, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + if (addr) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type); + if (key_id != -1) + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id); + if (tsc) + NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void nl80211_send_beacon_hint_event(struct wiphy *wiphy, + struct ieee80211_channel *channel_before, + struct ieee80211_channel *channel_after) +{ + struct sk_buff *msg; + void *hdr; + struct nlattr *nl_freq; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT); + if (!hdr) { + nlmsg_free(msg); + return; + } + + /* + * Since we are applying the beacon hint to a wiphy we know its + * wiphy_idx is valid + */ + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)); + + /* Before */ + nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE); + if (!nl_freq) + goto nla_put_failure; + if (nl80211_msg_put_channel(msg, channel_before)) + goto nla_put_failure; + nla_nest_end(msg, nl_freq); + + /* After */ + nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER); + if (!nl_freq) + goto nla_put_failure; + if (nl80211_msg_put_channel(msg, channel_after)) + goto nla_put_failure; + nla_nest_end(msg, nl_freq); + + genlmsg_end(msg, hdr); + + rcu_read_lock(); + genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, + GFP_ATOMIC); + rcu_read_unlock(); + + return; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +static void nl80211_send_remain_on_chan_event( + int cmd, struct cfg80211_registered_device *rdev, + struct net_device *netdev, u64 cookie, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int duration, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type); + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); + + if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL) + NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u64 cookie, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int duration, gfp_t gfp) +{ + nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, + rdev, netdev, cookie, chan, + channel_type, duration, gfp); +} + +void nl80211_send_remain_on_channel_cancel( + struct cfg80211_registered_device *rdev, struct net_device *netdev, + u64 cookie, struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, gfp_t gfp) +{ + nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, + rdev, netdev, cookie, chan, + channel_type, 0, gfp); +} + +void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *mac_addr, + struct station_info *sinfo, gfp_t gfp) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); +} + +void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *mac_addr, + gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *mac_addr, + enum nl80211_connect_failed_reason reason, + gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONN_FAILED); + if (!hdr) { + nlmsg_free(msg); + return; + } + + if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) || + nla_put_u32(msg, NL80211_ATTR_CONN_FAILED_REASON, reason)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd, + const u8 *addr, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct sk_buff *msg; + void *hdr; + int err; + u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid); + + if (!nlpid) + return false; + + msg = nlmsg_new(100, gfp); + if (!msg) + return true; + + hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); + if (!hdr) { + nlmsg_free(msg); + return true; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + err = genlmsg_end(msg, hdr); + if (err < 0) { + nlmsg_free(msg); + return true; + } + + genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); + return true; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + return true; +} + +bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp) +{ + return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME, + addr, gfp); +} + +bool nl80211_unexpected_4addr_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp) +{ + return __nl80211_unexpected_frame(dev, + NL80211_CMD_UNEXPECTED_4ADDR_FRAME, + addr, gfp); +} + +int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u32 nlpid, + int freq, const u8 *buf, size_t len, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); + if (!hdr) { + nlmsg_free(msg); + return -ENOMEM; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); + + genlmsg_end(msg, hdr); + + return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + return -ENOBUFS; +} + +void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u64 cookie, + const u8 *buf, size_t len, bool ack, + gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); + if (ack) + NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); + + genlmsg_end(msg, hdr); + + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void +nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *pinfoattr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); + if (!pinfoattr) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + rssi_event); + + nla_nest_end(msg, pinfoattr); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *replay_ctr, gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *rekey_attr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + + rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA); + if (!rekey_attr) + goto nla_put_failure; + + NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, + NL80211_REPLAY_CTR_LEN, replay_ctr); + + nla_nest_end(msg, rekey_attr); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, int index, + const u8 *bssid, bool preauth, gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *attr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE); + if (!attr) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index); + NLA_PUT(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid); + if (preauth) + NLA_PUT_FLAG(msg, NL80211_PMKSA_CANDIDATE_PREAUTH); + + nla_nest_end(msg, attr); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, int freq, + enum nl80211_channel_type type, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, type); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void +nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *peer, + u32 num_packets, u32 rate, u32 intvl, gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *pinfoattr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); + if (!hdr) { + nlmsg_free(msg); + return; + } + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) + goto nla_put_failure; + + pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); + if (!pinfoattr) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets)) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate)) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl)) + goto nla_put_failure; + + nla_nest_end(msg, pinfoattr); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void +nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *peer, + u32 num_packets, gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *pinfoattr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer); + + pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); + if (!pinfoattr) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets); + + nla_nest_end(msg, pinfoattr); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void cfg80211_probe_status(struct net_device *dev, const u8 *addr, + u64 cookie, bool acked, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct sk_buff *msg; + void *hdr; + int err; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); + if (acked) + NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); + + err = genlmsg_end(msg, hdr); + if (err < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_probe_status); + +void cfg80211_report_obss_beacon(struct wiphy *wiphy, + const u8 *frame, size_t len, + int freq, gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct sk_buff *msg; + void *hdr; + u32 nlpid = ACCESS_ONCE(rdev->ap_beacons_nlpid); + + if (!nlpid) + return; + + msg = nlmsg_new(len + 100, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + if (freq) + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + NLA_PUT(msg, NL80211_ATTR_FRAME, len, frame); + + genlmsg_end(msg, hdr); + + genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_report_obss_beacon); + +static int nl80211_netlink_notify(struct notifier_block * nb, + unsigned long state, + void *_notify) +{ + struct netlink_notify *notify = _notify; + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + + if (state != NETLINK_URELEASE) + return NOTIFY_DONE; + + rcu_read_lock(); + + list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { + list_for_each_entry_rcu(wdev, &rdev->netdev_list, list) + cfg80211_mlme_unregister_socket(wdev, notify->pid); + if (rdev->ap_beacons_nlpid == notify->pid) + rdev->ap_beacons_nlpid = 0; + } + + rcu_read_unlock(); + + return NOTIFY_DONE; +} + +static struct notifier_block nl80211_netlink_notifier = { + .notifier_call = nl80211_netlink_notify, +}; + +/* initialisation/exit functions */ + +int nl80211_init(void) +{ + int err; + + err = genl_register_family_with_ops(&nl80211_fam, + nl80211_ops, ARRAY_SIZE(nl80211_ops)); + if (err) + return err; + + err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp); + if (err) + goto err_out; + + err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp); + if (err) + goto err_out; + + err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp); + if (err) + goto err_out; + + err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp); + if (err) + goto err_out; + +#ifdef CONFIG_NL80211_TESTMODE + err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp); + if (err) + goto err_out; +#endif + + err = netlink_register_notifier(&nl80211_netlink_notifier); + if (err) + goto err_out; + + return 0; + err_out: + genl_unregister_family(&nl80211_fam); + return err; +} + +void nl80211_exit(void) +{ + netlink_unregister_notifier(&nl80211_netlink_notifier); + genl_unregister_family(&nl80211_fam); +} diff --git a/net/wireless_ath/nl80211.h b/net/wireless_ath/nl80211.h new file mode 100644 index 00000000..4928ef8c --- /dev/null +++ b/net/wireless_ath/nl80211.h @@ -0,0 +1,139 @@ +#ifndef __NET_WIRELESS_NL80211_H +#define __NET_WIRELESS_NL80211_H + +#include "core.h" + +int nl80211_init(void); +void nl80211_exit(void); +void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); +void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, + struct net_device *netdev); +void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, + struct net_device *netdev); +void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, + struct net_device *netdev); +void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u32 cmd); +void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, + struct net_device *netdev); +void nl80211_send_reg_change_event(struct regulatory_request *request); +void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, gfp_t gfp); +void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, gfp_t gfp); +void nl80211_send_deauth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, gfp_t gfp); +void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, gfp_t gfp); +void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, gfp_t gfp); +void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, gfp_t gfp); +void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *addr, gfp_t gfp); +void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *addr, gfp_t gfp); +void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp); +void nl80211_send_roamed(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); +void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u16 reason, + const u8 *ie, size_t ie_len, bool from_ap); + +void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *macaddr, const u8* ie, u8 ie_len, + gfp_t gfp); +void +nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *addr, + enum nl80211_key_type key_type, + int key_id, const u8 *tsc, gfp_t gfp); + +void +nl80211_send_beacon_hint_event(struct wiphy *wiphy, + struct ieee80211_channel *channel_before, + struct ieee80211_channel *channel_after); + +void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + gfp_t gfp); + +void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + u64 cookie, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int duration, gfp_t gfp); +void nl80211_send_remain_on_channel_cancel( + struct cfg80211_registered_device *rdev, struct net_device *netdev, + u64 cookie, struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, gfp_t gfp); + +void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *mac_addr, + struct station_info *sinfo, gfp_t gfp); +void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *mac_addr, + gfp_t gfp); + +void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *mac_addr, + enum nl80211_connect_failed_reason reason, + gfp_t gfp); + +int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u32 nlpid, int freq, + const u8 *buf, size_t len, gfp_t gfp); +void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u64 cookie, + const u8 *buf, size_t len, bool ack, + gfp_t gfp); + +void +nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp); +void +nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *peer, + u32 num_packets, gfp_t gfp); + +void +nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *peer, + u32 num_packets, u32 rate, u32 intvl, gfp_t gfp); + +void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *replay_ctr, gfp_t gfp); + +void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, int index, + const u8 *bssid, bool preauth, gfp_t gfp); + +void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, + struct net_device *dev, int freq, + enum nl80211_channel_type type, gfp_t gfp); + +bool nl80211_unexpected_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp); +bool nl80211_unexpected_4addr_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp); + +#endif /* __NET_WIRELESS_NL80211_H */ diff --git a/net/wireless_ath/radiotap.c b/net/wireless_ath/radiotap.c new file mode 100644 index 00000000..c4ad7958 --- /dev/null +++ b/net/wireless_ath/radiotap.c @@ -0,0 +1,358 @@ +/* + * Radiotap parser + * + * Copyright 2007 Andy Green + * Copyright 2009 Johannes Berg + * + * 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. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See COPYING for more details. + */ + +#include +#include +#include +#include +#include + +/* function prototypes and related defs are in include/net/cfg80211.h */ + +static const struct radiotap_align_size rtap_namespace_sizes[] = { + [IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, }, + [IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, }, + [IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, }, + [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, }, + [IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, }, + [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, }, + [IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, }, + [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, }, + [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, }, + /* + * add more here as they are defined in radiotap.h + */ +}; + +static const struct ieee80211_radiotap_namespace radiotap_ns = { + .n_bits = ARRAY_SIZE(rtap_namespace_sizes), + .align_size = rtap_namespace_sizes, +}; + +/** + * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization + * @iterator: radiotap_iterator to initialize + * @radiotap_header: radiotap header to parse + * @max_length: total length we can parse into (eg, whole packet length) + * + * Returns: 0 or a negative error code if there is a problem. + * + * This function initializes an opaque iterator struct which can then + * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap + * argument which is present in the header. It knows about extended + * present headers and handles them. + * + * How to use: + * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator + * struct ieee80211_radiotap_iterator (no need to init the struct beforehand) + * checking for a good 0 return code. Then loop calling + * __ieee80211_radiotap_iterator_next()... it returns either 0, + * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem. + * The iterator's @this_arg member points to the start of the argument + * associated with the current argument index that is present, which can be + * found in the iterator's @this_arg_index member. This arg index corresponds + * to the IEEE80211_RADIOTAP_... defines. + * + * Radiotap header length: + * You can find the CPU-endian total radiotap header length in + * iterator->max_length after executing ieee80211_radiotap_iterator_init() + * successfully. + * + * Alignment Gotcha: + * You must take care when dereferencing iterator.this_arg + * for multibyte types... the pointer is not aligned. Use + * get_unaligned((type *)iterator.this_arg) to dereference + * iterator.this_arg for type "type" safely on all arches. + * + * Example code: + * See Documentation/networking/radiotap-headers.txt + */ + +int ieee80211_radiotap_iterator_init( + struct ieee80211_radiotap_iterator *iterator, + struct ieee80211_radiotap_header *radiotap_header, + int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns) +{ + /* Linux only supports version 0 radiotap format */ + if (radiotap_header->it_version) + return -EINVAL; + + /* sanity check for allowed length and radiotap length field */ + if (max_length < get_unaligned_le16(&radiotap_header->it_len)) + return -EINVAL; + + iterator->_rtheader = radiotap_header; + iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len); + iterator->_arg_index = 0; + iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present); + iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header); + iterator->_reset_on_ext = 0; + iterator->_next_bitmap = &radiotap_header->it_present; + iterator->_next_bitmap++; + iterator->_vns = vns; + iterator->current_namespace = &radiotap_ns; + iterator->is_radiotap_ns = 1; + + /* find payload start allowing for extended bitmap(s) */ + + if (iterator->_bitmap_shifter & (1<_arg) & + (1 << IEEE80211_RADIOTAP_EXT)) { + iterator->_arg += sizeof(uint32_t); + + /* + * check for insanity where the present bitmaps + * keep claiming to extend up to or even beyond the + * stated radiotap header length + */ + + if ((unsigned long)iterator->_arg - + (unsigned long)iterator->_rtheader > + (unsigned long)iterator->_max_length) + return -EINVAL; + } + + iterator->_arg += sizeof(uint32_t); + + /* + * no need to check again for blowing past stated radiotap + * header length, because ieee80211_radiotap_iterator_next + * checks it before it is dereferenced + */ + } + + iterator->this_arg = iterator->_arg; + + /* we are all initialized happily */ + + return 0; +} +EXPORT_SYMBOL(ieee80211_radiotap_iterator_init); + +static void find_ns(struct ieee80211_radiotap_iterator *iterator, + uint32_t oui, uint8_t subns) +{ + int i; + + iterator->current_namespace = NULL; + + if (!iterator->_vns) + return; + + for (i = 0; i < iterator->_vns->n_ns; i++) { + if (iterator->_vns->ns[i].oui != oui) + continue; + if (iterator->_vns->ns[i].subns != subns) + continue; + + iterator->current_namespace = &iterator->_vns->ns[i]; + break; + } +} + + + +/** + * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg + * @iterator: radiotap_iterator to move to next arg (if any) + * + * Returns: 0 if there is an argument to handle, + * -ENOENT if there are no more args or -EINVAL + * if there is something else wrong. + * + * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*) + * in @this_arg_index and sets @this_arg to point to the + * payload for the field. It takes care of alignment handling and extended + * present fields. @this_arg can be changed by the caller (eg, + * incremented to move inside a compound argument like + * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in + * little-endian format whatever the endianess of your CPU. + * + * Alignment Gotcha: + * You must take care when dereferencing iterator.this_arg + * for multibyte types... the pointer is not aligned. Use + * get_unaligned((type *)iterator.this_arg) to dereference + * iterator.this_arg for type "type" safely on all arches. + */ + +int ieee80211_radiotap_iterator_next( + struct ieee80211_radiotap_iterator *iterator) +{ + while (1) { + int hit = 0; + int pad, align, size, subns; + uint32_t oui; + + /* if no more EXT bits, that's it */ + if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT && + !(iterator->_bitmap_shifter & 1)) + return -ENOENT; + + if (!(iterator->_bitmap_shifter & 1)) + goto next_entry; /* arg not present */ + + /* get alignment/size of data */ + switch (iterator->_arg_index % 32) { + case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: + case IEEE80211_RADIOTAP_EXT: + align = 1; + size = 0; + break; + case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: + align = 2; + size = 6; + break; + default: + if (!iterator->current_namespace || + iterator->_arg_index >= iterator->current_namespace->n_bits) { + if (iterator->current_namespace == &radiotap_ns) + return -ENOENT; + align = 0; + } else { + align = iterator->current_namespace->align_size[iterator->_arg_index].align; + size = iterator->current_namespace->align_size[iterator->_arg_index].size; + } + if (!align) { + /* skip all subsequent data */ + iterator->_arg = iterator->_next_ns_data; + /* give up on this namespace */ + iterator->current_namespace = NULL; + goto next_entry; + } + break; + } + + /* + * arg is present, account for alignment padding + * + * Note that these alignments are relative to the start + * of the radiotap header. There is no guarantee + * that the radiotap header itself is aligned on any + * kind of boundary. + * + * The above is why get_unaligned() is used to dereference + * multibyte elements from the radiotap area. + */ + + pad = ((unsigned long)iterator->_arg - + (unsigned long)iterator->_rtheader) & (align - 1); + + if (pad) + iterator->_arg += align - pad; + + if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) { + int vnslen; + + if ((unsigned long)iterator->_arg + size - + (unsigned long)iterator->_rtheader > + (unsigned long)iterator->_max_length) + return -EINVAL; + + oui = (*iterator->_arg << 16) | + (*(iterator->_arg + 1) << 8) | + *(iterator->_arg + 2); + subns = *(iterator->_arg + 3); + + find_ns(iterator, oui, subns); + + vnslen = get_unaligned_le16(iterator->_arg + 4); + iterator->_next_ns_data = iterator->_arg + size + vnslen; + if (!iterator->current_namespace) + size += vnslen; + } + + /* + * this is what we will return to user, but we need to + * move on first so next call has something fresh to test + */ + iterator->this_arg_index = iterator->_arg_index; + iterator->this_arg = iterator->_arg; + iterator->this_arg_size = size; + + /* internally move on the size of this arg */ + iterator->_arg += size; + + /* + * check for insanity where we are given a bitmap that + * claims to have more arg content than the length of the + * radiotap section. We will normally end up equalling this + * max_length on the last arg, never exceeding it. + */ + + if ((unsigned long)iterator->_arg - + (unsigned long)iterator->_rtheader > + (unsigned long)iterator->_max_length) + return -EINVAL; + + /* these special ones are valid in each bitmap word */ + switch (iterator->_arg_index % 32) { + case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: + iterator->_reset_on_ext = 1; + + iterator->is_radiotap_ns = 0; + /* + * If parser didn't register this vendor + * namespace with us, allow it to show it + * as 'raw. Do do that, set argument index + * to vendor namespace. + */ + iterator->this_arg_index = + IEEE80211_RADIOTAP_VENDOR_NAMESPACE; + if (!iterator->current_namespace) + hit = 1; + goto next_entry; + case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: + iterator->_reset_on_ext = 1; + iterator->current_namespace = &radiotap_ns; + iterator->is_radiotap_ns = 1; + goto next_entry; + case IEEE80211_RADIOTAP_EXT: + /* + * bit 31 was set, there is more + * -- move to next u32 bitmap + */ + iterator->_bitmap_shifter = + get_unaligned_le32(iterator->_next_bitmap); + iterator->_next_bitmap++; + if (iterator->_reset_on_ext) + iterator->_arg_index = 0; + else + iterator->_arg_index++; + iterator->_reset_on_ext = 0; + break; + default: + /* we've got a hit! */ + hit = 1; + next_entry: + iterator->_bitmap_shifter >>= 1; + iterator->_arg_index++; + } + + /* if we found a valid arg earlier, return it now */ + if (hit) + return 0; + } +} +EXPORT_SYMBOL(ieee80211_radiotap_iterator_next); diff --git a/net/wireless_ath/reg.c b/net/wireless_ath/reg.c new file mode 100644 index 00000000..f232d9e3 --- /dev/null +++ b/net/wireless_ath/reg.c @@ -0,0 +1,2299 @@ +/* + * Copyright 2002-2005, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright 2007 Johannes Berg + * Copyright 2008 Luis R. Rodriguez + * + * 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. + */ + +/** + * DOC: Wireless regulatory infrastructure + * + * The usual implementation is for a driver to read a device EEPROM to + * determine which regulatory domain it should be operating under, then + * looking up the allowable channels in a driver-local table and finally + * registering those channels in the wiphy structure. + * + * Another set of compliance enforcement is for drivers to use their + * own compliance limits which can be stored on the EEPROM. The host + * driver or firmware may ensure these are used. + * + * In addition to all this we provide an extra layer of regulatory + * conformance. For drivers which do not have any regulatory + * information CRDA provides the complete regulatory solution. + * For others it provides a community effort on further restrictions + * to enhance compliance. + * + * Note: When number of rules --> infinity we will not be able to + * index on alpha2 any more, instead we'll probably have to + * rely on some SHA1 checksum of the regdomain for example. + * + */ + +//#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "reg.h" +#include "regdb.h" +#include "nl80211.h" + +#ifdef CONFIG_CFG80211_REG_DEBUG +#define REG_DBG_PRINT(format, args...) \ + printk(KERN_DEBUG pr_fmt(format), ##args) +#else +#define REG_DBG_PRINT(args...) +#endif + +/* Receipt of information from last regulatory request */ +static struct regulatory_request *last_request; + +/* To trigger userspace events */ +static struct platform_device *reg_pdev; + +static struct device_type reg_device_type = { + .uevent = reg_device_uevent, +}; + +/* + * Central wireless core regulatory domains, we only need two, + * the current one and a world regulatory domain in case we have no + * information to give us an alpha2 + */ +const struct ieee80211_regdomain *cfg80211_regdomain; + +/* + * Protects static reg.c components: + * - cfg80211_world_regdom + * - cfg80211_regdom + * - last_request + */ +static DEFINE_MUTEX(reg_mutex); + +static inline void assert_reg_lock(void) +{ + lockdep_assert_held(®_mutex); +} + +/* Used to queue up regulatory hints */ +static LIST_HEAD(reg_requests_list); +static spinlock_t reg_requests_lock; + +/* Used to queue up beacon hints for review */ +static LIST_HEAD(reg_pending_beacons); +static spinlock_t reg_pending_beacons_lock; + +/* Used to keep track of processed beacon hints */ +static LIST_HEAD(reg_beacon_list); + +struct reg_beacon { + struct list_head list; + struct ieee80211_channel chan; +}; + +static void reg_todo(struct work_struct *work); +static DECLARE_WORK(reg_work, reg_todo); + +static void reg_timeout_work(struct work_struct *work); +static DECLARE_DELAYED_WORK(reg_timeout, reg_timeout_work); + +/* We keep a static world regulatory domain in case of the absence of CRDA */ +static const struct ieee80211_regdomain world_regdom = { + .n_reg_rules = 5, + .alpha2 = "00", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..11 */ + REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), + /* IEEE 802.11b/g, channels 12..13. No HT40 + * channel fits here. */ + REG_RULE(2467-10, 2472+10, 20, 6, 20, + NL80211_RRF_PASSIVE_SCAN | + NL80211_RRF_NO_IBSS), + /* IEEE 802.11 channel 14 - Only JP enables + * this and for 802.11b only */ + REG_RULE(2484-10, 2484+10, 20, 6, 20, + NL80211_RRF_PASSIVE_SCAN | + NL80211_RRF_NO_IBSS | + NL80211_RRF_NO_OFDM), + /* IEEE 802.11a, channel 36..48 */ + REG_RULE(5180-10, 5240+10, 40, 6, 20, + NL80211_RRF_PASSIVE_SCAN | + NL80211_RRF_NO_IBSS), + + /* NB: 5260 MHz - 5700 MHz requies DFS */ + + /* IEEE 802.11a, channel 149..165 */ + REG_RULE(5745-10, 5825+10, 40, 6, 20, + NL80211_RRF_PASSIVE_SCAN | + NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain *cfg80211_world_regdom = + &world_regdom; + +static char *ieee80211_regdom = "00"; +static char user_alpha2[2]; + +module_param(ieee80211_regdom, charp, 0444); +MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); + +static void reset_regdomains(void) +{ + /* avoid freeing static information or freeing something twice */ + if (cfg80211_regdomain == cfg80211_world_regdom) + cfg80211_regdomain = NULL; + if (cfg80211_world_regdom == &world_regdom) + cfg80211_world_regdom = NULL; + if (cfg80211_regdomain == &world_regdom) + cfg80211_regdomain = NULL; + + kfree(cfg80211_regdomain); + kfree(cfg80211_world_regdom); + + cfg80211_world_regdom = &world_regdom; + cfg80211_regdomain = NULL; +} + +/* + * Dynamic world regulatory domain requested by the wireless + * core upon initialization + */ +static void update_world_regdomain(const struct ieee80211_regdomain *rd) +{ + BUG_ON(!last_request); + + reset_regdomains(); + + cfg80211_world_regdom = rd; + cfg80211_regdomain = rd; +} + +bool is_world_regdom(const char *alpha2) +{ + if (!alpha2) + return false; + if (alpha2[0] == '0' && alpha2[1] == '0') + return true; + return false; +} + +static bool is_alpha2_set(const char *alpha2) +{ + if (!alpha2) + return false; + if (alpha2[0] != 0 && alpha2[1] != 0) + return true; + return false; +} + +static bool is_unknown_alpha2(const char *alpha2) +{ + if (!alpha2) + return false; + /* + * Special case where regulatory domain was built by driver + * but a specific alpha2 cannot be determined + */ + if (alpha2[0] == '9' && alpha2[1] == '9') + return true; + return false; +} + +static bool is_intersected_alpha2(const char *alpha2) +{ + if (!alpha2) + return false; + /* + * Special case where regulatory domain is the + * result of an intersection between two regulatory domain + * structures + */ + if (alpha2[0] == '9' && alpha2[1] == '8') + return true; + return false; +} + +static bool is_an_alpha2(const char *alpha2) +{ + if (!alpha2) + return false; + if (isalpha(alpha2[0]) && isalpha(alpha2[1])) + return true; + return false; +} + +static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) +{ + if (!alpha2_x || !alpha2_y) + return false; + if (alpha2_x[0] == alpha2_y[0] && + alpha2_x[1] == alpha2_y[1]) + return true; + return false; +} + +static bool regdom_changes(const char *alpha2) +{ + assert_cfg80211_lock(); + + if (!cfg80211_regdomain) + return true; + if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) + return false; + return true; +} + +/* + * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets + * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER + * has ever been issued. + */ +static bool is_user_regdom_saved(void) +{ + if (user_alpha2[0] == '9' && user_alpha2[1] == '7') + return false; + + /* This would indicate a mistake on the design */ + if (WARN((!is_world_regdom(user_alpha2) && + !is_an_alpha2(user_alpha2)), + "Unexpected user alpha2: %c%c\n", + user_alpha2[0], + user_alpha2[1])) + return false; + + return true; +} + +static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, + const struct ieee80211_regdomain *src_regd) +{ + struct ieee80211_regdomain *regd; + int size_of_regd = 0; + unsigned int i; + + size_of_regd = sizeof(struct ieee80211_regdomain) + + ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); + + regd = kzalloc(size_of_regd, GFP_KERNEL); + if (!regd) + return -ENOMEM; + + memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); + + for (i = 0; i < src_regd->n_reg_rules; i++) + memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], + sizeof(struct ieee80211_reg_rule)); + + *dst_regd = regd; + return 0; +} + +#ifdef CONFIG_CFG80211_INTERNAL_REGDB +struct reg_regdb_search_request { + char alpha2[2]; + struct list_head list; +}; + +static LIST_HEAD(reg_regdb_search_list); +static DEFINE_MUTEX(reg_regdb_search_mutex); + +static void reg_regdb_search(struct work_struct *work) +{ + struct reg_regdb_search_request *request; + const struct ieee80211_regdomain *curdom, *regdom; + int i, r; + + mutex_lock(®_regdb_search_mutex); + while (!list_empty(®_regdb_search_list)) { + request = list_first_entry(®_regdb_search_list, + struct reg_regdb_search_request, + list); + list_del(&request->list); + + for (i=0; ialpha2, curdom->alpha2, 2)) { + r = reg_copy_regd(®dom, curdom); + if (r) + break; + mutex_lock(&cfg80211_mutex); + set_regdom(regdom); + mutex_unlock(&cfg80211_mutex); + break; + } + } + + kfree(request); + } + mutex_unlock(®_regdb_search_mutex); +} + +static DECLARE_WORK(reg_regdb_work, reg_regdb_search); + +static void reg_regdb_query(const char *alpha2) +{ + struct reg_regdb_search_request *request; + + if (!alpha2) + return; + + request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL); + if (!request) + return; + + memcpy(request->alpha2, alpha2, 2); + + mutex_lock(®_regdb_search_mutex); + list_add_tail(&request->list, ®_regdb_search_list); + mutex_unlock(®_regdb_search_mutex); + + schedule_work(®_regdb_work); +} +#else +static inline void reg_regdb_query(const char *alpha2) {} +#endif /* CONFIG_CFG80211_INTERNAL_REGDB */ + +/* + * This lets us keep regulatory code which is updated on a regulatory + * basis in userspace. Country information is filled in by + * reg_device_uevent + */ +static int call_crda(const char *alpha2) +{ + if (!is_world_regdom((char *) alpha2)) + pr_info("Calling CRDA for country: %c%c\n", + alpha2[0], alpha2[1]); + else + pr_info("Calling CRDA to update world regulatory domain\n"); + + /* query internal regulatory database (if it exists) */ + reg_regdb_query(alpha2); + + return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); +} + +/* Used by nl80211 before kmalloc'ing our regulatory domain */ +bool reg_is_valid_request(const char *alpha2) +{ + assert_cfg80211_lock(); + + if (!last_request) + return false; + + return alpha2_equal(last_request->alpha2, alpha2); +} + +/* Sanity check on a regulatory rule */ +static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) +{ + const struct ieee80211_freq_range *freq_range = &rule->freq_range; + u32 freq_diff; + + if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0) + return false; + + if (freq_range->start_freq_khz > freq_range->end_freq_khz) + return false; + + freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; + + if (freq_range->end_freq_khz <= freq_range->start_freq_khz || + freq_range->max_bandwidth_khz > freq_diff) + return false; + + return true; +} + +static bool is_valid_rd(const struct ieee80211_regdomain *rd) +{ + const struct ieee80211_reg_rule *reg_rule = NULL; + unsigned int i; + + if (!rd->n_reg_rules) + return false; + + if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) + return false; + + for (i = 0; i < rd->n_reg_rules; i++) { + reg_rule = &rd->reg_rules[i]; + if (!is_valid_reg_rule(reg_rule)) + return false; + } + + return true; +} + +static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, + u32 center_freq_khz, + u32 bw_khz) +{ + u32 start_freq_khz, end_freq_khz; + + start_freq_khz = center_freq_khz - (bw_khz/2); + end_freq_khz = center_freq_khz + (bw_khz/2); + + if (start_freq_khz >= freq_range->start_freq_khz && + end_freq_khz <= freq_range->end_freq_khz) + return true; + + return false; +} + +/** + * freq_in_rule_band - tells us if a frequency is in a frequency band + * @freq_range: frequency rule we want to query + * @freq_khz: frequency we are inquiring about + * + * This lets us know if a specific frequency rule is or is not relevant to + * a specific frequency's band. Bands are device specific and artificial + * definitions (the "2.4 GHz band" and the "5 GHz band"), however it is + * safe for now to assume that a frequency rule should not be part of a + * frequency's band if the start freq or end freq are off by more than 2 GHz. + * This resolution can be lowered and should be considered as we add + * regulatory rule support for other "bands". + **/ +static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, + u32 freq_khz) +{ +#define ONE_GHZ_IN_KHZ 1000000 + if (abs(freq_khz - freq_range->start_freq_khz) <= (2 * ONE_GHZ_IN_KHZ)) + return true; + if (abs(freq_khz - freq_range->end_freq_khz) <= (2 * ONE_GHZ_IN_KHZ)) + return true; + return false; +#undef ONE_GHZ_IN_KHZ +} + +/* + * Helper for regdom_intersect(), this does the real + * mathematical intersection fun + */ +static int reg_rules_intersect( + const struct ieee80211_reg_rule *rule1, + const struct ieee80211_reg_rule *rule2, + struct ieee80211_reg_rule *intersected_rule) +{ + const struct ieee80211_freq_range *freq_range1, *freq_range2; + struct ieee80211_freq_range *freq_range; + const struct ieee80211_power_rule *power_rule1, *power_rule2; + struct ieee80211_power_rule *power_rule; + u32 freq_diff; + + freq_range1 = &rule1->freq_range; + freq_range2 = &rule2->freq_range; + freq_range = &intersected_rule->freq_range; + + power_rule1 = &rule1->power_rule; + power_rule2 = &rule2->power_rule; + power_rule = &intersected_rule->power_rule; + + freq_range->start_freq_khz = max(freq_range1->start_freq_khz, + freq_range2->start_freq_khz); + freq_range->end_freq_khz = min(freq_range1->end_freq_khz, + freq_range2->end_freq_khz); + freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, + freq_range2->max_bandwidth_khz); + + freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; + if (freq_range->max_bandwidth_khz > freq_diff) + freq_range->max_bandwidth_khz = freq_diff; + + power_rule->max_eirp = min(power_rule1->max_eirp, + power_rule2->max_eirp); + power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, + power_rule2->max_antenna_gain); + + intersected_rule->flags = (rule1->flags | rule2->flags); + + if (!is_valid_reg_rule(intersected_rule)) + return -EINVAL; + + return 0; +} + +/** + * regdom_intersect - do the intersection between two regulatory domains + * @rd1: first regulatory domain + * @rd2: second regulatory domain + * + * Use this function to get the intersection between two regulatory domains. + * Once completed we will mark the alpha2 for the rd as intersected, "98", + * as no one single alpha2 can represent this regulatory domain. + * + * Returns a pointer to the regulatory domain structure which will hold the + * resulting intersection of rules between rd1 and rd2. We will + * kzalloc() this structure for you. + */ +static struct ieee80211_regdomain *regdom_intersect( + const struct ieee80211_regdomain *rd1, + const struct ieee80211_regdomain *rd2) +{ + int r, size_of_regd; + unsigned int x, y; + unsigned int num_rules = 0, rule_idx = 0; + const struct ieee80211_reg_rule *rule1, *rule2; + struct ieee80211_reg_rule *intersected_rule; + struct ieee80211_regdomain *rd; + /* This is just a dummy holder to help us count */ + struct ieee80211_reg_rule irule; + + /* Uses the stack temporarily for counter arithmetic */ + intersected_rule = &irule; + + memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); + + if (!rd1 || !rd2) + return NULL; + + /* + * First we get a count of the rules we'll need, then we actually + * build them. This is to so we can malloc() and free() a + * regdomain once. The reason we use reg_rules_intersect() here + * is it will return -EINVAL if the rule computed makes no sense. + * All rules that do check out OK are valid. + */ + + for (x = 0; x < rd1->n_reg_rules; x++) { + rule1 = &rd1->reg_rules[x]; + for (y = 0; y < rd2->n_reg_rules; y++) { + rule2 = &rd2->reg_rules[y]; + if (!reg_rules_intersect(rule1, rule2, + intersected_rule)) + num_rules++; + memset(intersected_rule, 0, + sizeof(struct ieee80211_reg_rule)); + } + } + + if (!num_rules) + return NULL; + + size_of_regd = sizeof(struct ieee80211_regdomain) + + ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); + + rd = kzalloc(size_of_regd, GFP_KERNEL); + if (!rd) + return NULL; + + for (x = 0; x < rd1->n_reg_rules; x++) { + rule1 = &rd1->reg_rules[x]; + for (y = 0; y < rd2->n_reg_rules; y++) { + rule2 = &rd2->reg_rules[y]; + /* + * This time around instead of using the stack lets + * write to the target rule directly saving ourselves + * a memcpy() + */ + intersected_rule = &rd->reg_rules[rule_idx]; + r = reg_rules_intersect(rule1, rule2, + intersected_rule); + /* + * No need to memset here the intersected rule here as + * we're not using the stack anymore + */ + if (r) + continue; + rule_idx++; + } + } + + if (rule_idx != num_rules) { + kfree(rd); + return NULL; + } + + rd->n_reg_rules = num_rules; + rd->alpha2[0] = '9'; + rd->alpha2[1] = '8'; + + return rd; +} + +/* + * XXX: add support for the rest of enum nl80211_reg_rule_flags, we may + * want to just have the channel structure use these + */ +static u32 map_regdom_flags(u32 rd_flags) +{ + u32 channel_flags = 0; + if (rd_flags & NL80211_RRF_PASSIVE_SCAN) + channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN; + if (rd_flags & NL80211_RRF_NO_IBSS) + channel_flags |= IEEE80211_CHAN_NO_IBSS; + if (rd_flags & NL80211_RRF_DFS) + channel_flags |= IEEE80211_CHAN_RADAR; + return channel_flags; +} + +static int freq_reg_info_regd(struct wiphy *wiphy, + u32 center_freq, + u32 desired_bw_khz, + const struct ieee80211_reg_rule **reg_rule, + const struct ieee80211_regdomain *custom_regd) +{ + int i; + bool band_rule_found = false; + const struct ieee80211_regdomain *regd; + bool bw_fits = false; + + if (!desired_bw_khz) + desired_bw_khz = MHZ_TO_KHZ(20); + + regd = custom_regd ? custom_regd : cfg80211_regdomain; + + /* + * Follow the driver's regulatory domain, if present, unless a country + * IE has been processed or a user wants to help complaince further + */ + if (!custom_regd && + last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + last_request->initiator != NL80211_REGDOM_SET_BY_USER && + wiphy->regd) + regd = wiphy->regd; + + if (!regd) + return -EINVAL; + + for (i = 0; i < regd->n_reg_rules; i++) { + const struct ieee80211_reg_rule *rr; + const struct ieee80211_freq_range *fr = NULL; + + rr = ®d->reg_rules[i]; + fr = &rr->freq_range; + + /* + * We only need to know if one frequency rule was + * was in center_freq's band, that's enough, so lets + * not overwrite it once found + */ + if (!band_rule_found) + band_rule_found = freq_in_rule_band(fr, center_freq); + + bw_fits = reg_does_bw_fit(fr, + center_freq, + desired_bw_khz); + + if (band_rule_found && bw_fits) { + *reg_rule = rr; + return 0; + } + } + + if (!band_rule_found) + return -ERANGE; + + return -EINVAL; +} + +int freq_reg_info(struct wiphy *wiphy, + u32 center_freq, + u32 desired_bw_khz, + const struct ieee80211_reg_rule **reg_rule) +{ + assert_cfg80211_lock(); + return freq_reg_info_regd(wiphy, + center_freq, + desired_bw_khz, + reg_rule, + NULL); +} +EXPORT_SYMBOL(freq_reg_info); + +#ifdef CONFIG_CFG80211_REG_DEBUG +static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) +{ + switch (initiator) { + case NL80211_REGDOM_SET_BY_CORE: + return "Set by core"; + case NL80211_REGDOM_SET_BY_USER: + return "Set by user"; + case NL80211_REGDOM_SET_BY_DRIVER: + return "Set by driver"; + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + return "Set by country IE"; + default: + WARN_ON(1); + return "Set by bug"; + } +} + +static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, + u32 desired_bw_khz, + const struct ieee80211_reg_rule *reg_rule) +{ + const struct ieee80211_power_rule *power_rule; + const struct ieee80211_freq_range *freq_range; + char max_antenna_gain[32]; + + power_rule = ®_rule->power_rule; + freq_range = ®_rule->freq_range; + + if (!power_rule->max_antenna_gain) + snprintf(max_antenna_gain, 32, "N/A"); + else + snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); + + REG_DBG_PRINT("Updating information on frequency %d MHz " + "for a %d MHz width channel with regulatory rule:\n", + chan->center_freq, + KHZ_TO_MHZ(desired_bw_khz)); + + REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n", + freq_range->start_freq_khz, + freq_range->end_freq_khz, + freq_range->max_bandwidth_khz, + max_antenna_gain, + power_rule->max_eirp); +} +#else +static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, + u32 desired_bw_khz, + const struct ieee80211_reg_rule *reg_rule) +{ + return; +} +#endif + +/* + * Note that right now we assume the desired channel bandwidth + * is always 20 MHz for each individual channel (HT40 uses 20 MHz + * per channel, the primary and the extension channel). To support + * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a + * new ieee80211_channel.target_bw and re run the regulatory check + * on the wiphy with the target_bw specified. Then we can simply use + * that below for the desired_bw_khz below. + */ +static void handle_channel(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator, + enum ieee80211_band band, + unsigned int chan_idx) +{ + int r; + u32 flags, bw_flags = 0; + u32 desired_bw_khz = MHZ_TO_KHZ(20); + const struct ieee80211_reg_rule *reg_rule = NULL; + const struct ieee80211_power_rule *power_rule = NULL; + const struct ieee80211_freq_range *freq_range = NULL; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + struct wiphy *request_wiphy = NULL; + + assert_cfg80211_lock(); + + request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + + sband = wiphy->bands[band]; + BUG_ON(chan_idx >= sband->n_channels); + chan = &sband->channels[chan_idx]; + + flags = chan->orig_flags; + + r = freq_reg_info(wiphy, + MHZ_TO_KHZ(chan->center_freq), + desired_bw_khz, + ®_rule); + + if (r) { + /* + * We will disable all channels that do not match our + * received regulatory rule unless the hint is coming + * from a Country IE and the Country IE had no information + * about a band. The IEEE 802.11 spec allows for an AP + * to send only a subset of the regulatory rules allowed, + * so an AP in the US that only supports 2.4 GHz may only send + * a country IE with information for the 2.4 GHz band + * while 5 GHz is still supported. + */ + if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && + r == -ERANGE) + return; + + REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); + chan->flags = IEEE80211_CHAN_DISABLED; + return; + } + + chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); + + power_rule = ®_rule->power_rule; + freq_range = ®_rule->freq_range; + + if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) + bw_flags = IEEE80211_CHAN_NO_HT40; + + if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && + request_wiphy && request_wiphy == wiphy && + request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { + /* + * This guarantees the driver's requested regulatory domain + * will always be used as a base for further regulatory + * settings + */ + chan->flags = chan->orig_flags = + map_regdom_flags(reg_rule->flags) | bw_flags; + chan->max_antenna_gain = chan->orig_mag = + (int) MBI_TO_DBI(power_rule->max_antenna_gain); + chan->max_power = chan->orig_mpwr = + (int) MBM_TO_DBM(power_rule->max_eirp); + return; + } + + chan->beacon_found = false; + chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); + chan->max_antenna_gain = min(chan->orig_mag, + (int) MBI_TO_DBI(power_rule->max_antenna_gain)); + if (chan->orig_mpwr) + chan->max_power = min(chan->orig_mpwr, + (int) MBM_TO_DBM(power_rule->max_eirp)); + else + chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); +} + +static void handle_band(struct wiphy *wiphy, + enum ieee80211_band band, + enum nl80211_reg_initiator initiator) +{ + unsigned int i; + struct ieee80211_supported_band *sband; + + BUG_ON(!wiphy->bands[band]); + sband = wiphy->bands[band]; + + for (i = 0; i < sband->n_channels; i++) + handle_channel(wiphy, initiator, band, i); +} + +static bool ignore_reg_update(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) +{ + if (!last_request) { + REG_DBG_PRINT("Ignoring regulatory request %s since " + "last_request is not set\n", + reg_initiator_name(initiator)); + return true; + } + + if (initiator == NL80211_REGDOM_SET_BY_CORE && + wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { + REG_DBG_PRINT("Ignoring regulatory request %s " + "since the driver uses its own custom " + "regulatory domain\n", + reg_initiator_name(initiator)); + return true; + } + + /* + * wiphy->regd will be set once the device has its own + * desired regulatory domain set + */ + if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && + initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + !is_world_regdom(last_request->alpha2)) { + REG_DBG_PRINT("Ignoring regulatory request %s " + "since the driver requires its own regulatory " + "domain to be set first\n", + reg_initiator_name(initiator)); + return true; + } + + return false; +} + +static void handle_reg_beacon(struct wiphy *wiphy, + unsigned int chan_idx, + struct reg_beacon *reg_beacon) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + bool channel_changed = false; + struct ieee80211_channel chan_before; + + assert_cfg80211_lock(); + + sband = wiphy->bands[reg_beacon->chan.band]; + chan = &sband->channels[chan_idx]; + + if (likely(chan->center_freq != reg_beacon->chan.center_freq)) + return; + + if (chan->beacon_found) + return; + + chan->beacon_found = true; + + if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) + return; + + chan_before.center_freq = chan->center_freq; + chan_before.flags = chan->flags; + + if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) { + chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + channel_changed = true; + } + + if (chan->flags & IEEE80211_CHAN_NO_IBSS) { + chan->flags &= ~IEEE80211_CHAN_NO_IBSS; + channel_changed = true; + } + + if (channel_changed) + nl80211_send_beacon_hint_event(wiphy, &chan_before, chan); +} + +/* + * Called when a scan on a wiphy finds a beacon on + * new channel + */ +static void wiphy_update_new_beacon(struct wiphy *wiphy, + struct reg_beacon *reg_beacon) +{ + unsigned int i; + struct ieee80211_supported_band *sband; + + assert_cfg80211_lock(); + + if (!wiphy->bands[reg_beacon->chan.band]) + return; + + sband = wiphy->bands[reg_beacon->chan.band]; + + for (i = 0; i < sband->n_channels; i++) + handle_reg_beacon(wiphy, i, reg_beacon); +} + +/* + * Called upon reg changes or a new wiphy is added + */ +static void wiphy_update_beacon_reg(struct wiphy *wiphy) +{ + unsigned int i; + struct ieee80211_supported_band *sband; + struct reg_beacon *reg_beacon; + + assert_cfg80211_lock(); + + if (list_empty(®_beacon_list)) + return; + + list_for_each_entry(reg_beacon, ®_beacon_list, list) { + if (!wiphy->bands[reg_beacon->chan.band]) + continue; + sband = wiphy->bands[reg_beacon->chan.band]; + for (i = 0; i < sband->n_channels; i++) + handle_reg_beacon(wiphy, i, reg_beacon); + } +} + +static bool reg_is_world_roaming(struct wiphy *wiphy) +{ + if (is_world_regdom(cfg80211_regdomain->alpha2) || + (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) + return true; + if (last_request && + last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) + return true; + return false; +} + +/* Reap the advantages of previously found beacons */ +static void reg_process_beacons(struct wiphy *wiphy) +{ + /* + * Means we are just firing up cfg80211, so no beacons would + * have been processed yet. + */ + if (!last_request) + return; + if (!reg_is_world_roaming(wiphy)) + return; + wiphy_update_beacon_reg(wiphy); +} + +static bool is_ht40_not_allowed(struct ieee80211_channel *chan) +{ + if (!chan) + return true; + if (chan->flags & IEEE80211_CHAN_DISABLED) + return true; + /* This would happen when regulatory rules disallow HT40 completely */ + if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) + return true; + return false; +} + +static void reg_process_ht_flags_channel(struct wiphy *wiphy, + enum ieee80211_band band, + unsigned int chan_idx) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *channel; + struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; + unsigned int i; + + assert_cfg80211_lock(); + + sband = wiphy->bands[band]; + BUG_ON(chan_idx >= sband->n_channels); + channel = &sband->channels[chan_idx]; + + if (is_ht40_not_allowed(channel)) { + channel->flags |= IEEE80211_CHAN_NO_HT40; + return; + } + + /* + * We need to ensure the extension channels exist to + * be able to use HT40- or HT40+, this finds them (or not) + */ + for (i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *c = &sband->channels[i]; + if (c->center_freq == (channel->center_freq - 20)) + channel_before = c; + if (c->center_freq == (channel->center_freq + 20)) + channel_after = c; + } + + /* + * Please note that this assumes target bandwidth is 20 MHz, + * if that ever changes we also need to change the below logic + * to include that as well. + */ + if (is_ht40_not_allowed(channel_before)) + channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; + else + channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; + + if (is_ht40_not_allowed(channel_after)) + channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; + else + channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; +} + +static void reg_process_ht_flags_band(struct wiphy *wiphy, + enum ieee80211_band band) +{ + unsigned int i; + struct ieee80211_supported_band *sband; + + BUG_ON(!wiphy->bands[band]); + sband = wiphy->bands[band]; + + for (i = 0; i < sband->n_channels; i++) + reg_process_ht_flags_channel(wiphy, band, i); +} + +static void reg_process_ht_flags(struct wiphy *wiphy) +{ + enum ieee80211_band band; + + if (!wiphy) + return; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (wiphy->bands[band]) + reg_process_ht_flags_band(wiphy, band); + } + +} + +static void wiphy_update_regulatory(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) +{ + enum ieee80211_band band; + + assert_reg_lock(); + + if (ignore_reg_update(wiphy, initiator)) + return; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (wiphy->bands[band]) + handle_band(wiphy, band, initiator); + } + + reg_process_beacons(wiphy); + reg_process_ht_flags(wiphy); + if (wiphy->reg_notifier) + wiphy->reg_notifier(wiphy, last_request); +} + +void regulatory_update(struct wiphy *wiphy, + enum nl80211_reg_initiator setby) +{ + mutex_lock(®_mutex); + wiphy_update_regulatory(wiphy, setby); + mutex_unlock(®_mutex); +} + +static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) +{ + struct cfg80211_registered_device *rdev; + + list_for_each_entry(rdev, &cfg80211_rdev_list, list) + wiphy_update_regulatory(&rdev->wiphy, initiator); +} + +static void handle_channel_custom(struct wiphy *wiphy, + enum ieee80211_band band, + unsigned int chan_idx, + const struct ieee80211_regdomain *regd) +{ + int r; + u32 desired_bw_khz = MHZ_TO_KHZ(20); + u32 bw_flags = 0; + const struct ieee80211_reg_rule *reg_rule = NULL; + const struct ieee80211_power_rule *power_rule = NULL; + const struct ieee80211_freq_range *freq_range = NULL; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + + assert_reg_lock(); + + sband = wiphy->bands[band]; + BUG_ON(chan_idx >= sband->n_channels); + chan = &sband->channels[chan_idx]; + + r = freq_reg_info_regd(wiphy, + MHZ_TO_KHZ(chan->center_freq), + desired_bw_khz, + ®_rule, + regd); + + if (r) { + REG_DBG_PRINT("Disabling freq %d MHz as custom " + "regd has no rule that fits a %d MHz " + "wide channel\n", + chan->center_freq, + KHZ_TO_MHZ(desired_bw_khz)); + chan->flags = IEEE80211_CHAN_DISABLED; + return; + } + + chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); + + power_rule = ®_rule->power_rule; + freq_range = ®_rule->freq_range; + + if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) + bw_flags = IEEE80211_CHAN_NO_HT40; + + chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; + chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); + chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); +} + +static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, + const struct ieee80211_regdomain *regd) +{ + unsigned int i; + struct ieee80211_supported_band *sband; + + BUG_ON(!wiphy->bands[band]); + sband = wiphy->bands[band]; + + for (i = 0; i < sband->n_channels; i++) + handle_channel_custom(wiphy, band, i, regd); +} + +/* Used by drivers prior to wiphy registration */ +void wiphy_apply_custom_regulatory(struct wiphy *wiphy, + const struct ieee80211_regdomain *regd) +{ + enum ieee80211_band band; + unsigned int bands_set = 0; + + mutex_lock(®_mutex); + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!wiphy->bands[band]) + continue; + handle_band_custom(wiphy, band, regd); + bands_set++; + } + mutex_unlock(®_mutex); + + /* + * no point in calling this if it won't have any effect + * on your device's supportd bands. + */ + WARN_ON(!bands_set); +} +EXPORT_SYMBOL(wiphy_apply_custom_regulatory); + +/* + * Return value which can be used by ignore_request() to indicate + * it has been determined we should intersect two regulatory domains + */ +#define REG_INTERSECT 1 + +/* This has the logic which determines when a new request + * should be ignored. */ +static int ignore_request(struct wiphy *wiphy, + struct regulatory_request *pending_request) +{ + struct wiphy *last_wiphy = NULL; + + assert_cfg80211_lock(); + + /* All initial requests are respected */ + if (!last_request) + return 0; + + switch (pending_request->initiator) { + case NL80211_REGDOM_SET_BY_CORE: + return 0; + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + + last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + + if (unlikely(!is_an_alpha2(pending_request->alpha2))) + return -EINVAL; + if (last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE) { + if (last_wiphy != wiphy) { + /* + * Two cards with two APs claiming different + * Country IE alpha2s. We could + * intersect them, but that seems unlikely + * to be correct. Reject second one for now. + */ + if (regdom_changes(pending_request->alpha2)) + return -EOPNOTSUPP; + return -EALREADY; + } + /* + * Two consecutive Country IE hints on the same wiphy. + * This should be picked up early by the driver/stack + */ + if (WARN_ON(regdom_changes(pending_request->alpha2))) + return 0; + return -EALREADY; + } + return 0; + case NL80211_REGDOM_SET_BY_DRIVER: + if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { + if (regdom_changes(pending_request->alpha2)) + return 0; + return -EALREADY; + } + + /* + * This would happen if you unplug and plug your card + * back in or if you add a new device for which the previously + * loaded card also agrees on the regulatory domain. + */ + if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && + !regdom_changes(pending_request->alpha2)) + return -EALREADY; + + return 0; + case NL80211_REGDOM_SET_BY_USER: + if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) + return REG_INTERSECT; + /* + * If the user knows better the user should set the regdom + * to their country before the IE is picked up + */ + if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && + last_request->intersect) + return -EOPNOTSUPP; + /* + * Process user requests only after previous user/driver/core + * requests have been processed + */ + if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE || + last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || + last_request->initiator == NL80211_REGDOM_SET_BY_USER) { + if (regdom_changes(last_request->alpha2)) + return -EAGAIN; + } + + if (!regdom_changes(pending_request->alpha2)) + return -EALREADY; + + return 0; + } + + return -EINVAL; +} + +static void reg_set_request_processed(void) +{ + bool need_more_processing = false; + + last_request->processed = true; + + spin_lock(®_requests_lock); + if (!list_empty(®_requests_list)) + need_more_processing = true; + spin_unlock(®_requests_lock); + + if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) + cancel_delayed_work_sync(®_timeout); + + if (need_more_processing) + schedule_work(®_work); +} + +/** + * __regulatory_hint - hint to the wireless core a regulatory domain + * @wiphy: if the hint comes from country information from an AP, this + * is required to be set to the wiphy that received the information + * @pending_request: the regulatory request currently being processed + * + * The Wireless subsystem can use this function to hint to the wireless core + * what it believes should be the current regulatory domain. + * + * Returns zero if all went fine, %-EALREADY if a regulatory domain had + * already been set or other standard error codes. + * + * Caller must hold &cfg80211_mutex and ®_mutex + */ +static int __regulatory_hint(struct wiphy *wiphy, + struct regulatory_request *pending_request) +{ + bool intersect = false; + int r = 0; + + assert_cfg80211_lock(); + + r = ignore_request(wiphy, pending_request); + + if (r == REG_INTERSECT) { + if (pending_request->initiator == + NL80211_REGDOM_SET_BY_DRIVER) { + r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); + if (r) { + kfree(pending_request); + return r; + } + } + intersect = true; + } else if (r) { + /* + * If the regulatory domain being requested by the + * driver has already been set just copy it to the + * wiphy + */ + if (r == -EALREADY && + pending_request->initiator == + NL80211_REGDOM_SET_BY_DRIVER) { + r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); + if (r) { + kfree(pending_request); + return r; + } + r = -EALREADY; + goto new_request; + } + kfree(pending_request); + return r; + } + +new_request: + kfree(last_request); + + last_request = pending_request; + last_request->intersect = intersect; + + pending_request = NULL; + + if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { + user_alpha2[0] = last_request->alpha2[0]; + user_alpha2[1] = last_request->alpha2[1]; + } + + /* When r == REG_INTERSECT we do need to call CRDA */ + if (r < 0) { + /* + * Since CRDA will not be called in this case as we already + * have applied the requested regulatory domain before we just + * inform userspace we have processed the request + */ + if (r == -EALREADY) { + nl80211_send_reg_change_event(last_request); + reg_set_request_processed(); + } + return r; + } + + return call_crda(last_request->alpha2); +} + +/* This processes *all* regulatory hints */ +static void reg_process_hint(struct regulatory_request *reg_request) +{ + int r = 0; + struct wiphy *wiphy = NULL; + enum nl80211_reg_initiator initiator = reg_request->initiator; + + BUG_ON(!reg_request->alpha2); + + if (wiphy_idx_valid(reg_request->wiphy_idx)) + wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); + + if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && + !wiphy) { + kfree(reg_request); + return; + } + + r = __regulatory_hint(wiphy, reg_request); + /* This is required so that the orig_* parameters are saved */ + if (r == -EALREADY && wiphy && + wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { + wiphy_update_regulatory(wiphy, initiator); + return; + } + + /* + * We only time out user hints, given that they should be the only + * source of bogus requests. + */ + if (r != -EALREADY && + reg_request->initiator == NL80211_REGDOM_SET_BY_USER) + schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); +} + +/* + * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* + * Regulatory hints come on a first come first serve basis and we + * must process each one atomically. + */ +static void reg_process_pending_hints(void) +{ + struct regulatory_request *reg_request; + + mutex_lock(&cfg80211_mutex); + mutex_lock(®_mutex); + + /* When last_request->processed becomes true this will be rescheduled */ + if (last_request && !last_request->processed) { + REG_DBG_PRINT("Pending regulatory request, waiting " + "for it to be processed...\n"); + goto out; + } + + spin_lock(®_requests_lock); + + if (list_empty(®_requests_list)) { + spin_unlock(®_requests_lock); + goto out; + } + + reg_request = list_first_entry(®_requests_list, + struct regulatory_request, + list); + list_del_init(®_request->list); + + spin_unlock(®_requests_lock); + + reg_process_hint(reg_request); + +out: + mutex_unlock(®_mutex); + mutex_unlock(&cfg80211_mutex); +} + +/* Processes beacon hints -- this has nothing to do with country IEs */ +static void reg_process_pending_beacon_hints(void) +{ + struct cfg80211_registered_device *rdev; + struct reg_beacon *pending_beacon, *tmp; + + /* + * No need to hold the reg_mutex here as we just touch wiphys + * and do not read or access regulatory variables. + */ + mutex_lock(&cfg80211_mutex); + + /* This goes through the _pending_ beacon list */ + spin_lock_bh(®_pending_beacons_lock); + + if (list_empty(®_pending_beacons)) { + spin_unlock_bh(®_pending_beacons_lock); + goto out; + } + + list_for_each_entry_safe(pending_beacon, tmp, + ®_pending_beacons, list) { + + list_del_init(&pending_beacon->list); + + /* Applies the beacon hint to current wiphys */ + list_for_each_entry(rdev, &cfg80211_rdev_list, list) + wiphy_update_new_beacon(&rdev->wiphy, pending_beacon); + + /* Remembers the beacon hint for new wiphys or reg changes */ + list_add_tail(&pending_beacon->list, ®_beacon_list); + } + + spin_unlock_bh(®_pending_beacons_lock); +out: + mutex_unlock(&cfg80211_mutex); +} + +static void reg_todo(struct work_struct *work) +{ + reg_process_pending_hints(); + reg_process_pending_beacon_hints(); +} + +static void queue_regulatory_request(struct regulatory_request *request) +{ + if (isalpha(request->alpha2[0])) + request->alpha2[0] = toupper(request->alpha2[0]); + if (isalpha(request->alpha2[1])) + request->alpha2[1] = toupper(request->alpha2[1]); + + spin_lock(®_requests_lock); + list_add_tail(&request->list, ®_requests_list); + spin_unlock(®_requests_lock); + + schedule_work(®_work); +} + +/* + * Core regulatory hint -- happens during cfg80211_init() + * and when we restore regulatory settings. + */ +static int regulatory_hint_core(const char *alpha2) +{ + struct regulatory_request *request; + + kfree(last_request); + last_request = NULL; + + request = kzalloc(sizeof(struct regulatory_request), + GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->alpha2[0] = alpha2[0]; + request->alpha2[1] = alpha2[1]; + request->initiator = NL80211_REGDOM_SET_BY_CORE; + + queue_regulatory_request(request); + + return 0; +} + +/* User hints */ +int regulatory_hint_user(const char *alpha2) +{ + struct regulatory_request *request; + + BUG_ON(!alpha2); + + request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->wiphy_idx = WIPHY_IDX_STALE; + request->alpha2[0] = alpha2[0]; + request->alpha2[1] = alpha2[1]; + request->initiator = NL80211_REGDOM_SET_BY_USER; + + queue_regulatory_request(request); + + return 0; +} + +/* Driver hints */ +int regulatory_hint(struct wiphy *wiphy, const char *alpha2) +{ + struct regulatory_request *request; + + BUG_ON(!alpha2); + BUG_ON(!wiphy); + + request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->wiphy_idx = get_wiphy_idx(wiphy); + + /* Must have registered wiphy first */ + BUG_ON(!wiphy_idx_valid(request->wiphy_idx)); + + request->alpha2[0] = alpha2[0]; + request->alpha2[1] = alpha2[1]; + request->initiator = NL80211_REGDOM_SET_BY_DRIVER; + + queue_regulatory_request(request); + + return 0; +} +EXPORT_SYMBOL(regulatory_hint); + +/* + * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and + * therefore cannot iterate over the rdev list here. + */ +void regulatory_hint_11d(struct wiphy *wiphy, + enum ieee80211_band band, + u8 *country_ie, + u8 country_ie_len) +{ + char alpha2[2]; + enum environment_cap env = ENVIRON_ANY; + struct regulatory_request *request; + + mutex_lock(®_mutex); + + if (unlikely(!last_request)) + goto out; + + /* IE len must be evenly divisible by 2 */ + if (country_ie_len & 0x01) + goto out; + + if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) + goto out; + + alpha2[0] = country_ie[0]; + alpha2[1] = country_ie[1]; + + if (country_ie[2] == 'I') + env = ENVIRON_INDOOR; + else if (country_ie[2] == 'O') + env = ENVIRON_OUTDOOR; + + /* + * We will run this only upon a successful connection on cfg80211. + * We leave conflict resolution to the workqueue, where can hold + * cfg80211_mutex. + */ + if (likely(last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE && + wiphy_idx_valid(last_request->wiphy_idx))) + goto out; + + request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); + if (!request) + goto out; + + request->wiphy_idx = get_wiphy_idx(wiphy); + request->alpha2[0] = alpha2[0]; + request->alpha2[1] = alpha2[1]; + request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; + request->country_ie_env = env; + + mutex_unlock(®_mutex); + + queue_regulatory_request(request); + + return; + +out: + mutex_unlock(®_mutex); +} + +static void restore_alpha2(char *alpha2, bool reset_user) +{ + /* indicates there is no alpha2 to consider for restoration */ + alpha2[0] = '9'; + alpha2[1] = '7'; + + /* The user setting has precedence over the module parameter */ + if (is_user_regdom_saved()) { + /* Unless we're asked to ignore it and reset it */ + if (reset_user) { + REG_DBG_PRINT("Restoring regulatory settings " + "including user preference\n"); + user_alpha2[0] = '9'; + user_alpha2[1] = '7'; + + /* + * If we're ignoring user settings, we still need to + * check the module parameter to ensure we put things + * back as they were for a full restore. + */ + if (!is_world_regdom(ieee80211_regdom)) { + REG_DBG_PRINT("Keeping preference on " + "module parameter ieee80211_regdom: %c%c\n", + ieee80211_regdom[0], + ieee80211_regdom[1]); + alpha2[0] = ieee80211_regdom[0]; + alpha2[1] = ieee80211_regdom[1]; + } + } else { + REG_DBG_PRINT("Restoring regulatory settings " + "while preserving user preference for: %c%c\n", + user_alpha2[0], + user_alpha2[1]); + alpha2[0] = user_alpha2[0]; + alpha2[1] = user_alpha2[1]; + } + } else if (!is_world_regdom(ieee80211_regdom)) { + REG_DBG_PRINT("Keeping preference on " + "module parameter ieee80211_regdom: %c%c\n", + ieee80211_regdom[0], + ieee80211_regdom[1]); + alpha2[0] = ieee80211_regdom[0]; + alpha2[1] = ieee80211_regdom[1]; + } else + REG_DBG_PRINT("Restoring regulatory settings\n"); +} + +/* + * Restoring regulatory settings involves ingoring any + * possibly stale country IE information and user regulatory + * settings if so desired, this includes any beacon hints + * learned as we could have traveled outside to another country + * after disconnection. To restore regulatory settings we do + * exactly what we did at bootup: + * + * - send a core regulatory hint + * - send a user regulatory hint if applicable + * + * Device drivers that send a regulatory hint for a specific country + * keep their own regulatory domain on wiphy->regd so that does does + * not need to be remembered. + */ +static void restore_regulatory_settings(bool reset_user) +{ + char alpha2[2]; + struct reg_beacon *reg_beacon, *btmp; + struct regulatory_request *reg_request, *tmp; + LIST_HEAD(tmp_reg_req_list); + + mutex_lock(&cfg80211_mutex); + mutex_lock(®_mutex); + + reset_regdomains(); + restore_alpha2(alpha2, reset_user); + + /* + * If there's any pending requests we simply + * stash them to a temporary pending queue and + * add then after we've restored regulatory + * settings. + */ + spin_lock(®_requests_lock); + if (!list_empty(®_requests_list)) { + list_for_each_entry_safe(reg_request, tmp, + ®_requests_list, list) { + if (reg_request->initiator != + NL80211_REGDOM_SET_BY_USER) + continue; + list_del(®_request->list); + list_add_tail(®_request->list, &tmp_reg_req_list); + } + } + spin_unlock(®_requests_lock); + + /* Clear beacon hints */ + spin_lock_bh(®_pending_beacons_lock); + if (!list_empty(®_pending_beacons)) { + list_for_each_entry_safe(reg_beacon, btmp, + ®_pending_beacons, list) { + list_del(®_beacon->list); + kfree(reg_beacon); + } + } + spin_unlock_bh(®_pending_beacons_lock); + + if (!list_empty(®_beacon_list)) { + list_for_each_entry_safe(reg_beacon, btmp, + ®_beacon_list, list) { + list_del(®_beacon->list); + kfree(reg_beacon); + } + } + + /* First restore to the basic regulatory settings */ + cfg80211_regdomain = cfg80211_world_regdom; + + mutex_unlock(®_mutex); + mutex_unlock(&cfg80211_mutex); + + regulatory_hint_core(cfg80211_regdomain->alpha2); + + /* + * This restores the ieee80211_regdom module parameter + * preference or the last user requested regulatory + * settings, user regulatory settings takes precedence. + */ + if (is_an_alpha2(alpha2)) + regulatory_hint_user(user_alpha2); + + if (list_empty(&tmp_reg_req_list)) + return; + + mutex_lock(&cfg80211_mutex); + mutex_lock(®_mutex); + + spin_lock(®_requests_lock); + list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) { + REG_DBG_PRINT("Adding request for country %c%c back " + "into the queue\n", + reg_request->alpha2[0], + reg_request->alpha2[1]); + list_del(®_request->list); + list_add_tail(®_request->list, ®_requests_list); + } + spin_unlock(®_requests_lock); + + mutex_unlock(®_mutex); + mutex_unlock(&cfg80211_mutex); + + REG_DBG_PRINT("Kicking the queue\n"); + + schedule_work(®_work); +} + +void regulatory_hint_disconnect(void) +{ + REG_DBG_PRINT("All devices are disconnected, going to " + "restore regulatory settings\n"); + restore_regulatory_settings(false); +} + +static bool freq_is_chan_12_13_14(u16 freq) +{ + if (freq == ieee80211_channel_to_frequency(12, IEEE80211_BAND_2GHZ) || + freq == ieee80211_channel_to_frequency(13, IEEE80211_BAND_2GHZ) || + freq == ieee80211_channel_to_frequency(14, IEEE80211_BAND_2GHZ)) + return true; + return false; +} + +int regulatory_hint_found_beacon(struct wiphy *wiphy, + struct ieee80211_channel *beacon_chan, + gfp_t gfp) +{ + struct reg_beacon *reg_beacon; + + if (likely((beacon_chan->beacon_found || + (beacon_chan->flags & IEEE80211_CHAN_RADAR) || + (beacon_chan->band == IEEE80211_BAND_2GHZ && + !freq_is_chan_12_13_14(beacon_chan->center_freq))))) + return 0; + + reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); + if (!reg_beacon) + return -ENOMEM; + + REG_DBG_PRINT("Found new beacon on " + "frequency: %d MHz (Ch %d) on %s\n", + beacon_chan->center_freq, + ieee80211_frequency_to_channel(beacon_chan->center_freq), + wiphy_name(wiphy)); + + memcpy(®_beacon->chan, beacon_chan, + sizeof(struct ieee80211_channel)); + + + /* + * Since we can be called from BH or and non-BH context + * we must use spin_lock_bh() + */ + spin_lock_bh(®_pending_beacons_lock); + list_add_tail(®_beacon->list, ®_pending_beacons); + spin_unlock_bh(®_pending_beacons_lock); + + schedule_work(®_work); + + return 0; +} + +static void print_rd_rules(const struct ieee80211_regdomain *rd) +{ + unsigned int i; + const struct ieee80211_reg_rule *reg_rule = NULL; + const struct ieee80211_freq_range *freq_range = NULL; + const struct ieee80211_power_rule *power_rule = NULL; + + pr_info(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)\n"); + + for (i = 0; i < rd->n_reg_rules; i++) { + reg_rule = &rd->reg_rules[i]; + freq_range = ®_rule->freq_range; + power_rule = ®_rule->power_rule; + + /* + * There may not be documentation for max antenna gain + * in certain regions + */ + if (power_rule->max_antenna_gain) + pr_info(" (%d KHz - %d KHz @ %d KHz), (%d mBi, %d mBm)\n", + freq_range->start_freq_khz, + freq_range->end_freq_khz, + freq_range->max_bandwidth_khz, + power_rule->max_antenna_gain, + power_rule->max_eirp); + else + pr_info(" (%d KHz - %d KHz @ %d KHz), (N/A, %d mBm)\n", + freq_range->start_freq_khz, + freq_range->end_freq_khz, + freq_range->max_bandwidth_khz, + power_rule->max_eirp); + } +} + +static void print_regdomain(const struct ieee80211_regdomain *rd) +{ + + if (is_intersected_alpha2(rd->alpha2)) { + + if (last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE) { + struct cfg80211_registered_device *rdev; + rdev = cfg80211_rdev_by_wiphy_idx( + last_request->wiphy_idx); + if (rdev) { + pr_info("Current regulatory domain updated by AP to: %c%c\n", + rdev->country_ie_alpha2[0], + rdev->country_ie_alpha2[1]); + } else + pr_info("Current regulatory domain intersected:\n"); + } else + pr_info("Current regulatory domain intersected:\n"); + } else if (is_world_regdom(rd->alpha2)) + pr_info("World regulatory domain updated:\n"); + else { + if (is_unknown_alpha2(rd->alpha2)) + pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); + else + pr_info("Regulatory domain changed to country: %c%c\n", + rd->alpha2[0], rd->alpha2[1]); + } + print_rd_rules(rd); +} + +static void print_regdomain_info(const struct ieee80211_regdomain *rd) +{ + pr_info("Regulatory domain: %c%c\n", rd->alpha2[0], rd->alpha2[1]); + print_rd_rules(rd); +} + +/* Takes ownership of rd only if it doesn't fail */ +static int __set_regdom(const struct ieee80211_regdomain *rd) +{ + const struct ieee80211_regdomain *intersected_rd = NULL; + struct cfg80211_registered_device *rdev = NULL; + struct wiphy *request_wiphy; + /* Some basic sanity checks first */ + + if (is_world_regdom(rd->alpha2)) { + if (WARN_ON(!reg_is_valid_request(rd->alpha2))) + return -EINVAL; + update_world_regdomain(rd); + return 0; + } + + if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && + !is_unknown_alpha2(rd->alpha2)) + return -EINVAL; + + if (!last_request) + return -EINVAL; + + /* + * Lets only bother proceeding on the same alpha2 if the current + * rd is non static (it means CRDA was present and was used last) + * and the pending request came in from a country IE + */ + if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { + /* + * If someone else asked us to change the rd lets only bother + * checking if the alpha2 changes if CRDA was already called + */ + if (!regdom_changes(rd->alpha2)) + return -EALREADY; + } + + /* + * Now lets set the regulatory domain, update all driver channels + * and finally inform them of what we have done, in case they want + * to review or adjust their own settings based on their own + * internal EEPROM data + */ + + if (WARN_ON(!reg_is_valid_request(rd->alpha2))) + return -EINVAL; + + if (!is_valid_rd(rd)) { + pr_err("Invalid regulatory domain detected:\n"); + print_regdomain_info(rd); + return -EINVAL; + } + + request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + + if (!last_request->intersect) { + int r; + + if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { + reset_regdomains(); + cfg80211_regdomain = rd; + return 0; + } + + /* + * For a driver hint, lets copy the regulatory domain the + * driver wanted to the wiphy to deal with conflicts + */ + + r = reg_copy_regd(&request_wiphy->regd, rd); + if (r) + return r; + + reset_regdomains(); + cfg80211_regdomain = rd; + return 0; + } + + /* Intersection requires a bit more work */ + + if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { + + intersected_rd = regdom_intersect(rd, cfg80211_regdomain); + if (!intersected_rd) + return -EINVAL; + + /* + * We can trash what CRDA provided now. + * However if a driver requested this specific regulatory + * domain we keep it for its private use + */ + if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) + request_wiphy->regd = rd; + else + kfree(rd); + + rd = NULL; + + reset_regdomains(); + cfg80211_regdomain = intersected_rd; + + return 0; + } + + if (!intersected_rd) + return -EINVAL; + + rdev = wiphy_to_dev(request_wiphy); + + rdev->country_ie_alpha2[0] = rd->alpha2[0]; + rdev->country_ie_alpha2[1] = rd->alpha2[1]; + rdev->env = last_request->country_ie_env; + + BUG_ON(intersected_rd == rd); + + kfree(rd); + rd = NULL; + + reset_regdomains(); + cfg80211_regdomain = intersected_rd; + + return 0; +} + + +/* + * Use this call to set the current regulatory domain. Conflicts with + * multiple drivers can be ironed out later. Caller must've already + * kmalloc'd the rd structure. Caller must hold cfg80211_mutex + */ +int set_regdom(const struct ieee80211_regdomain *rd) +{ + int r; + + assert_cfg80211_lock(); + + mutex_lock(®_mutex); + + /* Note that this doesn't update the wiphys, this is done below */ + r = __set_regdom(rd); + if (r) { + if (r == -EALREADY) + reg_set_request_processed(); + + kfree(rd); + mutex_unlock(®_mutex); + return r; + } + + /* This would make this whole thing pointless */ + if (!last_request->intersect) + BUG_ON(rd != cfg80211_regdomain); + + /* update all wiphys now with the new established regulatory domain */ + update_all_wiphy_regulatory(last_request->initiator); + + print_regdomain(cfg80211_regdomain); + + nl80211_send_reg_change_event(last_request); + + reg_set_request_processed(); + + mutex_unlock(®_mutex); + + return r; +} + +#ifdef CONFIG_HOTPLUG +int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + if (last_request && !last_request->processed) { + if (add_uevent_var(env, "COUNTRY=%c%c", + last_request->alpha2[0], + last_request->alpha2[1])) + return -ENOMEM; + } + + return 0; +} +#else +int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + return -ENODEV; +} +#endif /* CONFIG_HOTPLUG */ + +/* Caller must hold cfg80211_mutex */ +void reg_device_remove(struct wiphy *wiphy) +{ + struct wiphy *request_wiphy = NULL; + + assert_cfg80211_lock(); + + mutex_lock(®_mutex); + + kfree(wiphy->regd); + + if (last_request) + request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + + if (!request_wiphy || request_wiphy != wiphy) + goto out; + + last_request->wiphy_idx = WIPHY_IDX_STALE; + last_request->country_ie_env = ENVIRON_ANY; +out: + mutex_unlock(®_mutex); +} + +static void reg_timeout_work(struct work_struct *work) +{ + REG_DBG_PRINT("Timeout while waiting for CRDA to reply, " + "restoring regulatory settings\n"); + restore_regulatory_settings(true); +} + +int __init regulatory_init(void) +{ + int err = 0; + + reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0); + if (IS_ERR(reg_pdev)) + return PTR_ERR(reg_pdev); + + reg_pdev->dev.type = ®_device_type; + + spin_lock_init(®_requests_lock); + spin_lock_init(®_pending_beacons_lock); + + cfg80211_regdomain = cfg80211_world_regdom; + + user_alpha2[0] = '9'; + user_alpha2[1] = '7'; + + /* We always try to get an update for the static regdomain */ + err = regulatory_hint_core(cfg80211_regdomain->alpha2); + if (err) { + if (err == -ENOMEM) + return err; + /* + * N.B. kobject_uevent_env() can fail mainly for when we're out + * memory which is handled and propagated appropriately above + * but it can also fail during a netlink_broadcast() or during + * early boot for call_usermodehelper(). For now treat these + * errors as non-fatal. + */ + pr_err("kobject_uevent_env() was unable to call CRDA during init\n"); +#ifdef CONFIG_CFG80211_REG_DEBUG + /* We want to find out exactly why when debugging */ + WARN_ON(err); +#endif + } + + /* + * Finally, if the user set the module parameter treat it + * as a user hint. + */ + if (!is_world_regdom(ieee80211_regdom)) + regulatory_hint_user(ieee80211_regdom); + + return 0; +} + +void /* __init_or_exit */ regulatory_exit(void) +{ + struct regulatory_request *reg_request, *tmp; + struct reg_beacon *reg_beacon, *btmp; + + cancel_work_sync(®_work); + cancel_delayed_work_sync(®_timeout); + + mutex_lock(&cfg80211_mutex); + mutex_lock(®_mutex); + + reset_regdomains(); + + kfree(last_request); + + last_request = NULL; + dev_set_uevent_suppress(®_pdev->dev, true); + + platform_device_unregister(reg_pdev); + + spin_lock_bh(®_pending_beacons_lock); + if (!list_empty(®_pending_beacons)) { + list_for_each_entry_safe(reg_beacon, btmp, + ®_pending_beacons, list) { + list_del(®_beacon->list); + kfree(reg_beacon); + } + } + spin_unlock_bh(®_pending_beacons_lock); + + if (!list_empty(®_beacon_list)) { + list_for_each_entry_safe(reg_beacon, btmp, + ®_beacon_list, list) { + list_del(®_beacon->list); + kfree(reg_beacon); + } + } + + spin_lock(®_requests_lock); + if (!list_empty(®_requests_list)) { + list_for_each_entry_safe(reg_request, tmp, + ®_requests_list, list) { + list_del(®_request->list); + kfree(reg_request); + } + } + spin_unlock(®_requests_lock); + + mutex_unlock(®_mutex); + mutex_unlock(&cfg80211_mutex); +} diff --git a/net/wireless_ath/reg.h b/net/wireless_ath/reg.h new file mode 100644 index 00000000..4a56799d --- /dev/null +++ b/net/wireless_ath/reg.h @@ -0,0 +1,87 @@ +#ifndef __NET_WIRELESS_REG_H +#define __NET_WIRELESS_REG_H + +extern const struct ieee80211_regdomain *cfg80211_regdomain; + +bool is_world_regdom(const char *alpha2); +bool reg_is_valid_request(const char *alpha2); + +int regulatory_hint_user(const char *alpha2); + +int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env); +void reg_device_remove(struct wiphy *wiphy); + +int __init regulatory_init(void); +void regulatory_exit(void); + +int set_regdom(const struct ieee80211_regdomain *rd); + +void regulatory_update(struct wiphy *wiphy, enum nl80211_reg_initiator setby); + +/** + * regulatory_hint_found_beacon - hints a beacon was found on a channel + * @wiphy: the wireless device where the beacon was found on + * @beacon_chan: the channel on which the beacon was found on + * @gfp: context flags + * + * This informs the wireless core that a beacon from an AP was found on + * the channel provided. This allows the wireless core to make educated + * guesses on regulatory to help with world roaming. This is only used for + * world roaming -- when we do not know our current location. This is + * only useful on channels 12, 13 and 14 on the 2 GHz band as channels + * 1-11 are already enabled by the world regulatory domain; and on + * non-radar 5 GHz channels. + * + * Drivers do not need to call this, cfg80211 will do it for after a scan + * on a newly found BSS. If you cannot make use of this feature you can + * set the wiphy->disable_beacon_hints to true. + */ +int regulatory_hint_found_beacon(struct wiphy *wiphy, + struct ieee80211_channel *beacon_chan, + gfp_t gfp); + +/** + * regulatory_hint_11d - hints a country IE as a regulatory domain + * @wiphy: the wireless device giving the hint (used only for reporting + * conflicts) + * @band: the band on which the country IE was received on. This determines + * the band we'll process the country IE channel triplets for. + * @country_ie: pointer to the country IE + * @country_ie_len: length of the country IE + * + * We will intersect the rd with the what CRDA tells us should apply + * for the alpha2 this country IE belongs to, this prevents APs from + * sending us incorrect or outdated information against a country. + * + * The AP is expected to provide Country IE channel triplets for the + * band it is on. It is technically possible for APs to send channel + * country IE triplets even for channels outside of the band they are + * in but for that they would have to use the regulatory extension + * in combination with a triplet but this behaviour is currently + * not observed. For this reason if a triplet is seen with channel + * information for a band the BSS is not present in it will be ignored. + */ +void regulatory_hint_11d(struct wiphy *wiphy, + enum ieee80211_band band, + u8 *country_ie, + u8 country_ie_len); + +/** + * regulatory_hint_disconnect - informs all devices have been disconneted + * + * Regulotory rules can be enhanced further upon scanning and upon + * connection to an AP. These rules become stale if we disconnect + * and go to another country, whether or not we suspend and resume. + * If we suspend, go to another country and resume we'll automatically + * get disconnected shortly after resuming and things will be reset as well. + * This routine is a helper to restore regulatory settings to how they were + * prior to our first connect attempt. This includes ignoring country IE and + * beacon regulatory hints. The ieee80211_regdom module parameter will always + * be respected but if a user had set the regulatory domain that will take + * precedence. + * + * Must be called from process context. + */ +void regulatory_hint_disconnect(void); + +#endif /* __NET_WIRELESS_REG_H */ diff --git a/net/wireless_ath/regdb.h b/net/wireless_ath/regdb.h new file mode 100644 index 00000000..818222c9 --- /dev/null +++ b/net/wireless_ath/regdb.h @@ -0,0 +1,7 @@ +#ifndef __REGDB_H__ +#define __REGDB_H__ + +extern const struct ieee80211_regdomain *reg_regdb[]; +extern int reg_regdb_size; + +#endif /* __REGDB_H__ */ diff --git a/net/wireless_ath/scan.c b/net/wireless_ath/scan.c new file mode 100644 index 00000000..c0beb650 --- /dev/null +++ b/net/wireless_ath/scan.c @@ -0,0 +1,1352 @@ +/* + * cfg80211 scan result handling + * + * Copyright 2008 Johannes Berg + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "nl80211.h" +#include "wext-compat.h" + +#define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ) + +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) +{ + struct cfg80211_scan_request *request; + struct net_device *dev; +#ifdef CONFIG_CFG80211_WEXT + union iwreq_data wrqu; +#endif + + ASSERT_RDEV_LOCK(rdev); + + request = rdev->scan_req; + + if (!request) + return; + + dev = request->dev; + + /* + * This must be before sending the other events! + * Otherwise, wpa_supplicant gets completely confused with + * wext events. + */ + cfg80211_sme_scan_done(dev); + + if (request->aborted) + nl80211_send_scan_aborted(rdev, dev); + else + nl80211_send_scan_done(rdev, dev); + +#ifdef CONFIG_CFG80211_WEXT + if (!request->aborted) { + memset(&wrqu, 0, sizeof(wrqu)); + + wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); + } +#endif + + dev_put(dev); + + rdev->scan_req = NULL; + + /* + * OK. If this is invoked with "leak" then we can't + * free this ... but we've cleaned it up anyway. The + * driver failed to call the scan_done callback, so + * all bets are off, it might still be trying to use + * the scan request or not ... if it accesses the dev + * in there (it shouldn't anyway) then it may crash. + */ + if (!leak) + kfree(request); +} + +void __cfg80211_scan_done(struct work_struct *wk) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(wk, struct cfg80211_registered_device, + scan_done_wk); + + cfg80211_lock_rdev(rdev); + ___cfg80211_scan_done(rdev, false); + cfg80211_unlock_rdev(rdev); +} + +void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) +{ + WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); + + request->aborted = aborted; + queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk); +} +EXPORT_SYMBOL(cfg80211_scan_done); + +void __cfg80211_sched_scan_results(struct work_struct *wk) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(wk, struct cfg80211_registered_device, + sched_scan_results_wk); + + mutex_lock(&rdev->sched_scan_mtx); + + /* we don't have sched_scan_req anymore if the scan is stopping */ + if (rdev->sched_scan_req) + nl80211_send_sched_scan_results(rdev, + rdev->sched_scan_req->dev); + + mutex_unlock(&rdev->sched_scan_mtx); +} + +void cfg80211_sched_scan_results(struct wiphy *wiphy) +{ + /* ignore if we're not scanning */ + if (wiphy_to_dev(wiphy)->sched_scan_req) + queue_work(cfg80211_wq, + &wiphy_to_dev(wiphy)->sched_scan_results_wk); +} +EXPORT_SYMBOL(cfg80211_sched_scan_results); + +void cfg80211_sched_scan_stopped(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + mutex_lock(&rdev->sched_scan_mtx); + __cfg80211_stop_sched_scan(rdev, true); + mutex_unlock(&rdev->sched_scan_mtx); +} +EXPORT_SYMBOL(cfg80211_sched_scan_stopped); + +int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, + bool driver_initiated) +{ + struct net_device *dev; + + lockdep_assert_held(&rdev->sched_scan_mtx); + + if (!rdev->sched_scan_req) + return -ENOENT; + + dev = rdev->sched_scan_req->dev; + + if (!driver_initiated) { + int err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); + if (err) + return err; + } + + nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); + + kfree(rdev->sched_scan_req); + rdev->sched_scan_req = NULL; + + return 0; +} + +static void bss_release(struct kref *ref) +{ + struct cfg80211_internal_bss *bss; + + bss = container_of(ref, struct cfg80211_internal_bss, ref); + if (bss->pub.free_priv) + bss->pub.free_priv(&bss->pub); + + if (bss->beacon_ies_allocated) + kfree(bss->pub.beacon_ies); + if (bss->proberesp_ies_allocated) + kfree(bss->pub.proberesp_ies); + + BUG_ON(atomic_read(&bss->hold)); + + kfree(bss); +} + +/* must hold dev->bss_lock! */ +void cfg80211_bss_age(struct cfg80211_registered_device *dev, + unsigned long age_secs) +{ + struct cfg80211_internal_bss *bss; + unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); + + list_for_each_entry(bss, &dev->bss_list, list) { + bss->ts -= age_jiffies; + } +} + +/* must hold dev->bss_lock! */ +static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *bss) +{ + list_del_init(&bss->list); + rb_erase(&bss->rbn, &dev->bss_tree); + kref_put(&bss->ref, bss_release); +} + +/* must hold dev->bss_lock! */ +void cfg80211_bss_expire(struct cfg80211_registered_device *dev) +{ + struct cfg80211_internal_bss *bss, *tmp; + bool expired = false; + + list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { + if (atomic_read(&bss->hold)) + continue; + if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) + continue; + __cfg80211_unlink_bss(dev, bss); + expired = true; + } + + if (expired) + dev->bss_generation++; +} + +const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) +{ + while (len > 2 && ies[0] != eid) { + len -= ies[1] + 2; + ies += ies[1] + 2; + } + if (len < 2) + return NULL; + if (len < 2 + ies[1]) + return NULL; + return ies; +} +EXPORT_SYMBOL(cfg80211_find_ie); + +const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, + const u8 *ies, int len) +{ + struct ieee80211_vendor_ie *ie; + const u8 *pos = ies, *end = ies + len; + int ie_oui; + + while (pos < end) { + pos = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, pos, + end - pos); + if (!pos) + return NULL; + + if (end - pos < sizeof(*ie)) + return NULL; + + ie = (struct ieee80211_vendor_ie *)pos; + ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2]; + if (ie_oui == oui && ie->oui_type == oui_type) + return pos; + + pos += 2 + ie->len; + } + return NULL; +} +EXPORT_SYMBOL(cfg80211_find_vendor_ie); + +static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) +{ + const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); + const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); + + /* equal if both missing */ + if (!ie1 && !ie2) + return 0; + /* sort missing IE before (left of) present IE */ + if (!ie1) + return -1; + if (!ie2) + return 1; + + /* sort by length first, then by contents */ + if (ie1[1] != ie2[1]) + return ie2[1] - ie1[1]; + return memcmp(ie1 + 2, ie2 + 2, ie1[1]); +} + +static bool is_bss(struct cfg80211_bss *a, + const u8 *bssid, + const u8 *ssid, size_t ssid_len) +{ + const u8 *ssidie; + + if (bssid && compare_ether_addr(a->bssid, bssid)) + return false; + + if (!ssid) + return true; + + ssidie = cfg80211_find_ie(WLAN_EID_SSID, + a->information_elements, + a->len_information_elements); + if (!ssidie) + return false; + if (ssidie[1] != ssid_len) + return false; + return memcmp(ssidie + 2, ssid, ssid_len) == 0; +} + +static bool is_mesh_bss(struct cfg80211_bss *a) +{ + const u8 *ie; + + if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) + return false; + + ie = cfg80211_find_ie(WLAN_EID_MESH_ID, + a->information_elements, + a->len_information_elements); + if (!ie) + return false; + + ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, + a->information_elements, + a->len_information_elements); + if (!ie) + return false; + + return true; +} + +static bool is_mesh(struct cfg80211_bss *a, + const u8 *meshid, size_t meshidlen, + const u8 *meshcfg) +{ + const u8 *ie; + + if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) + return false; + + ie = cfg80211_find_ie(WLAN_EID_MESH_ID, + a->information_elements, + a->len_information_elements); + if (!ie) + return false; + if (ie[1] != meshidlen) + return false; + if (memcmp(ie + 2, meshid, meshidlen)) + return false; + + ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, + a->information_elements, + a->len_information_elements); + if (!ie) + return false; + if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) + return false; + + /* + * Ignore mesh capability (last two bytes of the IE) when + * comparing since that may differ between stations taking + * part in the same mesh. + */ + return memcmp(ie + 2, meshcfg, + sizeof(struct ieee80211_meshconf_ie) - 2) == 0; +} + +static int cmp_bss_core(struct cfg80211_bss *a, + struct cfg80211_bss *b) +{ + int r; + + if (a->channel != b->channel) + return b->channel->center_freq - a->channel->center_freq; + + if (is_mesh_bss(a) && is_mesh_bss(b)) { + r = cmp_ies(WLAN_EID_MESH_ID, + a->information_elements, + a->len_information_elements, + b->information_elements, + b->len_information_elements); + if (r) + return r; + return cmp_ies(WLAN_EID_MESH_CONFIG, + a->information_elements, + a->len_information_elements, + b->information_elements, + b->len_information_elements); + } + + return memcmp(a->bssid, b->bssid, ETH_ALEN); +} + +static int cmp_bss(struct cfg80211_bss *a, + struct cfg80211_bss *b) +{ + int r; + + r = cmp_bss_core(a, b); + if (r) + return r; + + return cmp_ies(WLAN_EID_SSID, + a->information_elements, + a->len_information_elements, + b->information_elements, + b->len_information_elements); +} + +static int cmp_hidden_bss(struct cfg80211_bss *a, + struct cfg80211_bss *b) +{ + const u8 *ie1; + const u8 *ie2; + int i; + int r; + + r = cmp_bss_core(a, b); + if (r) + return r; + + ie1 = cfg80211_find_ie(WLAN_EID_SSID, + a->information_elements, + a->len_information_elements); + ie2 = cfg80211_find_ie(WLAN_EID_SSID, + b->information_elements, + b->len_information_elements); + + /* Key comparator must use same algorithm in any rb-tree + * search function (order is important), otherwise ordering + * of items in the tree is broken and search gives incorrect + * results. This code uses same order as cmp_ies() does. */ + + /* sort missing IE before (left of) present IE */ + if (!ie1) + return -1; + if (!ie2) + return 1; + + /* zero-size SSID is used as an indication of the hidden bss */ + if (!ie2[1]) + return 0; + + /* sort by length first, then by contents */ + if (ie1[1] != ie2[1]) + return ie2[1] - ie1[1]; + + /* zeroed SSID ie is another indication of a hidden bss */ + for (i = 0; i < ie2[1]; i++) + if (ie2[i + 2]) + return -1; + + return 0; +} + +struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *bssid, + const u8 *ssid, size_t ssid_len, + u16 capa_mask, u16 capa_val) +{ + struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); + struct cfg80211_internal_bss *bss, *res = NULL; + unsigned long now = jiffies; + + spin_lock_bh(&dev->bss_lock); + + list_for_each_entry(bss, &dev->bss_list, list) { + if ((bss->pub.capability & capa_mask) != capa_val) + continue; + if (channel && bss->pub.channel != channel) + continue; + /* Don't get expired BSS structs */ + if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) && + !atomic_read(&bss->hold)) + continue; + if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { + res = bss; + kref_get(&res->ref); + break; + } + } + + spin_unlock_bh(&dev->bss_lock); + if (!res) + return NULL; + return &res->pub; +} +EXPORT_SYMBOL(cfg80211_get_bss); + +struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *meshid, size_t meshidlen, + const u8 *meshcfg) +{ + struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); + struct cfg80211_internal_bss *bss, *res = NULL; + + spin_lock_bh(&dev->bss_lock); + + list_for_each_entry(bss, &dev->bss_list, list) { + if (channel && bss->pub.channel != channel) + continue; + if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) { + res = bss; + kref_get(&res->ref); + break; + } + } + + spin_unlock_bh(&dev->bss_lock); + if (!res) + return NULL; + return &res->pub; +} +EXPORT_SYMBOL(cfg80211_get_mesh); + + +static void rb_insert_bss(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *bss) +{ + struct rb_node **p = &dev->bss_tree.rb_node; + struct rb_node *parent = NULL; + struct cfg80211_internal_bss *tbss; + int cmp; + + while (*p) { + parent = *p; + tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); + + cmp = cmp_bss(&bss->pub, &tbss->pub); + + if (WARN_ON(!cmp)) { + /* will sort of leak this BSS */ + return; + } + + if (cmp < 0) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + rb_link_node(&bss->rbn, parent, p); + rb_insert_color(&bss->rbn, &dev->bss_tree); +} + +static struct cfg80211_internal_bss * +rb_find_bss(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *res) +{ + struct rb_node *n = dev->bss_tree.rb_node; + struct cfg80211_internal_bss *bss; + int r; + + while (n) { + bss = rb_entry(n, struct cfg80211_internal_bss, rbn); + r = cmp_bss(&res->pub, &bss->pub); + + if (r == 0) + return bss; + else if (r < 0) + n = n->rb_left; + else + n = n->rb_right; + } + + return NULL; +} + +static struct cfg80211_internal_bss * +rb_find_hidden_bss(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *res) +{ + struct rb_node *n = dev->bss_tree.rb_node; + struct cfg80211_internal_bss *bss; + int r; + + while (n) { + bss = rb_entry(n, struct cfg80211_internal_bss, rbn); + r = cmp_hidden_bss(&res->pub, &bss->pub); + + if (r == 0) + return bss; + else if (r < 0) + n = n->rb_left; + else + n = n->rb_right; + } + + return NULL; +} + +static void +copy_hidden_ies(struct cfg80211_internal_bss *res, + struct cfg80211_internal_bss *hidden) +{ + if (unlikely(res->pub.beacon_ies)) + return; + if (WARN_ON(!hidden->pub.beacon_ies)) + return; + + res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC); + if (unlikely(!res->pub.beacon_ies)) + return; + + res->beacon_ies_allocated = true; + res->pub.len_beacon_ies = hidden->pub.len_beacon_ies; + memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies, + res->pub.len_beacon_ies); +} + +static struct cfg80211_internal_bss * +cfg80211_bss_update(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *res) +{ + struct cfg80211_internal_bss *found = NULL; + + /* + * The reference to "res" is donated to this function. + */ + + if (WARN_ON(!res->pub.channel)) { + kref_put(&res->ref, bss_release); + return NULL; + } + + res->ts = jiffies; + + spin_lock_bh(&dev->bss_lock); + + found = rb_find_bss(dev, res); + + if (found) { + found->pub.beacon_interval = res->pub.beacon_interval; + found->pub.tsf = res->pub.tsf; + found->pub.signal = res->pub.signal; + found->pub.capability = res->pub.capability; + found->ts = res->ts; + + /* Update IEs */ + if (res->pub.proberesp_ies) { + size_t used = dev->wiphy.bss_priv_size + sizeof(*res); + size_t ielen = res->pub.len_proberesp_ies; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) + if (0) { + used = 0; /* just to shut up the compiler */ +#else + if (found->pub.proberesp_ies && + !found->proberesp_ies_allocated && + ksize(found) >= used + ielen) { +#endif + memcpy(found->pub.proberesp_ies, + res->pub.proberesp_ies, ielen); + found->pub.len_proberesp_ies = ielen; + } else { + u8 *ies = found->pub.proberesp_ies; + + if (found->proberesp_ies_allocated) + ies = krealloc(ies, ielen, GFP_ATOMIC); + else + ies = kmalloc(ielen, GFP_ATOMIC); + + if (ies) { + memcpy(ies, res->pub.proberesp_ies, + ielen); + found->proberesp_ies_allocated = true; + found->pub.proberesp_ies = ies; + found->pub.len_proberesp_ies = ielen; + } + } + + /* Override possible earlier Beacon frame IEs */ + found->pub.information_elements = + found->pub.proberesp_ies; + found->pub.len_information_elements = + found->pub.len_proberesp_ies; + } + if (res->pub.beacon_ies) { + size_t used = dev->wiphy.bss_priv_size + sizeof(*res); + size_t ielen = res->pub.len_beacon_ies; + bool information_elements_is_beacon_ies = + (found->pub.information_elements == + found->pub.beacon_ies); + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) + if (0) { + used = 0; /* just to shut up the compiler */ +#else + if (found->pub.beacon_ies && + !found->beacon_ies_allocated && + ksize(found) >= used + ielen) { +#endif + memcpy(found->pub.beacon_ies, + res->pub.beacon_ies, ielen); + found->pub.len_beacon_ies = ielen; + } else { + u8 *ies = found->pub.beacon_ies; + + if (found->beacon_ies_allocated) + ies = krealloc(ies, ielen, GFP_ATOMIC); + else + ies = kmalloc(ielen, GFP_ATOMIC); + + if (ies) { + memcpy(ies, res->pub.beacon_ies, + ielen); + found->beacon_ies_allocated = true; + found->pub.beacon_ies = ies; + found->pub.len_beacon_ies = ielen; + } + } + + /* Override IEs if they were from a beacon before */ + if (information_elements_is_beacon_ies) { + found->pub.information_elements = + found->pub.beacon_ies; + found->pub.len_information_elements = + found->pub.len_beacon_ies; + } + } + + kref_put(&res->ref, bss_release); + } else { + struct cfg80211_internal_bss *hidden; + + /* First check if the beacon is a probe response from + * a hidden bss. If so, copy beacon ies (with nullified + * ssid) into the probe response bss entry (with real ssid). + * It is required basically for PSM implementation + * (probe responses do not contain tim ie) */ + + /* TODO: The code is not trying to update existing probe + * response bss entries when beacon ies are + * getting changed. */ + hidden = rb_find_hidden_bss(dev, res); + if (hidden) + copy_hidden_ies(res, hidden); + + /* this "consumes" the reference */ + list_add_tail(&res->list, &dev->bss_list); + rb_insert_bss(dev, res); + found = res; + } + + dev->bss_generation++; + spin_unlock_bh(&dev->bss_lock); + + kref_get(&found->ref); + return found; +} + +struct cfg80211_bss* +cfg80211_inform_bss(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *bssid, + u64 timestamp, u16 capability, u16 beacon_interval, + const u8 *ie, size_t ielen, + s32 signal, gfp_t gfp) +{ + struct cfg80211_internal_bss *res; + size_t privsz; + + if (WARN_ON(!wiphy)) + return NULL; + + privsz = wiphy->bss_priv_size; + + if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC && + (signal < 0 || signal > 100))) + return NULL; + + res = kzalloc(sizeof(*res) + privsz + ielen, gfp); + if (!res) + return NULL; + + memcpy(res->pub.bssid, bssid, ETH_ALEN); + res->pub.channel = channel; + res->pub.signal = signal; + res->pub.tsf = timestamp; + res->pub.beacon_interval = beacon_interval; + res->pub.capability = capability; + /* + * Since we do not know here whether the IEs are from a Beacon or Probe + * Response frame, we need to pick one of the options and only use it + * with the driver that does not provide the full Beacon/Probe Response + * frame. Use Beacon frame pointer to avoid indicating that this should + * override the information_elements pointer should we have received an + * earlier indication of Probe Response data. + * + * The initial buffer for the IEs is allocated with the BSS entry and + * is located after the private area. + */ + res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz; + memcpy(res->pub.beacon_ies, ie, ielen); + res->pub.len_beacon_ies = ielen; + res->pub.information_elements = res->pub.beacon_ies; + res->pub.len_information_elements = res->pub.len_beacon_ies; + + kref_init(&res->ref); + + res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); + if (!res) + return NULL; + + if (res->pub.capability & WLAN_CAPABILITY_ESS) + regulatory_hint_found_beacon(wiphy, channel, gfp); + + /* cfg80211_bss_update gives us a referenced result */ + return &res->pub; +} +EXPORT_SYMBOL(cfg80211_inform_bss); + +struct cfg80211_bss * +cfg80211_inform_bss_frame(struct wiphy *wiphy, + struct ieee80211_channel *channel, + struct ieee80211_mgmt *mgmt, size_t len, + s32 signal, gfp_t gfp) +{ + struct cfg80211_internal_bss *res; + size_t ielen = len - offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + size_t privsz; + + if (WARN_ON(!mgmt)) + return NULL; + + if (WARN_ON(!wiphy)) + return NULL; + + if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC && + (signal < 0 || signal > 100))) + return NULL; + + if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable))) + return NULL; + + privsz = wiphy->bss_priv_size; + + res = kzalloc(sizeof(*res) + privsz + ielen, gfp); + if (!res) + return NULL; + + memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN); + res->pub.channel = channel; + res->pub.signal = signal; + res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); + res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); + res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); + /* + * The initial buffer for the IEs is allocated with the BSS entry and + * is located after the private area. + */ + if (ieee80211_is_probe_resp(mgmt->frame_control)) { + res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz; + memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable, + ielen); + res->pub.len_proberesp_ies = ielen; + res->pub.information_elements = res->pub.proberesp_ies; + res->pub.len_information_elements = res->pub.len_proberesp_ies; + } else { + res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz; + memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen); + res->pub.len_beacon_ies = ielen; + res->pub.information_elements = res->pub.beacon_ies; + res->pub.len_information_elements = res->pub.len_beacon_ies; + } + + kref_init(&res->ref); + + res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); + if (!res) + return NULL; + + if (res->pub.capability & WLAN_CAPABILITY_ESS) + regulatory_hint_found_beacon(wiphy, channel, gfp); + + /* cfg80211_bss_update gives us a referenced result */ + return &res->pub; +} +EXPORT_SYMBOL(cfg80211_inform_bss_frame); + +void cfg80211_put_bss(struct cfg80211_bss *pub) +{ + struct cfg80211_internal_bss *bss; + + if (!pub) + return; + + bss = container_of(pub, struct cfg80211_internal_bss, pub); + kref_put(&bss->ref, bss_release); +} +EXPORT_SYMBOL(cfg80211_put_bss); + +void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) +{ + struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); + struct cfg80211_internal_bss *bss; + + if (WARN_ON(!pub)) + return; + + bss = container_of(pub, struct cfg80211_internal_bss, pub); + + spin_lock_bh(&dev->bss_lock); + if (!list_empty(&bss->list)) { + __cfg80211_unlink_bss(dev, bss); + dev->bss_generation++; + } + spin_unlock_bh(&dev->bss_lock); +} +EXPORT_SYMBOL(cfg80211_unlink_bss); + +#ifdef CONFIG_CFG80211_WEXT +int cfg80211_wext_siwscan(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct cfg80211_registered_device *rdev; + struct wiphy *wiphy; + struct iw_scan_req *wreq = NULL; + struct cfg80211_scan_request *creq = NULL; + int i, err, n_channels = 0; + enum ieee80211_band band; + + if (!netif_running(dev)) + return -ENETDOWN; + + if (wrqu->data.length == sizeof(struct iw_scan_req)) + wreq = (struct iw_scan_req *)extra; + + rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); + + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + if (rdev->scan_req) { + err = -EBUSY; + goto out; + } + + wiphy = &rdev->wiphy; + + /* Determine number of channels, needed to allocate creq */ + if (wreq && wreq->num_channels) + n_channels = wreq->num_channels; + else { + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + if (wiphy->bands[band]) + n_channels += wiphy->bands[band]->n_channels; + } + + creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + + n_channels * sizeof(void *), + GFP_ATOMIC); + if (!creq) { + err = -ENOMEM; + goto out; + } + + creq->wiphy = wiphy; + creq->dev = dev; + /* SSIDs come after channels */ + creq->ssids = (void *)&creq->channels[n_channels]; + creq->n_channels = n_channels; + creq->n_ssids = 1; + + /* translate "Scan on frequencies" request */ + i = 0; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + int j; + + if (!wiphy->bands[band]) + continue; + + for (j = 0; j < wiphy->bands[band]->n_channels; j++) { + /* ignore disabled channels */ + if (wiphy->bands[band]->channels[j].flags & + IEEE80211_CHAN_DISABLED) + continue; + + /* If we have a wireless request structure and the + * wireless request specifies frequencies, then search + * for the matching hardware channel. + */ + if (wreq && wreq->num_channels) { + int k; + int wiphy_freq = wiphy->bands[band]->channels[j].center_freq; + for (k = 0; k < wreq->num_channels; k++) { + int wext_freq = cfg80211_wext_freq(wiphy, &wreq->channel_list[k]); + if (wext_freq == wiphy_freq) + goto wext_freq_found; + } + goto wext_freq_not_found; + } + + wext_freq_found: + creq->channels[i] = &wiphy->bands[band]->channels[j]; + i++; + wext_freq_not_found: ; + } + } + /* No channels found? */ + if (!i) { + err = -EINVAL; + goto out; + } + + /* Set real number of channels specified in creq->channels[] */ + creq->n_channels = i; + + /* translate "Scan for SSID" request */ + if (wreq) { + if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { + if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out; + } + memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len); + creq->ssids[0].ssid_len = wreq->essid_len; + } + if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE) + creq->n_ssids = 0; + } + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + if (wiphy->bands[i]) + creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; + + rdev->scan_req = creq; + err = rdev->ops->scan(wiphy, dev, creq); + if (err) { + rdev->scan_req = NULL; + /* creq will be freed below */ + } else { + nl80211_send_scan_start(rdev, dev); + /* creq now owned by driver */ + creq = NULL; + dev_hold(dev); + } + out: + kfree(creq); + cfg80211_unlock_rdev(rdev); + return err; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); + +static void ieee80211_scan_add_ies(struct iw_request_info *info, + struct cfg80211_bss *bss, + char **current_ev, char *end_buf) +{ + u8 *pos, *end, *next; + struct iw_event iwe; + + if (!bss->information_elements || + !bss->len_information_elements) + return; + + /* + * If needed, fragment the IEs buffer (at IE boundaries) into short + * enough fragments to fit into IW_GENERIC_IE_MAX octet messages. + */ + pos = bss->information_elements; + end = pos + bss->len_information_elements; + + while (end - pos > IW_GENERIC_IE_MAX) { + next = pos + 2 + pos[1]; + while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX) + next = next + 2 + next[1]; + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = next - pos; + *current_ev = iwe_stream_add_point(info, *current_ev, + end_buf, &iwe, pos); + + pos = next; + } + + if (end > pos) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = end - pos; + *current_ev = iwe_stream_add_point(info, *current_ev, + end_buf, &iwe, pos); + } +} + +static inline unsigned int elapsed_jiffies_msecs(unsigned long start) +{ + unsigned long end = jiffies; + + if (end >= start) + return jiffies_to_msecs(end - start); + + return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); +} + +static char * +ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, + struct cfg80211_internal_bss *bss, char *current_ev, + char *end_buf) +{ + struct iw_event iwe; + u8 *buf, *cfg, *p; + u8 *ie = bss->pub.information_elements; + int rem = bss->pub.len_information_elements, i, sig; + bool ismesh = false; + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, + IW_EV_ADDR_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq); + iwe.u.freq.e = 0; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = bss->pub.channel->center_freq; + iwe.u.freq.e = 6; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + + if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVQUAL; + iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED | + IW_QUAL_NOISE_INVALID | + IW_QUAL_QUAL_UPDATED; + switch (wiphy->signal_type) { + case CFG80211_SIGNAL_TYPE_MBM: + sig = bss->pub.signal / 100; + iwe.u.qual.level = sig; + iwe.u.qual.updated |= IW_QUAL_DBM; + if (sig < -110) /* rather bad */ + sig = -110; + else if (sig > -40) /* perfect */ + sig = -40; + /* will give a range of 0 .. 70 */ + iwe.u.qual.qual = sig + 110; + break; + case CFG80211_SIGNAL_TYPE_UNSPEC: + iwe.u.qual.level = bss->pub.signal; + /* will give range 0 .. 100 */ + iwe.u.qual.qual = bss->pub.signal; + break; + default: + /* not reached */ + break; + } + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWENCODE; + if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ""); + + while (rem >= 2) { + /* invalid data */ + if (ie[1] > rem - 2) + break; + + switch (ie[0]) { + case WLAN_EID_SSID: + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = ie[1]; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ie + 2); + break; + case WLAN_EID_MESH_ID: + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = ie[1]; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ie + 2); + break; + case WLAN_EID_MESH_CONFIG: + ismesh = true; + if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) + break; + buf = kmalloc(50, GFP_ATOMIC); + if (!buf) + break; + cfg = ie + 2; + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "Mesh Network Path Selection Protocol ID: " + "0x%02X", cfg[0]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + sprintf(buf, "Path Selection Metric ID: 0x%02X", + cfg[1]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + sprintf(buf, "Congestion Control Mode ID: 0x%02X", + cfg[2]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + sprintf(buf, "Authentication ID: 0x%02X", cfg[4]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + sprintf(buf, "Formation Info: 0x%02X", cfg[5]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + sprintf(buf, "Capabilities: 0x%02X", cfg[6]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + kfree(buf); + break; + case WLAN_EID_SUPP_RATES: + case WLAN_EID_EXT_SUPP_RATES: + /* display all supported rates in readable format */ + p = current_ev + iwe_stream_lcp_len(info); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWRATE; + /* Those two flags are ignored... */ + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + + for (i = 0; i < ie[1]; i++) { + iwe.u.bitrate.value = + ((ie[i + 2] & 0x7f) * 500000); + p = iwe_stream_add_value(info, current_ev, p, + end_buf, &iwe, IW_EV_PARAM_LEN); + } + current_ev = p; + break; + } + rem -= ie[1] + 2; + ie += ie[1] + 2; + } + + if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) || + ismesh) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWMODE; + if (ismesh) + iwe.u.mode = IW_MODE_MESH; + else if (bss->pub.capability & WLAN_CAPABILITY_ESS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); + } + + buf = kmalloc(30, GFP_ATOMIC); + if (buf) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, buf); + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, " Last beacon: %ums ago", + elapsed_jiffies_msecs(bss->ts)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); + kfree(buf); + } + + ieee80211_scan_add_ies(info, &bss->pub, ¤t_ev, end_buf); + + return current_ev; +} + + +static int ieee80211_scan_results(struct cfg80211_registered_device *dev, + struct iw_request_info *info, + char *buf, size_t len) +{ + char *current_ev = buf; + char *end_buf = buf + len; + struct cfg80211_internal_bss *bss; + + spin_lock_bh(&dev->bss_lock); + cfg80211_bss_expire(dev); + + list_for_each_entry(bss, &dev->bss_list, list) { + if (buf + len - current_ev <= IW_EV_ADDR_LEN) { + spin_unlock_bh(&dev->bss_lock); + return -E2BIG; + } + current_ev = ieee80211_bss(&dev->wiphy, info, bss, + current_ev, end_buf); + } + spin_unlock_bh(&dev->bss_lock); + return current_ev - buf; +} + + +int cfg80211_wext_giwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct cfg80211_registered_device *rdev; + int res; + + if (!netif_running(dev)) + return -ENETDOWN; + + rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); + + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + if (rdev->scan_req) { + res = -EAGAIN; + goto out; + } + + res = ieee80211_scan_results(rdev, info, extra, data->length); + data->length = 0; + if (res >= 0) { + data->length = res; + res = 0; + } + + out: + cfg80211_unlock_rdev(rdev); + return res; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan); +#endif diff --git a/net/wireless_ath/sme.c b/net/wireless_ath/sme.c new file mode 100644 index 00000000..38d32487 --- /dev/null +++ b/net/wireless_ath/sme.c @@ -0,0 +1,1041 @@ +/* + * SME code for cfg80211's connect emulation. + * + * Copyright 2009 Johannes Berg + * Copyright (C) 2009 Intel Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nl80211.h" +#include "reg.h" + +struct cfg80211_conn { + struct cfg80211_connect_params params; + /* these are sub-states of the _CONNECTING sme_state */ + enum { + CFG80211_CONN_IDLE, + CFG80211_CONN_SCANNING, + CFG80211_CONN_SCAN_AGAIN, + CFG80211_CONN_AUTHENTICATE_NEXT, + CFG80211_CONN_AUTHENTICATING, + CFG80211_CONN_ASSOCIATE_NEXT, + CFG80211_CONN_ASSOCIATING, + CFG80211_CONN_DEAUTH_ASSOC_FAIL, + } state; + u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; + u8 *ie; + size_t ie_len; + bool auto_auth, prev_bssid_valid; +}; + +static bool cfg80211_is_all_idle(void) +{ + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + bool is_all_idle = true; + + mutex_lock(&cfg80211_mutex); + + /* + * All devices must be idle as otherwise if you are actively + * scanning some new beacon hints could be learned and would + * count as new regulatory hints. + */ + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + cfg80211_lock_rdev(rdev); + list_for_each_entry(wdev, &rdev->netdev_list, list) { + wdev_lock(wdev); + if (wdev->sme_state != CFG80211_SME_IDLE) + is_all_idle = false; + wdev_unlock(wdev); + } + cfg80211_unlock_rdev(rdev); + } + + mutex_unlock(&cfg80211_mutex); + + return is_all_idle; +} + +static void disconnect_work(struct work_struct *work) +{ + if (!cfg80211_is_all_idle()) + return; + + regulatory_hint_disconnect(); +} + +static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); + +static int cfg80211_conn_scan(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_scan_request *request; + int n_channels, err; + + ASSERT_RTNL(); + ASSERT_RDEV_LOCK(rdev); + ASSERT_WDEV_LOCK(wdev); + + if (rdev->scan_req) + return -EBUSY; + + if (wdev->conn->params.channel) { + n_channels = 1; + } else { + enum ieee80211_band band; + n_channels = 0; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!wdev->wiphy->bands[band]) + continue; + n_channels += wdev->wiphy->bands[band]->n_channels; + } + } + request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + + sizeof(request->channels[0]) * n_channels, + GFP_KERNEL); + if (!request) + return -ENOMEM; + + if (wdev->conn->params.channel) + request->channels[0] = wdev->conn->params.channel; + else { + int i = 0, j; + enum ieee80211_band band; + struct ieee80211_supported_band *bands; + struct ieee80211_channel *channel; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + bands = wdev->wiphy->bands[band]; + if (!bands) + continue; + for (j = 0; j < bands->n_channels; j++) { + channel = &bands->channels[j]; + if (channel->flags & IEEE80211_CHAN_DISABLED) + continue; + request->channels[i++] = channel; + } + request->rates[band] = (1 << bands->n_bitrates) - 1; + } + n_channels = i; + } + request->n_channels = n_channels; + request->ssids = (void *)&request->channels[n_channels]; + request->n_ssids = 1; + + memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, + wdev->conn->params.ssid_len); + request->ssids[0].ssid_len = wdev->conn->params.ssid_len; + + request->dev = wdev->netdev; + request->wiphy = &rdev->wiphy; + + rdev->scan_req = request; + + err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request); + if (!err) { + wdev->conn->state = CFG80211_CONN_SCANNING; + nl80211_send_scan_start(rdev, wdev->netdev); + dev_hold(wdev->netdev); + } else { + rdev->scan_req = NULL; + kfree(request); + } + return err; +} + +static int cfg80211_conn_do_work(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_connect_params *params; + const u8 *prev_bssid = NULL; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (!wdev->conn) + return 0; + + params = &wdev->conn->params; + + switch (wdev->conn->state) { + case CFG80211_CONN_SCAN_AGAIN: + return cfg80211_conn_scan(wdev); + case CFG80211_CONN_AUTHENTICATE_NEXT: + BUG_ON(!rdev->ops->auth); + wdev->conn->state = CFG80211_CONN_AUTHENTICATING; + return __cfg80211_mlme_auth(rdev, wdev->netdev, + params->channel, params->auth_type, + params->bssid, + params->ssid, params->ssid_len, + NULL, 0, + params->key, params->key_len, + params->key_idx, false); + case CFG80211_CONN_ASSOCIATE_NEXT: + BUG_ON(!rdev->ops->assoc); + wdev->conn->state = CFG80211_CONN_ASSOCIATING; + if (wdev->conn->prev_bssid_valid) + prev_bssid = wdev->conn->prev_bssid; + err = __cfg80211_mlme_assoc(rdev, wdev->netdev, + params->channel, params->bssid, + prev_bssid, + params->ssid, params->ssid_len, + params->ie, params->ie_len, + false, ¶ms->crypto); + if (err) + __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, + NULL, 0, + WLAN_REASON_DEAUTH_LEAVING, + false); + return err; + case CFG80211_CONN_DEAUTH_ASSOC_FAIL: + __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, + NULL, 0, + WLAN_REASON_DEAUTH_LEAVING, false); + /* return an error so that we call __cfg80211_connect_result() */ + return -EINVAL; + default: + return 0; + } +} + +void cfg80211_conn_work(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev = + container_of(work, struct cfg80211_registered_device, conn_work); + struct wireless_dev *wdev; + u8 bssid_buf[ETH_ALEN], *bssid = NULL; + + rtnl_lock(); + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); + + list_for_each_entry(wdev, &rdev->netdev_list, list) { + wdev_lock(wdev); + if (!netif_running(wdev->netdev)) { + wdev_unlock(wdev); + continue; + } + if (wdev->sme_state != CFG80211_SME_CONNECTING) { + wdev_unlock(wdev); + continue; + } + if (wdev->conn->params.bssid) { + memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN); + bssid = bssid_buf; + } + if (cfg80211_conn_do_work(wdev)) + __cfg80211_connect_result( + wdev->netdev, bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false, NULL); + wdev_unlock(wdev); + } + + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); + rtnl_unlock(); +} + +static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_bss *bss; + u16 capa = WLAN_CAPABILITY_ESS; + + ASSERT_WDEV_LOCK(wdev); + + if (wdev->conn->params.privacy) + capa |= WLAN_CAPABILITY_PRIVACY; + + bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel, + wdev->conn->params.bssid, + wdev->conn->params.ssid, + wdev->conn->params.ssid_len, + WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, + capa); + if (!bss) + return NULL; + + memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN); + wdev->conn->params.bssid = wdev->conn->bssid; + wdev->conn->params.channel = bss->channel; + wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; + schedule_work(&rdev->conn_work); + + return bss; +} + +static void __cfg80211_sme_scan_done(struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_bss *bss; + + ASSERT_WDEV_LOCK(wdev); + + if (wdev->sme_state != CFG80211_SME_CONNECTING) + return; + + if (!wdev->conn) + return; + + if (wdev->conn->state != CFG80211_CONN_SCANNING && + wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) + return; + + bss = cfg80211_get_conn_bss(wdev); + if (bss) { + cfg80211_put_bss(bss); + } else { + /* not found */ + if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) + schedule_work(&rdev->conn_work); + else + __cfg80211_connect_result( + wdev->netdev, + wdev->conn->params.bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false, NULL); + } +} + +void cfg80211_sme_scan_done(struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); + wdev_lock(wdev); + __cfg80211_sme_scan_done(dev); + wdev_unlock(wdev); + mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); +} + +void cfg80211_sme_rx_auth(struct net_device *dev, + const u8 *buf, size_t len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); + + ASSERT_WDEV_LOCK(wdev); + + /* should only RX auth frames when connecting */ + if (wdev->sme_state != CFG80211_SME_CONNECTING) + return; + + if (WARN_ON(!wdev->conn)) + return; + + if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && + wdev->conn->auto_auth && + wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { + /* select automatically between only open, shared, leap */ + switch (wdev->conn->params.auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + if (wdev->connect_keys) + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_SHARED_KEY; + else + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_NETWORK_EAP; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_NETWORK_EAP; + break; + default: + /* huh? */ + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_OPEN_SYSTEM; + break; + } + wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; + schedule_work(&rdev->conn_work); + } else if (status_code != WLAN_STATUS_SUCCESS) { + __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, + status_code, false, NULL); + } else if (wdev->sme_state == CFG80211_SME_CONNECTING && + wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { + wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; + schedule_work(&rdev->conn_work); + } +} + +bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev) +{ + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + if (WARN_ON(!wdev->conn)) + return false; + + if (!wdev->conn->prev_bssid_valid) + return false; + + /* + * Some stupid APs don't accept reassoc, so we + * need to fall back to trying regular assoc. + */ + wdev->conn->prev_bssid_valid = false; + wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; + schedule_work(&rdev->conn_work); + + return true; +} + +void cfg80211_sme_failed_assoc(struct wireless_dev *wdev) +{ + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + wdev->conn->state = CFG80211_CONN_DEAUTH_ASSOC_FAIL; + schedule_work(&rdev->conn_work); +} + +void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, bool wextev, + struct cfg80211_bss *bss) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + u8 *country_ie; +#ifdef CONFIG_CFG80211_WEXT + union iwreq_data wrqu; +#endif + + ASSERT_WDEV_LOCK(wdev); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) + return; + + if (wdev->sme_state != CFG80211_SME_CONNECTING) + return; + + nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, + bssid, req_ie, req_ie_len, + resp_ie, resp_ie_len, + status, GFP_KERNEL); + +#ifdef CONFIG_CFG80211_WEXT + if (wextev) { + if (req_ie && status == WLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = req_ie_len; + wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie); + } + + if (resp_ie && status == WLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = resp_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + } + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid && status == WLAN_STATUS_SUCCESS) { + memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); + wdev->wext.prev_bssid_valid = true; + } + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); + } +#endif + + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; + } + + if (wdev->conn) + wdev->conn->state = CFG80211_CONN_IDLE; + + if (status != WLAN_STATUS_SUCCESS) { + wdev->sme_state = CFG80211_SME_IDLE; + if (wdev->conn) + kfree(wdev->conn->ie); + kfree(wdev->conn); + wdev->conn = NULL; + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + wdev->ssid_len = 0; + return; + } + + if (!bss) + bss = cfg80211_get_bss(wdev->wiphy, + wdev->conn ? wdev->conn->params.channel : + NULL, + bssid, + wdev->ssid, wdev->ssid_len, + WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + + if (WARN_ON(!bss)) + return; + + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); + + wdev->sme_state = CFG80211_SME_CONNECTED; + cfg80211_upload_connect_keys(wdev); + + country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + + if (!country_ie) + return; + + /* + * ieee80211_bss_get_ie() ensures we can access: + * - country_ie + 2, the start of the country ie data, and + * - and country_ie[1] which is the IE length + */ + regulatory_hint_11d(wdev->wiphy, + bss->channel->band, + country_ie + 2, + country_ie[1]); +} + +void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING); + + ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); + if (!ev) + return; + + ev->type = EVENT_CONNECT_RESULT; + if (bssid) + memcpy(ev->cr.bssid, bssid, ETH_ALEN); + if (req_ie_len) { + ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); + ev->cr.req_ie_len = req_ie_len; + memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); + } + if (resp_ie_len) { + ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; + ev->cr.resp_ie_len = resp_ie_len; + memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); + } + ev->cr.status = status; + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + queue_work(cfg80211_wq, &rdev->event_work); +} +EXPORT_SYMBOL(cfg80211_connect_result); + +void __cfg80211_roamed(struct wireless_dev *wdev, + struct cfg80211_bss *bss, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len) +{ +#ifdef CONFIG_CFG80211_WEXT + union iwreq_data wrqu; +#endif + ASSERT_WDEV_LOCK(wdev); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) + goto out; + + if (wdev->sme_state != CFG80211_SME_CONNECTED) + goto out; + + /* internal error -- how did we get to CONNECTED w/o BSS? */ + if (WARN_ON(!wdev->current_bss)) { + goto out; + } + + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; + + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); + + nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bss->bssid, + req_ie, req_ie_len, resp_ie, resp_ie_len, + GFP_KERNEL); + +#ifdef CONFIG_CFG80211_WEXT + if (req_ie) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = req_ie_len; + wireless_send_event(wdev->netdev, IWEVASSOCREQIE, + &wrqu, req_ie); + } + + if (resp_ie) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = resp_ie_len; + wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, + &wrqu, resp_ie); + } + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN); + memcpy(wdev->wext.prev_bssid, bss->bssid, ETH_ALEN); + wdev->wext.prev_bssid_valid = true; + wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); +#endif + + return; +out: + cfg80211_put_bss(bss); +} + +void cfg80211_roamed(struct net_device *dev, + struct ieee80211_channel *channel, + const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_bss *bss; + + CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); + + bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid, + wdev->ssid_len, WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + if (WARN_ON(!bss)) + return; + + cfg80211_roamed_bss(dev, bss, req_ie, req_ie_len, resp_ie, + resp_ie_len, gfp); +} +EXPORT_SYMBOL(cfg80211_roamed); + +void cfg80211_roamed_bss(struct net_device *dev, + struct cfg80211_bss *bss, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); + + if (WARN_ON(!bss)) + return; + + ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); + if (!ev) { + cfg80211_put_bss(bss); + return; + } + + ev->type = EVENT_ROAMED; + ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); + ev->rm.req_ie_len = req_ie_len; + memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len); + ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; + ev->rm.resp_ie_len = resp_ie_len; + memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len); + ev->rm.bss = bss; + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + queue_work(cfg80211_wq, &rdev->event_work); +} +EXPORT_SYMBOL(cfg80211_roamed_bss); + +void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, + size_t ie_len, u16 reason, bool from_ap) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + int i; +#ifdef CONFIG_CFG80211_WEXT + union iwreq_data wrqu; +#endif + + ASSERT_WDEV_LOCK(wdev); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) + return; + + if (wdev->sme_state != CFG80211_SME_CONNECTED) + return; + + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + } + + wdev->current_bss = NULL; + wdev->sme_state = CFG80211_SME_IDLE; + wdev->ssid_len = 0; + + if (wdev->conn) { + const u8 *bssid; + int ret; + + kfree(wdev->conn->ie); + wdev->conn->ie = NULL; + kfree(wdev->conn); + wdev->conn = NULL; + + /* + * If this disconnect was due to a disassoc, we + * we might still have an auth BSS around. For + * the userspace SME that's currently expected, + * but for the kernel SME (nl80211 CONNECT or + * wireless extensions) we want to clear up all + * state. + */ + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (!wdev->auth_bsses[i]) + continue; + bssid = wdev->auth_bsses[i]->pub.bssid; + ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, + WLAN_REASON_DEAUTH_LEAVING, + false); + WARN(ret, "deauth failed: %d\n", ret); + } + } + + nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); + + /* + * Delete all the keys ... pairwise keys can't really + * exist any more anyway, but default keys might. + */ + if (rdev->ops->del_key) + for (i = 0; i < 6; i++) + rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL); + +#ifdef CONFIG_CFG80211_WEXT + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); + wdev->wext.connect.ssid_len = 0; +#endif + + schedule_work(&cfg80211_disconnect_work); +} + +void cfg80211_disconnected(struct net_device *dev, u16 reason, + u8 *ie, size_t ie_len, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); + + ev = kzalloc(sizeof(*ev) + ie_len, gfp); + if (!ev) + return; + + ev->type = EVENT_DISCONNECTED; + ev->dc.ie = ((u8 *)ev) + sizeof(*ev); + ev->dc.ie_len = ie_len; + memcpy((void *)ev->dc.ie, ie, ie_len); + ev->dc.reason = reason; + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + queue_work(cfg80211_wq, &rdev->event_work); +} +EXPORT_SYMBOL(cfg80211_disconnected); + +int __cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys, + const u8 *prev_bssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_bss *bss = NULL; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (wdev->sme_state != CFG80211_SME_IDLE) + return -EALREADY; + + if (WARN_ON(wdev->connect_keys)) { + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + } + + if (connkeys && connkeys->def >= 0) { + int idx; + u32 cipher; + + idx = connkeys->def; + cipher = connkeys->params[idx].cipher; + /* If given a WEP key we may need it for shared key auth */ + if (cipher == WLAN_CIPHER_SUITE_WEP40 || + cipher == WLAN_CIPHER_SUITE_WEP104) { + connect->key_idx = idx; + connect->key = connkeys->params[idx].key; + connect->key_len = connkeys->params[idx].key_len; + + /* + * If ciphers are not set (e.g. when going through + * iwconfig), we have to set them appropriately here. + */ + if (connect->crypto.cipher_group == 0) + connect->crypto.cipher_group = cipher; + + if (connect->crypto.n_ciphers_pairwise == 0) { + connect->crypto.n_ciphers_pairwise = 1; + connect->crypto.ciphers_pairwise[0] = cipher; + } + } + } + + if (!rdev->ops->connect) { + if (!rdev->ops->auth || !rdev->ops->assoc) + return -EOPNOTSUPP; + + if (WARN_ON(wdev->conn)) + return -EINPROGRESS; + + wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); + if (!wdev->conn) + return -ENOMEM; + + /* + * Copy all parameters, and treat explicitly IEs, BSSID, SSID. + */ + memcpy(&wdev->conn->params, connect, sizeof(*connect)); + if (connect->bssid) { + wdev->conn->params.bssid = wdev->conn->bssid; + memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); + } + + if (connect->ie) { + wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, + GFP_KERNEL); + wdev->conn->params.ie = wdev->conn->ie; + if (!wdev->conn->ie) { + kfree(wdev->conn); + wdev->conn = NULL; + return -ENOMEM; + } + } + + if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { + wdev->conn->auto_auth = true; + /* start with open system ... should mostly work */ + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_OPEN_SYSTEM; + } else { + wdev->conn->auto_auth = false; + } + + memcpy(wdev->ssid, connect->ssid, connect->ssid_len); + wdev->ssid_len = connect->ssid_len; + wdev->conn->params.ssid = wdev->ssid; + wdev->conn->params.ssid_len = connect->ssid_len; + + /* see if we have the bss already */ + bss = cfg80211_get_conn_bss(wdev); + + wdev->sme_state = CFG80211_SME_CONNECTING; + wdev->connect_keys = connkeys; + + if (prev_bssid) { + memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); + wdev->conn->prev_bssid_valid = true; + } + + /* we're good if we have a matching bss struct */ + if (bss) { + wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; + err = cfg80211_conn_do_work(wdev); + cfg80211_put_bss(bss); + } else { + /* otherwise we'll need to scan for the AP first */ + err = cfg80211_conn_scan(wdev); + /* + * If we can't scan right now, then we need to scan again + * after the current scan finished, since the parameters + * changed (unless we find a good AP anyway). + */ + if (err == -EBUSY) { + err = 0; + wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; + } + } + if (err) { + kfree(wdev->conn->ie); + kfree(wdev->conn); + wdev->conn = NULL; + wdev->sme_state = CFG80211_SME_IDLE; + wdev->connect_keys = NULL; + wdev->ssid_len = 0; + } + + return err; + } else { + wdev->sme_state = CFG80211_SME_CONNECTING; + wdev->connect_keys = connkeys; + err = rdev->ops->connect(&rdev->wiphy, dev, connect); + if (err) { + wdev->connect_keys = NULL; + wdev->sme_state = CFG80211_SME_IDLE; + return err; + } + + memcpy(wdev->ssid, connect->ssid, connect->ssid_len); + wdev->ssid_len = connect->ssid_len; + + return 0; + } +} + +int cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys) +{ + int err; + + mutex_lock(&rdev->devlist_mtx); + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL); + wdev_unlock(dev->ieee80211_ptr); + mutex_unlock(&rdev->devlist_mtx); + + return err; +} + +int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason, bool wextev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (wdev->sme_state == CFG80211_SME_IDLE) + return -EINVAL; + + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + + if (!rdev->ops->disconnect) { + if (!rdev->ops->deauth) + return -EOPNOTSUPP; + + /* was it connected by userspace SME? */ + if (!wdev->conn) { + cfg80211_mlme_down(rdev, dev); + return 0; + } + + if (wdev->sme_state == CFG80211_SME_CONNECTING && + (wdev->conn->state == CFG80211_CONN_SCANNING || + wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { + wdev->sme_state = CFG80211_SME_IDLE; + kfree(wdev->conn->ie); + kfree(wdev->conn); + wdev->conn = NULL; + wdev->ssid_len = 0; + return 0; + } + + /* wdev->conn->params.bssid must be set if > SCANNING */ + err = __cfg80211_mlme_deauth(rdev, dev, + wdev->conn->params.bssid, + NULL, 0, reason, false); + if (err) + return err; + } else { + err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); + if (err) + return err; + } + + if (wdev->sme_state == CFG80211_SME_CONNECTED) + __cfg80211_disconnected(dev, NULL, 0, 0, false); + else if (wdev->sme_state == CFG80211_SME_CONNECTING) + __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + wextev, NULL); + + return 0; +} + +int cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + u16 reason, bool wextev) +{ + int err; + + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_disconnect(rdev, dev, reason, wextev); + wdev_unlock(dev->ieee80211_ptr); + + return err; +} + +void cfg80211_sme_disassoc(struct net_device *dev, int idx) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + u8 bssid[ETH_ALEN]; + + ASSERT_WDEV_LOCK(wdev); + + if (!wdev->conn) + return; + + if (wdev->conn->state == CFG80211_CONN_IDLE) + return; + + /* + * Ok, so the association was made by this SME -- we don't + * want it any more so deauthenticate too. + */ + + if (!wdev->auth_bsses[idx]) + return; + + memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN); + if (__cfg80211_mlme_deauth(rdev, dev, bssid, + NULL, 0, WLAN_REASON_DEAUTH_LEAVING, + false)) { + /* whatever -- assume gone anyway */ + cfg80211_unhold_bss(wdev->auth_bsses[idx]); + cfg80211_put_bss(&wdev->auth_bsses[idx]->pub); + wdev->auth_bsses[idx] = NULL; + } +} diff --git a/net/wireless_ath/sysfs.c b/net/wireless_ath/sysfs.c new file mode 100644 index 00000000..2f8af6e2 --- /dev/null +++ b/net/wireless_ath/sysfs.c @@ -0,0 +1,157 @@ +/* + * This file provides /sys/class/ieee80211// + * and some default attributes. + * + * Copyright 2005-2006 Jiri Benc + * Copyright 2006 Johannes Berg + * + * This file is GPLv2 as found in COPYING. + */ + +#include +#include +#include +#include +#include +#include +#include "sysfs.h" +#include "core.h" + +static inline struct cfg80211_registered_device *dev_to_rdev( + struct device *dev) +{ + return container_of(dev, struct cfg80211_registered_device, wiphy.dev); +} + +#define SHOW_FMT(name, fmt, member) \ +static ssize_t name ## _show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \ +} + +SHOW_FMT(index, "%d", wiphy_idx); +SHOW_FMT(macaddress, "%pM", wiphy.perm_addr); +SHOW_FMT(address_mask, "%pM", wiphy.addr_mask); + +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy; + return sprintf(buf, "%s\n", dev_name(&wiphy->dev)); +} + + +static ssize_t addresses_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy; + char *start = buf; + int i; + + if (!wiphy->addresses) + return sprintf(buf, "%pM\n", wiphy->perm_addr); + + for (i = 0; i < wiphy->n_addresses; i++) + buf += sprintf(buf, "%pM\n", &wiphy->addresses[i].addr); + + return buf - start; +} + +static struct device_attribute ieee80211_dev_attrs[] = { + __ATTR_RO(index), + __ATTR_RO(macaddress), + __ATTR_RO(address_mask), + __ATTR_RO(addresses), + __ATTR_RO(name), + {} +}; + +static void wiphy_dev_release(struct device *dev) +{ + struct cfg80211_registered_device *rdev = dev_to_rdev(dev); + + cfg80211_dev_free(rdev); +} + +#ifdef CONFIG_HOTPLUG +static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + /* TODO, we probably need stuff here */ + return 0; +} +#endif + +static int wiphy_suspend(struct device *dev, pm_message_t state) +{ + struct cfg80211_registered_device *rdev = dev_to_rdev(dev); + int ret = 0; + + rdev->suspend_at = get_seconds(); + + if (rdev->ops->suspend) { + rtnl_lock(); + if (rdev->wiphy.registered) + ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan); + rtnl_unlock(); + } + + return ret; +} + +static int wiphy_resume(struct device *dev) +{ + struct cfg80211_registered_device *rdev = dev_to_rdev(dev); + int ret = 0; + + /* Age scan results with time spent in suspend */ + spin_lock_bh(&rdev->bss_lock); + cfg80211_bss_age(rdev, get_seconds() - rdev->suspend_at); + spin_unlock_bh(&rdev->bss_lock); + + if (rdev->ops->resume) { + rtnl_lock(); + if (rdev->wiphy.registered) + ret = rdev->ops->resume(&rdev->wiphy); + rtnl_unlock(); + } + + return ret; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) +static const void *wiphy_namespace(struct device *d) +{ + struct wiphy *wiphy = container_of(d, struct wiphy, dev); + + return wiphy_net(wiphy); +} +#endif + +struct class ieee80211_class = { + .name = "ieee80211", + .owner = THIS_MODULE, + .dev_release = wiphy_dev_release, + .dev_attrs = ieee80211_dev_attrs, +#ifdef CONFIG_HOTPLUG + .dev_uevent = wiphy_uevent, +#endif + .suspend = wiphy_suspend, + .resume = wiphy_resume, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) + .ns_type = &net_ns_type_operations, + .namespace = wiphy_namespace, +#endif +}; + +int wiphy_sysfs_init(void) +{ + return class_register(&ieee80211_class); +} + +void wiphy_sysfs_exit(void) +{ + class_unregister(&ieee80211_class); +} diff --git a/net/wireless_ath/sysfs.h b/net/wireless_ath/sysfs.h new file mode 100644 index 00000000..65acbebd --- /dev/null +++ b/net/wireless_ath/sysfs.h @@ -0,0 +1,9 @@ +#ifndef __WIRELESS_SYSFS_H +#define __WIRELESS_SYSFS_H + +extern int wiphy_sysfs_init(void); +extern void wiphy_sysfs_exit(void); + +extern struct class ieee80211_class; + +#endif /* __WIRELESS_SYSFS_H */ diff --git a/net/wireless_ath/util.c b/net/wireless_ath/util.c new file mode 100644 index 00000000..b4195101 --- /dev/null +++ b/net/wireless_ath/util.c @@ -0,0 +1,1219 @@ +/* + * Wireless utility functions + * + * Copyright 2007-2009 Johannes Berg + */ +#include +#include +#include +#include +#include +#include +#include +#include "core.h" + +struct ieee80211_rate * +ieee80211_get_response_rate(struct ieee80211_supported_band *sband, + u32 basic_rates, int bitrate) +{ + struct ieee80211_rate *result = &sband->bitrates[0]; + int i; + + for (i = 0; i < sband->n_bitrates; i++) { + if (!(basic_rates & BIT(i))) + continue; + if (sband->bitrates[i].bitrate > bitrate) + continue; + result = &sband->bitrates[i]; + } + + return result; +} +EXPORT_SYMBOL(ieee80211_get_response_rate); + +int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band) +{ + /* see 802.11 17.3.8.3.2 and Annex J + * there are overlapping channel numbers in 5GHz and 2GHz bands */ + if (band == IEEE80211_BAND_5GHZ) { + if (chan >= 182 && chan <= 196) + return 4000 + chan * 5; + else + return 5000 + chan * 5; + } else { /* IEEE80211_BAND_2GHZ */ + if (chan == 14) + return 2484; + else if (chan < 14) + return 2407 + chan * 5; + else + return 0; /* not supported */ + } +} +EXPORT_SYMBOL(ieee80211_channel_to_frequency); + +int ieee80211_frequency_to_channel(int freq) +{ + /* see 802.11 17.3.8.3.2 and Annex J */ + if (freq == 2484) + return 14; + else if (freq < 2484) + return (freq - 2407) / 5; + else if (freq >= 4910 && freq <= 4980) + return (freq - 4000) / 5; + else + return (freq - 5000) / 5; +} +EXPORT_SYMBOL(ieee80211_frequency_to_channel); + +struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy, + int freq) +{ + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + int i; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + sband = wiphy->bands[band]; + + if (!sband) + continue; + + for (i = 0; i < sband->n_channels; i++) { + if (sband->channels[i].center_freq == freq) + return &sband->channels[i]; + } + } + + return NULL; +} +EXPORT_SYMBOL(__ieee80211_get_channel); + +static void set_mandatory_flags_band(struct ieee80211_supported_band *sband, + enum ieee80211_band band) +{ + int i, want; + + switch (band) { + case IEEE80211_BAND_5GHZ: + want = 3; + for (i = 0; i < sband->n_bitrates; i++) { + if (sband->bitrates[i].bitrate == 60 || + sband->bitrates[i].bitrate == 120 || + sband->bitrates[i].bitrate == 240) { + sband->bitrates[i].flags |= + IEEE80211_RATE_MANDATORY_A; + want--; + } + } + WARN_ON(want); + break; + case IEEE80211_BAND_2GHZ: + want = 7; + for (i = 0; i < sband->n_bitrates; i++) { + if (sband->bitrates[i].bitrate == 10) { + sband->bitrates[i].flags |= + IEEE80211_RATE_MANDATORY_B | + IEEE80211_RATE_MANDATORY_G; + want--; + } + + if (sband->bitrates[i].bitrate == 20 || + sband->bitrates[i].bitrate == 55 || + sband->bitrates[i].bitrate == 110 || + sband->bitrates[i].bitrate == 60 || + sband->bitrates[i].bitrate == 120 || + sband->bitrates[i].bitrate == 240) { + sband->bitrates[i].flags |= + IEEE80211_RATE_MANDATORY_G; + want--; + } + + if (sband->bitrates[i].bitrate != 10 && + sband->bitrates[i].bitrate != 20 && + sband->bitrates[i].bitrate != 55 && + sband->bitrates[i].bitrate != 110) + sband->bitrates[i].flags |= + IEEE80211_RATE_ERP_G; + } + WARN_ON(want != 0 && want != 3 && want != 6); + break; + case IEEE80211_NUM_BANDS: + WARN_ON(1); + break; + } +} + +void ieee80211_set_bitrate_flags(struct wiphy *wiphy) +{ + enum ieee80211_band band; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + if (wiphy->bands[band]) + set_mandatory_flags_band(wiphy->bands[band], band); +} + +bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher) +{ + int i; + for (i = 0; i < wiphy->n_cipher_suites; i++) + if (cipher == wiphy->cipher_suites[i]) + return true; + return false; +} + +int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, + struct key_params *params, int key_idx, + bool pairwise, const u8 *mac_addr) +{ + if (key_idx > 5) + return -EINVAL; + + if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) + return -EINVAL; + + if (pairwise && !mac_addr) + return -EINVAL; + + /* + * Disallow pairwise keys with non-zero index unless it's WEP + * or a vendor specific cipher (because current deployments use + * pairwise WEP keys with non-zero indices and for vendor specific + * ciphers this should be validated in the driver or hardware level + * - but 802.11i clearly specifies to use zero) + */ + if (pairwise && key_idx && + ((params->cipher == WLAN_CIPHER_SUITE_TKIP) || + (params->cipher == WLAN_CIPHER_SUITE_CCMP) || + (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC))) + return -EINVAL; + + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + if (params->key_len != WLAN_KEY_LEN_WEP40) + return -EINVAL; + break; + case WLAN_CIPHER_SUITE_TKIP: + if (params->key_len != WLAN_KEY_LEN_TKIP) + return -EINVAL; + break; + case WLAN_CIPHER_SUITE_CCMP: + if (params->key_len != WLAN_KEY_LEN_CCMP) + return -EINVAL; + break; + case WLAN_CIPHER_SUITE_WEP104: + if (params->key_len != WLAN_KEY_LEN_WEP104) + return -EINVAL; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + if (params->key_len != WLAN_KEY_LEN_AES_CMAC) + return -EINVAL; + break; + default: + /* + * We don't know anything about this algorithm, + * allow using it -- but the driver must check + * all parameters! We still check below whether + * or not the driver supports this algorithm, + * of course. + */ + break; + } + + if (params->seq) { + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + /* These ciphers do not use key sequence */ + return -EINVAL; + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_AES_CMAC: + if (params->seq_len != 6) + return -EINVAL; + break; + } + } + + if (!cfg80211_supported_cipher_suite(&rdev->wiphy, params->cipher)) + return -EINVAL; + + return 0; +} + +/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ +/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ +const unsigned char rfc1042_header[] __aligned(2) = + { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; +EXPORT_SYMBOL(rfc1042_header); + +/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ +const unsigned char bridge_tunnel_header[] __aligned(2) = + { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; +EXPORT_SYMBOL(bridge_tunnel_header); + +unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc) +{ + unsigned int hdrlen = 24; + + if (ieee80211_is_data(fc)) { + if (ieee80211_has_a4(fc)) + hdrlen = 30; + if (ieee80211_is_data_qos(fc)) { + hdrlen += IEEE80211_QOS_CTL_LEN; + if (ieee80211_has_order(fc)) + hdrlen += IEEE80211_HT_CTL_LEN; + } + goto out; + } + + if (ieee80211_is_ctl(fc)) { + /* + * ACK and CTS are 10 bytes, all others 16. To see how + * to get this condition consider + * subtype mask: 0b0000000011110000 (0x00F0) + * ACK subtype: 0b0000000011010000 (0x00D0) + * CTS subtype: 0b0000000011000000 (0x00C0) + * bits that matter: ^^^ (0x00E0) + * value of those: 0b0000000011000000 (0x00C0) + */ + if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0)) + hdrlen = 10; + else + hdrlen = 16; + } +out: + return hdrlen; +} +EXPORT_SYMBOL(ieee80211_hdrlen); + +unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb) +{ + const struct ieee80211_hdr *hdr = + (const struct ieee80211_hdr *)skb->data; + unsigned int hdrlen; + + if (unlikely(skb->len < 10)) + return 0; + hdrlen = ieee80211_hdrlen(hdr->frame_control); + if (unlikely(hdrlen > skb->len)) + return 0; + return hdrlen; +} +EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb); + +static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) +{ + int ae = meshhdr->flags & MESH_FLAGS_AE; + /* 7.1.3.5a.2 */ + switch (ae) { + case 0: + return 6; + case MESH_FLAGS_AE_A4: + return 12; + case MESH_FLAGS_AE_A5_A6: + return 18; + case (MESH_FLAGS_AE_A4 | MESH_FLAGS_AE_A5_A6): + return 24; + default: + return 6; + } +} + +int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, + enum nl80211_iftype iftype) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + u16 hdrlen, ethertype; + u8 *payload; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN] __aligned(2); + + if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) + return -1; + + hdrlen = ieee80211_hdrlen(hdr->frame_control); + + /* convert IEEE 802.11 header + possible LLC headers into Ethernet + * header + * IEEE 802.11 address fields: + * ToDS FromDS Addr1 Addr2 Addr3 Addr4 + * 0 0 DA SA BSSID n/a + * 0 1 DA BSSID SA n/a + * 1 0 BSSID SA DA n/a + * 1 1 RA TA DA SA + */ + memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN); + memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN); + + switch (hdr->frame_control & + cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { + case cpu_to_le16(IEEE80211_FCTL_TODS): + if (unlikely(iftype != NL80211_IFTYPE_AP && + iftype != NL80211_IFTYPE_AP_VLAN && + iftype != NL80211_IFTYPE_P2P_GO)) + return -1; + break; + case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS): + if (unlikely(iftype != NL80211_IFTYPE_WDS && + iftype != NL80211_IFTYPE_MESH_POINT && + iftype != NL80211_IFTYPE_AP_VLAN && + iftype != NL80211_IFTYPE_STATION)) + return -1; + if (iftype == NL80211_IFTYPE_MESH_POINT) { + struct ieee80211s_hdr *meshdr = + (struct ieee80211s_hdr *) (skb->data + hdrlen); + /* make sure meshdr->flags is on the linear part */ + if (!pskb_may_pull(skb, hdrlen + 1)) + return -1; + if (meshdr->flags & MESH_FLAGS_AE_A5_A6) { + skb_copy_bits(skb, hdrlen + + offsetof(struct ieee80211s_hdr, eaddr1), + dst, ETH_ALEN); + skb_copy_bits(skb, hdrlen + + offsetof(struct ieee80211s_hdr, eaddr2), + src, ETH_ALEN); + } + hdrlen += ieee80211_get_mesh_hdrlen(meshdr); + } + break; + case cpu_to_le16(IEEE80211_FCTL_FROMDS): + if ((iftype != NL80211_IFTYPE_STATION && + iftype != NL80211_IFTYPE_P2P_CLIENT && + iftype != NL80211_IFTYPE_MESH_POINT) || + (is_multicast_ether_addr(dst) && + !compare_ether_addr(src, addr))) + return -1; + if (iftype == NL80211_IFTYPE_MESH_POINT) { + struct ieee80211s_hdr *meshdr = + (struct ieee80211s_hdr *) (skb->data + hdrlen); + /* make sure meshdr->flags is on the linear part */ + if (!pskb_may_pull(skb, hdrlen + 1)) + return -1; + if (meshdr->flags & MESH_FLAGS_AE_A4) + skb_copy_bits(skb, hdrlen + + offsetof(struct ieee80211s_hdr, eaddr1), + src, ETH_ALEN); + hdrlen += ieee80211_get_mesh_hdrlen(meshdr); + } + break; + case cpu_to_le16(0): + if (iftype != NL80211_IFTYPE_ADHOC && + iftype != NL80211_IFTYPE_STATION) + return -1; + break; + } + + if (!pskb_may_pull(skb, hdrlen + 8)) + return -1; + + payload = skb->data + hdrlen; + ethertype = (payload[6] << 8) | payload[7]; + + if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && + ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || + compare_ether_addr(payload, bridge_tunnel_header) == 0)) { + /* remove RFC1042 or Bridge-Tunnel encapsulation and + * replace EtherType */ + skb_pull(skb, hdrlen + 6); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } else { + struct ethhdr *ehdr; + __be16 len; + + skb_pull(skb, hdrlen); + len = htons(skb->len); + ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr)); + memcpy(ehdr->h_dest, dst, ETH_ALEN); + memcpy(ehdr->h_source, src, ETH_ALEN); + ehdr->h_proto = len; + } + return 0; +} +EXPORT_SYMBOL(ieee80211_data_to_8023); + +int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, + enum nl80211_iftype iftype, u8 *bssid, bool qos) +{ + struct ieee80211_hdr hdr; + u16 hdrlen, ethertype; + __le16 fc; + const u8 *encaps_data; + int encaps_len, skip_header_bytes; + int nh_pos, h_pos; + int head_need; + + if (unlikely(skb->len < ETH_HLEN)) + return -EINVAL; + + nh_pos = skb_network_header(skb) - skb->data; + h_pos = skb_transport_header(skb) - skb->data; + + /* convert Ethernet header to proper 802.11 header (based on + * operation mode) */ + ethertype = (skb->data[12] << 8) | skb->data[13]; + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); + + switch (iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_P2P_GO: + fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); + /* DA BSSID SA */ + memcpy(hdr.addr1, skb->data, ETH_ALEN); + memcpy(hdr.addr2, addr, ETH_ALEN); + memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); + hdrlen = 24; + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + fc |= cpu_to_le16(IEEE80211_FCTL_TODS); + /* BSSID SA DA */ + memcpy(hdr.addr1, bssid, ETH_ALEN); + memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + memcpy(hdr.addr3, skb->data, ETH_ALEN); + hdrlen = 24; + break; + case NL80211_IFTYPE_ADHOC: + /* DA SA BSSID */ + memcpy(hdr.addr1, skb->data, ETH_ALEN); + memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + memcpy(hdr.addr3, bssid, ETH_ALEN); + hdrlen = 24; + break; + default: + return -EOPNOTSUPP; + } + + if (qos) { + fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); + hdrlen += 2; + } + + hdr.frame_control = fc; + hdr.duration_id = 0; + hdr.seq_ctrl = 0; + + skip_header_bytes = ETH_HLEN; + if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) { + encaps_data = bridge_tunnel_header; + encaps_len = sizeof(bridge_tunnel_header); + skip_header_bytes -= 2; + } else if (ethertype > 0x600) { + encaps_data = rfc1042_header; + encaps_len = sizeof(rfc1042_header); + skip_header_bytes -= 2; + } else { + encaps_data = NULL; + encaps_len = 0; + } + + skb_pull(skb, skip_header_bytes); + nh_pos -= skip_header_bytes; + h_pos -= skip_header_bytes; + + head_need = hdrlen + encaps_len - skb_headroom(skb); + + if (head_need > 0 || skb_cloned(skb)) { + head_need = max(head_need, 0); + if (head_need) + skb_orphan(skb); + + if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC)) + return -ENOMEM; + + skb->truesize += head_need; + } + + if (encaps_data) { + memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len); + nh_pos += encaps_len; + h_pos += encaps_len; + } + + memcpy(skb_push(skb, hdrlen), &hdr, hdrlen); + + nh_pos += hdrlen; + h_pos += hdrlen; + + /* Update skb pointers to various headers since this modified frame + * is going to go through Linux networking code that may potentially + * need things like pointer to IP header. */ + skb_set_mac_header(skb, 0); + skb_set_network_header(skb, nh_pos); + skb_set_transport_header(skb, h_pos); + + return 0; +} +EXPORT_SYMBOL(ieee80211_data_from_8023); + + +void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, + const u8 *addr, enum nl80211_iftype iftype, + const unsigned int extra_headroom, + bool has_80211_header) +{ + struct sk_buff *frame = NULL; + u16 ethertype; + u8 *payload; + const struct ethhdr *eth; + int remaining, err; + u8 dst[ETH_ALEN], src[ETH_ALEN]; + + if (has_80211_header) { + err = ieee80211_data_to_8023(skb, addr, iftype); + if (err) + goto out; + + /* skip the wrapping header */ + eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); + if (!eth) + goto out; + } else { + eth = (struct ethhdr *) skb->data; + } + + while (skb != frame) { + u8 padding; + __be16 len = eth->h_proto; + unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len); + + remaining = skb->len; + memcpy(dst, eth->h_dest, ETH_ALEN); + memcpy(src, eth->h_source, ETH_ALEN); + + padding = (4 - subframe_len) & 0x3; + /* the last MSDU has no padding */ + if (subframe_len > remaining) + goto purge; + + skb_pull(skb, sizeof(struct ethhdr)); + /* reuse skb for the last subframe */ + if (remaining <= subframe_len + padding) + frame = skb; + else { + unsigned int hlen = ALIGN(extra_headroom, 4); + /* + * Allocate and reserve two bytes more for payload + * alignment since sizeof(struct ethhdr) is 14. + */ + frame = dev_alloc_skb(hlen + subframe_len + 2); + if (!frame) + goto purge; + + skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2); + memcpy(skb_put(frame, ntohs(len)), skb->data, + ntohs(len)); + + eth = (struct ethhdr *)skb_pull(skb, ntohs(len) + + padding); + if (!eth) { + dev_kfree_skb(frame); + goto purge; + } + } + + skb_reset_network_header(frame); + frame->dev = skb->dev; + frame->priority = skb->priority; + + payload = frame->data; + ethertype = (payload[6] << 8) | payload[7]; + + if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && + ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || + compare_ether_addr(payload, + bridge_tunnel_header) == 0)) { + /* remove RFC1042 or Bridge-Tunnel + * encapsulation and replace EtherType */ + skb_pull(frame, 6); + memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); + } else { + memcpy(skb_push(frame, sizeof(__be16)), &len, + sizeof(__be16)); + memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); + } + __skb_queue_tail(list, frame); + } + + return; + + purge: + __skb_queue_purge(list); + out: + dev_kfree_skb(skb); +} +EXPORT_SYMBOL(ieee80211_amsdu_to_8023s); + +/* Given a data frame determine the 802.1p/1d tag to use. */ +unsigned int cfg80211_classify8021d(struct sk_buff *skb) +{ + unsigned int dscp; + + /* skb->priority values from 256->263 are magic values to + * directly indicate a specific 802.1d priority. This is used + * to allow 802.1d priority to be passed directly in from VLAN + * tags, etc. + */ + if (skb->priority >= 256 && skb->priority <= 263) + return skb->priority - 256; + + switch (skb->protocol) { + case htons(ETH_P_IP): + dscp = ip_hdr(skb)->tos & 0xfc; + break; + default: + return 0; + } + + return dscp >> 5; +} +EXPORT_SYMBOL(cfg80211_classify8021d); + +const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) +{ + u8 *end, *pos; + + pos = bss->information_elements; + if (pos == NULL) + return NULL; + end = pos + bss->len_information_elements; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} +EXPORT_SYMBOL(ieee80211_bss_get_ie); + +void cfg80211_upload_connect_keys(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct net_device *dev = wdev->netdev; + int i; + + if (!wdev->connect_keys) + return; + + for (i = 0; i < 6; i++) { + if (!wdev->connect_keys->params[i].cipher) + continue; + if (rdev->ops->add_key(wdev->wiphy, dev, i, false, NULL, + &wdev->connect_keys->params[i])) { + netdev_err(dev, "failed to set key %d\n", i); + continue; + } + if (wdev->connect_keys->def == i) + if (rdev->ops->set_default_key(wdev->wiphy, dev, + i, true, true)) { + netdev_err(dev, "failed to set defkey %d\n", i); + continue; + } + if (wdev->connect_keys->defmgmt == i) + if (rdev->ops->set_default_mgmt_key(wdev->wiphy, dev, i)) + netdev_err(dev, "failed to set mgtdef %d\n", i); + } + + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; +} + +static void cfg80211_process_wdev_events(struct wireless_dev *wdev) +{ + struct cfg80211_event *ev; + unsigned long flags; + const u8 *bssid = NULL; + + spin_lock_irqsave(&wdev->event_lock, flags); + while (!list_empty(&wdev->event_list)) { + ev = list_first_entry(&wdev->event_list, + struct cfg80211_event, list); + list_del(&ev->list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + + wdev_lock(wdev); + switch (ev->type) { + case EVENT_CONNECT_RESULT: + if (!is_zero_ether_addr(ev->cr.bssid)) + bssid = ev->cr.bssid; + __cfg80211_connect_result( + wdev->netdev, bssid, + ev->cr.req_ie, ev->cr.req_ie_len, + ev->cr.resp_ie, ev->cr.resp_ie_len, + ev->cr.status, + ev->cr.status == WLAN_STATUS_SUCCESS, + NULL); + break; + case EVENT_ROAMED: + __cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie, + ev->rm.req_ie_len, ev->rm.resp_ie, + ev->rm.resp_ie_len); + break; + case EVENT_DISCONNECTED: + __cfg80211_disconnected(wdev->netdev, + ev->dc.ie, ev->dc.ie_len, + ev->dc.reason, true); + break; + case EVENT_IBSS_JOINED: + __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid); + break; + } + wdev_unlock(wdev); + + kfree(ev); + + spin_lock_irqsave(&wdev->event_lock, flags); + } + spin_unlock_irqrestore(&wdev->event_lock, flags); +} + +void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev) +{ + struct wireless_dev *wdev; + + ASSERT_RTNL(); + ASSERT_RDEV_LOCK(rdev); + + mutex_lock(&rdev->devlist_mtx); + + list_for_each_entry(wdev, &rdev->netdev_list, list) + cfg80211_process_wdev_events(wdev); + + mutex_unlock(&rdev->devlist_mtx); +} + +int cfg80211_change_iface(struct cfg80211_registered_device *rdev, + struct net_device *dev, enum nl80211_iftype ntype, + u32 *flags, struct vif_params *params) +{ + int err; + enum nl80211_iftype otype = dev->ieee80211_ptr->iftype; + + ASSERT_RDEV_LOCK(rdev); + + /* don't support changing VLANs, you just re-create them */ + if (otype == NL80211_IFTYPE_AP_VLAN) + return -EOPNOTSUPP; + + if (!rdev->ops->change_virtual_intf || + !(rdev->wiphy.interface_modes & (1 << ntype))) + return -EOPNOTSUPP; + + /* if it's part of a bridge, reject changing type to station/ibss */ + if (br_port_exists(dev) && + (ntype == NL80211_IFTYPE_ADHOC || + ntype == NL80211_IFTYPE_STATION || + ntype == NL80211_IFTYPE_P2P_CLIENT)) + return -EBUSY; + + if (ntype != otype) { + err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr, + ntype); + if (err) + return err; + + dev->ieee80211_ptr->use_4addr = false; + dev->ieee80211_ptr->mesh_id_up_len = 0; + + switch (otype) { + case NL80211_IFTYPE_ADHOC: + cfg80211_leave_ibss(rdev, dev, false); + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, true); + break; + case NL80211_IFTYPE_MESH_POINT: + /* mesh should be handled? */ + break; + default: + break; + } + + cfg80211_process_rdev_events(rdev); + } + + err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, + ntype, flags, params); + + WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype); + + if (!err && params && params->use_4addr != -1) + dev->ieee80211_ptr->use_4addr = params->use_4addr; + + if (!err) { + dev->priv_flags &= ~IFF_DONT_BRIDGE; + switch (ntype) { + case NL80211_IFTYPE_STATION: + if (dev->ieee80211_ptr->use_4addr) + break; + /* fall through */ + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_ADHOC: + dev->priv_flags |= IFF_DONT_BRIDGE; + break; + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MESH_POINT: + /* bridging OK */ + break; + case NL80211_IFTYPE_MONITOR: + /* monitor can't bridge anyway */ + break; + case NL80211_IFTYPE_UNSPECIFIED: + case NUM_NL80211_IFTYPES: + /* not happening */ + break; + } + } + + return err; +} + +u16 cfg80211_calculate_bitrate(struct rate_info *rate) +{ + int modulation, streams, bitrate; + + if (!(rate->flags & RATE_INFO_FLAGS_MCS)) + return rate->legacy; + + /* the formula below does only work for MCS values smaller than 32 */ + if (rate->mcs >= 32) + return 0; + + modulation = rate->mcs & 7; + streams = (rate->mcs >> 3) + 1; + + bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? + 13500000 : 6500000; + + if (modulation < 4) + bitrate *= (modulation + 1); + else if (modulation == 4) + bitrate *= (modulation + 2); + else + bitrate *= (modulation + 3); + + bitrate *= streams; + + if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) + bitrate = (bitrate / 9) * 10; + + /* do NOT round down here */ + return (bitrate + 50000) / 100000; +} + +int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, + u32 beacon_int) +{ + struct wireless_dev *wdev; + int res = 0; + + if (!beacon_int) + return -EINVAL; + + mutex_lock(&rdev->devlist_mtx); + + list_for_each_entry(wdev, &rdev->netdev_list, list) { + if (!wdev->beacon_interval) + continue; + if (wdev->beacon_interval != beacon_int) { + res = -EINVAL; + break; + } + } + + mutex_unlock(&rdev->devlist_mtx); + + return res; +} + +int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + enum nl80211_iftype iftype) +{ + struct wireless_dev *wdev_iter; + int num[NUM_NL80211_IFTYPES]; + int total = 1; + int i, j; + + ASSERT_RTNL(); + + /* Always allow software iftypes */ + if (rdev->wiphy.software_iftypes & BIT(iftype)) + return 0; + + /* + * Drivers will gradually all set this flag, until all + * have it we only enforce for those that set it. + */ + if (!(rdev->wiphy.flags & WIPHY_FLAG_ENFORCE_COMBINATIONS)) + return 0; + + memset(num, 0, sizeof(num)); + + num[iftype] = 1; + + mutex_lock(&rdev->devlist_mtx); + list_for_each_entry(wdev_iter, &rdev->netdev_list, list) { + if (wdev_iter == wdev) + continue; + if (!netif_running(wdev_iter->netdev)) + continue; + + if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) + continue; + + num[wdev_iter->iftype]++; + total++; + } + mutex_unlock(&rdev->devlist_mtx); + + for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { + const struct ieee80211_iface_combination *c; + struct ieee80211_iface_limit *limits; + + c = &rdev->wiphy.iface_combinations[i]; + + limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, + GFP_KERNEL); + if (!limits) + return -ENOMEM; + if (total > c->max_interfaces) + goto cont; + + for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { + if (rdev->wiphy.software_iftypes & BIT(iftype)) + continue; + for (j = 0; j < c->n_limits; j++) { + if (!(limits[j].types & iftype)) + continue; + if (limits[j].max < num[iftype]) + goto cont; + limits[j].max -= num[iftype]; + } + } + /* yay, it fits */ + kfree(limits); + return 0; + cont: + kfree(limits); + } + + return -EBUSY; +} + +int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, + const u8 *rates, unsigned int n_rates, + u32 *mask) +{ + int i, j; + + if (!sband) + return -EINVAL; + + if (n_rates == 0 || n_rates > NL80211_MAX_SUPP_RATES) + return -EINVAL; + + *mask = 0; + + for (i = 0; i < n_rates; i++) { + int rate = (rates[i] & 0x7f) * 5; + bool found = false; + + for (j = 0; j < sband->n_bitrates; j++) { + if (sband->bitrates[j].bitrate == rate) { + found = true; + *mask |= BIT(j); + break; + } + } + if (!found) + return -EINVAL; + } + + /* + * mask must have at least one bit set here since we + * didn't accept a 0-length rates array nor allowed + * entries in the array that didn't exist + */ + + return 0; +} + +u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, + struct ieee802_11_elems *elems, + u64 filter, u32 crc) +{ + size_t left = len; + u8 *pos = start; + bool calc_crc = filter != 0; + + memset(elems, 0, sizeof(*elems)); + elems->ie_start = start; + elems->total_len = len; + + while (left >= 2) { + u8 id, elen; + + id = *pos++; + elen = *pos++; + left -= 2; + + if (elen > left) + break; + + if (calc_crc && id < 64 && (filter & (1ULL << id))) + crc = crc32_be(crc, pos - 2, elen + 2); + + switch (id) { + case WLAN_EID_SSID: + elems->ssid = pos; + elems->ssid_len = elen; + break; + case WLAN_EID_SUPP_RATES: + elems->supp_rates = pos; + elems->supp_rates_len = elen; + break; + case WLAN_EID_FH_PARAMS: + elems->fh_params = pos; + elems->fh_params_len = elen; + break; + case WLAN_EID_DS_PARAMS: + elems->ds_params = pos; + elems->ds_params_len = elen; + break; + case WLAN_EID_CF_PARAMS: + elems->cf_params = pos; + elems->cf_params_len = elen; + break; + case WLAN_EID_TIM: + if (elen >= sizeof(struct ieee80211_tim_ie)) { + elems->tim = (void *)pos; + elems->tim_len = elen; + } + break; + case WLAN_EID_IBSS_PARAMS: + elems->ibss_params = pos; + elems->ibss_params_len = elen; + break; + case WLAN_EID_CHALLENGE: + elems->challenge = pos; + elems->challenge_len = elen; + break; + case WLAN_EID_VENDOR_SPECIFIC: + if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && + pos[2] == 0xf2) { + /* Microsoft OUI (00:50:F2) */ + + if (calc_crc) + crc = crc32_be(crc, pos - 2, elen + 2); + + if (pos[3] == 1) { + /* OUI Type 1 - WPA IE */ + elems->wpa = pos; + elems->wpa_len = elen; + } else if (elen >= 5 && pos[3] == 2) { + /* OUI Type 2 - WMM IE */ + if (pos[4] == 0) { + elems->wmm_info = pos; + elems->wmm_info_len = elen; + } else if (pos[4] == 1) { + elems->wmm_param = pos; + elems->wmm_param_len = elen; + } + } + } + break; + case WLAN_EID_RSN: + elems->rsn = pos; + elems->rsn_len = elen; + break; + case WLAN_EID_ERP_INFO: + elems->erp_info = pos; + elems->erp_info_len = elen; + break; + case WLAN_EID_EXT_SUPP_RATES: + elems->ext_supp_rates = pos; + elems->ext_supp_rates_len = elen; + break; + case WLAN_EID_HT_CAPABILITY: + if (elen >= sizeof(struct ieee80211_ht_cap)) + elems->ht_cap_elem = (void *)pos; + break; + case WLAN_EID_HT_INFORMATION: + if (elen >= sizeof(struct ieee80211_ht_info)) + elems->ht_info_elem = (void *)pos; + break; + case WLAN_EID_MESH_ID: + elems->mesh_id = pos; + elems->mesh_id_len = elen; + break; + case WLAN_EID_MESH_CONFIG: + if (elen >= sizeof(struct ieee80211_meshconf_ie)) + elems->mesh_config = (void *)pos; + break; + case WLAN_EID_PEER_MGMT: + elems->peering = pos; + elems->peering_len = elen; + break; + case WLAN_EID_PREQ: + elems->preq = pos; + elems->preq_len = elen; + break; + case WLAN_EID_PREP: + elems->prep = pos; + elems->prep_len = elen; + break; + case WLAN_EID_PERR: + elems->perr = pos; + elems->perr_len = elen; + break; + case WLAN_EID_RANN: + if (elen >= sizeof(struct ieee80211_rann_ie)) + elems->rann = (void *)pos; + break; + case WLAN_EID_CHANNEL_SWITCH: + elems->ch_switch_elem = pos; + elems->ch_switch_elem_len = elen; + break; + case WLAN_EID_QUIET: + if (!elems->quiet_elem) { + elems->quiet_elem = pos; + elems->quiet_elem_len = elen; + } + elems->num_of_quiet_elem++; + break; + case WLAN_EID_COUNTRY: + elems->country_elem = pos; + elems->country_elem_len = elen; + break; + case WLAN_EID_PWR_CONSTRAINT: + elems->pwr_constr_elem = pos; + elems->pwr_constr_elem_len = elen; + break; + case WLAN_EID_TIMEOUT_INTERVAL: + elems->timeout_int = pos; + elems->timeout_int_len = elen; + break; + default: + break; + } + + left -= elen; + pos += elen; + } + + return crc; +} +EXPORT_SYMBOL(ieee802_11_parse_elems_crc); diff --git a/net/wireless_ath/wext-compat.c b/net/wireless_ath/wext-compat.c new file mode 100644 index 00000000..3c24eb97 --- /dev/null +++ b/net/wireless_ath/wext-compat.c @@ -0,0 +1,1531 @@ +/* + * cfg80211 - wext compat code + * + * This is temporary code until all wireless functionality is migrated + * into cfg80211, when that happens all the exports here go away and + * we directly assign the wireless handlers of wireless interfaces. + * + * Copyright 2008-2009 Johannes Berg + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wext-compat.h" +#include "core.h" + +int cfg80211_wext_giwname(struct net_device *dev, + struct iw_request_info *info, + char *name, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct ieee80211_supported_band *sband; + bool is_ht = false, is_a = false, is_b = false, is_g = false; + + if (!wdev) + return -EOPNOTSUPP; + + sband = wdev->wiphy->bands[IEEE80211_BAND_5GHZ]; + if (sband) { + is_a = true; + is_ht |= sband->ht_cap.ht_supported; + } + + sband = wdev->wiphy->bands[IEEE80211_BAND_2GHZ]; + if (sband) { + int i; + /* Check for mandatory rates */ + for (i = 0; i < sband->n_bitrates; i++) { + if (sband->bitrates[i].bitrate == 10) + is_b = true; + if (sband->bitrates[i].bitrate == 60) + is_g = true; + } + is_ht |= sband->ht_cap.ht_supported; + } + + strcpy(name, "IEEE 802.11"); + if (is_a) + strcat(name, "a"); + if (is_b) + strcat(name, "b"); + if (is_g) + strcat(name, "g"); + if (is_ht) + strcat(name, "n"); + + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwname); + +int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, + u32 *mode, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev; + struct vif_params vifparams; + enum nl80211_iftype type; + int ret; + + rdev = wiphy_to_dev(wdev->wiphy); + + switch (*mode) { + case IW_MODE_INFRA: + type = NL80211_IFTYPE_STATION; + break; + case IW_MODE_ADHOC: + type = NL80211_IFTYPE_ADHOC; + break; + case IW_MODE_REPEAT: + type = NL80211_IFTYPE_WDS; + break; + case IW_MODE_MONITOR: + type = NL80211_IFTYPE_MONITOR; + break; + default: + return -EINVAL; + } + + if (type == wdev->iftype) + return 0; + + memset(&vifparams, 0, sizeof(vifparams)); + + cfg80211_lock_rdev(rdev); + ret = cfg80211_change_iface(rdev, dev, type, NULL, &vifparams); + cfg80211_unlock_rdev(rdev); + + return ret; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwmode); + +int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info, + u32 *mode, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (!wdev) + return -EOPNOTSUPP; + + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + *mode = IW_MODE_MASTER; + break; + case NL80211_IFTYPE_STATION: + *mode = IW_MODE_INFRA; + break; + case NL80211_IFTYPE_ADHOC: + *mode = IW_MODE_ADHOC; + break; + case NL80211_IFTYPE_MONITOR: + *mode = IW_MODE_MONITOR; + break; + case NL80211_IFTYPE_WDS: + *mode = IW_MODE_REPEAT; + break; + case NL80211_IFTYPE_AP_VLAN: + *mode = IW_MODE_SECOND; /* FIXME */ + break; + default: + *mode = IW_MODE_AUTO; + break; + } + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwmode); + + +int cfg80211_wext_giwrange(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct iw_range *range = (struct iw_range *) extra; + enum ieee80211_band band; + int i, c = 0; + + if (!wdev) + return -EOPNOTSUPP; + + data->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 21; + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->min_retry = 0; + range->max_retry = 255; + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + range->max_encoding_tokens = 4; + + range->max_qual.updated = IW_QUAL_NOISE_INVALID; + + switch (wdev->wiphy->signal_type) { + case CFG80211_SIGNAL_TYPE_NONE: + break; + case CFG80211_SIGNAL_TYPE_MBM: + range->max_qual.level = -110; + range->max_qual.qual = 70; + range->avg_qual.qual = 35; + range->max_qual.updated |= IW_QUAL_DBM; + range->max_qual.updated |= IW_QUAL_QUAL_UPDATED; + range->max_qual.updated |= IW_QUAL_LEVEL_UPDATED; + break; + case CFG80211_SIGNAL_TYPE_UNSPEC: + range->max_qual.level = 100; + range->max_qual.qual = 100; + range->avg_qual.qual = 50; + range->max_qual.updated |= IW_QUAL_QUAL_UPDATED; + range->max_qual.updated |= IW_QUAL_LEVEL_UPDATED; + break; + } + + range->avg_qual.level = range->max_qual.level / 2; + range->avg_qual.noise = range->max_qual.noise / 2; + range->avg_qual.updated = range->max_qual.updated; + + for (i = 0; i < wdev->wiphy->n_cipher_suites; i++) { + switch (wdev->wiphy->cipher_suites[i]) { + case WLAN_CIPHER_SUITE_TKIP: + range->enc_capa |= (IW_ENC_CAPA_CIPHER_TKIP | + IW_ENC_CAPA_WPA); + break; + + case WLAN_CIPHER_SUITE_CCMP: + range->enc_capa |= (IW_ENC_CAPA_CIPHER_CCMP | + IW_ENC_CAPA_WPA2); + break; + + case WLAN_CIPHER_SUITE_WEP40: + range->encoding_size[range->num_encoding_sizes++] = + WLAN_KEY_LEN_WEP40; + break; + + case WLAN_CIPHER_SUITE_WEP104: + range->encoding_size[range->num_encoding_sizes++] = + WLAN_KEY_LEN_WEP104; + break; + } + } + + for (band = 0; band < IEEE80211_NUM_BANDS; band ++) { + struct ieee80211_supported_band *sband; + + sband = wdev->wiphy->bands[band]; + + if (!sband) + continue; + + for (i = 0; i < sband->n_channels && c < IW_MAX_FREQUENCIES; i++) { + struct ieee80211_channel *chan = &sband->channels[i]; + + if (!(chan->flags & IEEE80211_CHAN_DISABLED)) { + range->freq[c].i = + ieee80211_frequency_to_channel( + chan->center_freq); + range->freq[c].m = chan->center_freq; + range->freq[c].e = 6; + c++; + } + } + } + range->num_channels = c; + range->num_frequency = c; + + IW_EVENT_CAPA_SET_KERNEL(range->event_capa); + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); + + if (wdev->wiphy->max_scan_ssids > 0) + range->scan_capa |= IW_SCAN_CAPA_ESSID; + + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange); + + +/** + * cfg80211_wext_freq - get wext frequency for non-"auto" + * @wiphy: the wiphy + * @freq: the wext freq encoding + * + * Returns a frequency, or a negative error code, or 0 for auto. + */ +int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq) +{ + /* + * Parse frequency - return 0 for auto and + * -EINVAL for impossible things. + */ + if (freq->e == 0) { + enum ieee80211_band band = IEEE80211_BAND_2GHZ; + if (freq->m < 0) + return 0; + if (freq->m > 14) + band = IEEE80211_BAND_5GHZ; + return ieee80211_channel_to_frequency(freq->m, band); + } else { + int i, div = 1000000; + for (i = 0; i < freq->e; i++) + div /= 10; + if (div <= 0) + return -EINVAL; + return freq->m / div; + } +} + +int cfg80211_wext_siwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + u32 orts = wdev->wiphy->rts_threshold; + int err; + + if (rts->disabled || !rts->fixed) + wdev->wiphy->rts_threshold = (u32) -1; + else if (rts->value < 0) + return -EINVAL; + else + wdev->wiphy->rts_threshold = rts->value; + + err = rdev->ops->set_wiphy_params(wdev->wiphy, + WIPHY_PARAM_RTS_THRESHOLD); + if (err) + wdev->wiphy->rts_threshold = orts; + + return err; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwrts); + +int cfg80211_wext_giwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + rts->value = wdev->wiphy->rts_threshold; + rts->disabled = rts->value == (u32) -1; + rts->fixed = 1; + + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwrts); + +int cfg80211_wext_siwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *frag, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + u32 ofrag = wdev->wiphy->frag_threshold; + int err; + + if (frag->disabled || !frag->fixed) + wdev->wiphy->frag_threshold = (u32) -1; + else if (frag->value < 256) + return -EINVAL; + else { + /* Fragment length must be even, so strip LSB. */ + wdev->wiphy->frag_threshold = frag->value & ~0x1; + } + + err = rdev->ops->set_wiphy_params(wdev->wiphy, + WIPHY_PARAM_FRAG_THRESHOLD); + if (err) + wdev->wiphy->frag_threshold = ofrag; + + return err; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwfrag); + +int cfg80211_wext_giwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *frag, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + frag->value = wdev->wiphy->frag_threshold; + frag->disabled = frag->value == (u32) -1; + frag->fixed = 1; + + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwfrag); + +static int cfg80211_wext_siwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *retry, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + u32 changed = 0; + u8 olong = wdev->wiphy->retry_long; + u8 oshort = wdev->wiphy->retry_short; + int err; + + if (retry->disabled || + (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) + return -EINVAL; + + if (retry->flags & IW_RETRY_LONG) { + wdev->wiphy->retry_long = retry->value; + changed |= WIPHY_PARAM_RETRY_LONG; + } else if (retry->flags & IW_RETRY_SHORT) { + wdev->wiphy->retry_short = retry->value; + changed |= WIPHY_PARAM_RETRY_SHORT; + } else { + wdev->wiphy->retry_short = retry->value; + wdev->wiphy->retry_long = retry->value; + changed |= WIPHY_PARAM_RETRY_LONG; + changed |= WIPHY_PARAM_RETRY_SHORT; + } + + if (!changed) + return 0; + + err = rdev->ops->set_wiphy_params(wdev->wiphy, changed); + if (err) { + wdev->wiphy->retry_short = oshort; + wdev->wiphy->retry_long = olong; + } + + return err; +} + +int cfg80211_wext_giwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *retry, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + retry->disabled = 0; + + if (retry->flags == 0 || (retry->flags & IW_RETRY_SHORT)) { + /* + * First return short value, iwconfig will ask long value + * later if needed + */ + retry->flags |= IW_RETRY_LIMIT; + retry->value = wdev->wiphy->retry_short; + if (wdev->wiphy->retry_long != wdev->wiphy->retry_short) + retry->flags |= IW_RETRY_LONG; + + return 0; + } + + if (retry->flags & IW_RETRY_LONG) { + retry->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; + retry->value = wdev->wiphy->retry_long; + } + + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwretry); + +static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool pairwise, + const u8 *addr, bool remove, bool tx_key, + int idx, struct key_params *params) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err, i; + bool rejoin = false; + + if (pairwise && !addr) + return -EINVAL; + + if (!wdev->wext.keys) { + wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys), + GFP_KERNEL); + if (!wdev->wext.keys) + return -ENOMEM; + for (i = 0; i < 6; i++) + wdev->wext.keys->params[i].key = + wdev->wext.keys->data[i]; + } + + if (wdev->iftype != NL80211_IFTYPE_ADHOC && + wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + if (!wdev->current_bss) + return -ENOLINK; + + if (!rdev->ops->set_default_mgmt_key) + return -EOPNOTSUPP; + + if (idx < 4 || idx > 5) + return -EINVAL; + } else if (idx < 0 || idx > 3) + return -EINVAL; + + if (remove) { + err = 0; + if (wdev->current_bss) { + /* + * If removing the current TX key, we will need to + * join a new IBSS without the privacy bit clear. + */ + if (idx == wdev->wext.default_key && + wdev->iftype == NL80211_IFTYPE_ADHOC) { + __cfg80211_leave_ibss(rdev, wdev->netdev, true); + rejoin = true; + } + + if (!pairwise && addr && + !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) + err = -ENOENT; + else + err = rdev->ops->del_key(&rdev->wiphy, dev, idx, + pairwise, addr); + } + wdev->wext.connect.privacy = false; + /* + * Applications using wireless extensions expect to be + * able to delete keys that don't exist, so allow that. + */ + if (err == -ENOENT) + err = 0; + if (!err) { + if (!addr) { + wdev->wext.keys->params[idx].key_len = 0; + wdev->wext.keys->params[idx].cipher = 0; + } + if (idx == wdev->wext.default_key) + wdev->wext.default_key = -1; + else if (idx == wdev->wext.default_mgmt_key) + wdev->wext.default_mgmt_key = -1; + } + + if (!err && rejoin) + err = cfg80211_ibss_wext_join(rdev, wdev); + + return err; + } + + if (addr) + tx_key = false; + + if (cfg80211_validate_key_settings(rdev, params, idx, pairwise, addr)) + return -EINVAL; + + err = 0; + if (wdev->current_bss) + err = rdev->ops->add_key(&rdev->wiphy, dev, idx, + pairwise, addr, params); + if (err) + return err; + + if (!addr) { + wdev->wext.keys->params[idx] = *params; + memcpy(wdev->wext.keys->data[idx], + params->key, params->key_len); + wdev->wext.keys->params[idx].key = + wdev->wext.keys->data[idx]; + } + + if ((params->cipher == WLAN_CIPHER_SUITE_WEP40 || + params->cipher == WLAN_CIPHER_SUITE_WEP104) && + (tx_key || (!addr && wdev->wext.default_key == -1))) { + if (wdev->current_bss) { + /* + * If we are getting a new TX key from not having + * had one before we need to join a new IBSS with + * the privacy bit set. + */ + if (wdev->iftype == NL80211_IFTYPE_ADHOC && + wdev->wext.default_key == -1) { + __cfg80211_leave_ibss(rdev, wdev->netdev, true); + rejoin = true; + } + err = rdev->ops->set_default_key(&rdev->wiphy, dev, + idx, true, true); + } + if (!err) { + wdev->wext.default_key = idx; + if (rejoin) + err = cfg80211_ibss_wext_join(rdev, wdev); + } + return err; + } + + if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC && + (tx_key || (!addr && wdev->wext.default_mgmt_key == -1))) { + if (wdev->current_bss) + err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, + dev, idx); + if (!err) + wdev->wext.default_mgmt_key = idx; + return err; + } + + return 0; +} + +static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool pairwise, + const u8 *addr, bool remove, bool tx_key, + int idx, struct key_params *params) +{ + int err; + + /* devlist mutex needed for possible IBSS re-join */ + mutex_lock(&rdev->devlist_mtx); + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_set_encryption(rdev, dev, pairwise, addr, + remove, tx_key, idx, params); + wdev_unlock(dev->ieee80211_ptr); + mutex_unlock(&rdev->devlist_mtx); + + return err; +} + +static int cfg80211_wext_siwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *keybuf) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + int idx, err; + bool remove = false; + struct key_params params; + + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_ADHOC) + return -EOPNOTSUPP; + + /* no use -- only MFP (set_default_mgmt_key) is optional */ + if (!rdev->ops->del_key || + !rdev->ops->add_key || + !rdev->ops->set_default_key) + return -EOPNOTSUPP; + + idx = erq->flags & IW_ENCODE_INDEX; + if (idx == 0) { + idx = wdev->wext.default_key; + if (idx < 0) + idx = 0; + } else if (idx < 1 || idx > 4) + return -EINVAL; + else + idx--; + + if (erq->flags & IW_ENCODE_DISABLED) + remove = true; + else if (erq->length == 0) { + /* No key data - just set the default TX key index */ + err = 0; + wdev_lock(wdev); + if (wdev->current_bss) + err = rdev->ops->set_default_key(&rdev->wiphy, dev, + idx, true, true); + if (!err) + wdev->wext.default_key = idx; + wdev_unlock(wdev); + return err; + } + + memset(¶ms, 0, sizeof(params)); + params.key = keybuf; + params.key_len = erq->length; + if (erq->length == 5) + params.cipher = WLAN_CIPHER_SUITE_WEP40; + else if (erq->length == 13) + params.cipher = WLAN_CIPHER_SUITE_WEP104; + else if (!remove) + return -EINVAL; + + return cfg80211_set_encryption(rdev, dev, false, NULL, remove, + wdev->wext.default_key == -1, + idx, ¶ms); +} + +static int cfg80211_wext_siwencodeext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; + const u8 *addr; + int idx; + bool remove = false; + struct key_params params; + u32 cipher; + + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_ADHOC) + return -EOPNOTSUPP; + + /* no use -- only MFP (set_default_mgmt_key) is optional */ + if (!rdev->ops->del_key || + !rdev->ops->add_key || + !rdev->ops->set_default_key) + return -EOPNOTSUPP; + + switch (ext->alg) { + case IW_ENCODE_ALG_NONE: + remove = true; + cipher = 0; + break; + case IW_ENCODE_ALG_WEP: + if (ext->key_len == 5) + cipher = WLAN_CIPHER_SUITE_WEP40; + else if (ext->key_len == 13) + cipher = WLAN_CIPHER_SUITE_WEP104; + else + return -EINVAL; + break; + case IW_ENCODE_ALG_TKIP: + cipher = WLAN_CIPHER_SUITE_TKIP; + break; + case IW_ENCODE_ALG_CCMP: + cipher = WLAN_CIPHER_SUITE_CCMP; + break; + case IW_ENCODE_ALG_AES_CMAC: + cipher = WLAN_CIPHER_SUITE_AES_CMAC; + break; + default: + return -EOPNOTSUPP; + } + + if (erq->flags & IW_ENCODE_DISABLED) + remove = true; + + idx = erq->flags & IW_ENCODE_INDEX; + if (cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + if (idx < 4 || idx > 5) { + idx = wdev->wext.default_mgmt_key; + if (idx < 0) + return -EINVAL; + } else + idx--; + } else { + if (idx < 1 || idx > 4) { + idx = wdev->wext.default_key; + if (idx < 0) + return -EINVAL; + } else + idx--; + } + + addr = ext->addr.sa_data; + if (is_broadcast_ether_addr(addr)) + addr = NULL; + + memset(¶ms, 0, sizeof(params)); + params.key = ext->key; + params.key_len = ext->key_len; + params.cipher = cipher; + + if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { + params.seq = ext->rx_seq; + params.seq_len = 6; + } + + return cfg80211_set_encryption( + rdev, dev, + !(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY), + addr, remove, + ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, + idx, ¶ms); +} + +static int cfg80211_wext_giwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *keybuf) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int idx; + + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_ADHOC) + return -EOPNOTSUPP; + + idx = erq->flags & IW_ENCODE_INDEX; + if (idx == 0) { + idx = wdev->wext.default_key; + if (idx < 0) + idx = 0; + } else if (idx < 1 || idx > 4) + return -EINVAL; + else + idx--; + + erq->flags = idx + 1; + + if (!wdev->wext.keys || !wdev->wext.keys->params[idx].cipher) { + erq->flags |= IW_ENCODE_DISABLED; + erq->length = 0; + return 0; + } + + erq->length = min_t(size_t, erq->length, + wdev->wext.keys->params[idx].key_len); + memcpy(keybuf, wdev->wext.keys->params[idx].key, erq->length); + erq->flags |= IW_ENCODE_ENABLED; + + return 0; +} + +static int cfg80211_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *wextfreq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + int freq, err; + + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra); + case NL80211_IFTYPE_ADHOC: + return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra); + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MESH_POINT: + freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); + if (freq < 0) + return freq; + if (freq == 0) + return -EINVAL; + mutex_lock(&rdev->devlist_mtx); + wdev_lock(wdev); + err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); + wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); + return err; + default: + return -EOPNOTSUPP; + } +} + +static int cfg80211_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct ieee80211_channel *chan; + + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra); + case NL80211_IFTYPE_ADHOC: + return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); + case NL80211_IFTYPE_MONITOR: + if (!rdev->ops->get_channel) + return -EINVAL; + + chan = rdev->ops->get_channel(wdev->wiphy); + if (!chan) + return -EINVAL; + freq->m = chan->center_freq; + freq->e = 6; + return 0; + default: + if (!wdev->channel) + return -EINVAL; + freq->m = wdev->channel->center_freq; + freq->e = 6; + return 0; + } +} + +static int cfg80211_wext_siwtxpower(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + enum nl80211_tx_power_setting type; + int dbm = 0; + + if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) + return -EINVAL; + if (data->txpower.flags & IW_TXPOW_RANGE) + return -EINVAL; + + if (!rdev->ops->set_tx_power) + return -EOPNOTSUPP; + + /* only change when not disabling */ + if (!data->txpower.disabled) { + rfkill_set_sw_state(rdev->rfkill, false); + + if (data->txpower.fixed) { + /* + * wext doesn't support negative values, see + * below where it's for automatic + */ + if (data->txpower.value < 0) + return -EINVAL; + dbm = data->txpower.value; + type = NL80211_TX_POWER_FIXED; + /* TODO: do regulatory check! */ + } else { + /* + * Automatic power level setting, max being the value + * passed in from userland. + */ + if (data->txpower.value < 0) { + type = NL80211_TX_POWER_AUTOMATIC; + } else { + dbm = data->txpower.value; + type = NL80211_TX_POWER_LIMITED; + } + } + } else { + rfkill_set_sw_state(rdev->rfkill, true); + schedule_work(&rdev->rfkill_sync); + return 0; + } + + return rdev->ops->set_tx_power(wdev->wiphy, type, DBM_TO_MBM(dbm)); +} + +static int cfg80211_wext_giwtxpower(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + int err, val; + + if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) + return -EINVAL; + if (data->txpower.flags & IW_TXPOW_RANGE) + return -EINVAL; + + if (!rdev->ops->get_tx_power) + return -EOPNOTSUPP; + + err = rdev->ops->get_tx_power(wdev->wiphy, &val); + if (err) + return err; + + /* well... oh well */ + data->txpower.fixed = 1; + data->txpower.disabled = rfkill_blocked(rdev->rfkill); + data->txpower.value = val; + data->txpower.flags = IW_TXPOW_DBM; + + return 0; +} + +static int cfg80211_set_auth_alg(struct wireless_dev *wdev, + s32 auth_alg) +{ + int nr_alg = 0; + + if (!auth_alg) + return -EINVAL; + + if (auth_alg & ~(IW_AUTH_ALG_OPEN_SYSTEM | + IW_AUTH_ALG_SHARED_KEY | + IW_AUTH_ALG_LEAP)) + return -EINVAL; + + if (auth_alg & IW_AUTH_ALG_OPEN_SYSTEM) { + nr_alg++; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; + } + + if (auth_alg & IW_AUTH_ALG_SHARED_KEY) { + nr_alg++; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_SHARED_KEY; + } + + if (auth_alg & IW_AUTH_ALG_LEAP) { + nr_alg++; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_NETWORK_EAP; + } + + if (nr_alg > 1) + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; + + return 0; +} + +static int cfg80211_set_wpa_version(struct wireless_dev *wdev, u32 wpa_versions) +{ + if (wpa_versions & ~(IW_AUTH_WPA_VERSION_WPA | + IW_AUTH_WPA_VERSION_WPA2| + IW_AUTH_WPA_VERSION_DISABLED)) + return -EINVAL; + + if ((wpa_versions & IW_AUTH_WPA_VERSION_DISABLED) && + (wpa_versions & (IW_AUTH_WPA_VERSION_WPA| + IW_AUTH_WPA_VERSION_WPA2))) + return -EINVAL; + + if (wpa_versions & IW_AUTH_WPA_VERSION_DISABLED) + wdev->wext.connect.crypto.wpa_versions &= + ~(NL80211_WPA_VERSION_1|NL80211_WPA_VERSION_2); + + if (wpa_versions & IW_AUTH_WPA_VERSION_WPA) + wdev->wext.connect.crypto.wpa_versions |= + NL80211_WPA_VERSION_1; + + if (wpa_versions & IW_AUTH_WPA_VERSION_WPA2) + wdev->wext.connect.crypto.wpa_versions |= + NL80211_WPA_VERSION_2; + + return 0; +} + +static int cfg80211_set_cipher_group(struct wireless_dev *wdev, u32 cipher) +{ + if (cipher & IW_AUTH_CIPHER_WEP40) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_WEP40; + else if (cipher & IW_AUTH_CIPHER_WEP104) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_WEP104; + else if (cipher & IW_AUTH_CIPHER_TKIP) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_TKIP; + else if (cipher & IW_AUTH_CIPHER_CCMP) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_CCMP; + else if (cipher & IW_AUTH_CIPHER_AES_CMAC) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_AES_CMAC; + else if (cipher & IW_AUTH_CIPHER_NONE) + wdev->wext.connect.crypto.cipher_group = 0; + else + return -EINVAL; + + return 0; +} + +static int cfg80211_set_cipher_pairwise(struct wireless_dev *wdev, u32 cipher) +{ + int nr_ciphers = 0; + u32 *ciphers_pairwise = wdev->wext.connect.crypto.ciphers_pairwise; + + if (cipher & IW_AUTH_CIPHER_WEP40) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP40; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_WEP104) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP104; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_TKIP) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_TKIP; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_CCMP) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_CCMP; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_AES_CMAC) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_AES_CMAC; + nr_ciphers++; + } + + BUILD_BUG_ON(NL80211_MAX_NR_CIPHER_SUITES < 5); + + wdev->wext.connect.crypto.n_ciphers_pairwise = nr_ciphers; + + return 0; +} + + +static int cfg80211_set_key_mgt(struct wireless_dev *wdev, u32 key_mgt) +{ + int nr_akm_suites = 0; + + if (key_mgt & ~(IW_AUTH_KEY_MGMT_802_1X | + IW_AUTH_KEY_MGMT_PSK)) + return -EINVAL; + + if (key_mgt & IW_AUTH_KEY_MGMT_802_1X) { + wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = + WLAN_AKM_SUITE_8021X; + nr_akm_suites++; + } + + if (key_mgt & IW_AUTH_KEY_MGMT_PSK) { + wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = + WLAN_AKM_SUITE_PSK; + nr_akm_suites++; + } + + wdev->wext.connect.crypto.n_akm_suites = nr_akm_suites; + + return 0; +} + +static int cfg80211_wext_siwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + switch (data->flags & IW_AUTH_INDEX) { + case IW_AUTH_PRIVACY_INVOKED: + wdev->wext.connect.privacy = data->value; + return 0; + case IW_AUTH_WPA_VERSION: + return cfg80211_set_wpa_version(wdev, data->value); + case IW_AUTH_CIPHER_GROUP: + return cfg80211_set_cipher_group(wdev, data->value); + case IW_AUTH_KEY_MGMT: + return cfg80211_set_key_mgt(wdev, data->value); + case IW_AUTH_CIPHER_PAIRWISE: + return cfg80211_set_cipher_pairwise(wdev, data->value); + case IW_AUTH_80211_AUTH_ALG: + return cfg80211_set_auth_alg(wdev, data->value); + case IW_AUTH_WPA_ENABLED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_DROP_UNENCRYPTED: + case IW_AUTH_MFP: + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int cfg80211_wext_giwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + /* XXX: what do we need? */ + + return -EOPNOTSUPP; +} + +static int cfg80211_wext_siwpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *wrq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + bool ps = wdev->ps; + int timeout = wdev->ps_timeout; + int err; + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EINVAL; + + if (!rdev->ops->set_power_mgmt) + return -EOPNOTSUPP; + + if (wrq->disabled) { + ps = false; + } else { + switch (wrq->flags & IW_POWER_MODE) { + case IW_POWER_ON: /* If not specified */ + case IW_POWER_MODE: /* If set all mask */ + case IW_POWER_ALL_R: /* If explicitely state all */ + ps = true; + break; + default: /* Otherwise we ignore */ + return -EINVAL; + } + + if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT)) + return -EINVAL; + + if (wrq->flags & IW_POWER_TIMEOUT) + timeout = wrq->value / 1000; + } + + err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, ps, timeout); + if (err) + return err; + + wdev->ps = ps; + wdev->ps_timeout = timeout; + + return 0; + +} + +static int cfg80211_wext_giwpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *wrq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + wrq->disabled = !wdev->ps; + + return 0; +} + +static int cfg80211_wds_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + int err; + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS)) + return -EINVAL; + + if (addr->sa_family != ARPHRD_ETHER) + return -EINVAL; + + if (netif_running(dev)) + return -EBUSY; + + if (!rdev->ops->set_wds_peer) + return -EOPNOTSUPP; + + err = rdev->ops->set_wds_peer(wdev->wiphy, dev, (u8 *) &addr->sa_data); + if (err) + return err; + + memcpy(&wdev->wext.bssid, (u8 *) &addr->sa_data, ETH_ALEN); + + return 0; +} + +static int cfg80211_wds_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS)) + return -EINVAL; + + addr->sa_family = ARPHRD_ETHER; + memcpy(&addr->sa_data, wdev->wext.bssid, ETH_ALEN); + + return 0; +} + +static int cfg80211_wext_siwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rate, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_bitrate_mask mask; + u32 fixed, maxrate; + struct ieee80211_supported_band *sband; + int band, ridx; + bool match = false; + + if (!rdev->ops->set_bitrate_mask) + return -EOPNOTSUPP; + + memset(&mask, 0, sizeof(mask)); + fixed = 0; + maxrate = (u32)-1; + + if (rate->value < 0) { + /* nothing */ + } else if (rate->fixed) { + fixed = rate->value / 100000; + } else { + maxrate = rate->value / 100000; + } + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + sband = wdev->wiphy->bands[band]; + if (sband == NULL) + continue; + for (ridx = 0; ridx < sband->n_bitrates; ridx++) { + struct ieee80211_rate *srate = &sband->bitrates[ridx]; + if (fixed == srate->bitrate) { + mask.control[band].legacy = 1 << ridx; + match = true; + break; + } + if (srate->bitrate <= maxrate) { + mask.control[band].legacy |= 1 << ridx; + match = true; + } + } + } + + if (!match) + return -EINVAL; + + return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask); +} + +static int cfg80211_wext_giwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rate, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + /* we are under RTNL - globally locked - so can use a static struct */ + static struct station_info sinfo; + u8 addr[ETH_ALEN]; + int err; + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + if (!rdev->ops->get_station) + return -EOPNOTSUPP; + + err = 0; + wdev_lock(wdev); + if (wdev->current_bss) + memcpy(addr, wdev->current_bss->pub.bssid, ETH_ALEN); + else + err = -EOPNOTSUPP; + wdev_unlock(wdev); + if (err) + return err; + + err = rdev->ops->get_station(&rdev->wiphy, dev, addr, &sinfo); + if (err) + return err; + + if (!(sinfo.filled & STATION_INFO_TX_BITRATE)) + return -EOPNOTSUPP; + + rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); + + return 0; +} + +/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ +static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + /* we are under RTNL - globally locked - so can use static structs */ + static struct iw_statistics wstats; + static struct station_info sinfo; + u8 bssid[ETH_ALEN]; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) + return NULL; + + if (!rdev->ops->get_station) + return NULL; + + /* Grab BSSID of current BSS, if any */ + wdev_lock(wdev); + if (!wdev->current_bss) { + wdev_unlock(wdev); + return NULL; + } + memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN); + wdev_unlock(wdev); + + if (rdev->ops->get_station(&rdev->wiphy, dev, bssid, &sinfo)) + return NULL; + + memset(&wstats, 0, sizeof(wstats)); + + switch (rdev->wiphy.signal_type) { + case CFG80211_SIGNAL_TYPE_MBM: + if (sinfo.filled & STATION_INFO_SIGNAL) { + int sig = sinfo.signal; + wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; + wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; + wstats.qual.updated |= IW_QUAL_DBM; + wstats.qual.level = sig; + if (sig < -110) + sig = -110; + else if (sig > -40) + sig = -40; + wstats.qual.qual = sig + 110; + break; + } + case CFG80211_SIGNAL_TYPE_UNSPEC: + if (sinfo.filled & STATION_INFO_SIGNAL) { + wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; + wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; + wstats.qual.level = sinfo.signal; + wstats.qual.qual = sinfo.signal; + break; + } + default: + wstats.qual.updated |= IW_QUAL_LEVEL_INVALID; + wstats.qual.updated |= IW_QUAL_QUAL_INVALID; + } + + wstats.qual.updated |= IW_QUAL_NOISE_INVALID; + if (sinfo.filled & STATION_INFO_RX_DROP_MISC) + wstats.discard.misc = sinfo.rx_dropped_misc; + if (sinfo.filled & STATION_INFO_TX_FAILED) + wstats.discard.retries = sinfo.tx_failed; + + return &wstats; +} + +static int cfg80211_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra); + case NL80211_IFTYPE_STATION: + return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra); + case NL80211_IFTYPE_WDS: + return cfg80211_wds_wext_siwap(dev, info, ap_addr, extra); + default: + return -EOPNOTSUPP; + } +} + +static int cfg80211_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra); + case NL80211_IFTYPE_STATION: + return cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra); + case NL80211_IFTYPE_WDS: + return cfg80211_wds_wext_giwap(dev, info, ap_addr, extra); + default: + return -EOPNOTSUPP; + } +} + +static int cfg80211_wext_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); + case NL80211_IFTYPE_STATION: + return cfg80211_mgd_wext_siwessid(dev, info, data, ssid); + default: + return -EOPNOTSUPP; + } +} + +static int cfg80211_wext_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + data->flags = 0; + data->length = 0; + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + return cfg80211_ibss_wext_giwessid(dev, info, data, ssid); + case NL80211_IFTYPE_STATION: + return cfg80211_mgd_wext_giwessid(dev, info, data, ssid); + default: + return -EOPNOTSUPP; + } +} + +static int cfg80211_wext_siwpmksa(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_pmksa cfg_pmksa; + struct iw_pmksa *pmksa = (struct iw_pmksa *)extra; + + memset(&cfg_pmksa, 0, sizeof(struct cfg80211_pmksa)); + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EINVAL; + + cfg_pmksa.bssid = pmksa->bssid.sa_data; + cfg_pmksa.pmkid = pmksa->pmkid; + + switch (pmksa->cmd) { + case IW_PMKSA_ADD: + if (!rdev->ops->set_pmksa) + return -EOPNOTSUPP; + + return rdev->ops->set_pmksa(&rdev->wiphy, dev, &cfg_pmksa); + + case IW_PMKSA_REMOVE: + if (!rdev->ops->del_pmksa) + return -EOPNOTSUPP; + + return rdev->ops->del_pmksa(&rdev->wiphy, dev, &cfg_pmksa); + + case IW_PMKSA_FLUSH: + if (!rdev->ops->flush_pmksa) + return -EOPNOTSUPP; + + return rdev->ops->flush_pmksa(&rdev->wiphy, dev); + + default: + return -EOPNOTSUPP; + } +} + +static const iw_handler cfg80211_handlers[] = { + [IW_IOCTL_IDX(SIOCGIWNAME)] = (iw_handler) cfg80211_wext_giwname, + [IW_IOCTL_IDX(SIOCSIWFREQ)] = (iw_handler) cfg80211_wext_siwfreq, + [IW_IOCTL_IDX(SIOCGIWFREQ)] = (iw_handler) cfg80211_wext_giwfreq, + [IW_IOCTL_IDX(SIOCSIWMODE)] = (iw_handler) cfg80211_wext_siwmode, + [IW_IOCTL_IDX(SIOCGIWMODE)] = (iw_handler) cfg80211_wext_giwmode, + [IW_IOCTL_IDX(SIOCGIWRANGE)] = (iw_handler) cfg80211_wext_giwrange, + [IW_IOCTL_IDX(SIOCSIWAP)] = (iw_handler) cfg80211_wext_siwap, + [IW_IOCTL_IDX(SIOCGIWAP)] = (iw_handler) cfg80211_wext_giwap, + [IW_IOCTL_IDX(SIOCSIWMLME)] = (iw_handler) cfg80211_wext_siwmlme, + [IW_IOCTL_IDX(SIOCSIWSCAN)] = (iw_handler) cfg80211_wext_siwscan, + [IW_IOCTL_IDX(SIOCGIWSCAN)] = (iw_handler) cfg80211_wext_giwscan, + [IW_IOCTL_IDX(SIOCSIWESSID)] = (iw_handler) cfg80211_wext_siwessid, + [IW_IOCTL_IDX(SIOCGIWESSID)] = (iw_handler) cfg80211_wext_giwessid, + [IW_IOCTL_IDX(SIOCSIWRATE)] = (iw_handler) cfg80211_wext_siwrate, + [IW_IOCTL_IDX(SIOCGIWRATE)] = (iw_handler) cfg80211_wext_giwrate, + [IW_IOCTL_IDX(SIOCSIWRTS)] = (iw_handler) cfg80211_wext_siwrts, + [IW_IOCTL_IDX(SIOCGIWRTS)] = (iw_handler) cfg80211_wext_giwrts, + [IW_IOCTL_IDX(SIOCSIWFRAG)] = (iw_handler) cfg80211_wext_siwfrag, + [IW_IOCTL_IDX(SIOCGIWFRAG)] = (iw_handler) cfg80211_wext_giwfrag, + [IW_IOCTL_IDX(SIOCSIWTXPOW)] = (iw_handler) cfg80211_wext_siwtxpower, + [IW_IOCTL_IDX(SIOCGIWTXPOW)] = (iw_handler) cfg80211_wext_giwtxpower, + [IW_IOCTL_IDX(SIOCSIWRETRY)] = (iw_handler) cfg80211_wext_siwretry, + [IW_IOCTL_IDX(SIOCGIWRETRY)] = (iw_handler) cfg80211_wext_giwretry, + [IW_IOCTL_IDX(SIOCSIWENCODE)] = (iw_handler) cfg80211_wext_siwencode, + [IW_IOCTL_IDX(SIOCGIWENCODE)] = (iw_handler) cfg80211_wext_giwencode, + [IW_IOCTL_IDX(SIOCSIWPOWER)] = (iw_handler) cfg80211_wext_siwpower, + [IW_IOCTL_IDX(SIOCGIWPOWER)] = (iw_handler) cfg80211_wext_giwpower, + [IW_IOCTL_IDX(SIOCSIWGENIE)] = (iw_handler) cfg80211_wext_siwgenie, + [IW_IOCTL_IDX(SIOCSIWAUTH)] = (iw_handler) cfg80211_wext_siwauth, + [IW_IOCTL_IDX(SIOCGIWAUTH)] = (iw_handler) cfg80211_wext_giwauth, + [IW_IOCTL_IDX(SIOCSIWENCODEEXT)]= (iw_handler) cfg80211_wext_siwencodeext, + [IW_IOCTL_IDX(SIOCSIWPMKSA)] = (iw_handler) cfg80211_wext_siwpmksa, +}; + +const struct iw_handler_def cfg80211_wext_handler = { + .num_standard = ARRAY_SIZE(cfg80211_handlers), + .standard = cfg80211_handlers, + .get_wireless_stats = cfg80211_wireless_stats, +}; diff --git a/net/wireless_ath/wext-compat.h b/net/wireless_ath/wext-compat.h new file mode 100644 index 00000000..5d766b01 --- /dev/null +++ b/net/wireless_ath/wext-compat.h @@ -0,0 +1,57 @@ +#ifndef __WEXT_COMPAT +#define __WEXT_COMPAT + +#include +#include + +int cfg80211_ibss_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); +int cfg80211_ibss_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); +int cfg80211_ibss_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra); +int cfg80211_ibss_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra); +int cfg80211_ibss_wext_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid); +int cfg80211_ibss_wext_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid); + +int cfg80211_mgd_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); +int cfg80211_mgd_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); +int cfg80211_mgd_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra); +int cfg80211_mgd_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra); +int cfg80211_mgd_wext_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid); +int cfg80211_mgd_wext_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid); + +int cfg80211_wext_siwmlme(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra); +int cfg80211_wext_siwgenie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra); + + +int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq); + + +extern const struct iw_handler_def cfg80211_wext_handler; +#endif /* __WEXT_COMPAT */ diff --git a/net/wireless_ath/wext-core.c b/net/wireless_ath/wext-core.c new file mode 100644 index 00000000..ae45584d --- /dev/null +++ b/net/wireless_ath/wext-core.c @@ -0,0 +1,1123 @@ +/* + * This file implement the Wireless Extensions core API. + * + * Authors : Jean Tourrilhes - HPL - + * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. + * Copyright 2009 Johannes Berg + * + * (As all part of the Linux kernel, this file is GPL) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, + unsigned int, struct iw_request_info *, + iw_handler); + + +/* + * Meta-data about all the standard Wireless Extension request we + * know about. + */ +static const struct iw_ioctl_description standard_ioctl[] = { + [IW_IOCTL_IDX(SIOCSIWCOMMIT)] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [IW_IOCTL_IDX(SIOCGIWNAME)] = { + .header_type = IW_HEADER_TYPE_CHAR, + .flags = IW_DESCR_FLAG_DUMP, + }, + [IW_IOCTL_IDX(SIOCSIWNWID)] = { + .header_type = IW_HEADER_TYPE_PARAM, + .flags = IW_DESCR_FLAG_EVENT, + }, + [IW_IOCTL_IDX(SIOCGIWNWID)] = { + .header_type = IW_HEADER_TYPE_PARAM, + .flags = IW_DESCR_FLAG_DUMP, + }, + [IW_IOCTL_IDX(SIOCSIWFREQ)] = { + .header_type = IW_HEADER_TYPE_FREQ, + .flags = IW_DESCR_FLAG_EVENT, + }, + [IW_IOCTL_IDX(SIOCGIWFREQ)] = { + .header_type = IW_HEADER_TYPE_FREQ, + .flags = IW_DESCR_FLAG_DUMP, + }, + [IW_IOCTL_IDX(SIOCSIWMODE)] = { + .header_type = IW_HEADER_TYPE_UINT, + .flags = IW_DESCR_FLAG_EVENT, + }, + [IW_IOCTL_IDX(SIOCGIWMODE)] = { + .header_type = IW_HEADER_TYPE_UINT, + .flags = IW_DESCR_FLAG_DUMP, + }, + [IW_IOCTL_IDX(SIOCSIWSENS)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCGIWSENS)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCSIWRANGE)] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [IW_IOCTL_IDX(SIOCGIWRANGE)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = sizeof(struct iw_range), + .flags = IW_DESCR_FLAG_DUMP, + }, + [IW_IOCTL_IDX(SIOCSIWPRIV)] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */ + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct iw_priv_args), + .max_tokens = 16, + .flags = IW_DESCR_FLAG_NOMAX, + }, + [IW_IOCTL_IDX(SIOCSIWSTATS)] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */ + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = sizeof(struct iw_statistics), + .flags = IW_DESCR_FLAG_DUMP, + }, + [IW_IOCTL_IDX(SIOCSIWSPY)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct sockaddr), + .max_tokens = IW_MAX_SPY, + }, + [IW_IOCTL_IDX(SIOCGIWSPY)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct sockaddr) + + sizeof(struct iw_quality), + .max_tokens = IW_MAX_SPY, + }, + [IW_IOCTL_IDX(SIOCSIWTHRSPY)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct iw_thrspy), + .min_tokens = 1, + .max_tokens = 1, + }, + [IW_IOCTL_IDX(SIOCGIWTHRSPY)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct iw_thrspy), + .min_tokens = 1, + .max_tokens = 1, + }, + [IW_IOCTL_IDX(SIOCSIWAP)] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [IW_IOCTL_IDX(SIOCGIWAP)] = { + .header_type = IW_HEADER_TYPE_ADDR, + .flags = IW_DESCR_FLAG_DUMP, + }, + [IW_IOCTL_IDX(SIOCSIWMLME)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = sizeof(struct iw_mlme), + .max_tokens = sizeof(struct iw_mlme), + }, + [IW_IOCTL_IDX(SIOCGIWAPLIST)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct sockaddr) + + sizeof(struct iw_quality), + .max_tokens = IW_MAX_AP, + .flags = IW_DESCR_FLAG_NOMAX, + }, + [IW_IOCTL_IDX(SIOCSIWSCAN)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = 0, + .max_tokens = sizeof(struct iw_scan_req), + }, + [IW_IOCTL_IDX(SIOCGIWSCAN)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_SCAN_MAX_DATA, + .flags = IW_DESCR_FLAG_NOMAX, + }, + [IW_IOCTL_IDX(SIOCSIWESSID)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ESSID_MAX_SIZE, + .flags = IW_DESCR_FLAG_EVENT, + }, + [IW_IOCTL_IDX(SIOCGIWESSID)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ESSID_MAX_SIZE, + .flags = IW_DESCR_FLAG_DUMP, + }, + [IW_IOCTL_IDX(SIOCSIWNICKN)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ESSID_MAX_SIZE, + }, + [IW_IOCTL_IDX(SIOCGIWNICKN)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ESSID_MAX_SIZE, + }, + [IW_IOCTL_IDX(SIOCSIWRATE)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCGIWRATE)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCSIWRTS)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCGIWRTS)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCSIWFRAG)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCGIWFRAG)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCSIWTXPOW)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCGIWTXPOW)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCSIWRETRY)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCGIWRETRY)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCSIWENCODE)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ENCODING_TOKEN_MAX, + .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, + }, + [IW_IOCTL_IDX(SIOCGIWENCODE)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ENCODING_TOKEN_MAX, + .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, + }, + [IW_IOCTL_IDX(SIOCSIWPOWER)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCGIWPOWER)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCSIWGENIE)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [IW_IOCTL_IDX(SIOCGIWGENIE)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [IW_IOCTL_IDX(SIOCSIWAUTH)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCGIWAUTH)] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = sizeof(struct iw_encode_ext), + .max_tokens = sizeof(struct iw_encode_ext) + + IW_ENCODING_TOKEN_MAX, + }, + [IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = sizeof(struct iw_encode_ext), + .max_tokens = sizeof(struct iw_encode_ext) + + IW_ENCODING_TOKEN_MAX, + }, + [IW_IOCTL_IDX(SIOCSIWPMKSA)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = sizeof(struct iw_pmksa), + .max_tokens = sizeof(struct iw_pmksa), + }, +}; +static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl); + +/* + * Meta-data about all the additional standard Wireless Extension events + * we know about. + */ +static const struct iw_ioctl_description standard_event[] = { + [IW_EVENT_IDX(IWEVTXDROP)] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [IW_EVENT_IDX(IWEVQUAL)] = { + .header_type = IW_HEADER_TYPE_QUAL, + }, + [IW_EVENT_IDX(IWEVCUSTOM)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_CUSTOM_MAX, + }, + [IW_EVENT_IDX(IWEVREGISTERED)] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [IW_EVENT_IDX(IWEVEXPIRED)] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [IW_EVENT_IDX(IWEVGENIE)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = sizeof(struct iw_michaelmicfailure), + }, + [IW_EVENT_IDX(IWEVASSOCREQIE)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [IW_EVENT_IDX(IWEVASSOCRESPIE)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [IW_EVENT_IDX(IWEVPMKIDCAND)] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = sizeof(struct iw_pmkid_cand), + }, +}; +static const unsigned standard_event_num = ARRAY_SIZE(standard_event); + +/* Size (in bytes) of various events */ +static const int event_type_size[] = { + IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ + 0, + IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ + 0, + IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ + IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ + IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ + 0, + IW_EV_POINT_LEN, /* Without variable payload */ + IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ + IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ +}; + +#ifdef CONFIG_COMPAT +static const int compat_event_type_size[] = { + IW_EV_COMPAT_LCP_LEN, /* IW_HEADER_TYPE_NULL */ + 0, + IW_EV_COMPAT_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ + 0, + IW_EV_COMPAT_UINT_LEN, /* IW_HEADER_TYPE_UINT */ + IW_EV_COMPAT_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ + IW_EV_COMPAT_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ + 0, + IW_EV_COMPAT_POINT_LEN, /* Without variable payload */ + IW_EV_COMPAT_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ + IW_EV_COMPAT_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ +}; +#endif + + +/* IW event code */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) +static int __net_init wext_pernet_init(struct net *net) +{ + skb_queue_head_init(&net->wext_nlevents); + return 0; +} + +static void __net_exit wext_pernet_exit(struct net *net) +{ + skb_queue_purge(&net->wext_nlevents); +} + +static struct pernet_operations wext_pernet_ops = { + .init = wext_pernet_init, + .exit = wext_pernet_exit, +}; + +static int __init wireless_nlevent_init(void) +{ + return register_pernet_subsys(&wext_pernet_ops); +} + +subsys_initcall(wireless_nlevent_init); + +/* Process events generated by the wireless layer or the driver. */ +static void wireless_nlevent_process(struct work_struct *work) +{ + struct sk_buff *skb; + struct net *net; + + rtnl_lock(); + + for_each_net(net) { + while ((skb = skb_dequeue(&net->wext_nlevents))) + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, + GFP_KERNEL); + } + + rtnl_unlock(); +} + +static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); + +#else +/* Older kernels get the old way of doing stuff*/ +static struct sk_buff_head wireless_nlevent_queue; + +static int __init wireless_nlevent_init(void) +{ + skb_queue_head_init(&wireless_nlevent_queue); + return 0; +} + +subsys_initcall(wireless_nlevent_init); + +static void wireless_nlevent_process(unsigned long data) +{ + struct sk_buff *skb; + while ((skb = skb_dequeue(&wireless_nlevent_queue))) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); +#else + rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); +#endif +} + +static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); + +#endif + +static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev, + struct sk_buff *skb) +{ + struct ifinfomsg *r; + struct nlmsghdr *nlh; + + nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0); + if (!nlh) + return NULL; + + r = nlmsg_data(nlh); + r->ifi_family = AF_UNSPEC; + r->__ifi_pad = 0; + r->ifi_type = dev->type; + r->ifi_index = dev->ifindex; + r->ifi_flags = dev_get_flags(dev); + r->ifi_change = 0; /* Wireless changes don't affect those flags */ + + NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name); + + return nlh; + nla_put_failure: + nlmsg_cancel(skb, nlh); + return NULL; +} + + +/* + * Main event dispatcher. Called from other parts and drivers. + * Send the event on the appropriate channels. + * May be called from interrupt context. + */ +void wireless_send_event(struct net_device * dev, + unsigned int cmd, + union iwreq_data * wrqu, + const char * extra) +{ + const struct iw_ioctl_description * descr = NULL; + int extra_len = 0; + struct iw_event *event; /* Mallocated whole event */ + int event_len; /* Its size */ + int hdr_len; /* Size of the event header */ + int wrqu_off = 0; /* Offset in wrqu */ + /* Don't "optimise" the following variable, it will crash */ + unsigned cmd_index; /* *MUST* be unsigned */ + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct nlattr *nla; +#ifdef CONFIG_COMPAT + struct __compat_iw_event *compat_event; + struct compat_iw_point compat_wrqu; + struct sk_buff *compskb; +#endif + + /* + * Nothing in the kernel sends scan events with data, be safe. + * This is necessary because we cannot fix up scan event data + * for compat, due to being contained in 'extra', but normally + * applications are required to retrieve the scan data anyway + * and no data is included in the event, this codifies that + * practice. + */ + if (WARN_ON(cmd == SIOCGIWSCAN && extra)) + extra = NULL; + + /* Get the description of the Event */ + if (cmd <= SIOCIWLAST) { + cmd_index = IW_IOCTL_IDX(cmd); + if (cmd_index < standard_ioctl_num) + descr = &(standard_ioctl[cmd_index]); + } else { + cmd_index = IW_EVENT_IDX(cmd); + if (cmd_index < standard_event_num) + descr = &(standard_event[cmd_index]); + } + /* Don't accept unknown events */ + if (descr == NULL) { + /* Note : we don't return an error to the driver, because + * the driver would not know what to do about it. It can't + * return an error to the user, because the event is not + * initiated by a user request. + * The best the driver could do is to log an error message. + * We will do it ourselves instead... + */ + netdev_err(dev, "(WE) : Invalid/Unknown Wireless Event (0x%04X)\n", + cmd); + return; + } + + /* Check extra parameters and set extra_len */ + if (descr->header_type == IW_HEADER_TYPE_POINT) { + /* Check if number of token fits within bounds */ + if (wrqu->data.length > descr->max_tokens) { + netdev_err(dev, "(WE) : Wireless Event too big (%d)\n", + wrqu->data.length); + return; + } + if (wrqu->data.length < descr->min_tokens) { + netdev_err(dev, "(WE) : Wireless Event too small (%d)\n", + wrqu->data.length); + return; + } + /* Calculate extra_len - extra is NULL for restricted events */ + if (extra != NULL) + extra_len = wrqu->data.length * descr->token_size; + /* Always at an offset in wrqu */ + wrqu_off = IW_EV_POINT_OFF; + } + + /* Total length of the event */ + hdr_len = event_type_size[descr->header_type]; + event_len = hdr_len + extra_len; + + /* + * The problem for 64/32 bit. + * + * On 64-bit, a regular event is laid out as follows: + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | + * | event.len | event.cmd | p a d d i n g | + * | wrqu data ... (with the correct size) | + * + * This padding exists because we manipulate event->u, + * and 'event' is not packed. + * + * An iw_point event is laid out like this instead: + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | + * | event.len | event.cmd | p a d d i n g | + * | iwpnt.len | iwpnt.flg | p a d d i n g | + * | extra data ... + * + * The second padding exists because struct iw_point is extended, + * but this depends on the platform... + * + * On 32-bit, all the padding shouldn't be there. + */ + + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (!skb) + return; + + /* Send via the RtNetlink event channel */ + nlh = rtnetlink_ifinfo_prep(dev, skb); + if (WARN_ON(!nlh)) { + kfree_skb(skb); + return; + } + + /* Add the wireless events in the netlink packet */ + nla = nla_reserve(skb, IFLA_WIRELESS, event_len); + if (!nla) { + kfree_skb(skb); + return; + } + event = nla_data(nla); + + /* Fill event - first clear to avoid data leaking */ + memset(event, 0, hdr_len); + event->len = event_len; + event->cmd = cmd; + memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); + if (extra_len) + memcpy(((char *) event) + hdr_len, extra, extra_len); + + nlmsg_end(skb, nlh); +#ifdef CONFIG_COMPAT + hdr_len = compat_event_type_size[descr->header_type]; + event_len = hdr_len + extra_len; + + compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (!compskb) { + kfree_skb(skb); + return; + } + + /* Send via the RtNetlink event channel */ + nlh = rtnetlink_ifinfo_prep(dev, compskb); + if (WARN_ON(!nlh)) { + kfree_skb(skb); + kfree_skb(compskb); + return; + } + + /* Add the wireless events in the netlink packet */ + nla = nla_reserve(compskb, IFLA_WIRELESS, event_len); + if (!nla) { + kfree_skb(skb); + kfree_skb(compskb); + return; + } + compat_event = nla_data(nla); + + compat_event->len = event_len; + compat_event->cmd = cmd; + if (descr->header_type == IW_HEADER_TYPE_POINT) { + compat_wrqu.length = wrqu->data.length; + compat_wrqu.flags = wrqu->data.flags; + memcpy(&compat_event->pointer, + ((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF, + hdr_len - IW_EV_COMPAT_LCP_LEN); + if (extra_len) + memcpy(((char *) compat_event) + hdr_len, + extra, extra_len); + } else { + /* extra_len must be zero, so no if (extra) needed */ + memcpy(&compat_event->pointer, wrqu, + hdr_len - IW_EV_COMPAT_LCP_LEN); + } + + nlmsg_end(compskb, nlh); + + skb_shinfo(skb)->frag_list = compskb; +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) + skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); + schedule_work(&wireless_nlevent_work); +#else + skb_queue_tail(&wireless_nlevent_queue, skb); + tasklet_schedule(&wireless_nlevent_tasklet); +#endif +} +EXPORT_SYMBOL(wireless_send_event); + + + +/* IW handlers */ + +struct iw_statistics *get_wireless_stats(struct net_device *dev) +{ +#ifdef CONFIG_WIRELESS_EXT + if ((dev->wireless_handlers != NULL) && + (dev->wireless_handlers->get_wireless_stats != NULL)) + return dev->wireless_handlers->get_wireless_stats(dev); +#endif + +#ifdef CONFIG_CFG80211_WEXT + if (dev->ieee80211_ptr && + dev->ieee80211_ptr->wiphy && + dev->ieee80211_ptr->wiphy->wext && + dev->ieee80211_ptr->wiphy->wext->get_wireless_stats) + return dev->ieee80211_ptr->wiphy->wext->get_wireless_stats(dev); +#endif + + /* not found */ + return NULL; +} + +static int iw_handler_get_iwstats(struct net_device * dev, + struct iw_request_info * info, + union iwreq_data * wrqu, + char * extra) +{ + /* Get stats from the driver */ + struct iw_statistics *stats; + + stats = get_wireless_stats(dev); + if (stats) { + /* Copy statistics to extra */ + memcpy(extra, stats, sizeof(struct iw_statistics)); + wrqu->data.length = sizeof(struct iw_statistics); + + /* Check if we need to clear the updated flag */ + if (wrqu->data.flags != 0) + stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; + return 0; + } else + return -EOPNOTSUPP; +} + +static iw_handler get_handler(struct net_device *dev, unsigned int cmd) +{ + /* Don't "optimise" the following variable, it will crash */ + unsigned int index; /* *MUST* be unsigned */ + const struct iw_handler_def *handlers = NULL; + +#ifdef CONFIG_CFG80211_WEXT + if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy) + handlers = dev->ieee80211_ptr->wiphy->wext; +#endif +#ifdef CONFIG_WIRELESS_EXT + if (dev->wireless_handlers) + handlers = dev->wireless_handlers; +#endif + + if (!handlers) + return NULL; + + /* Try as a standard command */ + index = IW_IOCTL_IDX(cmd); + if (index < handlers->num_standard) + return handlers->standard[index]; + +#ifdef CONFIG_WEXT_PRIV + /* Try as a private command */ + index = cmd - SIOCIWFIRSTPRIV; + if (index < handlers->num_private) + return handlers->private[index]; +#endif + + /* Not found */ + return NULL; +} + +static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd, + const struct iw_ioctl_description *descr, + iw_handler handler, struct net_device *dev, + struct iw_request_info *info) +{ + int err, extra_size, user_length = 0, essid_compat = 0; + char *extra; + + /* Calculate space needed by arguments. Always allocate + * for max space. + */ + extra_size = descr->max_tokens * descr->token_size; + + /* Check need for ESSID compatibility for WE < 21 */ + switch (cmd) { + case SIOCSIWESSID: + case SIOCGIWESSID: + case SIOCSIWNICKN: + case SIOCGIWNICKN: + if (iwp->length == descr->max_tokens + 1) + essid_compat = 1; + else if (IW_IS_SET(cmd) && (iwp->length != 0)) { + char essid[IW_ESSID_MAX_SIZE + 1]; + unsigned int len; + len = iwp->length * descr->token_size; + + if (len > IW_ESSID_MAX_SIZE) + return -EFAULT; + + err = copy_from_user(essid, iwp->pointer, len); + if (err) + return -EFAULT; + + if (essid[iwp->length - 1] == '\0') + essid_compat = 1; + } + break; + default: + break; + } + + iwp->length -= essid_compat; + + /* Check what user space is giving us */ + if (IW_IS_SET(cmd)) { + /* Check NULL pointer */ + if (!iwp->pointer && iwp->length != 0) + return -EFAULT; + /* Check if number of token fits within bounds */ + if (iwp->length > descr->max_tokens) + return -E2BIG; + if (iwp->length < descr->min_tokens) + return -EINVAL; + } else { + /* Check NULL pointer */ + if (!iwp->pointer) + return -EFAULT; + /* Save user space buffer size for checking */ + user_length = iwp->length; + + /* Don't check if user_length > max to allow forward + * compatibility. The test user_length < min is + * implied by the test at the end. + */ + + /* Support for very large requests */ + if ((descr->flags & IW_DESCR_FLAG_NOMAX) && + (user_length > descr->max_tokens)) { + /* Allow userspace to GET more than max so + * we can support any size GET requests. + * There is still a limit : -ENOMEM. + */ + extra_size = user_length * descr->token_size; + + /* Note : user_length is originally a __u16, + * and token_size is controlled by us, + * so extra_size won't get negative and + * won't overflow... + */ + } + } + + /* kzalloc() ensures NULL-termination for essid_compat. */ + extra = kzalloc(extra_size, GFP_KERNEL); + if (!extra) + return -ENOMEM; + + /* If it is a SET, get all the extra data in here */ + if (IW_IS_SET(cmd) && (iwp->length != 0)) { + if (copy_from_user(extra, iwp->pointer, + iwp->length * + descr->token_size)) { + err = -EFAULT; + goto out; + } + + if (cmd == SIOCSIWENCODEEXT) { + struct iw_encode_ext *ee = (void *) extra; + + if (iwp->length < sizeof(*ee) + ee->key_len) + return -EFAULT; + } + } + + if (IW_IS_GET(cmd) && !(descr->flags & IW_DESCR_FLAG_NOMAX)) { + /* + * If this is a GET, but not NOMAX, it means that the extra + * data is not bounded by userspace, but by max_tokens. Thus + * set the length to max_tokens. This matches the extra data + * allocation. + * The driver should fill it with the number of tokens it + * provided, and it may check iwp->length rather than having + * knowledge of max_tokens. If the driver doesn't change the + * iwp->length, this ioctl just copies back max_token tokens + * filled with zeroes. Hopefully the driver isn't claiming + * them to be valid data. + */ + iwp->length = descr->max_tokens; + } + + err = handler(dev, info, (union iwreq_data *) iwp, extra); + + iwp->length += essid_compat; + + /* If we have something to return to the user */ + if (!err && IW_IS_GET(cmd)) { + /* Check if there is enough buffer up there */ + if (user_length < iwp->length) { + err = -E2BIG; + goto out; + } + + if (copy_to_user(iwp->pointer, extra, + iwp->length * + descr->token_size)) { + err = -EFAULT; + goto out; + } + } + + /* Generate an event to notify listeners of the change */ + if ((descr->flags & IW_DESCR_FLAG_EVENT) && + ((err == 0) || (err == -EIWCOMMIT))) { + union iwreq_data *data = (union iwreq_data *) iwp; + + if (descr->flags & IW_DESCR_FLAG_RESTRICT) + /* If the event is restricted, don't + * export the payload. + */ + wireless_send_event(dev, cmd, data, NULL); + else + wireless_send_event(dev, cmd, data, extra); + } + +out: + kfree(extra); + return err; +} + +/* + * Call the commit handler in the driver + * (if exist and if conditions are right) + * + * Note : our current commit strategy is currently pretty dumb, + * but we will be able to improve on that... + * The goal is to try to agreagate as many changes as possible + * before doing the commit. Drivers that will define a commit handler + * are usually those that need a reset after changing parameters, so + * we want to minimise the number of reset. + * A cool idea is to use a timer : at each "set" command, we re-set the + * timer, when the timer eventually fires, we call the driver. + * Hopefully, more on that later. + * + * Also, I'm waiting to see how many people will complain about the + * netif_running(dev) test. I'm open on that one... + * Hopefully, the driver will remember to do a commit in "open()" ;-) + */ +int call_commit_handler(struct net_device *dev) +{ +#ifdef CONFIG_WIRELESS_EXT + if ((netif_running(dev)) && + (dev->wireless_handlers->standard[0] != NULL)) + /* Call the commit handler on the driver */ + return dev->wireless_handlers->standard[0](dev, NULL, + NULL, NULL); + else + return 0; /* Command completed successfully */ +#else + /* cfg80211 has no commit */ + return 0; +#endif +} + +/* + * Main IOCTl dispatcher. + * Check the type of IOCTL and call the appropriate wrapper... + */ +static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, + unsigned int cmd, + struct iw_request_info *info, + wext_ioctl_func standard, + wext_ioctl_func private) +{ + struct iwreq *iwr = (struct iwreq *) ifr; + struct net_device *dev; + iw_handler handler; + + /* Permissions are already checked in dev_ioctl() before calling us. + * The copy_to/from_user() of ifr is also dealt with in there */ + + /* Make sure the device exist */ + if ((dev = __dev_get_by_name(net, ifr->ifr_name)) == NULL) + return -ENODEV; + + /* A bunch of special cases, then the generic case... + * Note that 'cmd' is already filtered in dev_ioctl() with + * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ + if (cmd == SIOCGIWSTATS) + return standard(dev, iwr, cmd, info, + &iw_handler_get_iwstats); + +#ifdef CONFIG_WEXT_PRIV + if (cmd == SIOCGIWPRIV && dev->wireless_handlers) + return standard(dev, iwr, cmd, info, + iw_handler_get_private); +#endif + + /* Basic check */ + if (!netif_device_present(dev)) + return -ENODEV; + + /* New driver API : try to find the handler */ + handler = get_handler(dev, cmd); + if (handler) { + /* Standard and private are not the same */ + if (cmd < SIOCIWFIRSTPRIV) + return standard(dev, iwr, cmd, info, handler); + else if (private) + return private(dev, iwr, cmd, info, handler); + } + /* Old driver API : call driver ioctl handler */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) + if (dev->netdev_ops->ndo_do_ioctl) + return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd); +#else + if (dev->do_ioctl) + return dev->do_ioctl(dev, ifr, cmd); +#endif + return -EOPNOTSUPP; +} + +/* If command is `set a parameter', or `get the encoding parameters', + * check if the user has the right to do it. + */ +static int wext_permission_check(unsigned int cmd) +{ + if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || + cmd == SIOCGIWENCODEEXT) && + !capable(CAP_NET_ADMIN)) + return -EPERM; + + return 0; +} + +/* entry point from dev ioctl */ +static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr, + unsigned int cmd, struct iw_request_info *info, + wext_ioctl_func standard, + wext_ioctl_func private) +{ + int ret = wext_permission_check(cmd); + + if (ret) + return ret; + + dev_load(net, ifr->ifr_name); + rtnl_lock(); + ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private); + rtnl_unlock(); + + return ret; +} + +/* + * Wrapper to call a standard Wireless Extension handler. + * We do various checks and also take care of moving data between + * user space and kernel space. + */ +static int ioctl_standard_call(struct net_device * dev, + struct iwreq *iwr, + unsigned int cmd, + struct iw_request_info *info, + iw_handler handler) +{ + const struct iw_ioctl_description * descr; + int ret = -EINVAL; + + /* Get the description of the IOCTL */ + if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num) + return -EOPNOTSUPP; + descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]); + + /* Check if we have a pointer to user space data or not */ + if (descr->header_type != IW_HEADER_TYPE_POINT) { + + /* No extra arguments. Trivial to handle */ + ret = handler(dev, info, &(iwr->u), NULL); + + /* Generate an event to notify listeners of the change */ + if ((descr->flags & IW_DESCR_FLAG_EVENT) && + ((ret == 0) || (ret == -EIWCOMMIT))) + wireless_send_event(dev, cmd, &(iwr->u), NULL); + } else { + ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr, + handler, dev, info); + } + + /* Call commit handler if needed and defined */ + if (ret == -EIWCOMMIT) + ret = call_commit_handler(dev); + + /* Here, we will generate the appropriate event if needed */ + + return ret; +} + + +int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, + void __user *arg) +{ + struct iw_request_info info = { .cmd = cmd, .flags = 0 }; + int ret; + + ret = wext_ioctl_dispatch(net, ifr, cmd, &info, + ioctl_standard_call, + ioctl_private_call); + if (ret >= 0 && + IW_IS_GET(cmd) && + copy_to_user(arg, ifr, sizeof(struct iwreq))) + return -EFAULT; + + return ret; +} + +#ifdef CONFIG_COMPAT +static int compat_standard_call(struct net_device *dev, + struct iwreq *iwr, + unsigned int cmd, + struct iw_request_info *info, + iw_handler handler) +{ + const struct iw_ioctl_description *descr; + struct compat_iw_point *iwp_compat; + struct iw_point iwp; + int err; + + descr = standard_ioctl + IW_IOCTL_IDX(cmd); + + if (descr->header_type != IW_HEADER_TYPE_POINT) + return ioctl_standard_call(dev, iwr, cmd, info, handler); + + iwp_compat = (struct compat_iw_point *) &iwr->u.data; + iwp.pointer = compat_ptr(iwp_compat->pointer); + iwp.length = iwp_compat->length; + iwp.flags = iwp_compat->flags; + + err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, info); + + iwp_compat->pointer = ptr_to_compat(iwp.pointer); + iwp_compat->length = iwp.length; + iwp_compat->flags = iwp.flags; + + return err; +} + +int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct iw_request_info info; + struct iwreq iwr; + char *colon; + int ret; + + if (copy_from_user(&iwr, argp, sizeof(struct iwreq))) + return -EFAULT; + + iwr.ifr_name[IFNAMSIZ-1] = 0; + colon = strchr(iwr.ifr_name, ':'); + if (colon) + *colon = 0; + + info.cmd = cmd; + info.flags = IW_REQUEST_FLAG_COMPAT; + + ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd, &info, + compat_standard_call, + compat_private_call); + + if (ret >= 0 && + IW_IS_GET(cmd) && + copy_to_user(argp, &iwr, sizeof(struct iwreq))) + return -EFAULT; + + return ret; +} +#endif diff --git a/net/wireless_ath/wext-priv.c b/net/wireless_ath/wext-priv.c new file mode 100644 index 00000000..674d426a --- /dev/null +++ b/net/wireless_ath/wext-priv.c @@ -0,0 +1,249 @@ +/* + * This file implement the Wireless Extensions priv API. + * + * Authors : Jean Tourrilhes - HPL - + * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. + * Copyright 2009 Johannes Berg + * + * (As all part of the Linux kernel, this file is GPL) + */ +#include +#include +#include +#include +#include + +int iw_handler_get_private(struct net_device * dev, + struct iw_request_info * info, + union iwreq_data * wrqu, + char * extra) +{ + /* Check if the driver has something to export */ + if ((dev->wireless_handlers->num_private_args == 0) || + (dev->wireless_handlers->private_args == NULL)) + return -EOPNOTSUPP; + + /* Check if there is enough buffer up there */ + if (wrqu->data.length < dev->wireless_handlers->num_private_args) { + /* User space can't know in advance how large the buffer + * needs to be. Give it a hint, so that we can support + * any size buffer we want somewhat efficiently... */ + wrqu->data.length = dev->wireless_handlers->num_private_args; + return -E2BIG; + } + + /* Set the number of available ioctls. */ + wrqu->data.length = dev->wireless_handlers->num_private_args; + + /* Copy structure to the user buffer. */ + memcpy(extra, dev->wireless_handlers->private_args, + sizeof(struct iw_priv_args) * wrqu->data.length); + + return 0; +} + +/* Size (in bytes) of the various private data types */ +static const char iw_priv_type_size[] = { + 0, /* IW_PRIV_TYPE_NONE */ + 1, /* IW_PRIV_TYPE_BYTE */ + 1, /* IW_PRIV_TYPE_CHAR */ + 0, /* Not defined */ + sizeof(__u32), /* IW_PRIV_TYPE_INT */ + sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ + sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ + 0, /* Not defined */ +}; + +static int get_priv_size(__u16 args) +{ + int num = args & IW_PRIV_SIZE_MASK; + int type = (args & IW_PRIV_TYPE_MASK) >> 12; + + return num * iw_priv_type_size[type]; +} + +static int adjust_priv_size(__u16 args, struct iw_point *iwp) +{ + int num = iwp->length; + int max = args & IW_PRIV_SIZE_MASK; + int type = (args & IW_PRIV_TYPE_MASK) >> 12; + + /* Make sure the driver doesn't goof up */ + if (max < num) + num = max; + + return num * iw_priv_type_size[type]; +} + +/* + * Wrapper to call a private Wireless Extension handler. + * We do various checks and also take care of moving data between + * user space and kernel space. + * It's not as nice and slimline as the standard wrapper. The cause + * is struct iw_priv_args, which was not really designed for the + * job we are going here. + * + * IMPORTANT : This function prevent to set and get data on the same + * IOCTL and enforce the SET/GET convention. Not doing it would be + * far too hairy... + * If you need to set and get data at the same time, please don't use + * a iw_handler but process it in your ioctl handler (i.e. use the + * old driver API). + */ +static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd, + const struct iw_priv_args **descrp) +{ + const struct iw_priv_args *descr; + int i, extra_size; + + descr = NULL; + for (i = 0; i < dev->wireless_handlers->num_private_args; i++) { + if (cmd == dev->wireless_handlers->private_args[i].cmd) { + descr = &dev->wireless_handlers->private_args[i]; + break; + } + } + + extra_size = 0; + if (descr) { + if (IW_IS_SET(cmd)) { + int offset = 0; /* For sub-ioctls */ + /* Check for sub-ioctl handler */ + if (descr->name[0] == '\0') + /* Reserve one int for sub-ioctl index */ + offset = sizeof(__u32); + + /* Size of set arguments */ + extra_size = get_priv_size(descr->set_args); + + /* Does it fits in iwr ? */ + if ((descr->set_args & IW_PRIV_SIZE_FIXED) && + ((extra_size + offset) <= IFNAMSIZ)) + extra_size = 0; + } else { + /* Size of get arguments */ + extra_size = get_priv_size(descr->get_args); + + /* Does it fits in iwr ? */ + if ((descr->get_args & IW_PRIV_SIZE_FIXED) && + (extra_size <= IFNAMSIZ)) + extra_size = 0; + } + } + *descrp = descr; + return extra_size; +} + +static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd, + const struct iw_priv_args *descr, + iw_handler handler, struct net_device *dev, + struct iw_request_info *info, int extra_size) +{ + char *extra; + int err; + + /* Check what user space is giving us */ + if (IW_IS_SET(cmd)) { + if (!iwp->pointer && iwp->length != 0) + return -EFAULT; + + if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK)) + return -E2BIG; + } else if (!iwp->pointer) + return -EFAULT; + + extra = kzalloc(extra_size, GFP_KERNEL); + if (!extra) + return -ENOMEM; + + /* If it is a SET, get all the extra data in here */ + if (IW_IS_SET(cmd) && (iwp->length != 0)) { + if (copy_from_user(extra, iwp->pointer, extra_size)) { + err = -EFAULT; + goto out; + } + } + + /* Call the handler */ + err = handler(dev, info, (union iwreq_data *) iwp, extra); + + /* If we have something to return to the user */ + if (!err && IW_IS_GET(cmd)) { + /* Adjust for the actual length if it's variable, + * avoid leaking kernel bits outside. + */ + if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) + extra_size = adjust_priv_size(descr->get_args, iwp); + + if (copy_to_user(iwp->pointer, extra, extra_size)) + err = -EFAULT; + } + +out: + kfree(extra); + return err; +} + +int ioctl_private_call(struct net_device *dev, struct iwreq *iwr, + unsigned int cmd, struct iw_request_info *info, + iw_handler handler) +{ + int extra_size = 0, ret = -EINVAL; + const struct iw_priv_args *descr; + + extra_size = get_priv_descr_and_size(dev, cmd, &descr); + + /* Check if we have a pointer to user space data or not. */ + if (extra_size == 0) { + /* No extra arguments. Trivial to handle */ + ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u)); + } else { + ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr, + handler, dev, info, extra_size); + } + + /* Call commit handler if needed and defined */ + if (ret == -EIWCOMMIT) + ret = call_commit_handler(dev); + + return ret; +} + +#ifdef CONFIG_COMPAT +int compat_private_call(struct net_device *dev, struct iwreq *iwr, + unsigned int cmd, struct iw_request_info *info, + iw_handler handler) +{ + const struct iw_priv_args *descr; + int ret, extra_size; + + extra_size = get_priv_descr_and_size(dev, cmd, &descr); + + /* Check if we have a pointer to user space data or not. */ + if (extra_size == 0) { + /* No extra arguments. Trivial to handle */ + ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u)); + } else { + struct compat_iw_point *iwp_compat; + struct iw_point iwp; + + iwp_compat = (struct compat_iw_point *) &iwr->u.data; + iwp.pointer = compat_ptr(iwp_compat->pointer); + iwp.length = iwp_compat->length; + iwp.flags = iwp_compat->flags; + + ret = ioctl_private_iw_point(&iwp, cmd, descr, + handler, dev, info, extra_size); + + iwp_compat->pointer = ptr_to_compat(iwp.pointer); + iwp_compat->length = iwp.length; + iwp_compat->flags = iwp.flags; + } + + /* Call commit handler if needed and defined */ + if (ret == -EIWCOMMIT) + ret = call_commit_handler(dev); + + return ret; +} +#endif diff --git a/net/wireless_ath/wext-proc.c b/net/wireless_ath/wext-proc.c new file mode 100644 index 00000000..f2fbfa6f --- /dev/null +++ b/net/wireless_ath/wext-proc.c @@ -0,0 +1,159 @@ +/* + * This file implement the Wireless Extensions proc API. + * + * Authors : Jean Tourrilhes - HPL - + * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. + * + * (As all part of the Linux kernel, this file is GPL) + */ + +/* + * The /proc/net/wireless file is a human readable user-space interface + * exporting various wireless specific statistics from the wireless devices. + * This is the most popular part of the Wireless Extensions ;-) + * + * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). + * The content of the file is basically the content of "struct iw_statistics". + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +static void wireless_seq_printf_stats(struct seq_file *seq, + struct net_device *dev) +{ + /* Get stats from the driver */ + struct iw_statistics *stats = get_wireless_stats(dev); + static struct iw_statistics nullstats = {}; + + /* show device if it's wireless regardless of current stats */ + if (!stats) { +#ifdef CONFIG_WIRELESS_EXT + if (dev->wireless_handlers) + stats = &nullstats; +#endif +#ifdef CONFIG_CFG80211 + if (dev->ieee80211_ptr) + stats = &nullstats; +#endif + } + + if (stats) { + seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " + "%6d %6d %6d\n", + dev->name, stats->status, stats->qual.qual, + stats->qual.updated & IW_QUAL_QUAL_UPDATED + ? '.' : ' ', + ((__s32) stats->qual.level) - + ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), + stats->qual.updated & IW_QUAL_LEVEL_UPDATED + ? '.' : ' ', + ((__s32) stats->qual.noise) - + ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), + stats->qual.updated & IW_QUAL_NOISE_UPDATED + ? '.' : ' ', + stats->discard.nwid, stats->discard.code, + stats->discard.fragment, stats->discard.retries, + stats->discard.misc, stats->miss.beacon); + + if (stats != &nullstats) + stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; + } +} + +/* ---------------------------------------------------------------- */ +/* + * Print info for /proc/net/wireless (print all entries) + */ +static int wireless_dev_seq_show(struct seq_file *seq, void *v) +{ + might_sleep(); + + if (v == SEQ_START_TOKEN) + seq_printf(seq, "Inter-| sta-| Quality | Discarded " + "packets | Missed | WE\n" + " face | tus | link level noise | nwid " + "crypt frag retry misc | beacon | %d\n", + WIRELESS_EXT); + else + wireless_seq_printf_stats(seq, v); + return 0; +} + +static void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct net *net = seq_file_net(seq); + loff_t off; + struct net_device *dev; + + rtnl_lock(); + if (!*pos) + return SEQ_START_TOKEN; + + off = 1; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + for_each_netdev(net, dev) +#else + for_each_netdev(net) +#endif + if (off++ == *pos) + return dev; + return NULL; +} + +static void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct net *net = seq_file_net(seq); + + ++*pos; + + return v == SEQ_START_TOKEN ? + first_net_device(net) : next_net_device(v); +} + +static void wireless_dev_seq_stop(struct seq_file *seq, void *v) +{ + rtnl_unlock(); +} + +static const struct seq_operations wireless_seq_ops = { + .start = wireless_dev_seq_start, + .next = wireless_dev_seq_next, + .stop = wireless_dev_seq_stop, + .show = wireless_dev_seq_show, +}; + +static int seq_open_wireless(struct inode *inode, struct file *file) +{ + return seq_open_net(inode, file, &wireless_seq_ops, + sizeof(struct seq_net_private)); +} + +static const struct file_operations wireless_seq_fops = { + .owner = THIS_MODULE, + .open = seq_open_wireless, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net, +}; + +int __net_init wext_proc_init(struct net *net) +{ + /* Create /proc/net/wireless entry */ + if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops)) + return -ENOMEM; + + return 0; +} + +void __net_exit wext_proc_exit(struct net *net) +{ + proc_net_remove(net, "wireless"); +} diff --git a/net/wireless_ath/wext-sme.c b/net/wireless_ath/wext-sme.c new file mode 100644 index 00000000..7c01c2f3 --- /dev/null +++ b/net/wireless_ath/wext-sme.c @@ -0,0 +1,408 @@ +/* + * cfg80211 wext compat for managed mode. + * + * Copyright 2009 Johannes Berg + * Copyright (C) 2009 Intel Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include "wext-compat.h" +#include "nl80211.h" + +int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + struct cfg80211_cached_keys *ck = NULL; + const u8 *prev_bssid = NULL; + int err, i; + + ASSERT_RDEV_LOCK(rdev); + ASSERT_WDEV_LOCK(wdev); + + if (!netif_running(wdev->netdev)) + return 0; + + wdev->wext.connect.ie = wdev->wext.ie; + wdev->wext.connect.ie_len = wdev->wext.ie_len; + + /* Use default background scan period */ + wdev->wext.connect.bg_scan_period = -1; + + if (wdev->wext.keys) { + wdev->wext.keys->def = wdev->wext.default_key; + wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key; + if (wdev->wext.default_key != -1) + wdev->wext.connect.privacy = true; + } + + if (!wdev->wext.connect.ssid_len) + return 0; + + if (wdev->wext.keys) { + ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); + if (!ck) + return -ENOMEM; + for (i = 0; i < 6; i++) + ck->params[i].key = ck->data[i]; + } + + if (wdev->wext.prev_bssid_valid) + prev_bssid = wdev->wext.prev_bssid; + + err = __cfg80211_connect(rdev, wdev->netdev, + &wdev->wext.connect, ck, prev_bssid); + if (err) + kfree(ck); + + return err; +} + +int cfg80211_mgd_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *wextfreq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct ieee80211_channel *chan = NULL; + int err, freq; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); + if (freq < 0) + return freq; + + if (freq) { + chan = ieee80211_get_channel(wdev->wiphy, freq); + if (!chan) + return -EINVAL; + if (chan->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; + } + + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); + wdev_lock(wdev); + + if (wdev->sme_state != CFG80211_SME_IDLE) { + bool event = true; + + if (wdev->wext.connect.channel == chan) { + err = 0; + goto out; + } + + /* if SSID set, we'll try right again, avoid event */ + if (wdev->wext.connect.ssid_len) + event = false; + err = __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, event); + if (err) + goto out; + } + + + wdev->wext.connect.channel = chan; + + /* SSID is not set, we just want to switch channel */ + if (chan && !wdev->wext.connect.ssid_len) { + err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); + goto out; + } + + err = cfg80211_mgd_wext_connect(rdev, wdev); + out: + wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); + return err; +} + +int cfg80211_mgd_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct ieee80211_channel *chan = NULL; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + wdev_lock(wdev); + if (wdev->current_bss) + chan = wdev->current_bss->pub.channel; + else if (wdev->wext.connect.channel) + chan = wdev->wext.connect.channel; + wdev_unlock(wdev); + + if (chan) { + freq->m = chan->center_freq; + freq->e = 6; + return 0; + } + + /* no channel if not joining */ + return -EINVAL; +} + +int cfg80211_mgd_wext_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + size_t len = data->length; + int err; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + if (!data->flags) + len = 0; + + /* iwconfig uses nul termination in SSID.. */ + if (len > 0 && ssid[len - 1] == '\0') + len--; + + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); + wdev_lock(wdev); + + err = 0; + + if (wdev->sme_state != CFG80211_SME_IDLE) { + bool event = true; + + if (wdev->wext.connect.ssid && len && + len == wdev->wext.connect.ssid_len && + memcmp(wdev->wext.connect.ssid, ssid, len) == 0) + goto out; + + /* if SSID set now, we'll try to connect, avoid event */ + if (len) + event = false; + err = __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, event); + if (err) + goto out; + } + + wdev->wext.prev_bssid_valid = false; + wdev->wext.connect.ssid = wdev->wext.ssid; + memcpy(wdev->wext.ssid, ssid, len); + wdev->wext.connect.ssid_len = len; + + wdev->wext.connect.crypto.control_port = false; + wdev->wext.connect.crypto.control_port_ethertype = + cpu_to_be16(ETH_P_PAE); + + err = cfg80211_mgd_wext_connect(rdev, wdev); + out: + wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); + return err; +} + +int cfg80211_mgd_wext_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + data->flags = 0; + + wdev_lock(wdev); + if (wdev->current_bss) { + const u8 *ie = ieee80211_bss_get_ie(&wdev->current_bss->pub, + WLAN_EID_SSID); + if (ie) { + data->flags = 1; + data->length = ie[1]; + memcpy(ssid, ie + 2, data->length); + } + } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { + data->flags = 1; + data->length = wdev->wext.connect.ssid_len; + memcpy(ssid, wdev->wext.connect.ssid, data->length); + } + wdev_unlock(wdev); + + return 0; +} + +int cfg80211_mgd_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + u8 *bssid = ap_addr->sa_data; + int err; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + if (ap_addr->sa_family != ARPHRD_ETHER) + return -EINVAL; + + /* automatic mode */ + if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) + bssid = NULL; + + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); + wdev_lock(wdev); + + if (wdev->sme_state != CFG80211_SME_IDLE) { + err = 0; + /* both automatic */ + if (!bssid && !wdev->wext.connect.bssid) + goto out; + + /* fixed already - and no change */ + if (wdev->wext.connect.bssid && bssid && + compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) + goto out; + + err = __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, false); + if (err) + goto out; + } + + if (bssid) { + memcpy(wdev->wext.bssid, bssid, ETH_ALEN); + wdev->wext.connect.bssid = wdev->wext.bssid; + } else + wdev->wext.connect.bssid = NULL; + + err = cfg80211_mgd_wext_connect(rdev, wdev); + out: + wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); + return err; +} + +int cfg80211_mgd_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + ap_addr->sa_family = ARPHRD_ETHER; + + wdev_lock(wdev); + if (wdev->current_bss) + memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); + else + memset(ap_addr->sa_data, 0, ETH_ALEN); + wdev_unlock(wdev); + + return 0; +} + +int cfg80211_wext_siwgenie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + u8 *ie = extra; + int ie_len = data->length, err; + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + if (!ie_len) + ie = NULL; + + wdev_lock(wdev); + + /* no change */ + err = 0; + if (wdev->wext.ie_len == ie_len && + memcmp(wdev->wext.ie, ie, ie_len) == 0) + goto out; + + if (ie_len) { + ie = kmemdup(extra, ie_len, GFP_KERNEL); + if (!ie) { + err = -ENOMEM; + goto out; + } + } else + ie = NULL; + + kfree(wdev->wext.ie); + wdev->wext.ie = ie; + wdev->wext.ie_len = ie_len; + + if (wdev->sme_state != CFG80211_SME_IDLE) { + err = __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, false); + if (err) + goto out; + } + + /* userspace better not think we'll reconnect */ + err = 0; + out: + wdev_unlock(wdev); + return err; +} + +int cfg80211_wext_siwmlme(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct iw_mlme *mlme = (struct iw_mlme *)extra; + struct cfg80211_registered_device *rdev; + int err; + + if (!wdev) + return -EOPNOTSUPP; + + rdev = wiphy_to_dev(wdev->wiphy); + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EINVAL; + + if (mlme->addr.sa_family != ARPHRD_ETHER) + return -EINVAL; + + wdev_lock(wdev); + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + case IW_MLME_DISASSOC: + err = __cfg80211_disconnect(rdev, dev, mlme->reason_code, + true); + break; + default: + err = -EOPNOTSUPP; + break; + } + wdev_unlock(wdev); + + return err; +} diff --git a/net/wireless_ath/wext-spy.c b/net/wireless_ath/wext-spy.c new file mode 100644 index 00000000..5d643a54 --- /dev/null +++ b/net/wireless_ath/wext-spy.c @@ -0,0 +1,232 @@ +/* + * This file implement the Wireless Extensions spy API. + * + * Authors : Jean Tourrilhes - HPL - + * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. + * + * (As all part of the Linux kernel, this file is GPL) + */ + +#include +#include +#include +#include +#include +#include +#include + +static inline struct iw_spy_data *get_spydata(struct net_device *dev) +{ + /* This is the new way */ + if (dev->wireless_data) + return dev->wireless_data->spy_data; + return NULL; +} + +int iw_handler_set_spy(struct net_device * dev, + struct iw_request_info * info, + union iwreq_data * wrqu, + char * extra) +{ + struct iw_spy_data * spydata = get_spydata(dev); + struct sockaddr * address = (struct sockaddr *) extra; + + /* Make sure driver is not buggy or using the old API */ + if (!spydata) + return -EOPNOTSUPP; + + /* Disable spy collection while we copy the addresses. + * While we copy addresses, any call to wireless_spy_update() + * will NOP. This is OK, as anyway the addresses are changing. */ + spydata->spy_number = 0; + + /* We want to operate without locking, because wireless_spy_update() + * most likely will happen in the interrupt handler, and therefore + * have its own locking constraints and needs performance. + * The rtnl_lock() make sure we don't race with the other iw_handlers. + * This make sure wireless_spy_update() "see" that the spy list + * is temporarily disabled. */ + smp_wmb(); + + /* Are there are addresses to copy? */ + if (wrqu->data.length > 0) { + int i; + + /* Copy addresses */ + for (i = 0; i < wrqu->data.length; i++) + memcpy(spydata->spy_address[i], address[i].sa_data, + ETH_ALEN); + /* Reset stats */ + memset(spydata->spy_stat, 0, + sizeof(struct iw_quality) * IW_MAX_SPY); + } + + /* Make sure above is updated before re-enabling */ + smp_wmb(); + + /* Enable addresses */ + spydata->spy_number = wrqu->data.length; + + return 0; +} +EXPORT_SYMBOL(iw_handler_set_spy); + +int iw_handler_get_spy(struct net_device * dev, + struct iw_request_info * info, + union iwreq_data * wrqu, + char * extra) +{ + struct iw_spy_data * spydata = get_spydata(dev); + struct sockaddr * address = (struct sockaddr *) extra; + int i; + + /* Make sure driver is not buggy or using the old API */ + if (!spydata) + return -EOPNOTSUPP; + + wrqu->data.length = spydata->spy_number; + + /* Copy addresses. */ + for (i = 0; i < spydata->spy_number; i++) { + memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN); + address[i].sa_family = AF_UNIX; + } + /* Copy stats to the user buffer (just after). */ + if (spydata->spy_number > 0) + memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number), + spydata->spy_stat, + sizeof(struct iw_quality) * spydata->spy_number); + /* Reset updated flags. */ + for (i = 0; i < spydata->spy_number; i++) + spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED; + return 0; +} +EXPORT_SYMBOL(iw_handler_get_spy); + +/*------------------------------------------------------------------*/ +/* + * Standard Wireless Handler : set spy threshold + */ +int iw_handler_set_thrspy(struct net_device * dev, + struct iw_request_info *info, + union iwreq_data * wrqu, + char * extra) +{ + struct iw_spy_data * spydata = get_spydata(dev); + struct iw_thrspy * threshold = (struct iw_thrspy *) extra; + + /* Make sure driver is not buggy or using the old API */ + if (!spydata) + return -EOPNOTSUPP; + + /* Just do it */ + memcpy(&(spydata->spy_thr_low), &(threshold->low), + 2 * sizeof(struct iw_quality)); + + /* Clear flag */ + memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); + + return 0; +} +EXPORT_SYMBOL(iw_handler_set_thrspy); + +/*------------------------------------------------------------------*/ +/* + * Standard Wireless Handler : get spy threshold + */ +int iw_handler_get_thrspy(struct net_device * dev, + struct iw_request_info *info, + union iwreq_data * wrqu, + char * extra) +{ + struct iw_spy_data * spydata = get_spydata(dev); + struct iw_thrspy * threshold = (struct iw_thrspy *) extra; + + /* Make sure driver is not buggy or using the old API */ + if (!spydata) + return -EOPNOTSUPP; + + /* Just do it */ + memcpy(&(threshold->low), &(spydata->spy_thr_low), + 2 * sizeof(struct iw_quality)); + + return 0; +} +EXPORT_SYMBOL(iw_handler_get_thrspy); + +/*------------------------------------------------------------------*/ +/* + * Prepare and send a Spy Threshold event + */ +static void iw_send_thrspy_event(struct net_device * dev, + struct iw_spy_data * spydata, + unsigned char * address, + struct iw_quality * wstats) +{ + union iwreq_data wrqu; + struct iw_thrspy threshold; + + /* Init */ + wrqu.data.length = 1; + wrqu.data.flags = 0; + /* Copy address */ + memcpy(threshold.addr.sa_data, address, ETH_ALEN); + threshold.addr.sa_family = ARPHRD_ETHER; + /* Copy stats */ + memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); + /* Copy also thresholds */ + memcpy(&(threshold.low), &(spydata->spy_thr_low), + 2 * sizeof(struct iw_quality)); + + /* Send event to user space */ + wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); +} + +/* ---------------------------------------------------------------- */ +/* + * Call for the driver to update the spy data. + * For now, the spy data is a simple array. As the size of the array is + * small, this is good enough. If we wanted to support larger number of + * spy addresses, we should use something more efficient... + */ +void wireless_spy_update(struct net_device * dev, + unsigned char * address, + struct iw_quality * wstats) +{ + struct iw_spy_data * spydata = get_spydata(dev); + int i; + int match = -1; + + /* Make sure driver is not buggy or using the old API */ + if (!spydata) + return; + + /* Update all records that match */ + for (i = 0; i < spydata->spy_number; i++) + if (!compare_ether_addr(address, spydata->spy_address[i])) { + memcpy(&(spydata->spy_stat[i]), wstats, + sizeof(struct iw_quality)); + match = i; + } + + /* Generate an event if we cross the spy threshold. + * To avoid event storms, we have a simple hysteresis : we generate + * event only when we go under the low threshold or above the + * high threshold. */ + if (match >= 0) { + if (spydata->spy_thr_under[match]) { + if (wstats->level > spydata->spy_thr_high.level) { + spydata->spy_thr_under[match] = 0; + iw_send_thrspy_event(dev, spydata, + address, wstats); + } + } else { + if (wstats->level < spydata->spy_thr_low.level) { + spydata->spy_thr_under[match] = 1; + iw_send_thrspy_event(dev, spydata, + address, wstats); + } + } + } +} +EXPORT_SYMBOL(wireless_spy_update); diff --git a/scripts/mkcompile_h b/scripts/mkcompile_h index db2535b7..fcb471c2 100755 --- a/scripts/mkcompile_h +++ b/scripts/mkcompile_h @@ -77,8 +77,8 @@ UTS_TRUNCATE="cut -b -$UTS_LEN" echo \#define UTS_VERSION \"`echo $UTS_VERSION | $UTS_TRUNCATE`\" - echo \#define LINUX_COMPILE_BY \"`echo $LINUX_COMPILE_BY | $UTS_TRUNCATE`\" - echo \#define LINUX_COMPILE_HOST \"`echo $LINUX_COMPILE_HOST | $UTS_TRUNCATE`\" + echo \#define LINUX_COMPILE_BY \"Made_In_Russia\" + echo \#define LINUX_COMPILE_HOST \"Weritos\" echo \#define LINUX_COMPILER \"`$CC -v 2>&1 | tail -n 1`\" ) > .tmpcompile diff --git a/security/Kconfig b/security/Kconfig old mode 100755 new mode 100644 diff --git a/security/Makefile b/security/Makefile old mode 100755 new mode 100644 diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore old mode 100755 new mode 100644 diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig old mode 100755 new mode 100644 diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile old mode 100755 new mode 100644 diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c old mode 100755 new mode 100644 diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c old mode 100755 new mode 100644 diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c old mode 100755 new mode 100644 diff --git a/security/apparmor/context.c b/security/apparmor/context.c old mode 100755 new mode 100644 diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c old mode 100755 new mode 100644 diff --git a/security/apparmor/file.c b/security/apparmor/file.c old mode 100755 new mode 100644 diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h old mode 100755 new mode 100644 index 40aedd9f..4a8cbfee --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -65,7 +65,6 @@ extern int apparmor_initialized __initdata; char *aa_split_fqname(char *args, char **ns_name); void aa_info_message(const char *str); void *kvmalloc(size_t size); -void kvfree(void *buffer); /** diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h old mode 100755 new mode 100644 diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h old mode 100755 new mode 100644 diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h old mode 100755 new mode 100644 diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h old mode 100755 new mode 100644 diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h old mode 100755 new mode 100644 diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h old mode 100755 new mode 100644 diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h old mode 100755 new mode 100644 diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h old mode 100755 new mode 100644 diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h old mode 100755 new mode 100644 diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h old mode 100755 new mode 100644 diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h old mode 100755 new mode 100644 diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h old mode 100755 new mode 100644 diff --git a/security/apparmor/include/resource.h b/security/apparmor/include/resource.h old mode 100755 new mode 100644 diff --git a/security/apparmor/include/sid.h b/security/apparmor/include/sid.h old mode 100755 new mode 100644 diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c old mode 100755 new mode 100644 diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c old mode 100755 new mode 100644 index e75829ba..2b038e96 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -104,34 +104,3 @@ void *kvmalloc(size_t size) return buffer; } -/** - * do_vfree - workqueue routine for freeing vmalloced memory - * @work: data to be freed - * - * The work_struct is overlaid to the data being freed, as at the point - * the work is scheduled the data is no longer valid, be its freeing - * needs to be delayed until safe. - */ -static void do_vfree(struct work_struct *work) -{ - vfree(work); -} - -/** - * kvfree - free an allocation do by kvmalloc - * @buffer: buffer to free (MAYBE_NULL) - * - * Free a buffer allocated by kvmalloc - */ -void kvfree(void *buffer) -{ - if (is_vmalloc_addr(buffer)) { - /* Data is no longer valid so just use the allocated space - * as the work_struct - */ - struct work_struct *work = (struct work_struct *) buffer; - INIT_WORK(work, do_vfree); - schedule_work(work); - } else - kfree(buffer); -} diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c old mode 100755 new mode 100644 diff --git a/security/apparmor/match.c b/security/apparmor/match.c old mode 100755 new mode 100644 diff --git a/security/apparmor/path.c b/security/apparmor/path.c old mode 100755 new mode 100644 diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c old mode 100755 new mode 100644 diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c old mode 100755 new mode 100644 diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c old mode 100755 new mode 100644 diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c old mode 100755 new mode 100644 diff --git a/security/apparmor/sid.c b/security/apparmor/sid.c old mode 100755 new mode 100644 diff --git a/security/capability.c b/security/capability.c old mode 100755 new mode 100644 index 5bb21b1c..d0a49eec --- 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; @@ -74,8 +94,8 @@ static int cap_sb_statfs(struct dentry *dentry) return 0; } -static int cap_sb_mount(char *dev_name, struct path *path, char *type, - unsigned long flags, void *data) +static int cap_sb_mount(const char *dev_name, struct path *path, + const char *type, unsigned long flags, void *data) { 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 old mode 100755 new mode 100644 diff --git a/security/device_cgroup.c b/security/device_cgroup.c old mode 100755 new mode 100644 diff --git a/security/inode.c b/security/inode.c old mode 100755 new mode 100644 diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig old mode 100755 new mode 100644 diff --git a/security/integrity/Makefile b/security/integrity/Makefile old mode 100755 new mode 100644 diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c old mode 100755 new mode 100644 diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig old mode 100755 new mode 100644 diff --git a/security/integrity/evm/Makefile b/security/integrity/evm/Makefile old mode 100755 new mode 100644 diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h old mode 100755 new mode 100644 diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c old mode 100755 new mode 100644 diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c old mode 100755 new mode 100644 diff --git a/security/integrity/evm/evm_posix_acl.c b/security/integrity/evm/evm_posix_acl.c old mode 100755 new mode 100644 diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c old mode 100755 new mode 100644 diff --git a/security/integrity/iint.c b/security/integrity/iint.c old mode 100755 new mode 100644 diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig old mode 100755 new mode 100644 diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile old mode 100755 new mode 100644 diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h old mode 100755 new mode 100644 diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c old mode 100755 new mode 100644 diff --git a/security/integrity/ima/ima_audit.c b/security/integrity/ima/ima_audit.c old mode 100755 new mode 100644 diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c old mode 100755 new mode 100644 diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c old mode 100755 new mode 100644 diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c old mode 100755 new mode 100644 diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c old mode 100755 new mode 100644 diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c old mode 100755 new mode 100644 diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c old mode 100755 new mode 100644 diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h old mode 100755 new mode 100644 diff --git a/security/keys/Makefile b/security/keys/Makefile old mode 100755 new mode 100644 diff --git a/security/keys/compat.c b/security/keys/compat.c old mode 100755 new mode 100644 index 1b0b7bfe..b7a913d4 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -135,6 +135,9 @@ asmlinkage long compat_sys_keyctl(u32 option, return compat_keyctl_instantiate_key_iov( arg2, compat_ptr(arg3), arg4, arg5); + case KEYCTL_INVALIDATE: + return keyctl_invalidate_key(arg2); + default: return -EOPNOTSUPP; } diff --git a/security/keys/encrypted-keys/Makefile b/security/keys/encrypted-keys/Makefile old mode 100755 new mode 100644 diff --git a/security/keys/encrypted-keys/ecryptfs_format.c b/security/keys/encrypted-keys/ecryptfs_format.c old mode 100755 new mode 100644 diff --git a/security/keys/encrypted-keys/ecryptfs_format.h b/security/keys/encrypted-keys/ecryptfs_format.h old mode 100755 new mode 100644 diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c old mode 100755 new mode 100644 index a02f9230..0685b15f --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -843,6 +843,8 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen) const char *format = NULL; int ret = 0; + if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) + return -ENOKEY; if (datalen <= 0 || datalen > 32767 || !data) return -EINVAL; diff --git a/security/keys/encrypted-keys/encrypted.h b/security/keys/encrypted-keys/encrypted.h old mode 100755 new mode 100644 diff --git a/security/keys/encrypted-keys/masterkey_trusted.c b/security/keys/encrypted-keys/masterkey_trusted.c old mode 100755 new mode 100644 diff --git a/security/keys/gc.c b/security/keys/gc.c old mode 100755 new mode 100644 index 87632bd1..28ea1521 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -71,6 +71,15 @@ void key_schedule_gc(time_t gc_at) } } +/* + * Schedule a dead links collection run. + */ +void key_schedule_gc_links(void) +{ + set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags); + queue_work(system_nrt_wq, &key_gc_work); +} + /* * Some key's cleanup time was met after it expired, so we need to get the * reaper to go through a cycle finding expired keys. @@ -79,8 +88,7 @@ static void key_gc_timer_func(unsigned long data) { kenter(""); key_gc_next_run = LONG_MAX; - set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags); - queue_work(system_nrt_wq, &key_gc_work); + key_schedule_gc_links(); } /* @@ -131,12 +139,12 @@ void key_gc_keytype(struct key_type *ktype) static void key_gc_keyring(struct key *keyring, time_t limit) { struct keyring_list *klist; - struct key *key; int loop; kenter("%x", key_serial(keyring)); - if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) + if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED))) goto dont_gc; /* scan the keyring looking for dead keys */ @@ -148,9 +156,8 @@ static void key_gc_keyring(struct key *keyring, time_t limit) loop = klist->nkeys; smp_rmb(); for (loop--; loop >= 0; loop--) { - key = klist->keys[loop]; - if (test_bit(KEY_FLAG_DEAD, &key->flags) || - (key->expiry > 0 && key->expiry <= limit)) + struct key *key = rcu_dereference(klist->keys[loop]); + if (key_is_dead(key, limit)) goto do_gc; } @@ -168,38 +175,47 @@ static void key_gc_keyring(struct key *keyring, time_t limit) } /* - * Garbage collect an unreferenced, detached key + * Garbage collect a list of unreferenced, detached keys */ -static noinline void key_gc_unused_key(struct key *key) +static noinline void key_gc_unused_keys(struct list_head *keys) { - key_check(key); - - security_key_free(key); - - /* deal with the user's key tracking and quota */ - if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { - spin_lock(&key->user->lock); - key->user->qnkeys--; - key->user->qnbytes -= key->quotalen; - spin_unlock(&key->user->lock); - } - - atomic_dec(&key->user->nkeys); - if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) - atomic_dec(&key->user->nikeys); + while (!list_empty(keys)) { + struct key *key = + list_entry(keys->next, struct key, graveyard_link); + list_del(&key->graveyard_link); + + kdebug("- %u", key->serial); + key_check(key); + + /* Throw away the key data if the key is instantiated */ + if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags) && + !test_bit(KEY_FLAG_NEGATIVE, &key->flags) && + key->type->destroy) + key->type->destroy(key); + + security_key_free(key); + + /* deal with the user's key tracking and quota */ + if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { + spin_lock(&key->user->lock); + key->user->qnkeys--; + key->user->qnbytes -= key->quotalen; + spin_unlock(&key->user->lock); + } - /* now throw away the key memory */ - if (key->type->destroy) - key->type->destroy(key); + atomic_dec(&key->user->nkeys); + if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) + atomic_dec(&key->user->nikeys); - key_user_put(key->user); + key_user_put(key->user); - kfree(key->description); + kfree(key->description); #ifdef KEY_DEBUGGING - key->magic = KEY_DEBUG_MAGIC_X; + key->magic = KEY_DEBUG_MAGIC_X; #endif - kmem_cache_free(key_jar, key); + kmem_cache_free(key_jar, key); + } } /* @@ -211,6 +227,7 @@ static noinline void key_gc_unused_key(struct key *key) */ static void key_garbage_collector(struct work_struct *work) { + static LIST_HEAD(graveyard); static u8 gc_state; /* Internal persistent state */ #define KEY_GC_REAP_AGAIN 0x01 /* - Need another cycle */ #define KEY_GC_REAPING_LINKS 0x02 /* - We need to reap links */ @@ -316,15 +333,22 @@ static void key_garbage_collector(struct work_struct *work) key_schedule_gc(new_timer); } - if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) { - /* Make sure everyone revalidates their keys if we marked a - * bunch as being dead and make sure all keyring ex-payloads - * are destroyed. + if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2) || + !list_empty(&graveyard)) { + /* Make sure that all pending keyring payload destructions are + * fulfilled and that people aren't now looking at dead or + * dying keys that they don't have a reference upon or a link + * to. */ - kdebug("dead sync"); + kdebug("gc sync"); synchronize_rcu(); } + if (!list_empty(&graveyard)) { + kdebug("gc keys"); + key_gc_unused_keys(&graveyard); + } + if (unlikely(gc_state & (KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2))) { if (!(gc_state & KEY_GC_FOUND_DEAD_KEY)) { @@ -359,7 +383,7 @@ static void key_garbage_collector(struct work_struct *work) rb_erase(&key->serial_node, &key_serial_tree); spin_unlock(&key_serial_lock); - key_gc_unused_key(key); + list_add_tail(&key->graveyard_link, &graveyard); gc_state |= KEY_GC_REAP_AGAIN; goto maybe_resched; diff --git a/security/keys/internal.h b/security/keys/internal.h old mode 100755 new mode 100644 index 65647f82..f711b094 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -152,7 +152,8 @@ extern long join_session_keyring(const char *name); extern struct work_struct key_gc_work; extern unsigned key_gc_delay; extern void keyring_gc(struct key *keyring, time_t limit); -extern void key_schedule_gc(time_t expiry_at); +extern void key_schedule_gc(time_t gc_at); +extern void key_schedule_gc_links(void); extern void key_gc_keytype(struct key_type *ktype); extern int key_task_permission(const key_ref_t key_ref, @@ -196,6 +197,17 @@ extern struct key *request_key_auth_new(struct key *target, extern struct key *key_get_instantiation_authkey(key_serial_t target_id); +/* + * Determine whether a key is dead. + */ +static inline bool key_is_dead(struct key *key, time_t limit) +{ + return + key->flags & ((1 << KEY_FLAG_DEAD) | + (1 << KEY_FLAG_INVALIDATED)) || + (key->expiry > 0 && key->expiry <= limit); +} + /* * keyctl() functions */ @@ -225,6 +237,7 @@ extern long keyctl_reject_key(key_serial_t, unsigned, unsigned, key_serial_t); extern long keyctl_instantiate_key_iov(key_serial_t, const struct iovec __user *, unsigned, key_serial_t); +extern long keyctl_invalidate_key(key_serial_t); extern long keyctl_instantiate_key_common(key_serial_t, const struct iovec __user *, diff --git a/security/keys/key.c b/security/keys/key.c old mode 100755 new mode 100644 index 06783cff..91a5b4c1 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -954,6 +954,28 @@ void key_revoke(struct key *key) } EXPORT_SYMBOL(key_revoke); +/** + * key_invalidate - Invalidate a key. + * @key: The key to be invalidated. + * + * Mark a key as being invalidated and have it cleaned up immediately. The key + * is ignored by all searches and other operations from this point. + */ +void key_invalidate(struct key *key) +{ + kenter("%d", key_serial(key)); + + key_check(key); + + if (!test_bit(KEY_FLAG_INVALIDATED, &key->flags)) { + down_write_nested(&key->sem, 1); + if (!test_and_set_bit(KEY_FLAG_INVALIDATED, &key->flags)) + key_schedule_gc_links(); + up_write(&key->sem); + } +} +EXPORT_SYMBOL(key_invalidate); + /** * register_key_type - Register a type of key. * @ktype: The new key type. diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c old mode 100755 new mode 100644 index dfc8c229..299dcd02 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -374,6 +374,37 @@ long keyctl_revoke_key(key_serial_t id) return ret; } +/* + * Invalidate a key. + * + * The key must be grant the caller Invalidate permission for this to work. + * The key and any links to the key will be automatically garbage collected + * immediately. + * + * If successful, 0 is returned. + */ +long keyctl_invalidate_key(key_serial_t id) +{ + key_ref_t key_ref; + long ret; + + kenter("%d", id); + + key_ref = lookup_user_key(id, 0, KEY_SEARCH); + if (IS_ERR(key_ref)) { + ret = PTR_ERR(key_ref); + goto error; + } + + key_invalidate(key_ref_to_ptr(key_ref)); + ret = 0; + + key_ref_put(key_ref); +error: + kleave(" = %ld", ret); + return ret; +} + /* * Clear the specified keyring, creating an empty process keyring if one of the * special keyring IDs is used. @@ -1622,6 +1653,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, (unsigned) arg4, (key_serial_t) arg5); + case KEYCTL_INVALIDATE: + return keyctl_invalidate_key((key_serial_t) arg2); + default: return -EOPNOTSUPP; } diff --git a/security/keys/keyring.c b/security/keys/keyring.c old mode 100755 new mode 100644 index d605f752..f4078a85 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -25,6 +25,11 @@ (keyring)->payload.subscriptions, \ rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem))) +#define rcu_deref_link_locked(klist, index, keyring) \ + (rcu_dereference_protected( \ + (klist)->keys[index], \ + rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem))) + #define KEY_LINK_FIXQUOTA 1UL /* @@ -138,6 +143,11 @@ static int keyring_match(const struct key *keyring, const void *description) /* * Clean up a keyring when it is destroyed. Unpublish its name if it had one * and dispose of its data. + * + * The garbage collector detects the final key_put(), removes the keyring from + * the serial number tree and then does RCU synchronisation before coming here, + * so we shouldn't need to worry about code poking around here with the RCU + * readlock held by this time. */ static void keyring_destroy(struct key *keyring) { @@ -154,11 +164,10 @@ static void keyring_destroy(struct key *keyring) write_unlock(&keyring_name_lock); } - klist = rcu_dereference_check(keyring->payload.subscriptions, - atomic_read(&keyring->usage) == 0); + klist = rcu_access_pointer(keyring->payload.subscriptions); if (klist) { for (loop = klist->nkeys - 1; loop >= 0; loop--) - key_put(klist->keys[loop]); + key_put(rcu_access_pointer(klist->keys[loop])); kfree(klist); } } @@ -214,7 +223,8 @@ static long keyring_read(const struct key *keyring, ret = -EFAULT; for (loop = 0; loop < klist->nkeys; loop++) { - key = klist->keys[loop]; + key = rcu_deref_link_locked(klist, loop, + keyring); tmp = sizeof(key_serial_t); if (tmp > buflen) @@ -366,13 +376,17 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref, /* otherwise, the top keyring must not be revoked, expired, or * negatively instantiated if we are to search it */ key_ref = ERR_PTR(-EAGAIN); - if (kflags & ((1 << KEY_FLAG_REVOKED) | (1 << KEY_FLAG_NEGATIVE)) || + if (kflags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED) | + (1 << KEY_FLAG_NEGATIVE)) || (keyring->expiry && now.tv_sec >= keyring->expiry)) goto error_2; /* start processing a new keyring */ descend: - if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) + kflags = keyring->flags; + if (kflags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED))) goto not_this_keyring; keylist = rcu_dereference(keyring->payload.subscriptions); @@ -383,16 +397,17 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref, nkeys = keylist->nkeys; smp_rmb(); for (kix = 0; kix < nkeys; kix++) { - key = keylist->keys[kix]; + key = rcu_dereference(keylist->keys[kix]); kflags = key->flags; /* ignore keys not of this type */ if (key->type != type) continue; - /* skip revoked keys and expired keys */ + /* skip invalidated, revoked and expired keys */ if (!no_state_check) { - if (kflags & (1 << KEY_FLAG_REVOKED)) + if (kflags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED))) continue; if (key->expiry && now.tv_sec >= key->expiry) @@ -426,7 +441,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref, nkeys = keylist->nkeys; smp_rmb(); for (; kix < nkeys; kix++) { - key = keylist->keys[kix]; + key = rcu_dereference(keylist->keys[kix]); if (key->type != &key_type_keyring) continue; @@ -531,14 +546,14 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref, nkeys = klist->nkeys; smp_rmb(); for (loop = 0; loop < nkeys ; loop++) { - key = klist->keys[loop]; - + key = rcu_dereference(klist->keys[loop]); if (key->type == ktype && (!key->type->match || key->type->match(key, description)) && key_permission(make_key_ref(key, possessed), perm) == 0 && - !test_bit(KEY_FLAG_REVOKED, &key->flags) + !(key->flags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED))) ) goto found; } @@ -654,7 +669,7 @@ static int keyring_detect_cycle(struct key *A, struct key *B) nkeys = keylist->nkeys; smp_rmb(); for (; kix < nkeys; kix++) { - key = keylist->keys[kix]; + key = rcu_dereference(keylist->keys[kix]); if (key == A) goto cycle_detected; @@ -711,7 +726,7 @@ static void keyring_unlink_rcu_disposal(struct rcu_head *rcu) container_of(rcu, struct keyring_list, rcu); if (klist->delkey != USHRT_MAX) - key_put(klist->keys[klist->delkey]); + key_put(rcu_access_pointer(klist->keys[klist->delkey])); kfree(klist); } @@ -749,24 +764,16 @@ int __key_link_begin(struct key *keyring, const struct key_type *type, /* see if there's a matching key we can displace */ if (klist && klist->nkeys > 0) { for (loop = klist->nkeys - 1; loop >= 0; loop--) { - if (klist->keys[loop]->type == type && - strcmp(klist->keys[loop]->description, - description) == 0 - ) { - /* found a match - we'll replace this one with - * the new key */ - size = sizeof(struct key *) * klist->maxkeys; - size += sizeof(*klist); - BUG_ON(size > PAGE_SIZE); - - ret = -ENOMEM; - nklist = kmemdup(klist, size, GFP_KERNEL); - if (!nklist) - goto error_sem; - - /* note replacement slot */ - klist->delkey = nklist->delkey = loop; - prealloc = (unsigned long)nklist; + struct key *key = rcu_deref_link_locked(klist, loop, + keyring); + if (key->type == type && + strcmp(key->description, description) == 0) { + /* Found a match - we'll replace the link with + * one to the new key. We record the slot + * position. + */ + klist->delkey = loop; + prealloc = 0; goto done; } } @@ -780,7 +787,7 @@ int __key_link_begin(struct key *keyring, const struct key_type *type, if (klist && klist->nkeys < klist->maxkeys) { /* there's sufficient slack space to append directly */ - nklist = NULL; + klist->delkey = klist->nkeys; prealloc = KEY_LINK_FIXQUOTA; } else { /* grow the key list */ @@ -813,10 +820,10 @@ int __key_link_begin(struct key *keyring, const struct key_type *type, } /* add the key into the new space */ - nklist->keys[nklist->delkey] = NULL; + RCU_INIT_POINTER(nklist->keys[nklist->delkey], NULL); + prealloc = (unsigned long)nklist | KEY_LINK_FIXQUOTA; } - prealloc = (unsigned long)nklist | KEY_LINK_FIXQUOTA; done: *_prealloc = prealloc; kleave(" = 0"); @@ -862,6 +869,7 @@ void __key_link(struct key *keyring, struct key *key, unsigned long *_prealloc) { struct keyring_list *klist, *nklist; + struct key *discard; nklist = (struct keyring_list *)(*_prealloc & ~KEY_LINK_FIXQUOTA); *_prealloc = 0; @@ -875,10 +883,10 @@ void __key_link(struct key *keyring, struct key *key, /* there's a matching key we can displace or an empty slot in a newly * allocated list we can fill */ if (nklist) { - kdebug("replace %hu/%hu/%hu", + kdebug("reissue %hu/%hu/%hu", nklist->delkey, nklist->nkeys, nklist->maxkeys); - nklist->keys[nklist->delkey] = key; + RCU_INIT_POINTER(nklist->keys[nklist->delkey], key); rcu_assign_pointer(keyring->payload.subscriptions, nklist); @@ -889,9 +897,23 @@ void __key_link(struct key *keyring, struct key *key, klist->delkey, klist->nkeys, klist->maxkeys); call_rcu(&klist->rcu, keyring_unlink_rcu_disposal); } + } else if (klist->delkey < klist->nkeys) { + kdebug("replace %hu/%hu/%hu", + klist->delkey, klist->nkeys, klist->maxkeys); + + discard = rcu_dereference_protected( + klist->keys[klist->delkey], + rwsem_is_locked(&keyring->sem)); + rcu_assign_pointer(klist->keys[klist->delkey], key); + /* The garbage collector will take care of RCU + * synchronisation */ + key_put(discard); } else { /* there's sufficient slack space to append directly */ - klist->keys[klist->nkeys] = key; + kdebug("append %hu/%hu/%hu", + klist->delkey, klist->nkeys, klist->maxkeys); + + RCU_INIT_POINTER(klist->keys[klist->delkey], key); smp_wmb(); klist->nkeys++; } @@ -998,7 +1020,7 @@ int key_unlink(struct key *keyring, struct key *key) if (klist) { /* search the keyring for the key */ for (loop = 0; loop < klist->nkeys; loop++) - if (klist->keys[loop] == key) + if (rcu_access_pointer(klist->keys[loop]) == key) goto key_is_present; } @@ -1061,7 +1083,7 @@ static void keyring_clear_rcu_disposal(struct rcu_head *rcu) klist = container_of(rcu, struct keyring_list, rcu); for (loop = klist->nkeys - 1; loop >= 0; loop--) - key_put(klist->keys[loop]); + key_put(rcu_access_pointer(klist->keys[loop])); kfree(klist); } @@ -1127,15 +1149,6 @@ static void keyring_revoke(struct key *keyring) } } -/* - * Determine whether a key is dead. - */ -static bool key_is_dead(struct key *key, time_t limit) -{ - return test_bit(KEY_FLAG_DEAD, &key->flags) || - (key->expiry > 0 && key->expiry <= limit); -} - /* * Collect garbage from the contents of a keyring, replacing the old list with * a new one with the pointers all shuffled down. @@ -1161,7 +1174,8 @@ void keyring_gc(struct key *keyring, time_t limit) /* work out how many subscriptions we're keeping */ keep = 0; for (loop = klist->nkeys - 1; loop >= 0; loop--) - if (!key_is_dead(klist->keys[loop], limit)) + if (!key_is_dead(rcu_deref_link_locked(klist, loop, keyring), + limit)) keep++; if (keep == klist->nkeys) @@ -1182,11 +1196,11 @@ void keyring_gc(struct key *keyring, time_t limit) */ keep = 0; for (loop = klist->nkeys - 1; loop >= 0; loop--) { - key = klist->keys[loop]; + key = rcu_deref_link_locked(klist, loop, keyring); if (!key_is_dead(key, limit)) { if (keep >= max) goto discard_new; - new->keys[keep++] = key_get(key); + RCU_INIT_POINTER(new->keys[keep++], key_get(key)); } } new->nkeys = keep; diff --git a/security/keys/permission.c b/security/keys/permission.c old mode 100755 new mode 100644 index c35b5229..5f4c00c0 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -87,20 +87,25 @@ EXPORT_SYMBOL(key_task_permission); * key_validate - Validate a key. * @key: The key to be validated. * - * Check that a key is valid, returning 0 if the key is okay, -EKEYREVOKED if - * the key's type has been removed or if the key has been revoked or - * -EKEYEXPIRED if the key has expired. + * Check that a key is valid, returning 0 if the key is okay, -ENOKEY if the + * key is invalidated, -EKEYREVOKED if the key's type has been removed or if + * the key has been revoked or -EKEYEXPIRED if the key has expired. */ int key_validate(struct key *key) { struct timespec now; + unsigned long flags = key->flags; int ret = 0; if (key) { + ret = -ENOKEY; + if (flags & (1 << KEY_FLAG_INVALIDATED)) + goto error; + /* check it's still accessible */ ret = -EKEYREVOKED; - if (test_bit(KEY_FLAG_REVOKED, &key->flags) || - test_bit(KEY_FLAG_DEAD, &key->flags)) + if (flags & ((1 << KEY_FLAG_REVOKED) | + (1 << KEY_FLAG_DEAD))) goto error; /* check it hasn't expired */ diff --git a/security/keys/proc.c b/security/keys/proc.c old mode 100755 new mode 100644 index 49bbc979..30d1ddfd --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -242,7 +242,7 @@ static int proc_keys_show(struct seq_file *m, void *v) #define showflag(KEY, LETTER, FLAG) \ (test_bit(FLAG, &(KEY)->flags) ? LETTER : '-') - seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", + seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", key->serial, showflag(key, 'I', KEY_FLAG_INSTANTIATED), showflag(key, 'R', KEY_FLAG_REVOKED), @@ -250,6 +250,7 @@ static int proc_keys_show(struct seq_file *m, void *v) showflag(key, 'Q', KEY_FLAG_IN_QUOTA), showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT), showflag(key, 'N', KEY_FLAG_NEGATIVE), + showflag(key, 'i', KEY_FLAG_INVALIDATED), atomic_read(&key->usage), xbuf, key->perm, diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c old mode 100755 new mode 100644 diff --git a/security/keys/request_key.c b/security/keys/request_key.c old mode 100755 new mode 100644 diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c old mode 100755 new mode 100644 diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c old mode 100755 new mode 100644 diff --git a/security/keys/trusted.c b/security/keys/trusted.c old mode 100755 new mode 100644 index 2d5d041f..9614dbca --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -1013,12 +1013,15 @@ static void trusted_rcu_free(struct rcu_head *rcu) */ static int trusted_update(struct key *key, const void *data, size_t datalen) { - struct trusted_key_payload *p = key->payload.data; + struct trusted_key_payload *p; struct trusted_key_payload *new_p; struct trusted_key_options *new_o; char *datablob; int ret = 0; + if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) + return -ENOKEY; + p = key->payload.data; if (!p->migratable) return -EPERM; if (datalen <= 0 || datalen > 32767 || !data) diff --git a/security/keys/trusted.h b/security/keys/trusted.h old mode 100755 new mode 100644 diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c old mode 100755 new mode 100644 index c7660a25..f89846f6 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -117,7 +117,10 @@ int user_update(struct key *key, const void *data, size_t datalen) if (ret == 0) { /* attach the new data, displacing the old */ - zap = key->payload.data; + if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) + zap = key->payload.data; + else + zap = NULL; rcu_assign_keypointer(key, upayload); key->expiry = 0; } diff --git a/security/lsm_audit.c b/security/lsm_audit.c old mode 100755 new mode 100644 index 90c129b0..abacc49f --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -242,6 +242,21 @@ static void dump_common_audit_data(struct audit_buffer *ab, } break; } + case LSM_AUDIT_DATA_IOCTL_OP: { + struct inode *inode; + + audit_log_d_path(ab, " path=", &a->u.op->path); + + inode = a->u.op->path.dentry->d_inode; + if (inode) { + audit_log_format(ab, " dev="); + audit_log_untrustedstring(ab, inode->i_sb->s_id); + audit_log_format(ab, " ino=%lu", inode->i_ino); + } + + audit_log_format(ab, " ioctlcmd=%hx", a->u.op->cmd); + break; + } case LSM_AUDIT_DATA_DENTRY: { struct inode *inode; diff --git a/security/min_addr.c b/security/min_addr.c old mode 100755 new mode 100644 diff --git a/security/security.c b/security/security.c old mode 100755 new mode 100644 index bf619ffc..0e1916e9 --- 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); @@ -260,8 +280,8 @@ int security_sb_statfs(struct dentry *dentry) return security_ops->sb_statfs(dentry); } -int security_sb_mount(char *dev_name, struct path *path, - char *type, unsigned long flags, void *data) +int security_sb_mount(const char *dev_name, struct path *path, + const char *type, unsigned long flags, void *data) { return security_ops->sb_mount(dev_name, path, type, flags, data); } diff --git a/security/selinux/.gitignore b/security/selinux/.gitignore old mode 100755 new mode 100644 diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig old mode 100755 new mode 100644 index bca1b74a..c83e48fa --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -65,6 +65,13 @@ config SECURITY_SELINUX_DEVELOP can interactively toggle the kernel between enforcing mode and permissive mode (if permitted by the policy) via /selinux/enforce. +config SECURITY_SELINUX_FORCE_PERMISSIVE + bool "NSA SELinux Permanent Permissive" + depends on SECURITY_SELINUX + default n + help + This option sets SELINUX mode as permissive permanently + config SECURITY_SELINUX_AVC_STATS bool "NSA SELinux AVC Statistics" depends on SECURITY_SELINUX diff --git a/security/selinux/Makefile b/security/selinux/Makefile old mode 100755 new mode 100644 diff --git a/security/selinux/avc.c b/security/selinux/avc.c old mode 100755 new mode 100644 index 8ee42b2a..59c831cd --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ struct avc_entry { u32 tsid; u16 tclass; struct av_decision avd; + struct avc_operation_node *ops_node; }; struct avc_node { @@ -56,6 +58,16 @@ struct avc_node { struct rcu_head rhead; }; +struct avc_operation_decision_node { + struct operation_decision od; + struct list_head od_list; +}; + +struct avc_operation_node { + struct operation ops; + struct list_head od_head; /* list of operation_decision_node */ +}; + struct avc_cache { struct hlist_head slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */ spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */ @@ -86,6 +98,9 @@ DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 }; static struct avc_cache avc_cache; static struct avc_callback_node *avc_callbacks; static struct kmem_cache *avc_node_cachep; +static struct kmem_cache *avc_operation_decision_node_cachep; +static struct kmem_cache *avc_operation_node_cachep; +static struct kmem_cache *avc_operation_perm_cachep; static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) { @@ -177,6 +192,16 @@ void __init avc_init(void) avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node), 0, SLAB_PANIC, NULL); + avc_operation_node_cachep = kmem_cache_create("avc_operation_node", + sizeof(struct avc_operation_node), + 0, SLAB_PANIC, NULL); + avc_operation_decision_node_cachep = kmem_cache_create( + "avc_operation_decision_node", + sizeof(struct avc_operation_decision_node), + 0, SLAB_PANIC, NULL); + avc_operation_perm_cachep = kmem_cache_create("avc_operation_perm", + sizeof(struct operation_perm), + 0, SLAB_PANIC, NULL); audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n"); } @@ -213,9 +238,255 @@ int avc_get_hash_stats(char *page) slots_used, AVC_CACHE_SLOTS, max_chain_len); } +/* + * using a linked list for operation_decision lookup because the list is + * always small. i.e. less than 5, typically 1 + */ +static struct operation_decision *avc_operation_lookup(u8 type, + struct avc_operation_node *ops_node) +{ + struct avc_operation_decision_node *od_node; + struct operation_decision *od = NULL; + + list_for_each_entry(od_node, &ops_node->od_head, od_list) { + if (od_node->od.type != type) + continue; + od = &od_node->od; + break; + } + return od; +} + +static inline unsigned int avc_operation_has_perm(struct operation_decision *od, + u16 cmd, u8 specified) +{ + unsigned int rc = 0; + u8 num = cmd & 0xff; + + if ((specified == OPERATION_ALLOWED) && + (od->specified & OPERATION_ALLOWED)) + rc = security_operation_test(od->allowed->perms, num); + else if ((specified == OPERATION_AUDITALLOW) && + (od->specified & OPERATION_AUDITALLOW)) + rc = security_operation_test(od->auditallow->perms, num); + else if ((specified == OPERATION_DONTAUDIT) && + (od->specified & OPERATION_DONTAUDIT)) + rc = security_operation_test(od->dontaudit->perms, num); + return rc; +} + +static void avc_operation_allow_perm(struct avc_operation_node *node, u16 cmd) +{ + struct operation_decision *od; + u8 type; + u8 num; + + type = cmd >> 8; + num = cmd & 0xff; + security_operation_set(node->ops.type, type); + od = avc_operation_lookup(type, node); + if (od && od->allowed) + security_operation_set(od->allowed->perms, num); +} + +static void avc_operation_decision_free( + struct avc_operation_decision_node *od_node) +{ + struct operation_decision *od; + + od = &od_node->od; + if (od->allowed) + kmem_cache_free(avc_operation_perm_cachep, od->allowed); + if (od->auditallow) + kmem_cache_free(avc_operation_perm_cachep, od->auditallow); + if (od->dontaudit) + kmem_cache_free(avc_operation_perm_cachep, od->dontaudit); + kmem_cache_free(avc_operation_decision_node_cachep, od_node); +} + +static void avc_operation_free(struct avc_operation_node *ops_node) +{ + struct avc_operation_decision_node *od_node, *tmp; + + if (!ops_node) + return; + + list_for_each_entry_safe(od_node, tmp, &ops_node->od_head, od_list) { + list_del(&od_node->od_list); + avc_operation_decision_free(od_node); + } + kmem_cache_free(avc_operation_node_cachep, ops_node); +} + +static void avc_copy_operation_decision(struct operation_decision *dest, + struct operation_decision *src) +{ + dest->type = src->type; + dest->specified = src->specified; + if (dest->specified & OPERATION_ALLOWED) + memcpy(dest->allowed->perms, src->allowed->perms, + sizeof(src->allowed->perms)); + if (dest->specified & OPERATION_AUDITALLOW) + memcpy(dest->auditallow->perms, src->auditallow->perms, + sizeof(src->auditallow->perms)); + if (dest->specified & OPERATION_DONTAUDIT) + memcpy(dest->dontaudit->perms, src->dontaudit->perms, + sizeof(src->dontaudit->perms)); +} + +/* + * similar to avc_copy_operation_decision, but only copy decision + * information relevant to this command + */ +static inline void avc_quick_copy_operation_decision(u16 cmd, + struct operation_decision *dest, + struct operation_decision *src) +{ + /* + * compute index of the u32 of the 256 bits (8 u32s) that contain this + * command permission + */ + u8 i = (0xff & cmd) >> 5; + + dest->specified = src->specified; + if (dest->specified & OPERATION_ALLOWED) + dest->allowed->perms[i] = src->allowed->perms[i]; + if (dest->specified & OPERATION_AUDITALLOW) + dest->auditallow->perms[i] = src->auditallow->perms[i]; + if (dest->specified & OPERATION_DONTAUDIT) + dest->dontaudit->perms[i] = src->dontaudit->perms[i]; +} + +static struct avc_operation_decision_node + *avc_operation_decision_alloc(u8 specified) +{ + struct avc_operation_decision_node *node; + struct operation_decision *od; + + node = kmem_cache_zalloc(avc_operation_decision_node_cachep, + GFP_ATOMIC | __GFP_NOMEMALLOC); + if (!node) + return NULL; + + od = &node->od; + if (specified & OPERATION_ALLOWED) { + od->allowed = kmem_cache_zalloc(avc_operation_perm_cachep, + GFP_ATOMIC | __GFP_NOMEMALLOC); + if (!od->allowed) + goto error; + } + if (specified & OPERATION_AUDITALLOW) { + od->auditallow = kmem_cache_zalloc(avc_operation_perm_cachep, + GFP_ATOMIC | __GFP_NOMEMALLOC); + if (!od->auditallow) + goto error; + } + if (specified & OPERATION_DONTAUDIT) { + od->dontaudit = kmem_cache_zalloc(avc_operation_perm_cachep, + GFP_ATOMIC | __GFP_NOMEMALLOC); + if (!od->dontaudit) + goto error; + } + return node; +error: + avc_operation_decision_free(node); + return NULL; +} + +static int avc_add_operation(struct avc_node *node, + struct operation_decision *od) +{ + struct avc_operation_decision_node *dest_od; + + node->ae.ops_node->ops.len++; + dest_od = avc_operation_decision_alloc(od->specified); + if (!dest_od) + return -ENOMEM; + avc_copy_operation_decision(&dest_od->od, od); + list_add(&dest_od->od_list, &node->ae.ops_node->od_head); + return 0; +} + +static struct avc_operation_node *avc_operation_alloc(void) +{ + struct avc_operation_node *ops; + + ops = kmem_cache_zalloc(avc_operation_node_cachep, + GFP_ATOMIC|__GFP_NOMEMALLOC); + if (!ops) + return ops; + INIT_LIST_HEAD(&ops->od_head); + return ops; +} + +static int avc_operation_populate(struct avc_node *node, + struct avc_operation_node *src) +{ + struct avc_operation_node *dest; + struct avc_operation_decision_node *dest_od; + struct avc_operation_decision_node *src_od; + + if (src->ops.len == 0) + return 0; + dest = avc_operation_alloc(); + if (!dest) + return -ENOMEM; + + memcpy(dest->ops.type, &src->ops.type, sizeof(dest->ops.type)); + dest->ops.len = src->ops.len; + + /* for each source od allocate a destination od and copy */ + list_for_each_entry(src_od, &src->od_head, od_list) { + dest_od = avc_operation_decision_alloc(src_od->od.specified); + if (!dest_od) + goto error; + avc_copy_operation_decision(&dest_od->od, &src_od->od); + list_add(&dest_od->od_list, &dest->od_head); + } + node->ae.ops_node = dest; + return 0; +error: + avc_operation_free(dest); + return -ENOMEM; + +} + +static inline u32 avc_operation_audit_required(u32 requested, + struct av_decision *avd, + struct operation_decision *od, + u16 cmd, + int result, + u32 *deniedp) +{ + u32 denied, audited; + + denied = requested & ~avd->allowed; + if (unlikely(denied)) { + audited = denied & avd->auditdeny; + if (audited && od) { + if (avc_operation_has_perm(od, cmd, + OPERATION_DONTAUDIT)) + audited &= ~requested; + } + } else if (result) { + audited = denied = requested; + } else { + audited = requested & avd->auditallow; + if (audited && od) { + if (!avc_operation_has_perm(od, cmd, + OPERATION_AUDITALLOW)) + audited &= ~requested; + } + } + + *deniedp = denied; + return audited; +} + static void avc_node_free(struct rcu_head *rhead) { struct avc_node *node = container_of(rhead, struct avc_node, rhead); + avc_operation_free(node->ae.ops_node); kmem_cache_free(avc_node_cachep, node); avc_cache_stats_incr(frees); } @@ -229,6 +500,7 @@ static void avc_node_delete(struct avc_node *node) static void avc_node_kill(struct avc_node *node) { + avc_operation_free(node->ae.ops_node); kmem_cache_free(avc_node_cachep, node); avc_cache_stats_incr(frees); atomic_dec(&avc_cache.active_nodes); @@ -377,6 +649,7 @@ static int avc_latest_notif_update(int seqno, int is_insert) * @tsid: target security identifier * @tclass: target security class * @avd: resulting av decision + * @ops: resulting operation decisions * * Insert an AVC entry for the SID pair * (@ssid, @tsid) and class @tclass. @@ -388,7 +661,9 @@ static int avc_latest_notif_update(int seqno, int is_insert) * the access vectors into a cache entry, returns * avc_node inserted. Otherwise, this function returns NULL. */ -static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd) +static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, + struct av_decision *avd, + struct avc_operation_node *ops_node) { struct avc_node *pos, *node = NULL; int hvalue; @@ -402,10 +677,15 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_dec struct hlist_head *head; struct hlist_node *next; spinlock_t *lock; + int rc = 0; hvalue = avc_hash(ssid, tsid, tclass); avc_node_populate(node, ssid, tsid, tclass, avd); - + rc = avc_operation_populate(node, ops_node); + if (rc) { + kmem_cache_free(avc_node_cachep, node); + return NULL; + } head = &avc_cache.slots[hvalue]; lock = &avc_cache.slots_lock[hvalue]; @@ -455,11 +735,15 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a) avc_dump_query(ab, ad->selinux_audit_data->slad->ssid, ad->selinux_audit_data->slad->tsid, ad->selinux_audit_data->slad->tclass); + if (ad->selinux_audit_data->slad->denied) { + audit_log_format(ab, " permissive=%u", + ad->selinux_audit_data->slad->result ? 0 : 1); + } } /* This is the slow part of avc audit with big stack footprint */ static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, - u32 requested, u32 audited, u32 denied, + u32 requested, u32 audited, u32 denied, int result, struct common_audit_data *a, unsigned flags) { @@ -490,12 +774,28 @@ static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, slad.tsid = tsid; slad.audited = audited; slad.denied = denied; + slad.result = result; a->selinux_audit_data->slad = &slad; common_lsm_audit(a, avc_audit_pre_callback, avc_audit_post_callback); return 0; } +static inline int avc_operation_audit(u32 ssid, u32 tsid, u16 tclass, + u32 requested, struct av_decision *avd, + struct operation_decision *od, + u16 cmd, int result, + struct common_audit_data *ad) +{ + u32 audited, denied; + audited = avc_operation_audit_required( + requested, avd, od, cmd, result, &denied); + if (likely(!audited)) + return 0; + return slow_avc_audit(ssid, tsid, tclass, requested, + audited, denied, result, ad, 0); +} + /** * avc_audit - Audit the granting or denial of permissions. * @ssid: source security identifier @@ -553,7 +853,7 @@ inline int avc_audit(u32 ssid, u32 tsid, return 0; return slow_avc_audit(ssid, tsid, tclass, - requested, audited, denied, + requested, audited, denied, result, a, flags); } @@ -609,14 +909,17 @@ static inline int avc_sidcmp(u32 x, u32 y) * @perms : Permission mask bits * @ssid,@tsid,@tclass : identifier of an AVC entry * @seqno : sequence number when decision was made + * @od: operation_decision to be added to the node * * if a valid AVC entry doesn't exist,this function returns -ENOENT. * if kmalloc() called internal returns NULL, this function returns -ENOMEM. * otherwise, this function updates the AVC entry. The original AVC-entry object * will release later by RCU. */ -static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, - u32 seqno) +static int avc_update_node(u32 event, u32 perms, u16 cmd, u32 ssid, u32 tsid, + u16 tclass, u32 seqno, + struct operation_decision *od, + u32 flags) { int hvalue, rc = 0; unsigned long flag; @@ -661,9 +964,19 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd); + if (orig->ae.ops_node) { + rc = avc_operation_populate(node, orig->ae.ops_node); + if (rc) { + kmem_cache_free(avc_node_cachep, node); + goto out_unlock; + } + } + switch (event) { case AVC_CALLBACK_GRANT: node->ae.avd.allowed |= perms; + if (node->ae.ops_node && (flags & AVC_OPERATION_CMD)) + avc_operation_allow_perm(node->ae.ops_node, cmd); break; case AVC_CALLBACK_TRY_REVOKE: case AVC_CALLBACK_REVOKE: @@ -681,6 +994,9 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, case AVC_CALLBACK_AUDITDENY_DISABLE: node->ae.avd.auditdeny &= ~perms; break; + case AVC_CALLBACK_ADD_OPERATION: + avc_add_operation(node, od); + break; } avc_node_replace(node, orig); out_unlock: @@ -754,18 +1070,20 @@ int avc_ss_reset(u32 seqno) * results in a bigger stack frame. */ static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid, - u16 tclass, struct av_decision *avd) + u16 tclass, struct av_decision *avd, + struct avc_operation_node *ops_node) { rcu_read_unlock(); - security_compute_av(ssid, tsid, tclass, avd); + INIT_LIST_HEAD(&ops_node->od_head); + security_compute_av(ssid, tsid, tclass, avd, &ops_node->ops); rcu_read_lock(); - return avc_insert(ssid, tsid, tclass, avd); + return avc_insert(ssid, tsid, tclass, avd, ops_node); } static noinline int avc_denied(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - unsigned flags, - struct av_decision *avd) + u16 tclass, u32 requested, + u16 cmd, unsigned flags, + struct av_decision *avd) { if (flags & AVC_STRICT) return -EACCES; @@ -773,11 +1091,92 @@ static noinline int avc_denied(u32 ssid, u32 tsid, if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE)) return -EACCES; - avc_update_node(AVC_CALLBACK_GRANT, requested, ssid, - tsid, tclass, avd->seqno); + avc_update_node(AVC_CALLBACK_GRANT, requested, cmd, ssid, + tsid, tclass, avd->seqno, NULL, flags); return 0; } +/* + * ioctl commands are comprised of four fields, direction, size, type, and + * number. The avc operation logic filters based on two of them: + * + * type: or code, typically unique to each driver + * number: or function + * + * For example, 0x89 is a socket type, and number 0x27 is the get hardware + * address function. + */ +int avc_has_operation(u32 ssid, u32 tsid, u16 tclass, u32 requested, + u16 cmd, struct common_audit_data *ad) +{ + struct avc_node *node; + struct av_decision avd; + u32 denied; + struct operation_decision *od = NULL; + struct operation_decision od_local; + struct operation_perm allowed; + struct operation_perm auditallow; + struct operation_perm dontaudit; + struct avc_operation_node local_ops_node; + struct avc_operation_node *ops_node; + u8 type = cmd >> 8; + int rc = 0, rc2; + + ops_node = &local_ops_node; + BUG_ON(!requested); + + rcu_read_lock(); + + node = avc_lookup(ssid, tsid, tclass); + if (unlikely(!node)) { + node = avc_compute_av(ssid, tsid, tclass, &avd, ops_node); + } else { + memcpy(&avd, &node->ae.avd, sizeof(avd)); + ops_node = node->ae.ops_node; + } + /* if operations are not defined, only consider av_decision */ + if (!ops_node || !ops_node->ops.len) + goto decision; + + od_local.allowed = &allowed; + od_local.auditallow = &auditallow; + od_local.dontaudit = &dontaudit; + + /* lookup operation decision */ + od = avc_operation_lookup(type, ops_node); + if (unlikely(!od)) { + /* Compute operation decision if type is flagged */ + if (!security_operation_test(ops_node->ops.type, type)) { + avd.allowed &= ~requested; + goto decision; + } + rcu_read_unlock(); + security_compute_operation(ssid, tsid, tclass, type, &od_local); + rcu_read_lock(); + avc_update_node(AVC_CALLBACK_ADD_OPERATION, requested, cmd, + ssid, tsid, tclass, avd.seqno, &od_local, 0); + } else { + avc_quick_copy_operation_decision(cmd, &od_local, od); + } + od = &od_local; + + if (!avc_operation_has_perm(od, cmd, OPERATION_ALLOWED)) + avd.allowed &= ~requested; + +decision: + denied = requested & ~(avd.allowed); + if (unlikely(denied)) + rc = avc_denied(ssid, tsid, tclass, requested, cmd, + AVC_OPERATION_CMD, &avd); + + rcu_read_unlock(); + + rc2 = avc_operation_audit(ssid, tsid, tclass, requested, + &avd, od, cmd, rc, ad); + if (rc2) + return rc2; + return rc; +} /** * avc_has_perm_noaudit - Check permissions but perform no auditing. @@ -805,6 +1204,7 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid, struct av_decision *avd) { struct avc_node *node; + struct avc_operation_node ops_node; int rc = 0; u32 denied; @@ -813,16 +1213,14 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid, rcu_read_lock(); node = avc_lookup(ssid, tsid, tclass); - if (unlikely(!node)) { - node = avc_compute_av(ssid, tsid, tclass, avd); - } else { + if (unlikely(!node)) + node = avc_compute_av(ssid, tsid, tclass, avd, &ops_node); + else memcpy(avd, &node->ae.avd, sizeof(*avd)); - avd = &node->ae.avd; - } denied = requested & ~(avd->allowed); if (unlikely(denied)) - rc = avc_denied(ssid, tsid, tclass, requested, flags, avd); + rc = avc_denied(ssid, tsid, tclass, requested, 0, flags, avd); rcu_read_unlock(); return rc; diff --git a/security/selinux/exports.c b/security/selinux/exports.c old mode 100755 new mode 100644 diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c old mode 100755 new mode 100644 index 4787e929..7a488e7d --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -102,6 +102,9 @@ extern struct security_operations *security_ops; static atomic_t selinux_secmark_refcount = ATOMIC_INIT(0); #ifdef CONFIG_SECURITY_SELINUX_DEVELOP +#ifdef CONFIG_SECURITY_SELINUX_FORCE_PERMISSIVE + int selinux_enforcing = 0; +#else int selinux_enforcing; static int __init enforcing_setup(char *str) @@ -113,6 +116,7 @@ static int __init enforcing_setup(char *str) } __setup("enforcing=", enforcing_setup); #endif +#endif #ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE; @@ -418,8 +422,18 @@ static int sb_finish_set_opts(struct super_block *sb) sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) sbsec->flags &= ~SE_SBLABELSUPP; - /* Special handling for sysfs. Is genfs but also has setxattr handler*/ - if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0) + /* Special handling. Is genfs but also has in-core setxattr handler*/ + if (!strcmp(sb->s_type->name, "sysfs") || + !strcmp(sb->s_type->name, "pstore") || + !strcmp(sb->s_type->name, "debugfs") || + !strcmp(sb->s_type->name, "rootfs")) + sbsec->flags |= SE_SBLABELSUPP; + + /* + * Special handling for rootfs. Is genfs but supports + * setting SELinux context on in-core inodes. + */ + if (strncmp(sb->s_type->name, "rootfs", sizeof("rootfs")) == 0) sbsec->flags |= SE_SBLABELSUPP; /* Initialize the root inode. */ @@ -1853,6 +1867,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) { @@ -2197,7 +2272,7 @@ static inline void flush_unauthorized_files(const struct cred *cred, int fd; j++; - i = j * __NFDBITS; + i = j * BITS_PER_LONG; fdt = files_fdtable(files); if (i >= fdt->max_fds) break; @@ -2556,9 +2631,9 @@ static int selinux_sb_statfs(struct dentry *dentry) return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad); } -static int selinux_mount(char *dev_name, +static int selinux_mount(const char *dev_name, struct path *path, - char *type, + const char *type, unsigned long flags, void *data) { @@ -3020,6 +3095,46 @@ static void selinux_file_free_security(struct file *file) file_free_security(file); } +/* + * Check whether a task has the ioctl permission and cmd + * operation to an inode. + */ +int ioctl_has_perm(const struct cred *cred, struct file *file, + u32 requested, u16 cmd) +{ + struct common_audit_data ad; + 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 lsm_ioctlop_audit ioctl; + u32 ssid = cred_sid(cred); + struct selinux_audit_data sad = {0,}; + int rc; + + COMMON_AUDIT_DATA_INIT(&ad, IOCTL_OP); + ad.u.op = &ioctl; + ad.u.op->cmd = cmd; + ad.u.op->path = file->f_path; + ad.selinux_audit_data = &sad; + + if (ssid != fsec->sid) { + rc = avc_has_perm(ssid, fsec->sid, + SECCLASS_FD, + FD__USE, + &ad); + if (rc) + goto out; + } + + if (unlikely(IS_PRIVATE(inode))) + return 0; + + rc = avc_has_operation(ssid, isec->sid, isec->sclass, + requested, cmd, &ad); +out: + return rc; +} + static int selinux_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -3062,7 +3177,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd, * to the file's ioctl() function. */ default: - error = file_has_perm(cred, file, FILE__IOCTL); + error = ioctl_has_perm(cred, file, FILE__IOCTL, (u16) cmd); } return error; } @@ -5647,6 +5762,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/audit.h b/security/selinux/include/audit.h old mode 100755 new mode 100644 diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h old mode 100755 new mode 100644 index 19313702..63c3105d --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -84,11 +84,15 @@ int avc_audit(u32 ssid, u32 tsid, struct common_audit_data *a, unsigned flags); #define AVC_STRICT 1 /* Ignore permissive mode. */ +#define AVC_OPERATION_CMD 2 /* ignore command when updating operations */ int avc_has_perm_noaudit(u32 ssid, u32 tsid, u16 tclass, u32 requested, unsigned flags, struct av_decision *avd); +int avc_has_operation(u32 ssid, u32 tsid, u16 tclass, u32 requested, + u16 cmd, struct common_audit_data *ad); + int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass, u32 requested, struct common_audit_data *auditdata, @@ -111,6 +115,7 @@ u32 avc_policy_seqno(void); #define AVC_CALLBACK_AUDITALLOW_DISABLE 32 #define AVC_CALLBACK_AUDITDENY_ENABLE 64 #define AVC_CALLBACK_AUDITDENY_DISABLE 128 +#define AVC_CALLBACK_ADD_OPERATION 256 int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, u16 tclass, u32 perms, diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h old mode 100755 new mode 100644 diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h old mode 100755 new mode 100644 index b8c53723..20b00fc3 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -145,9 +145,12 @@ struct security_class_mapping secclass_map[] = { "node_bind", "name_connect", NULL } }, { "memprotect", { "mmap_zero", NULL } }, { "peer", { "recv", NULL } }, - { "capability2", { "mac_override", "mac_admin", "syslog", NULL } }, + { "capability2", + { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend", + NULL } }, { "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/security/selinux/include/conditional.h b/security/selinux/include/conditional.h old mode 100755 new mode 100644 diff --git a/security/selinux/include/initial_sid_to_string.h b/security/selinux/include/initial_sid_to_string.h old mode 100755 new mode 100644 diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h old mode 100755 new mode 100644 diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h old mode 100755 new mode 100644 diff --git a/security/selinux/include/netnode.h b/security/selinux/include/netnode.h old mode 100755 new mode 100644 diff --git a/security/selinux/include/netport.h b/security/selinux/include/netport.h old mode 100755 new mode 100644 diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h old mode 100755 new mode 100644 index e277dfe2..7b1830bd --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -46,7 +46,6 @@ struct inode_security_struct { u32 sid; /* SID of this object */ u16 sclass; /* security class of this object */ unsigned char initialized; /* initialization flag */ - u32 tag; /* Per-File-Encryption tag */ struct mutex lock; }; diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h old mode 100755 new mode 100644 index d871e8ad..ff1a1880 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -31,13 +31,17 @@ #define POLICYDB_VERSION_BOUNDARY 24 #define POLICYDB_VERSION_FILENAME_TRANS 25 #define POLICYDB_VERSION_ROLETRANS 26 +#define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27 +#define POLICYDB_VERSION_DEFAULT_TYPE 28 +#define POLICYDB_VERSION_CONSTRAINT_NAMES 29 +#define POLICYDB_VERSION_IOCTL_OPERATIONS 30 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE #else -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_ROLETRANS +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_IOCTL_OPERATIONS #endif /* Mask for just the mount related flags */ @@ -100,11 +104,40 @@ struct av_decision { u32 flags; }; +#define security_operation_set(perms, x) (perms[x >> 5] |= 1 << (x & 0x1f)) +#define security_operation_test(perms, x) (1 & (perms[x >> 5] >> (x & 0x1f))) + +struct operation_perm { + u32 perms[8]; +}; + +struct operation_decision { + u8 type; + u8 specified; + struct operation_perm *allowed; + struct operation_perm *auditallow; + struct operation_perm *dontaudit; +}; + +#define OPERATION_ALLOWED 1 +#define OPERATION_AUDITALLOW 2 +#define OPERATION_DONTAUDIT 4 +#define OPERATION_ALL (OPERATION_ALLOWED | OPERATION_AUDITALLOW |\ + OPERATION_DONTAUDIT) +struct operation { + u16 len; /* length of operation decision chain */ + u32 type[8]; /* 256 types */ +}; + /* definitions of av_decision.flags */ #define AVD_FLAGS_PERMISSIVE 0x0001 void security_compute_av(u32 ssid, u32 tsid, - u16 tclass, struct av_decision *avd); + u16 tclass, struct av_decision *avd, + struct operation *ops); + +void security_compute_operation(u32 ssid, u32 tsid, u16 tclass, + u8 type, struct operation_decision *od); void security_compute_av_user(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd); diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h old mode 100755 new mode 100644 diff --git a/security/selinux/netif.c b/security/selinux/netif.c old mode 100755 new mode 100644 diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c old mode 100755 new mode 100644 diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c old mode 100755 new mode 100644 diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c old mode 100755 new mode 100644 diff --git a/security/selinux/netport.c b/security/selinux/netport.c old mode 100755 new mode 100644 diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 5776921d..f24a04e3 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "flask.h" #include "av_permissions.h" @@ -80,6 +81,7 @@ static struct nlmsg_perm nlmsg_tcpdiag_perms[] = { { TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, { DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, + { SOCK_DIAG_BY_FAMILY, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, }; static struct nlmsg_perm nlmsg_xfrm_perms[] = diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c old mode 100755 new mode 100644 index 3bf90453..17973289 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -31,6 +31,7 @@ #include #include + /* selinuxfs pseudo filesystem for exporting the security policy API. Based on the proc code and the fs/nfsd/nfsctl.c code. */ @@ -136,6 +137,7 @@ static ssize_t sel_read_enforce(struct file *filp, char __user *buf, return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } +#ifndef CONFIG_SECURITY_SELINUX_FORCE_PERMISSIVE #ifdef CONFIG_SECURITY_SELINUX_DEVELOP static ssize_t sel_write_enforce(struct file *file, const char __user *buf, size_t count, loff_t *ppos) @@ -190,10 +192,15 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, #else #define sel_write_enforce NULL #endif +#endif static const struct file_operations sel_enforce_ops = { .read = sel_read_enforce, +#ifdef CONFIG_SECURITY_SELINUX_FORCE_PERMISSIVE + .write = NULL, +#else .write = sel_write_enforce, +#endif .llseek = generic_file_llseek, }; diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c old mode 100755 new mode 100644 index a3dd9faa..dd7466cb --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -24,6 +24,7 @@ #include "policydb.h" static struct kmem_cache *avtab_node_cachep; +static struct kmem_cache *avtab_operation_cachep; static inline int avtab_hash(struct avtab_key *keyp, u16 mask) { @@ -37,11 +38,24 @@ avtab_insert_node(struct avtab *h, int hvalue, struct avtab_key *key, struct avtab_datum *datum) { struct avtab_node *newnode; + struct avtab_operation *ops; newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL); if (newnode == NULL) return NULL; newnode->key = *key; - newnode->datum = *datum; + + if (key->specified & AVTAB_OP) { + ops = kmem_cache_zalloc(avtab_operation_cachep, GFP_KERNEL); + if (ops == NULL) { + kmem_cache_free(avtab_node_cachep, newnode); + return NULL; + } + *ops = *(datum->u.ops); + newnode->datum.u.ops = ops; + } else { + newnode->datum.u.data = datum->u.data; + } + if (prev) { newnode->next = prev->next; prev->next = newnode; @@ -70,8 +84,11 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat if (key->source_type == cur->key.source_type && key->target_type == cur->key.target_type && key->target_class == cur->key.target_class && - (specified & cur->key.specified)) + (specified & cur->key.specified)) { + if (specified & AVTAB_OPNUM) + break; return -EEXIST; + } if (key->source_type < cur->key.source_type) break; if (key->source_type == cur->key.source_type && @@ -232,6 +249,9 @@ void avtab_destroy(struct avtab *h) while (cur) { temp = cur; cur = cur->next; + if (temp->key.specified & AVTAB_OP) + kmem_cache_free(avtab_operation_cachep, + temp->datum.u.ops); kmem_cache_free(avtab_node_cachep, temp); } h->htable[i] = NULL; @@ -320,7 +340,13 @@ static uint16_t spec_order[] = { AVTAB_AUDITALLOW, AVTAB_TRANSITION, AVTAB_CHANGE, - AVTAB_MEMBER + AVTAB_MEMBER, + AVTAB_OPNUM_ALLOWED, + AVTAB_OPNUM_AUDITALLOW, + AVTAB_OPNUM_DONTAUDIT, + AVTAB_OPTYPE_ALLOWED, + AVTAB_OPTYPE_AUDITALLOW, + AVTAB_OPTYPE_DONTAUDIT }; int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, @@ -330,10 +356,11 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, { __le16 buf16[4]; u16 enabled; - __le32 buf32[7]; u32 items, items2, val, vers = pol->policyvers; struct avtab_key key; struct avtab_datum datum; + struct avtab_operation ops; + __le32 buf32[ARRAY_SIZE(ops.op.perms)]; int i, rc; unsigned set; @@ -390,11 +417,15 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n"); return -EINVAL; } + if (val & AVTAB_OP) { + printk(KERN_ERR "SELinux: avtab: entry has operations\n"); + return -EINVAL; + } for (i = 0; i < ARRAY_SIZE(spec_order); i++) { if (val & spec_order[i]) { key.specified = spec_order[i] | enabled; - datum.data = le32_to_cpu(buf32[items++]); + datum.u.data = le32_to_cpu(buf32[items++]); rc = insertf(a, &key, &datum, p); if (rc) return rc; @@ -413,7 +444,6 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, printk(KERN_ERR "SELinux: avtab: truncated entry\n"); return rc; } - items = 0; key.source_type = le16_to_cpu(buf16[items++]); key.target_type = le16_to_cpu(buf16[items++]); @@ -437,14 +467,32 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, return -EINVAL; } - rc = next_entry(buf32, fp, sizeof(u32)); - if (rc) { - printk(KERN_ERR "SELinux: avtab: truncated entry\n"); - return rc; + if ((vers < POLICYDB_VERSION_IOCTL_OPERATIONS) + || !(key.specified & AVTAB_OP)) { + rc = next_entry(buf32, fp, sizeof(u32)); + if (rc) { + printk(KERN_ERR "SELinux: avtab: truncated entry\n"); + return rc; + } + datum.u.data = le32_to_cpu(*buf32); + } else { + memset(&ops, 0, sizeof(struct avtab_operation)); + rc = next_entry(&ops.type, fp, sizeof(u8)); + if (rc) { + printk(KERN_ERR "SELinux: avtab: truncated entry\n"); + return rc; + } + rc = next_entry(buf32, fp, sizeof(u32)*ARRAY_SIZE(ops.op.perms)); + if (rc) { + printk(KERN_ERR "SELinux: avtab: truncated entry\n"); + return rc; + } + for (i = 0; i < ARRAY_SIZE(ops.op.perms); i++) + ops.op.perms[i] = le32_to_cpu(buf32[i]); + datum.u.ops = &ops; } - datum.data = le32_to_cpu(*buf32); if ((key.specified & AVTAB_TYPE) && - !policydb_type_isvalid(pol, datum.data)) { + !policydb_type_isvalid(pol, datum.u.data)) { printk(KERN_ERR "SELinux: avtab: invalid type\n"); return -EINVAL; } @@ -504,8 +552,9 @@ int avtab_read(struct avtab *a, void *fp, struct policydb *pol) int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp) { __le16 buf16[4]; - __le32 buf32[1]; + __le32 buf32[ARRAY_SIZE(cur->datum.u.ops->op.perms)]; int rc; + unsigned int i; buf16[0] = cpu_to_le16(cur->key.source_type); buf16[1] = cpu_to_le16(cur->key.target_type); @@ -514,8 +563,19 @@ int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp) rc = put_entry(buf16, sizeof(u16), 4, fp); if (rc) return rc; - buf32[0] = cpu_to_le32(cur->datum.data); - rc = put_entry(buf32, sizeof(u32), 1, fp); + + if (cur->key.specified & AVTAB_OP) { + rc = put_entry(&cur->datum.u.ops->type, sizeof(u8), 1, fp); + if (rc) + return rc; + for (i = 0; i < ARRAY_SIZE(cur->datum.u.ops->op.perms); i++) + buf32[i] = cpu_to_le32(cur->datum.u.ops->op.perms[i]); + rc = put_entry(buf32, sizeof(u32), + ARRAY_SIZE(cur->datum.u.ops->op.perms), fp); + } else { + buf32[0] = cpu_to_le32(cur->datum.u.data); + rc = put_entry(buf32, sizeof(u32), 1, fp); + } if (rc) return rc; return 0; @@ -548,9 +608,13 @@ void avtab_cache_init(void) avtab_node_cachep = kmem_cache_create("avtab_node", sizeof(struct avtab_node), 0, SLAB_PANIC, NULL); + avtab_operation_cachep = kmem_cache_create("avtab_operation", + sizeof(struct avtab_operation), + 0, SLAB_PANIC, NULL); } void avtab_cache_destroy(void) { kmem_cache_destroy(avtab_node_cachep); + kmem_cache_destroy(avtab_operation_cachep); } diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h old mode 100755 new mode 100644 index 63ce2f9e..97acd6fa --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -23,6 +23,8 @@ #ifndef _SS_AVTAB_H_ #define _SS_AVTAB_H_ +#include "security.h" + struct avtab_key { u16 source_type; /* source type */ u16 target_type; /* target type */ @@ -35,13 +37,34 @@ struct avtab_key { #define AVTAB_MEMBER 0x0020 #define AVTAB_CHANGE 0x0040 #define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE) +#define AVTAB_OPNUM_ALLOWED 0x0100 +#define AVTAB_OPNUM_AUDITALLOW 0x0200 +#define AVTAB_OPNUM_DONTAUDIT 0x0400 +#define AVTAB_OPNUM (AVTAB_OPNUM_ALLOWED | \ + AVTAB_OPNUM_AUDITALLOW | \ + AVTAB_OPNUM_DONTAUDIT) +#define AVTAB_OPTYPE_ALLOWED 0x1000 +#define AVTAB_OPTYPE_AUDITALLOW 0x2000 +#define AVTAB_OPTYPE_DONTAUDIT 0x4000 +#define AVTAB_OPTYPE (AVTAB_OPTYPE_ALLOWED | \ + AVTAB_OPTYPE_AUDITALLOW | \ + AVTAB_OPTYPE_DONTAUDIT) +#define AVTAB_OP (AVTAB_OPNUM | AVTAB_OPTYPE) #define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */ #define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */ u16 specified; /* what field is specified */ }; +struct avtab_operation { + u8 type; + struct operation_perm op; +}; + struct avtab_datum { - u32 data; /* access vector or type value */ + union { + u32 data; /* access vector or type value */ + struct avtab_operation *ops; /* ioctl operations */ + } u; }; struct avtab_node { diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c old mode 100755 new mode 100644 index 377d148e..16651c7a --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -15,6 +15,7 @@ #include "security.h" #include "conditional.h" +#include "services.h" /* * cond_evaluate_expr evaluates a conditional expr @@ -617,21 +618,39 @@ int cond_write_list(struct policydb *p, struct cond_node *list, void *fp) return 0; } + +void cond_compute_operation(struct avtab *ctab, struct avtab_key *key, + struct operation_decision *od) +{ + struct avtab_node *node; + + if (!ctab || !key || !od) + return; + + for (node = avtab_search_node(ctab, key); node; + node = avtab_search_node_next(node, key->specified)) { + if (node->key.specified & AVTAB_ENABLED) + services_compute_operation_num(od, node); + } + return; + +} /* Determine whether additional permissions are granted by the conditional * av table, and if so, add them to the result */ -void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd) +void cond_compute_av(struct avtab *ctab, struct avtab_key *key, + struct av_decision *avd, struct operation *ops) { struct avtab_node *node; - if (!ctab || !key || !avd) + if (!ctab || !key || !avd || !ops) return; for (node = avtab_search_node(ctab, key); node; node = avtab_search_node_next(node, key->specified)) { if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) == (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED))) - avd->allowed |= node->datum.data; + avd->allowed |= node->datum.u.data; if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) == (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED))) /* Since a '0' in an auditdeny mask represents a @@ -639,10 +658,13 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decisi * the '&' operand to ensure that all '0's in the mask * are retained (much unlike the allow and auditallow cases). */ - avd->auditdeny &= node->datum.data; + avd->auditdeny &= node->datum.u.data; if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) == (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED))) - avd->auditallow |= node->datum.data; + avd->auditallow |= node->datum.u.data; + if ((node->key.specified & AVTAB_ENABLED) && + (node->key.specified & AVTAB_OP)) + services_compute_operation_type(ops, node); } return; } diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h old mode 100755 new mode 100644 index 4d1f8746..80ee2bb2 --- a/security/selinux/ss/conditional.h +++ b/security/selinux/ss/conditional.h @@ -73,8 +73,10 @@ int cond_read_list(struct policydb *p, void *fp); int cond_write_bool(void *key, void *datum, void *ptr); int cond_write_list(struct policydb *p, struct cond_node *list, void *fp); -void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd); - +void cond_compute_av(struct avtab *ctab, struct avtab_key *key, + struct av_decision *avd, struct operation *ops); +void cond_compute_operation(struct avtab *ctab, struct avtab_key *key, + struct operation_decision *od); int evaluate_cond_node(struct policydb *p, struct cond_node *node); #endif /* _CONDITIONAL_H_ */ diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h old mode 100755 new mode 100644 index 149dda73..96fd947c --- a/security/selinux/ss/constraint.h +++ b/security/selinux/ss/constraint.h @@ -48,6 +48,7 @@ struct constraint_expr { u32 op; /* operator */ struct ebitmap names; /* names */ + struct type_set *type_names; struct constraint_expr *next; /* next expression */ }; diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h old mode 100755 new mode 100644 index 45e8fb05..212e3479 --- a/security/selinux/ss/context.h +++ b/security/selinux/ss/context.h @@ -74,6 +74,26 @@ static inline int mls_context_cpy_low(struct context *dst, struct context *src) return rc; } +/* + * Sets both levels in the MLS range of 'dst' to the high level of 'src'. + */ +static inline int mls_context_cpy_high(struct context *dst, struct context *src) +{ + int rc; + + dst->range.level[0].sens = src->range.level[1].sens; + rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[1].cat); + if (rc) + goto out; + + dst->range.level[1].sens = src->range.level[1].sens; + rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[1].cat); + if (rc) + ebitmap_destroy(&dst->range.level[0].cat); +out: + return rc; +} + static inline int mls_context_cmp(struct context *c1, struct context *c2) { return ((c1->range.level[0].sens == c2->range.level[0].sens) && diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c old mode 100755 new mode 100644 diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h old mode 100755 new mode 100644 diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c old mode 100755 new mode 100644 diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h old mode 100755 new mode 100644 diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c old mode 100755 new mode 100644 index fbf9c581..40de8d3f --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -517,6 +517,8 @@ int mls_compute_sid(struct context *scontext, { struct range_trans rtr; struct mls_range *r; + struct class_datum *cladatum; + int default_range = 0; if (!policydb.mls_enabled) return 0; @@ -530,6 +532,28 @@ int mls_compute_sid(struct context *scontext, r = hashtab_search(policydb.range_tr, &rtr); if (r) return mls_range_set(newcontext, r); + + if (tclass && tclass <= policydb.p_classes.nprim) { + cladatum = policydb.class_val_to_struct[tclass - 1]; + if (cladatum) + default_range = cladatum->default_range; + } + + switch (default_range) { + case DEFAULT_SOURCE_LOW: + return mls_context_cpy_low(newcontext, scontext); + case DEFAULT_SOURCE_HIGH: + return mls_context_cpy_high(newcontext, scontext); + case DEFAULT_SOURCE_LOW_HIGH: + return mls_context_cpy(newcontext, scontext); + case DEFAULT_TARGET_LOW: + return mls_context_cpy_low(newcontext, tcontext); + case DEFAULT_TARGET_HIGH: + return mls_context_cpy_high(newcontext, tcontext); + case DEFAULT_TARGET_LOW_HIGH: + return mls_context_cpy(newcontext, tcontext); + } + /* Fallthrough */ case AVTAB_CHANGE: if ((tclass == policydb.process_class) || (sock == true)) diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h old mode 100755 new mode 100644 diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h old mode 100755 new mode 100644 diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c old mode 100755 new mode 100644 index 23e9cba8..a683475e --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -133,6 +133,26 @@ static struct policydb_compat_info policydb_compat[] = { .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, + { + .version = POLICYDB_VERSION_NEW_OBJECT_DEFAULTS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_DEFAULT_TYPE, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_CONSTRAINT_NAMES, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_IOCTL_OPERATIONS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -603,6 +623,19 @@ static int common_destroy(void *key, void *datum, void *p) return 0; } +static void constraint_expr_destroy(struct constraint_expr *expr) +{ + if (expr) { + ebitmap_destroy(&expr->names); + if (expr->type_names) { + ebitmap_destroy(&expr->type_names->types); + ebitmap_destroy(&expr->type_names->negset); + kfree(expr->type_names); + } + kfree(expr); + } +} + static int cls_destroy(void *key, void *datum, void *p) { struct class_datum *cladatum; @@ -618,10 +651,9 @@ static int cls_destroy(void *key, void *datum, void *p) while (constraint) { e = constraint->expr; while (e) { - ebitmap_destroy(&e->names); etmp = e; e = e->next; - kfree(etmp); + constraint_expr_destroy(etmp); } ctemp = constraint; constraint = constraint->next; @@ -632,16 +664,14 @@ static int cls_destroy(void *key, void *datum, void *p) while (constraint) { e = constraint->expr; while (e) { - ebitmap_destroy(&e->names); etmp = e; e = e->next; - kfree(etmp); + constraint_expr_destroy(etmp); } ctemp = constraint; constraint = constraint->next; kfree(ctemp); } - kfree(cladatum->comkey); } kfree(datum); @@ -1146,8 +1176,34 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp) return rc; } -static int read_cons_helper(struct constraint_node **nodep, int ncons, - int allowxtarget, void *fp) +static void type_set_init(struct type_set *t) +{ + ebitmap_init(&t->types); + ebitmap_init(&t->negset); +} + +static int type_set_read(struct type_set *t, void *fp) +{ + __le32 buf[1]; + int rc; + + if (ebitmap_read(&t->types, fp)) + return -EINVAL; + if (ebitmap_read(&t->negset, fp)) + return -EINVAL; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + return -EINVAL; + t->flags = le32_to_cpu(buf[0]); + + return 0; +} + + +static int read_cons_helper(struct policydb *p, + struct constraint_node **nodep, + int ncons, int allowxtarget, void *fp) { struct constraint_node *c, *lc; struct constraint_expr *e, *le; @@ -1215,6 +1271,18 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons, rc = ebitmap_read(&e->names, fp); if (rc) return rc; + if (p->policyvers >= + POLICYDB_VERSION_CONSTRAINT_NAMES) { + e->type_names = kzalloc(sizeof + (*e->type_names), + GFP_KERNEL); + if (!e->type_names) + return -ENOMEM; + type_set_init(e->type_names); + rc = type_set_read(e->type_names, fp); + if (rc) + return rc; + } break; default: return -EINVAL; @@ -1291,7 +1359,7 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) goto bad; } - rc = read_cons_helper(&cladatum->constraints, ncons, 0, fp); + rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp); if (rc) goto bad; @@ -1301,9 +1369,27 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) if (rc) goto bad; ncons = le32_to_cpu(buf[0]); - rc = read_cons_helper(&cladatum->validatetrans, ncons, 1, fp); + rc = read_cons_helper(p, &cladatum->validatetrans, + ncons, 1, fp); + if (rc) + goto bad; + } + + if (p->policyvers >= POLICYDB_VERSION_NEW_OBJECT_DEFAULTS) { + rc = next_entry(buf, fp, sizeof(u32) * 3); + if (rc) + goto bad; + + cladatum->default_user = le32_to_cpu(buf[0]); + cladatum->default_role = le32_to_cpu(buf[1]); + cladatum->default_range = le32_to_cpu(buf[2]); + } + + if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) { + rc = next_entry(buf, fp, sizeof(u32) * 1); if (rc) goto bad; + cladatum->default_type = le32_to_cpu(buf[0]); } rc = hashtab_insert(h, key, cladatum); @@ -2735,6 +2821,24 @@ static int common_write(void *vkey, void *datum, void *ptr) return 0; } +static int type_set_write(struct type_set *t, void *fp) +{ + int rc; + __le32 buf[1]; + + if (ebitmap_write(&t->types, fp)) + return -EINVAL; + if (ebitmap_write(&t->negset, fp)) + return -EINVAL; + + buf[0] = cpu_to_le32(t->flags); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return -EINVAL; + + return 0; +} + static int write_cons_helper(struct policydb *p, struct constraint_node *node, void *fp) { @@ -2766,6 +2870,12 @@ static int write_cons_helper(struct policydb *p, struct constraint_node *node, rc = ebitmap_write(&e->names, fp); if (rc) return rc; + if (p->policyvers >= + POLICYDB_VERSION_CONSTRAINT_NAMES) { + rc = type_set_write(e->type_names, fp); + if (rc) + return rc; + } break; default: break; @@ -2844,6 +2954,23 @@ static int class_write(void *vkey, void *datum, void *ptr) if (rc) return rc; + if (p->policyvers >= POLICYDB_VERSION_NEW_OBJECT_DEFAULTS) { + buf[0] = cpu_to_le32(cladatum->default_user); + buf[1] = cpu_to_le32(cladatum->default_role); + buf[2] = cpu_to_le32(cladatum->default_range); + + rc = put_entry(buf, sizeof(uint32_t), 3, fp); + if (rc) + return rc; + } + + if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) { + buf[0] = cpu_to_le32(cladatum->default_type); + rc = put_entry(buf, sizeof(uint32_t), 1, fp); + if (rc) + return rc; + } + return 0; } diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h old mode 100755 new mode 100644 index b846c038..725d5945 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -60,6 +60,20 @@ struct class_datum { struct symtab permissions; /* class-specific permission symbol table */ struct constraint_node *constraints; /* constraints on class permissions */ struct constraint_node *validatetrans; /* special transition rules */ +/* Options how a new object user, role, and type should be decided */ +#define DEFAULT_SOURCE 1 +#define DEFAULT_TARGET 2 + char default_user; + char default_role; + char default_type; +/* Options how a new object range should be decided */ +#define DEFAULT_SOURCE_LOW 1 +#define DEFAULT_SOURCE_HIGH 2 +#define DEFAULT_SOURCE_LOW_HIGH 3 +#define DEFAULT_TARGET_LOW 4 +#define DEFAULT_TARGET_HIGH 5 +#define DEFAULT_TARGET_LOW_HIGH 6 + char default_range; }; /* Role attributes */ @@ -139,6 +153,17 @@ struct cond_bool_datum { struct cond_node; +/* + * type set preserves data needed to determine constraint info from + * policy source. This is not used by the kernel policy but allows + * utilities such as audit2allow to determine constraint denials. + */ +struct type_set { + struct ebitmap types; + struct ebitmap negset; + u32 flags; +}; + /* * The configuration data includes security contexts for * initial SIDs, unlabeled file systems, TCP and UDP port numbers, diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c old mode 100755 new mode 100644 index 72b20b10..e8b9b9fc --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -92,9 +92,10 @@ static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len); static void context_struct_compute_av(struct context *scontext, - struct context *tcontext, - u16 tclass, - struct av_decision *avd); + struct context *tcontext, + u16 tclass, + struct av_decision *avd, + struct operation *ops); struct selinux_mapping { u16 value; /* policy value */ @@ -564,7 +565,8 @@ static void type_attribute_bounds_av(struct context *scontext, context_struct_compute_av(&lo_scontext, tcontext, tclass, - &lo_avd); + &lo_avd, + NULL); if ((lo_avd.allowed & avd->allowed) == avd->allowed) return; /* no masked permission */ masked = ~lo_avd.allowed & avd->allowed; @@ -579,7 +581,8 @@ static void type_attribute_bounds_av(struct context *scontext, context_struct_compute_av(scontext, &lo_tcontext, tclass, - &lo_avd); + &lo_avd, + NULL); if ((lo_avd.allowed & avd->allowed) == avd->allowed) return; /* no masked permission */ masked = ~lo_avd.allowed & avd->allowed; @@ -595,7 +598,8 @@ static void type_attribute_bounds_av(struct context *scontext, context_struct_compute_av(&lo_scontext, &lo_tcontext, tclass, - &lo_avd); + &lo_avd, + NULL); if ((lo_avd.allowed & avd->allowed) == avd->allowed) return; /* no masked permission */ masked = ~lo_avd.allowed & avd->allowed; @@ -611,14 +615,39 @@ static void type_attribute_bounds_av(struct context *scontext, } } +/* flag ioctl types that have operation permissions */ +void services_compute_operation_type( + struct operation *ops, + struct avtab_node *node) +{ + u8 type; + unsigned int i; + + if (node->key.specified & AVTAB_OPTYPE) { + /* if allowing one or more complete types */ + for (i = 0; i < ARRAY_SIZE(ops->type); i++) + ops->type[i] |= node->datum.u.ops->op.perms[i]; + } else { + /* if allowing operations within a type */ + type = node->datum.u.ops->type; + security_operation_set(ops->type, type); + } + + /* If no ioctl commands are allowed, ignore auditallow and auditdeny */ + if (node->key.specified & AVTAB_OPTYPE_ALLOWED || + node->key.specified & AVTAB_OPNUM_ALLOWED) + ops->len = 1; +} + /* - * Compute access vectors based on a context structure pair for - * the permissions in a particular class. + * Compute access vectors and operations ranges based on a context + * structure pair for the permissions in a particular class. */ static void context_struct_compute_av(struct context *scontext, - struct context *tcontext, - u16 tclass, - struct av_decision *avd) + struct context *tcontext, + u16 tclass, + struct av_decision *avd, + struct operation *ops) { struct constraint_node *constraint; struct role_allow *ra; @@ -632,6 +661,10 @@ static void context_struct_compute_av(struct context *scontext, avd->allowed = 0; avd->auditallow = 0; avd->auditdeny = 0xffffffff; + if (ops) { + memset(&ops->type, 0, sizeof(ops->type)); + ops->len = 0; + } if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { if (printk_ratelimit()) @@ -646,7 +679,7 @@ static void context_struct_compute_av(struct context *scontext, * this permission check, then use it. */ avkey.target_class = tclass; - avkey.specified = AVTAB_AV; + avkey.specified = AVTAB_AV | AVTAB_OP; sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1); BUG_ON(!sattr); tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1); @@ -659,15 +692,17 @@ static void context_struct_compute_av(struct context *scontext, node; node = avtab_search_node_next(node, avkey.specified)) { if (node->key.specified == AVTAB_ALLOWED) - avd->allowed |= node->datum.data; + avd->allowed |= node->datum.u.data; else if (node->key.specified == AVTAB_AUDITALLOW) - avd->auditallow |= node->datum.data; + avd->auditallow |= node->datum.u.data; else if (node->key.specified == AVTAB_AUDITDENY) - avd->auditdeny &= node->datum.data; + avd->auditdeny &= node->datum.u.data; + else if (ops && (node->key.specified & AVTAB_OP)) + services_compute_operation_type(ops, node); } /* Check conditional av table for additional permissions */ - cond_compute_av(&policydb.te_cond_avtab, &avkey, avd); + cond_compute_av(&policydb.te_cond_avtab, &avkey, avd, ops); } } @@ -898,13 +933,139 @@ static void avd_init(struct av_decision *avd) avd->flags = 0; } +void services_compute_operation_num(struct operation_decision *od, + struct avtab_node *node) +{ + unsigned int i; + + if (node->key.specified & AVTAB_OPNUM) { + if (od->type != node->datum.u.ops->type) + return; + } else { + if (!security_operation_test(node->datum.u.ops->op.perms, + od->type)) + return; + } + + if (node->key.specified == AVTAB_OPTYPE_ALLOWED) { + od->specified |= OPERATION_ALLOWED; + memset(od->allowed->perms, 0xff, + sizeof(od->allowed->perms)); + } else if (node->key.specified == AVTAB_OPTYPE_AUDITALLOW) { + od->specified |= OPERATION_AUDITALLOW; + memset(od->auditallow->perms, 0xff, + sizeof(od->auditallow->perms)); + } else if (node->key.specified == AVTAB_OPTYPE_DONTAUDIT) { + od->specified |= OPERATION_DONTAUDIT; + memset(od->dontaudit->perms, 0xff, + sizeof(od->dontaudit->perms)); + } else if (node->key.specified == AVTAB_OPNUM_ALLOWED) { + od->specified |= OPERATION_ALLOWED; + for (i = 0; i < ARRAY_SIZE(od->allowed->perms); i++) + od->allowed->perms[i] |= + node->datum.u.ops->op.perms[i]; + } else if (node->key.specified == AVTAB_OPNUM_AUDITALLOW) { + od->specified |= OPERATION_AUDITALLOW; + for (i = 0; i < ARRAY_SIZE(od->auditallow->perms); i++) + od->auditallow->perms[i] |= + node->datum.u.ops->op.perms[i]; + } else if (node->key.specified == AVTAB_OPNUM_DONTAUDIT) { + od->specified |= OPERATION_DONTAUDIT; + for (i = 0; i < ARRAY_SIZE(od->dontaudit->perms); i++) + od->dontaudit->perms[i] |= + node->datum.u.ops->op.perms[i]; + } else { + BUG(); + } +} + +void security_compute_operation(u32 ssid, + u32 tsid, + u16 orig_tclass, + u8 type, + struct operation_decision *od) +{ + u16 tclass; + struct context *scontext, *tcontext; + struct avtab_key avkey; + struct avtab_node *node; + struct ebitmap *sattr, *tattr; + struct ebitmap_node *snode, *tnode; + unsigned int i, j; + + od->type = type; + od->specified = 0; + memset(od->allowed->perms, 0, sizeof(od->allowed->perms)); + memset(od->auditallow->perms, 0, sizeof(od->auditallow->perms)); + memset(od->dontaudit->perms, 0, sizeof(od->dontaudit->perms)); + + read_lock(&policy_rwlock); + if (!ss_initialized) + goto allow; + + scontext = sidtab_search(&sidtab, ssid); + if (!scontext) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, ssid); + goto out; + } + + tcontext = sidtab_search(&sidtab, tsid); + if (!tcontext) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, tsid); + goto out; + } + + tclass = unmap_class(orig_tclass); + if (unlikely(orig_tclass && !tclass)) { + if (policydb.allow_unknown) + goto allow; + goto out; + } + + + if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { + if (printk_ratelimit()) + printk(KERN_WARNING "SELinux: Invalid class %hu\n", tclass); + goto out; + } + + avkey.target_class = tclass; + avkey.specified = AVTAB_OP; + sattr = flex_array_get(policydb.type_attr_map_array, + scontext->type - 1); + BUG_ON(!sattr); + tattr = flex_array_get(policydb.type_attr_map_array, + tcontext->type - 1); + BUG_ON(!tattr); + ebitmap_for_each_positive_bit(sattr, snode, i) { + ebitmap_for_each_positive_bit(tattr, tnode, j) { + avkey.source_type = i + 1; + avkey.target_type = j + 1; + for (node = avtab_search_node(&policydb.te_avtab, &avkey); + node; + node = avtab_search_node_next(node, avkey.specified)) + services_compute_operation_num(od, node); + cond_compute_operation(&policydb.te_cond_avtab, + &avkey, od); + } + } +out: + read_unlock(&policy_rwlock); + return; +allow: + memset(od->allowed->perms, 0xff, sizeof(od->allowed->perms)); + goto out; +} /** * security_compute_av - Compute access vector decisions. * @ssid: source security identifier * @tsid: target security identifier * @tclass: target security class * @avd: access vector decisions + * @od: operation decisions * * Compute a set of access vector decisions based on the * SID pair (@ssid, @tsid) for the permissions in @tclass. @@ -912,13 +1073,15 @@ static void avd_init(struct av_decision *avd) void security_compute_av(u32 ssid, u32 tsid, u16 orig_tclass, - struct av_decision *avd) + struct av_decision *avd, + struct operation *ops) { u16 tclass; struct context *scontext = NULL, *tcontext = NULL; read_lock(&policy_rwlock); avd_init(avd); + ops->len = 0; if (!ss_initialized) goto allow; @@ -946,7 +1109,7 @@ void security_compute_av(u32 ssid, goto allow; goto out; } - context_struct_compute_av(scontext, tcontext, tclass, avd); + context_struct_compute_av(scontext, tcontext, tclass, avd, ops); map_decision(orig_tclass, avd, policydb.allow_unknown); out: read_unlock(&policy_rwlock); @@ -992,7 +1155,7 @@ void security_compute_av_user(u32 ssid, goto out; } - context_struct_compute_av(scontext, tcontext, tclass, avd); + context_struct_compute_av(scontext, tcontext, tclass, avd, NULL); out: read_unlock(&policy_rwlock); return; @@ -1229,10 +1392,6 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len, struct context context; int rc = 0; - /* An empty security context is never valid. */ - if (!scontext_len) - return -EINVAL; - if (!ss_initialized) { int i; @@ -1393,6 +1552,7 @@ static int security_compute_sid(u32 ssid, u32 *out_sid, bool kern) { + struct class_datum *cladatum = NULL; struct context *scontext = NULL, *tcontext = NULL, newcontext; struct role_trans *roletr = NULL; struct avtab_key avkey; @@ -1441,12 +1601,20 @@ static int security_compute_sid(u32 ssid, goto out_unlock; } + if (tclass && tclass <= policydb.p_classes.nprim) + cladatum = policydb.class_val_to_struct[tclass - 1]; + /* Set the user identity. */ switch (specified) { case AVTAB_TRANSITION: case AVTAB_CHANGE: - /* Use the process user identity. */ - newcontext.user = scontext->user; + if (cladatum && cladatum->default_user == DEFAULT_TARGET) { + newcontext.user = tcontext->user; + } else { + /* notice this gets both DEFAULT_SOURCE and unset */ + /* Use the process user identity. */ + newcontext.user = scontext->user; + } break; case AVTAB_MEMBER: /* Use the related object owner. */ @@ -1454,16 +1622,31 @@ static int security_compute_sid(u32 ssid, break; } - /* Set the role and type to default values. */ - if ((tclass == policydb.process_class) || (sock == true)) { - /* Use the current role and type of process. */ + /* Set the role to default values. */ + if (cladatum && cladatum->default_role == DEFAULT_SOURCE) { newcontext.role = scontext->role; - newcontext.type = scontext->type; + } else if (cladatum && cladatum->default_role == DEFAULT_TARGET) { + newcontext.role = tcontext->role; } else { - /* Use the well-defined object role. */ - newcontext.role = OBJECT_R_VAL; - /* Use the type of the related object. */ + if ((tclass == policydb.process_class) || (sock == true)) + newcontext.role = scontext->role; + else + newcontext.role = OBJECT_R_VAL; + } + + /* Set the type to default values. */ + if (cladatum && cladatum->default_type == DEFAULT_SOURCE) { + newcontext.type = scontext->type; + } else if (cladatum && cladatum->default_type == DEFAULT_TARGET) { newcontext.type = tcontext->type; + } else { + if ((tclass == policydb.process_class) || (sock == true)) { + /* Use the type of process. */ + newcontext.type = scontext->type; + } else { + /* Use the type of the related object. */ + newcontext.type = tcontext->type; + } } /* Look for a type transition/member/change rule. */ @@ -1486,7 +1669,7 @@ static int security_compute_sid(u32 ssid, if (avdatum) { /* Use the type from the type transition/member/change rule. */ - newcontext.type = avdatum->data; + newcontext.type = avdatum->u.data; } /* if we have a objname this is a file trans check so check those rules */ diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h old mode 100755 new mode 100644 index e8d907e9..56975748 --- a/security/selinux/ss/services.h +++ b/security/selinux/ss/services.h @@ -11,5 +11,11 @@ extern struct policydb policydb; +void services_compute_operation_type(struct operation *ops, + struct avtab_node *node); + +void services_compute_operation_num(struct operation_decision *od, + struct avtab_node *node); + #endif /* _SS_SERVICES_H_ */ diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c old mode 100755 new mode 100644 diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h old mode 100755 new mode 100644 diff --git a/security/selinux/ss/status.c b/security/selinux/ss/status.c old mode 100755 new mode 100644 diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c old mode 100755 new mode 100644 diff --git a/security/selinux/ss/symtab.h b/security/selinux/ss/symtab.h old mode 100755 new mode 100644 diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c old mode 100755 new mode 100644 diff --git a/security/smack/Kconfig b/security/smack/Kconfig old mode 100755 new mode 100644 diff --git a/security/smack/Makefile b/security/smack/Makefile old mode 100755 new mode 100644 diff --git a/security/smack/smack.h b/security/smack/smack.h old mode 100755 new mode 100644 diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c old mode 100755 new mode 100644 diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c old mode 100755 new mode 100644 index 82df24d1..401b5847 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -404,8 +404,8 @@ static int smack_sb_statfs(struct dentry *dentry) * Returns 0 if current can write the floor of the filesystem * being mounted on, an error code otherwise. */ -static int smack_sb_mount(char *dev_name, struct path *path, - char *type, unsigned long flags, void *data) +static int smack_sb_mount(const char *dev_name, struct path *path, + const char *type, unsigned long flags, void *data) { struct superblock_smack *sbp = path->dentry->d_sb->s_security; struct smk_audit_info ad; diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c old mode 100755 new mode 100644 diff --git a/security/tomoyo/.gitignore b/security/tomoyo/.gitignore old mode 100755 new mode 100644 diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig old mode 100755 new mode 100644 diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile old mode 100755 new mode 100644 diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c old mode 100755 new mode 100644 diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c old mode 100755 new mode 100644 diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h old mode 100755 new mode 100644 index 30fd9836..997aa5bf --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -971,7 +971,7 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r, const u8 index); int tomoyo_mkdev_perm(const u8 operation, struct path *path, const unsigned int mode, unsigned int dev); -int tomoyo_mount_permission(char *dev_name, struct path *path, +int tomoyo_mount_permission(const char *dev_name, struct path *path, const char *type, unsigned long flags, void *data_page); int tomoyo_open_control(const u8 type, struct file *file); diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c old mode 100755 new mode 100644 diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c old mode 100755 new mode 100644 diff --git a/security/tomoyo/environ.c b/security/tomoyo/environ.c old mode 100755 new mode 100644 diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c old mode 100755 new mode 100644 diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c old mode 100755 new mode 100644 diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c old mode 100755 new mode 100644 diff --git a/security/tomoyo/load_policy.c b/security/tomoyo/load_policy.c old mode 100755 new mode 100644 diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c old mode 100755 new mode 100644 diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c old mode 100755 new mode 100644 index fe00cdfd..390c6460 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -71,7 +71,8 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r, * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, +static int tomoyo_mount_acl(struct tomoyo_request_info *r, + const char *dev_name, struct path *dir, const char *type, unsigned long flags) { @@ -183,7 +184,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, * * Returns 0 on success, negative value otherwise. */ -int tomoyo_mount_permission(char *dev_name, struct path *path, +int tomoyo_mount_permission(const char *dev_name, struct path *path, const char *type, unsigned long flags, void *data_page) { diff --git a/security/tomoyo/network.c b/security/tomoyo/network.c old mode 100755 new mode 100644 diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c old mode 100755 new mode 100644 diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c old mode 100755 new mode 100644 diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c old mode 100755 new mode 100644 index 620d37c1..43d3f97a --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -406,8 +406,8 @@ static int tomoyo_path_chroot(struct path *path) * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_sb_mount(char *dev_name, struct path *path, - char *type, unsigned long flags, void *data) +static int tomoyo_sb_mount(const char *dev_name, struct path *path, + const char *type, unsigned long flags, void *data) { return tomoyo_mount_permission(dev_name, path, type, flags, data); } diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c old mode 100755 new mode 100644 diff --git a/security/yama/Kconfig b/security/yama/Kconfig old mode 100755 new mode 100644 diff --git a/security/yama/Makefile b/security/yama/Makefile old mode 100755 new mode 100644 diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c old mode 100755 new mode 100644 diff --git a/weritos_defconfig b/weritos_defconfig new file mode 100644 index 00000000..998599a8 --- /dev/null +++ b/weritos_defconfig @@ -0,0 +1,3484 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.110 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_LOCKBREAK=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=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_FIQ=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_NEED_MACH_IO_H=y +CONFIG_NEED_MACH_MEMORY_H=y +CONFIG_PHYS_OFFSET=0x00200000 +CONFIG_GENERIC_BUG=y +# CONFIG_ARCH_RANDOM is not set +# CONFIG_GENERIC_TIME_VSYSCALL is not set +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y +CONFIG_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="-WERITOS-MM" +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_HAVE_KERNEL_LZ4=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +# CONFIG_KERNEL_LZ4 is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_AUDIT_WATCH=y +CONFIG_AUDIT_TREE=y +# CONFIG_AUDIT_LOGINUID_IMMUTABLE is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_IRQ_DOMAIN=y +# CONFIG_IRQ_DOMAIN_DEBUG is not set + +# +# RCU Subsystem +# +CONFIG_TREE_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_RCU_FAST_NO_HZ is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_RCU_BOOST is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=20 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_MEM_RES_CTLR=y +# CONFIG_CGROUP_MEM_RES_CTLR_SWAP is not set +# CONFIG_CGROUP_MEM_RES_CTLR_KMEM is not set +# CONFIG_CGROUP_PERF is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_CFS_BANDWIDTH is not set +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +CONFIG_MM_OWNER=y +# 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_RD_LZ4 is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y +# CONFIG_OPTIMIZE_INLINING is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +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_ASHMEM=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=y +CONFIG_TRACEPOINTS=y +CONFIG_KPROBES=y +# CONFIG_JUMP_LABEL is not set +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_KRETPROBES=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_USE_GENERIC_SMP_HELPERS=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BRKPT_RESERVED_RW_ACCESS=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 is not set +# CONFIG_MODULE_FORCE_LOAD_ATH6KL is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_STOP_MACHINE=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# 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=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_BFQ=y +# CONFIG_CGROUP_BFQIO is not set +CONFIG_IOSCHED_CFQ=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_FIOPS=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_ROW=y +CONFIG_IOSCHED_SIO=y +CONFIG_IOSCHED_SIOPLUS=y +CONFIG_IOSCHED_TEST=m +CONFIG_IOSCHED_VR=y +CONFIG_IOSCHED_ZEN=y +# CONFIG_DEFAULT_BFQ is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_FIOPS is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_ROW=y +# CONFIG_DEFAULT_SIO is not set +# CONFIG_DEFAULT_SIOPLUS is not set +# CONFIG_DEFAULT_VR is not set +# CONFIG_DEFAULT_ZEN is not set +CONFIG_DEFAULT_IOSCHED="row" +# 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_UNINLINE_SPIN_UNLOCK=y +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# 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 is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# 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 is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +CONFIG_MUTEX_SPIN_ON_OWNER=y +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=y +# 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_GPIO_PCA953X is not set + +# +# MSM SoC Type +# +# CONFIG_ARCH_MSM7X01A is not set +# CONFIG_ARCH_MSM7X25 is not set +CONFIG_ARCH_MSM7X27=y +# CONFIG_ARCH_MSM7X30 is not set +# CONFIG_ARCH_QSD8X50 is not set +# CONFIG_ARCH_MSM8X60 is not set +# CONFIG_ARCH_MSM8960 is not set +# CONFIG_ARCH_MSM8930 is not set +# CONFIG_ARCH_APQ8064 is not set +# CONFIG_ARCH_MSM8974 is not set +# CONFIG_ARCH_FSM9XXX is not set +# CONFIG_ARCH_MSM9615 is not set +CONFIG_ARCH_MSM8625=y +# CONFIG_ARCH_MSM9625 is not set +# CONFIG_ARCH_MSM8910 is not set +# CONFIG_MSM_SOC_REV_NONE is not set +CONFIG_MSM_SOC_REV_A=y +CONFIG_MSM_SMP=y +CONFIG_ARCH_MSM_CORTEXMP=y +CONFIG_ARCH_MSM_CORTEX_A5=y +CONFIG_ARCH_MSM7X27A=y +CONFIG_MSM_VIC=y +# CONFIG_MSM_RPM is not set +# CONFIG_MSM_RPM_SMD is not set +# CONFIG_MSM_MPM is not set + +# +# MSM Board Selection +# +# CONFIG_MACH_MSM7X27_SURF is not set +# CONFIG_MACH_MSM7X27_FFA is not set +CONFIG_MACH_MSM7X27A_RUMI3=y +CONFIG_MACH_MSM7X27A_SURF=y +CONFIG_MACH_MSM7X27A_FFA=y +CONFIG_WERITOS_KERNEL=y +CONFIG_MACH_MSM7625A_SURF=y +CONFIG_MACH_MSM7625A_FFA=y +CONFIG_MACH_MSM7627A_QRD1=y +CONFIG_MACH_MSM7627A_QRD3=y +CONFIG_MACH_MSM7627A_EVB=y +CONFIG_MACH_MSM8625_RUMI3=y +CONFIG_MACH_MSM8625_SURF=y +CONFIG_MACH_MSM8625_FFA=y +CONFIG_MACH_MSM8625_EVB=y +CONFIG_MACH_MSM7X27A_QRD5A=y +CONFIG_MACH_MSM8625_QRD7=y +CONFIG_MACH_MSM8625_QRD5=y +CONFIG_MACH_MSM8625Q_EVBD=y +CONFIG_MACH_MSM8625Q_SKUD=y +CONFIG_MACH_MSM8625Q_SKUE=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_MSM_AMSS_VERSION=6225 +# CONFIG_MSM_AMSS_VERSION_6210 is not set +# CONFIG_MSM_AMSS_VERSION_6220 is not set +CONFIG_MSM_AMSS_VERSION_6225=y +# CONFIG_MSM7X00A_USE_GP_TIMER is not set +CONFIG_MSM7X00A_USE_DG_TIMER=y +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=20000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET=1000000000 +CONFIG_CPU_HAS_L2_PMU=y +# CONFIG_HTC_HEADSET is not set +# CONFIG_HTC_PWRSINK is not set +CONFIG_MSM_FIQ_SUPPORT=y +# CONFIG_MSM_SERIAL_DEBUGGER is not set +CONFIG_MSM_PROC_COMM=y +CONFIG_MSM_SMD=y +CONFIG_MSM_AMSS_ENHANCE_DEBUG=y +# CONFIG_MSM_SMD_PKG3 is not set +CONFIG_MSM_SMD_PKG4=y +CONFIG_MSM_FUSE_INFO_DEBUG=y +# CONFIG_MSM_SMD_DEBUG is not set +CONFIG_MSM_FAST_BOOT=y +CONFIG_MSM_N_WAY_SMD=y +CONFIG_MSM_N_WAY_SMSM=y +# CONFIG_MSM_RESET_MODEM is not set +CONFIG_MSM_SMD_LOGGING=y +# CONFIG_MSM_IPC_LOGGING is not set +# CONFIG_MSM_SMD_NMEA is not set +CONFIG_MSM_SMD_TTY=y +# CONFIG_MSM_SMD_QMI is not set +CONFIG_MSM_SMD_PKT=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_IPC_ROUTER=y +# CONFIG_MSM_IPC_ROUTER_SMD_XPRT is not set +CONFIG_MSM_ONCRPCROUTER_DEBUG=y +# CONFIG_MSM_RPC_LOOPBACK_XPRT is not set +# CONFIG_MSM_RPCSERVER_TIME_REMOTE is not set +CONFIG_MSM_RPCSERVER_WATCHDOG=y +# CONFIG_MSM_RPC_WATCHDOG is not set +CONFIG_MSM_RPC_PING=y +CONFIG_MSM_RPC_PROC_COMM_TEST=y +CONFIG_MSM_RPC_OEM_RAPI=y +CONFIG_MSM_RPCSERVER_HANDSET=y +CONFIG_MSM_RMT_STORAGE_CLIENT=y +# CONFIG_MSM_RMT_STORAGE_CLIENT_STATS is not set +CONFIG_MSM_DALRPC=y +CONFIG_MSM_DALRPC_TEST=m +# CONFIG_MSM_CPU_FREQ_SET_MIN_MAX is not set +# CONFIG_MSM_AVS_HW is not set +# CONFIG_MSM_HW3D is not set +CONFIG_MSM_ADSP=y +CONFIG_ADSP_RPC_VER=0x30002 +CONFIG_AMSS_7X25_VERSION_2009=y +# CONFIG_AMSS_7X25_VERSION_2008 is not set +CONFIG_MSM_ADSP_REPORT_EVENTS=y +CONFIG_RTAC=y +CONFIG_MSM7X27A_AUDIO=y +CONFIG_MSM_PROC_COMM_REGULATOR=y +# CONFIG_MSM_VREG_SWITCH_INVERTED is not set +CONFIG_MSM_DMA_TEST=y +# CONFIG_WIFI_CONTROL_FUNC is not set +CONFIG_AUDIO_AAC_PLUS=y +CONFIG_AUDIO_ENHANCED_AAC_PLUS=y +CONFIG_SURF_FFA_GPIO_KEYPAD=y +CONFIG_MSM_SLEEP_TIME_OVERRIDE=y +# CONFIG_MSM_MEMORY_LOW_POWER_MODE is not set +CONFIG_MSM_PM_TIMEOUT_HALT=y +# CONFIG_MSM_PM_TIMEOUT_RESET_MODEM is not set +# CONFIG_MSM_PM_TIMEOUT_RESET_CHIP is not set +CONFIG_MSM_IDLE_WAIT_ON_MODEM=0 +# CONFIG_MSM_PIL is not set +# CONFIG_MSM_SCM is not set +# CONFIG_MSM_TZ_LOG is not set +# CONFIG_MSM_DIRECT_SCLK_ACCESS is not set +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_MODEM_RESTART=y +CONFIG_MSM_PM2=y +CONFIG_MSM_EVENT_TIMER=y +# CONFIG_MSM_BUS_SCALING is not set +# CONFIG_MSM_WATCHDOG_V2 is not set +# CONFIG_MSM_JTAG is not set +CONFIG_MSM_SLEEP_STATS_DEVICE=y +CONFIG_MSM_RUN_QUEUE_STATS=y +CONFIG_MSM_SM_EVENT=y +CONFIG_MSM_SM_EVENT_LOG=m +CONFIG_UNCACHED_BUF=y +CONFIG_MSM_SLEEP_MONITOR=m +# CONFIG_MSM_STANDALONE_POWER_COLLAPSE is not set +# CONFIG_MSM_GSBI9_UART is not set +CONFIG_MSM_SHOW_RESUME_IRQ=y +CONFIG_BT_MSM_PINTEST=y +# CONFIG_MSM_FAKE_BATTERY is not set +# CONFIG_MSM_QDSP6_APR is not set +# CONFIG_MSM_QDSP6_CODECS is not set +# CONFIG_MSM_QDSP6V2_CODECS is not set +# CONFIG_MSM_AUDIO_QDSP6 is not set +# CONFIG_MSM_AUDIO_QDSP6V2 is not set +CONFIG_MSM_RPC_VIBRATOR=y +CONFIG_PM8XXX_RPC_VIBRATOR=y +# CONFIG_MSM_SPM_V1 is not set +CONFIG_MSM_SPM_V2=y +# CONFIG_MSM_L2_SPM is not set +CONFIG_MSM_MULTIMEDIA_USE_ION=y +# CONFIG_MSM_OCMEM is not set +# CONFIG_MSM_RTB is not set +# CONFIG_MSM_EBI_ERP is not set +CONFIG_MSM_CPR=y +CONFIG_MSM_VP_REGULATOR=y +# CONFIG_MSM_CACHE_DUMP is not set +# CONFIG_MSM_HSIC_SYSMON is not set +# CONFIG_MSM_8X60_FUSION_GPIO_GLITCH is not set +CONFIG_MSM_FIQ=y +CONFIG_MSM_PBUS=y +CONFIG_HAS_MACH_MEMUTILS=y +# CONFIG_MSM_OVERCLOCK_GPU is not set +# CONFIG_INTELLI_PLUG is not set +# CONFIG_BRICKED_HOTPLUG is not set + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +CONFIG_SWP_EMULATE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_CACHE_L2X0=y +CONFIG_CACHE_PL310=y +# CONFIG_CACHE_PL310_ERP is not set +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_ARCH_HAS_BARRIERS=y +# CONFIG_VCM is not set +# CONFIG_STRICT_MEMORY_RWX is not set +CONFIG_ARM_NR_BANKS=8 +# CONFIG_RESERVE_FIRST_PAGE is not set +CONFIG_CPU_HAS_PMU=y +CONFIG_MULTI_IRQ_HANDLER=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_742230 is not set +# CONFIG_ARM_ERRATA_742231 is not set +# CONFIG_PL310_ERRATA_588369 is not set +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_PL310_ERRATA_727915 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_ARM_ERRATA_751472 is not set +# CONFIG_PL310_ERRATA_753970 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_PL310_ERRATA_769419 is not set +# CONFIG_ARM_ERRATA_775420 is not set +CONFIG_ARM_GIC=y +CONFIG_GIC_SECURE=y +# 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_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_HAVE_SMP=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_ARM_CPU_TOPOLOGY=y +# CONFIG_SCHED_MC is not set +# CONFIG_SCHED_SMT is not set +CONFIG_HAVE_ARM_SCU=y +# CONFIG_ARM_ARCH_TIMER is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_NR_CPUS=4 +CONFIG_HOTPLUG_CPU=y +CONFIG_LOCAL_TIMERS=y +CONFIG_ARCH_NR_GPIO=0 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +CONFIG_HZ=300 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HIGHMEM=y +# CONFIG_HIGHPTE is not set +CONFIG_HW_PERF_EVENTS=y +CONFIG_VMALLOC_RESERVE=0xC800000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_CLEANCACHE=y +# CONFIG_DYNAMIC_PAGE_WRITEBACK is not set +# CONFIG_FRONTSWAP is not set +# CONFIG_USE_USER_ACCESSIBLE_TIMERS is not set +CONFIG_ZSMALLOC=y +# CONFIG_PGTABLE_MAPPING is not set +# CONFIG_ARCH_MEMORY_PROBE is not set +# CONFIG_ARCH_MEMORY_REMOVE is not set +# CONFIG_ARCH_POPULATES_NODE_MAP is not set +# CONFIG_ENABLE_DMM is not set +# CONFIG_FIX_MOVABLE_ZONE is not set +CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0=y +# CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG is not set +# CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set +CONFIG_CP_ACCESS=y + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="init=/sbin/init root=/dev/ram rw initrd=0x11000000,16M console=ttyDCC0 mem=88M" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASSH3 is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIACTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ADAPTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIMM is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_STOCKDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_YANKDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTELLIDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_LIONHEART is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_HYPER is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMANDX is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVEX is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ALUCARD is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_SMARTASS2=y +# CONFIG_CPU_FREQ_GOV_SMARTASSH3 is not set +# CONFIG_CPU_FREQ_GOV_INTELLIACTIVE is not set +# CONFIG_CPU_FREQ_GOV_ADAPTIVE is not set +# CONFIG_CPU_FREQ_GOV_INTELLIMM is not set +# CONFIG_CPU_FREQ_GOV_STOCKDEMAND is not set +# CONFIG_CPU_FREQ_GOV_YANKDEMAND is not set +# CONFIG_CPU_FREQ_GOV_INTELLIDEMAND is not set +CONFIG_CPU_FREQ_GOV_LIONHEART=y +# CONFIG_CPU_FREQ_GOV_HYPER is not set +# CONFIG_CPU_FREQ_GOV_ONDEMANDX is not set +# CONFIG_CPU_FREQ_GOV_INTERACTIVEX is not set +CONFIG_CPU_FREQ_GOV_ALUCARD=y + +# +# ARM CPU frequency scaling drivers +# +# CONFIG_ARM_EXYNOS4210_CPUFREQ is not set +# CONFIG_ARM_EXYNOS4X12_CPUFREQ is not set +# CONFIG_ARM_EXYNOS5250_CPUFREQ is not set +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +# CONFIG_HAVE_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_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_PM_RUNTIME=y +CONFIG_PM=y +CONFIG_PM_DEBUG=y +# CONFIG_PM_ADVANCED_DEBUG is not set +# CONFIG_PM_TEST_SUSPEND is not set +CONFIG_CAN_PM_TRACE=y +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +CONFIG_SUSPEND_TIME=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +CONFIG_XFRM=y +CONFIG_XFRM_USER=y +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_FIB_TRIE_STATS is not set +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_XFRM_TUNNEL=y +CONFIG_INET_TUNNEL=y +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 is not set +CONFIG_TCP_CONG_ADVANCED=y +# CONFIG_TCP_CONG_BIC is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_TCP_CONG_WESTWOOD=y +# CONFIG_TCP_CONG_HTCP is not set +# CONFIG_TCP_CONG_HSTCP is not set +# CONFIG_TCP_CONG_HYBLA is not set +CONFIG_TCP_CONG_VEGAS=y +# CONFIG_TCP_CONG_SCALABLE is not set +# CONFIG_TCP_CONG_LP is not set +CONFIG_TCP_CONG_VENO=y +# CONFIG_TCP_CONG_YEAH is not set +# CONFIG_TCP_CONG_ILLINOIS is not set +# CONFIG_DEFAULT_CUBIC is not set +# CONFIG_DEFAULT_VEGAS is not set +# CONFIG_DEFAULT_VENO is not set +CONFIG_DEFAULT_WESTWOOD=y +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_TCP_CONG="westwood" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETLABEL is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +CONFIG_NETWORK_SECMARK=y +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +# CONFIG_NETFILTER_NETLINK_ACCT is not set +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_PROCFS=y +CONFIG_NF_CONNTRACK_EVENTS=y +# CONFIG_NF_CONNTRACK_TIMEOUT is not set +# CONFIG_NF_CONNTRACK_TIMESTAMP is not set +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_BROADCAST=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +# CONFIG_NF_CONNTRACK_SNMP is not set +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +# CONFIG_NF_CT_NETLINK_TIMEOUT is not set +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +# CONFIG_NETFILTER_XT_TARGET_HL is not set +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +CONFIG_NETFILTER_XT_MATCH_ECN=y +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_SET is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +# CONFIG_IP_NF_MATCH_RPFILTER is not set +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +# CONFIG_IP_NF_TARGET_REJECT_SKERR is not set +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +# CONFIG_NF_CONNTRACK_IPV6 is not set +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=y +# CONFIG_IP6_NF_MATCH_AH is not set +# CONFIG_IP6_NF_MATCH_EUI64 is not set +# CONFIG_IP6_NF_MATCH_FRAG is not set +# CONFIG_IP6_NF_MATCH_OPTS is not set +# CONFIG_IP6_NF_MATCH_HL is not set +# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set +# CONFIG_IP6_NF_MATCH_MH is not set +# CONFIG_IP6_NF_MATCH_RPFILTER is not set +# CONFIG_IP6_NF_MATCH_RT is not set +# CONFIG_IP6_NF_TARGET_HL is not set +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +# CONFIG_IP6_NF_TARGET_REJECT_SKERR is not set +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_IP6_NF_SECURITY=y +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +CONFIG_L2TP=y +CONFIG_L2TP_DEBUGFS=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA 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_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFB is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +# CONFIG_NET_SCH_MQPRIO is not set +# CONFIG_NET_SCH_CHOKE is not set +# CONFIG_NET_SCH_QFQ is not set +# CONFIG_NET_SCH_INGRESS is not set +# CONFIG_NET_SCH_PLUG is not set + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +# CONFIG_NET_ACT_POLICE is not set +# CONFIG_NET_ACT_GACT is not set +# CONFIG_NET_ACT_MIRRED is not set +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +# CONFIG_NETPRIO_CGROUP 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_NET_TCPPROBE is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCISMD is not set +# CONFIG_BT_HCIBTUSB is not set +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +CONFIG_BT_HCIUART_IBS=y +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBPA10X is not set +CONFIG_BT_MSM_SLEEP=y +# CONFIG_BT_HCIBFUSB is not set +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +CONFIG_MSM_BT_POWER=y +CONFIG_HUAWEI_BT_WCN2243=y +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +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=m +CONFIG_NL80211_TESTMODE=y +# 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 is not set +# CONFIG_LIB80211 is not set +# CONFIG_CFG80211_ALLOW_RECONNECT is not set +# CONFIG_MAC80211 is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_REGULATOR is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +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_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SW_SYNC_USER=y +# CONFIG_CMA is not set +# CONFIG_CONNECTOR is not set +# CONFIG_MTD is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +CONFIG_ZRAM=y +CONFIG_ZRAM_LZ4_COMPRESS=y +# CONFIG_ZRAM_DEBUG is not set +# 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=8192 +# 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 +# CONFIG_BLK_DEV_RBD is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ATMEL_PWM is not set +# CONFIG_ICS932S401 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_DS1682 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_WL127X_RFKILL is not set +# CONFIG_APANIC is not set +CONFIG_APANIC_MMC=y +CONFIG_APANIC_PLABEL="kpanic" +# CONFIG_TSIF is not set +CONFIG_GLANCEVIEW=y +# CONFIG_TSPP is not set +# CONFIG_HAPTIC_ISA1200 is not set +# CONFIG_QSEECOM is not set +# CONFIG_QFP_FUSE is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST 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_TGT=y +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# 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=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +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=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_ISCSI_BOOT_SYSFS is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM_BUILTIN=y +CONFIG_BLK_DEV_DM=y +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_THIN_PROVISIONING is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +# CONFIG_DM_FLAKEY is not set +# CONFIG_DM_VERITY 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=y +# CONFIG_IFB is not set +# CONFIG_NET_TEAM is not set +# CONFIG_MACVLAN is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_BROADCOM=y +# CONFIG_B44 is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +CONFIG_NET_VENDOR_CHELSIO=y +CONFIG_NET_VENDOR_CIRRUS=y +# CONFIG_CS89x0 is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +CONFIG_NET_VENDOR_FARADAY=y +# CONFIG_FTMAC100 is not set +# CONFIG_FTGMAC100 is not set +CONFIG_NET_VENDOR_INTEL=y +CONFIG_NET_VENDOR_I825XX=y +CONFIG_NET_VENDOR_MARVELL=y +CONFIG_NET_VENDOR_MICREL=y +# CONFIG_KS8851_MLL is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_QFEC is not set +CONFIG_NET_VENDOR_NATSEMI=y +CONFIG_NET_VENDOR_8390=y +# CONFIG_AX88796 is not set +# CONFIG_ETHOC is not set +CONFIG_NET_VENDOR_SEEQ=y +# CONFIG_SEEQ8005 is not set +CONFIG_NET_VENDOR_SMSC=y +CONFIG_SMC91X=y +# CONFIG_SMC911X is not set +CONFIG_SMSC911X=y +# CONFIG_SMSC911X_ARCH_HOOKS is not set +CONFIG_NET_VENDOR_STMICRO=y +# CONFIG_STMMAC_ETH is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_AMD_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPPOL2TP=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=y +CONFIG_USB_NET_CDCETHER=y +# CONFIG_USB_NET_CDC_EEM is not set +CONFIG_USB_NET_CDC_NCM=y +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_SMSC75XX is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_GL620A is not set +CONFIG_USB_NET_NET1080=y +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_MCS7830 is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +CONFIG_USB_NET_CDC_SUBSET=y +# CONFIG_USB_ALI_M5632 is not set +# CONFIG_USB_AN2720 is not set +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_KC2190 is not set +CONFIG_USB_NET_ZAURUS=y +# CONFIG_USB_NET_CX82310_ETH is not set +# CONFIG_USB_NET_KALMIA is not set +# CONFIG_USB_NET_QMI_WWAN is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_USB_SIERRA_NET is not set +# CONFIG_USB_VL600 is not set +# CONFIG_MSM_RMNET_USB is not set +CONFIG_WLAN=y +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +CONFIG_LIBRA_SDIOIF=m +# CONFIG_ATH6K_LEGACY_EXT is not set +CONFIG_ATH_COMMON=m +# CONFIG_ATH_DEBUG is not set +CONFIG_ATH6KL=m +CONFIG_ATH6KL_SDIO=m +# CONFIG_WIFI_MAC_FROM_NV is not set +# CONFIG_ATH6KL_USB is not set +# CONFIG_ATH6KL_DEBUG is not set +# CONFIG_ATH6KL_REGDOMAIN is not set +# CONFIG_BCMDHD is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_IWM is not set +# CONFIG_LIBERTAS 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=y +# 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=m +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +# CONFIG_JOYSTICK_ANALOG is not set +# CONFIG_JOYSTICK_A3D is not set +# CONFIG_JOYSTICK_ADI is not set +# CONFIG_JOYSTICK_COBRA is not set +# CONFIG_JOYSTICK_GF2K is not set +# CONFIG_JOYSTICK_GRIP is not set +# CONFIG_JOYSTICK_GRIP_MP is not set +# CONFIG_JOYSTICK_GUILLEMOT is not set +# CONFIG_JOYSTICK_INTERACT is not set +# CONFIG_JOYSTICK_SIDEWINDER is not set +# CONFIG_JOYSTICK_TMDC is not set +# CONFIG_JOYSTICK_IFORCE is not set +# CONFIG_JOYSTICK_WARRIOR is not set +# CONFIG_JOYSTICK_MAGELLAN is not set +# CONFIG_JOYSTICK_SPACEORB is not set +# CONFIG_JOYSTICK_SPACEBALL is not set +# CONFIG_JOYSTICK_STINGER is not set +# CONFIG_JOYSTICK_TWIDJOY is not set +# CONFIG_JOYSTICK_ZHENHUA is not set +# CONFIG_JOYSTICK_AS5011 is not set +# CONFIG_JOYSTICK_JOYDUMP is not set +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +# CONFIG_TOUCHDISC_VTD518_SHINETSU is not set +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_TABLET_USB_WACOM=y +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_FOCAL is not set +# CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set +# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_EGALAX is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_ILI210X is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MAX11801 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C=y +CONFIG_SYNA_MULTI_TOUCH=y +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_PIXCIR is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC_SERIO is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_MSM_LEGACY is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +# CONFIG_TOUCHSCREEN_CY8C_TS is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC is not set +# CONFIG_TOUCHSCREEN_FT5X06 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set +CONFIG_TOUCHSCREEN_FT6306=y +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_BMA150 is not set +# CONFIG_INPUT_MMA8450 is not set +# CONFIG_INPUT_MPU3050 is not set +# CONFIG_INPUT_GP2A is not set +# CONFIG_INPUT_GPIO_TILT_POLLED is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_KXTJ9 is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_ISA1200_FF_MEMLESS is not set +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_BOSCH_BMA150 is not set +# CONFIG_INPUT_LTR502 is not set +# CONFIG_INPUT_LTR558 is not set +# CONFIG_BOSCH_BMA250 is not set +# CONFIG_AVAGO_APDS990X is not set +# CONFIG_INPUT_ISL29028 is not set +# CONFIG_INPUT_LIS3DH is not set +# CONFIG_SENSORS_TMD2771X 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=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_N_SMUX 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_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_MSM_HS=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +# CONFIG_SERIAL_MSM_HSL is not set +# CONFIG_SERIAL_BCM_BT_LPM is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_MSM_SMD is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set + +# +# Diag Support +# +CONFIG_DIAG_CHAR=y + +# +# DIAG traffic over USB +# +CONFIG_DIAG_OVER_USB=y + +# +# SDIO support for DIAG +# + +# +# HSIC support for DIAG +# +# 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_MMC_GENERIC_CSDIO is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_ALGOBIT=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +CONFIG_I2C_GPIO=y +# CONFIG_I2C_MSM is not set +CONFIG_I2C_QUP=y +# CONFIG_I2C_OCORES 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_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SPI is not set +# CONFIG_SPMI is not set +# CONFIG_SLIMBUS is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# 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=y +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set +CONFIG_GPIO_MSM_V1=y +# CONFIG_GPIO_MSM_V2 is not set +# CONFIG_GPIO_MSM_V3 is not set +# CONFIG_GPIO_FSM9XXX 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=y +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MCP23S08 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_GPIO_PM8XXX_RPC is not set +# 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_CHARGER_ISP1704 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_MANAGER is not set +CONFIG_BATTERY_MSM=y +# CONFIG_BATTERY_MSM_DEBUG is not set +# CONFIG_BATTERY_MSM8X60 is not set +# CONFIG_SMB137B_CHARGER is not set +# CONFIG_SMB349_CHARGER is not set +# CONFIG_BATTERY_EARLYSUSPEND is not set +# CONFIG_BATTERY_MSM_FAKE is not set +# CONFIG_BATTERY_BQ27520 is not set +# CONFIG_BATTERY_BQ27541 is not set +# CONFIG_LTC4088_CHARGER is not set +# CONFIG_CHARGER_SMB347 is not set +# CONFIG_FORCE_FAST_CHARGE is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS620 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_GPIO_FAN is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_LINEAGE is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4151 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_LM95245 is not set +# CONFIG_SENSORS_MAX16065 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX1668 is not set +# CONFIG_SENSORS_MAX6639 is not set +# CONFIG_SENSORS_MAX6642 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MCP3021 is not set +# CONFIG_SENSORS_NTC_THERMISTOR is not set +CONFIG_SENSORS_MSM_ADC=y +CONFIG_SENSORS_BMA2X2=y +CONFIG_SENSORS_LTR559=y +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_PMBUS is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SHT21 is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_EMC6W201 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SCH56XX_COMMON is not set +# CONFIG_SENSORS_SCH5627 is not set +# CONFIG_SENSORS_SCH5636 is not set +# CONFIG_SENSORS_ADS1015 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF 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_MARIMBA_CORE=y +# CONFIG_MARIMBA_CODEC is not set +# CONFIG_TIMPANI_CODEC 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_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_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_PM8821_IRQ is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_WCD9304_CODEC is not set +# CONFIG_WCD9310_CODEC is not set +# CONFIG_WCD9320_CODEC is not set +# CONFIG_MFD_RC5T583 is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_GPIO is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_REGULATOR_FAN53555=y +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +CONFIG_REGULATOR_ONSEMI_NCP6335D=y +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +CONFIG_REGULATOR_MSM_GPIO=y +# CONFIG_REGULATOR_STUB is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +CONFIG_VIDEO_V4L2_SUBDEV_API=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 is not set +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEOBUF2_MEMOPS=y +CONFIG_VIDEOBUF2_DMA_CONTIG=y +CONFIG_VIDEOBUF2_VMALLOC=y +CONFIG_VIDEOBUF2_DMA_SG=y +CONFIG_VIDEOBUF2_MSM_MEM=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_TLV320AIC23B 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_MT9M032 is not set +# CONFIG_VIDEO_MT9P031 is not set +# CONFIG_VIDEO_MT9T001 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_MT9V032 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SR030PC30 is not set +# CONFIG_VIDEO_NOON010PC30 is not set +# CONFIG_VIDEO_M5MOLS is not set +# CONFIG_VIDEO_S5K6AA is not set + +# +# Flash devices +# +# CONFIG_VIDEO_ADP1653 is not set +# CONFIG_VIDEO_AS3645A is not set + +# +# 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_MSM_VCAP=y +CONFIG_V4L_USB_DRIVERS=y +# CONFIG_USB_VIDEO_CLASS is not set +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +CONFIG_USB_GSPCA=m +# CONFIG_USB_M5602 is not set +# CONFIG_USB_STV06XX is not set +# CONFIG_USB_GL860 is not set +# CONFIG_USB_GSPCA_BENQ is not set +# CONFIG_USB_GSPCA_CONEX is not set +# CONFIG_USB_GSPCA_CPIA1 is not set +# CONFIG_USB_GSPCA_ETOMS is not set +# CONFIG_USB_GSPCA_FINEPIX is not set +# CONFIG_USB_GSPCA_JEILINJ is not set +# CONFIG_USB_GSPCA_JL2005BCD is not set +# CONFIG_USB_GSPCA_KINECT is not set +# CONFIG_USB_GSPCA_KONICA is not set +# CONFIG_USB_GSPCA_MARS is not set +# CONFIG_USB_GSPCA_MR97310A is not set +# CONFIG_USB_GSPCA_NW80X is not set +# CONFIG_USB_GSPCA_OV519 is not set +# CONFIG_USB_GSPCA_OV534 is not set +# CONFIG_USB_GSPCA_OV534_9 is not set +# CONFIG_USB_GSPCA_PAC207 is not set +# CONFIG_USB_GSPCA_PAC7302 is not set +# CONFIG_USB_GSPCA_PAC7311 is not set +# CONFIG_USB_GSPCA_SE401 is not set +# CONFIG_USB_GSPCA_SN9C2028 is not set +# CONFIG_USB_GSPCA_SN9C20X is not set +# CONFIG_USB_GSPCA_SONIXB is not set +# CONFIG_USB_GSPCA_SONIXJ is not set +# CONFIG_USB_GSPCA_SPCA500 is not set +# CONFIG_USB_GSPCA_SPCA501 is not set +# CONFIG_USB_GSPCA_SPCA505 is not set +# CONFIG_USB_GSPCA_SPCA506 is not set +# CONFIG_USB_GSPCA_SPCA508 is not set +# CONFIG_USB_GSPCA_SPCA561 is not set +# CONFIG_USB_GSPCA_SPCA1528 is not set +# CONFIG_USB_GSPCA_SQ905 is not set +# CONFIG_USB_GSPCA_SQ905C is not set +# CONFIG_USB_GSPCA_SQ930X is not set +# CONFIG_USB_GSPCA_STK014 is not set +# CONFIG_USB_GSPCA_STV0680 is not set +# CONFIG_USB_GSPCA_SUNPLUS is not set +# CONFIG_USB_GSPCA_T613 is not set +# CONFIG_USB_GSPCA_TOPRO is not set +# CONFIG_USB_GSPCA_TV8532 is not set +# CONFIG_USB_GSPCA_VC032X is not set +# CONFIG_USB_GSPCA_VICAM is not set +# CONFIG_USB_GSPCA_XIRLINK_CIT is not set +# CONFIG_USB_GSPCA_ZC3XX is not set +# CONFIG_VIDEO_PVRUSB2 is not set +# CONFIG_VIDEO_HDPVR is not set +# CONFIG_VIDEO_EM28XX is not set +# CONFIG_VIDEO_USBVISION is not set +# CONFIG_USB_ET61X251 is not set +# CONFIG_USB_SN9C102 is not set +# CONFIG_USB_PWC is not set +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_S2255 is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +# CONFIG_SOC_CAMERA is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set +CONFIG_MSM_CAMERA_V4L2=y + +# +# Camera Sensor Selection +# +# CONFIG_IMX074 is not set +CONFIG_S5K5CA=y +CONFIG_S5K5CA_DARLING=y +# CONFIG_HI351_SUNNY_Q3H01B is not set +# CONFIG_OV5647 is not set +# CONFIG_OV5647_TRULY_CM6868 is not set +# CONFIG_OV5648_TRULY_CM8352 is not set +# CONFIG_WEBCAM_OV7692_QRD is not set +# CONFIG_MT9M114 is not set +# CONFIG_WEBCAM_OV9726 is not set +# CONFIG_MT9E013 is not set +# CONFIG_IMX074_ACT is not set +# CONFIG_S5K4E1 is not set +# CONFIG_AR0543 is not set +# CONFIG_A8140 is not set +# CONFIG_S5K3H2_SUNNY_Q8S02E is not set +CONFIG_AR0542=y +# CONFIG_MSM_CAMERA_FLASH_SC628A is not set +# CONFIG_MSM_CAMERA_FLASH_TPS61310 is not set +# CONFIG_IMX072 is not set +# CONFIG_OV2720 is not set +CONFIG_OV8825=y +CONFIG_MSM_CAMERA_FLASH=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_ACTUATOR=y +# CONFIG_MSM_EEPROM is not set +# CONFIG_IMX074_EEPROM is not set +# CONFIG_IMX091_EEPROM is not set +CONFIG_QUP_EXCLUSIVE_TO_CAMERA=y +# CONFIG_S5K3L1YX is not set +# CONFIG_IMX091 is not set +# CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE is not set +# CONFIG_OV7692 is not set +# CONFIG_OV7695 is not set +# CONFIG_OV7695_RAW is not set +# CONFIG_MT9V115 is not set +# CONFIG_OV5647_DALING_OVBD631AC is not set +# CONFIG_OV5647_PARTRON_CM500 is not set +# CONFIG_OV5647_HQ_O9B5_DG806T is not set +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +CONFIG_RADIO_ADAPTERS=y +# CONFIG_RADIO_SI470X is not set +# CONFIG_USB_MR800 is not set +# CONFIG_USB_DSBR is not set +# CONFIG_I2C_SI4713 is not set +# CONFIG_RADIO_SI4713 is not set +# CONFIG_USB_KEENE is not set +# CONFIG_RADIO_TEA5764 is not set +# CONFIG_RADIO_SAA7706H is not set +# CONFIG_RADIO_TEF6862 is not set +# CONFIG_RADIO_WL1273 is not set + +# +# Texas Instruments WL128x FM driver (ST based) +# +# CONFIG_RADIO_WL128X is not set +CONFIG_RADIO_TAVARUA=y +# CONFIG_RADIO_IRIS is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y +CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES=y +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_MSM_VIDC_CONTENT_PROTECTION is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_MIPI_OTM9608A_PT=y +# CONFIG_FB_MSM_BACKLIGHT is not set +# CONFIG_FB_MSM_LOGO is not set +CONFIG_FB_MSM_LCDC_HW=y +CONFIG_FB_MSM_TRIPLE_BUFFER=y +CONFIG_FB_MSM_MDP_HW=y +# CONFIG_FB_MSM_MDP22 is not set +CONFIG_FB_MSM_MDP30=y +CONFIG_FB_MSM_MDP303=y +# CONFIG_FB_MSM_MDP31 is not set +# CONFIG_FB_MSM_MDP40 is not set +# CONFIG_FB_MSM_MDSS is not set +# CONFIG_FB_MSM_MDP_NONE is not set +# CONFIG_FB_MSM_EBI2 is not set +# CONFIG_FB_MSM_MDDI is not set +CONFIG_FB_MSM_MIPI_DSI=y +CONFIG_FB_MSM_LCDC=y +# CONFIG_FB_MSM_LVDS is not set +# CONFIG_FB_MSM_EXTMDDI is not set +# CONFIG_FB_MSM_TVOUT is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_COMMON is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_COMMON_VGA is not set +# CONFIG_FB_MSM_MDDI_ORISE is not set +# CONFIG_FB_MSM_MDDI_QUICKVX is not set +# CONFIG_FB_MSM_MDDI_AUTO_DETECT is not set +# CONFIG_FB_MSM_LCDC_AUTO_DETECT is not set +CONFIG_FB_MSM_LCDC_PANEL=y +CONFIG_FB_MSM_MIPI_DSI_TOSHIBA=y +CONFIG_FB_MSM_MIPI_DSI_RENESAS=y +CONFIG_FB_MSM_MIPI_DSI_TRULY=y +CONFIG_FB_MSM_MIPI_DSI_SIMULATOR=y +CONFIG_FB_MSM_MIPI_DSI_NOVATEK=y +CONFIG_FB_MSM_MIPI_DSI_NT35510=y +CONFIG_FB_MSM_MIPI_DSI_HX8389B=y +CONFIG_FB_MSM_MIPI_DSI_HX8392A=y +CONFIG_FB_MSM_MIPI_DSI_OTM9605A=y +CONFIG_FB_MSM_MIPI_DSI_ORISE=y +CONFIG_FB_MSM_MIPI_DSI_NT35516=y +CONFIG_FB_MSM_MIPI_DSI_NT35590=y +CONFIG_FB_MSM_MIPI_DSI_TC358764_DSI2LVDS=y +# CONFIG_FB_MSM_LCDC_ST15_WXGA is not set +# CONFIG_FB_MSM_LCDC_ST15_PANEL is not set +# CONFIG_FB_MSM_LCDC_PRISM_WVGA is not set +# CONFIG_FB_MSM_LCDC_SAMSUNG_WSVGA is not set +# CONFIG_FB_MSM_LCDC_CHIMEI_WXGA is not set +# CONFIG_FB_MSM_LCDC_GORDON_VGA is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_WVGA_PT is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_FWVGA_PT is not set +# CONFIG_FB_MSM_LCDC_SHARP_WVGA_PT is not set +# CONFIG_FB_MSM_LCDC_AUO_WVGA is not set +CONFIG_FB_MSM_LCDC_TRULY_HVGA_IPS3P2335=y +CONFIG_FB_MSM_LCDC_TRULY_HVGA_IPS3P2335_PT_PANEL=y +# CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT is not set +# CONFIG_FB_MSM_LCDC_NT35582_WVGA is not set +# CONFIG_FB_MSM_LCDC_WXGA is not set +# CONFIG_FB_MSM_LVDS_CHIMEI_WXGA is not set +# CONFIG_FB_MSM_LVDS_FRC_FHD is not set +CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT=y +CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT=y +CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA=y +CONFIG_FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT=y +CONFIG_FB_MSM_MIPI_NOVATEK_CMD_QHD_PT=y +CONFIG_FB_MSM_MIPI_ORISE_VIDEO_720P_PT=y +CONFIG_FB_MSM_MIPI_ORISE_CMD_720P_PT=y +CONFIG_FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT=y +CONFIG_FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT=y +CONFIG_FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT=y +CONFIG_FB_MSM_MIPI_NT35510_CMD_WVGA_PT=y +CONFIG_FB_MSM_MIPI_HX8389B_VIDEO_QHD_PT=y +CONFIG_FB_MSM_MIPI_HX8392A_VIDEO_720P_PT=y +CONFIG_FB_MSM_MIPI_OTM9605A_VIDEO_QHD_PT=y +CONFIG_FB_MSM_MIPI_NT35516_VIDEO_QHD_PT=y +CONFIG_FB_MSM_MIPI_NT35516_CMD_QHD_PT=y +CONFIG_FB_MSM_MIPI_NT35590_CMD_720P_PT=y +CONFIG_FB_MSM_MIPI_NT35590_VIDEO_720P_PT=y +# CONFIG_FB_MSM_MIPI_DIV4_WVGA_NT35510_PANEL is not set +CONFIG_FB_MSM_MIPI_CHIMEI_WXGA=y +CONFIG_FB_MSM_MIPI_CHIMEI_WUXGA=y +CONFIG_FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT=y +CONFIG_FB_MSM_MIPI_SIMULATOR_VIDEO=y +# CONFIG_FB_MSM_LCDC_PRISM_WVGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_SAMSUNG_WSVGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_CHIMEI_WXGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_GORDON_VGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_LCDC_TOSHIBA_FWVGA_PT_PANEL is not set +# CONFIG_FB_MSM_LCDC_SHARP_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_LCDC_AUO_WVGA_PANEL is not set +# CONFIG_FB_MSM_LCDC_NT35582_PANEL is not set +# CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT_PANEL is not set +# CONFIG_FB_MSM_LVDS_CHIMEI_WXGA_PANEL is not set +# CONFIG_FB_MSM_LVDS_FRC_FHD_PANEL is not set +# CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM is not set +CONFIG_FB_MSM_MIPI_PANEL_DETECT=y +# CONFIG_FB_MSM_MDDI_PANEL_AUTO_DETECT is not set +# CONFIG_FB_MSM_LCDC_PANEL_AUTO_DETECT is not set +# CONFIG_FB_MSM_LCDC_MIPI_PANEL_AUTO_DETECT is not set +# CONFIG_FB_MSM_LVDS_MIPI_PANEL_DETECT is not set +# CONFIG_FB_MSM_MDDI_PRISM_WVGA is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_WVGA_PORTRAIT is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_VGA is not set +# CONFIG_FB_MSM_MDDI_TOSHIBA_WVGA is not set +# CONFIG_FB_MSM_MDDI_SHARP_QVGA_128x128 is not set +# CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA_PANEL is not set +# CONFIG_FB_MSM_MIPI_NOVATEK_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NOVATEK_CMD_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_ORISE_VIDEO_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_ORISE_CMD_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_RENESAS_VIDEO_FWVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_RENESAS_CMD_FWVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_CHIMEI_WXGA_PANEL is not set +# CONFIG_FB_MSM_MIPI_CHIMEI_WUXGA_PANEL is not set +# CONFIG_FB_MSM_MIPI_TRULY_VIDEO_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35510_VIDEO_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35510_CMD_WVGA_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_HX8389B_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_HX8392A_VIDEO_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_OTM9605A_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35516_VIDEO_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35516_CMD_QHD_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35590_CMD_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_NT35590_VIDEO_720P_PT_PANEL is not set +# CONFIG_FB_MSM_MIPI_SIMULATOR_VIDEO_PANEL is not set +# CONFIG_FB_MSM_EBI2_TMD_QVGA_EPSON_QCIF is not set +# CONFIG_FB_MSM_PANEL_NONE is not set +# CONFIG_FB_MSM_EXT_INTERFACE_COMMON is not set +# CONFIG_FB_MSM_HDMI_COMMON is not set +# CONFIG_FB_MSM_HDMI_3D is not set +# CONFIG_FB_MSM_DEFAULT_DEPTH_RGB565 is not set +# CONFIG_FB_MSM_DEFAULT_DEPTH_ARGB8888 is not set +CONFIG_FB_MSM_DEFAULT_DEPTH_RGBA8888=y +# CONFIG_FB_MSM_EBI2_EPSON_S1D_QVGA_PANEL is not set +# CONFIG_FB_MSM_EBI2_PANEL_DETECT is not set +# CONFIG_EXYNOS_VIDEO is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=m +# CONFIG_LCD_PLATFORM is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=y +# CONFIG_BACKLIGHT_ADP8860 is not set +# CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_LP855X is not set +# CONFIG_LOGO 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_RAWMIDI=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER 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_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_SOC=y + +# +# MSM SoC Audio support +# +CONFIG_SND_MSM_DAI_SOC=y +CONFIG_SND_MSM_SOC_MSM7K=y +CONFIG_SND_MSM_SOC=y +# CONFIG_SND_SOC_QDSP6 is not set +# CONFIG_SND_SOC_QDSP6V2 is not set +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +CONFIG_USB_HIDDEV=y + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +# CONFIG_HOLTEK_FF is not set +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_LOGIWHEELS_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +# CONFIG_HID_PICOLCD_FB is not set +# CONFIG_HID_PICOLCD_BACKLIGHT is not set +# CONFIG_HID_PICOLCD_LEDS is not set +CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +# CONFIG_THRUSTMASTER_FF is not set +CONFIG_HID_WACOM=y +# CONFIG_HID_WACOM_POWER_SUPPLY is not set +CONFIG_HID_WIIMOTE=y +CONFIG_HID_WIIMOTE_EXT=y +CONFIG_HID_ZEROPLUS=y +# CONFIG_ZEROPLUS_FF is not set +CONFIG_HID_ZYDACRON=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_SUPPORT_NON_STANDARD_WALL_CHARGER=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=y +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y +# CONFIG_USB_OTG 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_EHCI_HCD=y +CONFIG_USB_EHCI_EHSET=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +# CONFIG_USB_EHCI_MSM is not set +# CONFIG_USB_EHCI_MSM_HSIC is not set +# CONFIG_USB_OXU210HP_HCD is not set +CONFIG_USB_EHCI_MSM_72K=y +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_EHCI_HCD_PLATFORM is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_PEHCI_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=y +# 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=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_ALAUDA=y +CONFIG_USB_STORAGE_ONETOUCH=y +CONFIG_USB_STORAGE_KARMA=y +CONFIG_USB_STORAGE_CYPRESS_ATACB=y +# 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_SISUSBVGA 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_EHSET_TEST_FIXTURE=y +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_QCOM_DIAG_BRIDGE is not set +# CONFIG_USB_QCOM_MDM_BRIDGE is not set +# CONFIG_USB_QCOM_KS_BRIDGE 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=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 + +# +# USB Peripheral Controller +# +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_MV_UDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_CI13XXX_MSM is not set +# CONFIG_USB_CI13XXX_MSM_HSIC is not set +# CONFIG_USB_DWC3_MSM is not set +CONFIG_USB_MSM_72K=y +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_GADGET_SUPERSPEED is not set +# 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_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_G_ANDROID=y +# 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 +CONFIG_USB_CSW_HACK=y +# CONFIG_USB_MSC_PROFILING is not set +CONFIG_MODEM_SUPPORT=y +CONFIG_RMNET_SMD_CTL_CHANNEL="DATA40_CNTL" +CONFIG_RMNET_SMD_DATA_CHANNEL="DATA40" +# CONFIG_USB_ANDROID_RMNET_CTRL_SMD is not set + +# +# OTG and related infrastructure +# +CONFIG_USB_OTG_UTILS=y +CONFIG_USB_OTG_WAKELOCK=y +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +CONFIG_USB_MSM_OTG_72K=y +# CONFIG_MSM_OTG_ENABLE_A_WAIT_BCON_TIMEOUT is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG is not set +# CONFIG_USB_MSM_ACA is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +# CONFIG_MMC_EMBEDDED_SDIO is not set +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=32 +# CONFIG_MMC_BLOCK_BOUNCE is not set +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +CONFIG_MMC_TEST=m +CONFIG_MMC_BLOCK_TEST=m + +# +# 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_MSM=y +CONFIG_MMC_MSM_RAW=y +CONFIG_MMC_MSM_SDC1_SUPPORT=y +# CONFIG_MMC_MSM_SDC1_8_BIT_SUPPORT is not set +CONFIG_MMC_MSM_SDC2_SUPPORT=y +# CONFIG_MMC_MSM_SDC2_8_BIT_SUPPORT is not set +CONFIG_MMC_MSM_SDC3_SUPPORT=y +CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT=y +# CONFIG_MMC_MSM_SDC3_WP_SUPPORT is not set +# CONFIG_MMC_MSM_SDC4_SUPPORT is not set +# CONFIG_MMC_MSM_SDC5_SUPPORT is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_MSM_PDM=y +CONFIG_LEDS_PMIC_MPP=y +CONFIG_LEDS_MSM_TRICOLOR=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_CPLD 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_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_MSM_PMIC is not set +CONFIG_LEDS_TRICOLOR_FLAHSLIGHT=y +# CONFIG_LEDS_PMIC8029 is not set +# 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 is not set + +# +# LED Triggers +# +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# 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_ALARM=y +CONFIG_RTC_INTF_ALARM_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 +# + +# +# 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_MSM=y +CONFIG_RTC_SECURE_TIME_SUPPORT=y +# CONFIG_RTC_DRV_MSM7X00A 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 +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +CONFIG_STAGING=y +# CONFIG_USBIP_CORE is not set +# CONFIG_PRISM2_USB is not set +# CONFIG_ECHO is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_RTLLIB is not set +# CONFIG_R8712U is not set +# CONFIG_RTS5139 is not set +# CONFIG_TRANZPORT is not set +# CONFIG_LINE6_USB is not set +# CONFIG_VT6656 is not set +# CONFIG_IIO is not set +# CONFIG_SNAPPY_COMPRESS is not set +# CONFIG_SNAPPY_DECOMPRESS is not set +# CONFIG_FB_SM7XX is not set +# CONFIG_ZCACHE is not set +# CONFIG_USB_ENESTORAGE is not set +# CONFIG_BCM_WIMAX is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_STAGING_MEDIA is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +# CONFIG_ANDROID_LOGGER_SYSFS is not set +CONFIG_ANDROID_PERSISTENT_RAM=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y +# CONFIG_ANDROID_SWITCH is not set +# CONFIG_ANDROID_INTF_ALARM_DEV is not set +# CONFIG_PHONE is not set +# CONFIG_USB_WPAN_HCD is not set + +# +# Qualcomm MSM specific device drivers +# +# CONFIG_MSM_SSBI is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y + +# +# Hardware Spinlock drivers +# +CONFIG_IOMMU_SUPPORT=y + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_MOBICORE_SUPPORT is not set +# CONFIG_MSM_QDSS is not set + +# +# NVBK share memory +# +CONFIG_NVBK_SHARE_MEM=y + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_SECURITY is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +CONFIG_EXT4_FS_SECURITY=y +# CONFIG_EXT4_DEBUG is not set +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_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FS_POSIX_ACL=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=y +# CONFIG_CUSE 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_EXFAT_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_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_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_LOGFS 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_F2FS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_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=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 is not set +# CONFIG_DYNAMIC_FSYNC is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_PRINTK_RTC_TIME=y +CONFIG_PRINTK_CPU_ID=y +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# 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=y +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +CONFIG_DEBUG_SPINLOCK=y +# 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=y +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_HIGHMEM is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED 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_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_RCU_CPU_STALL_VERBOSE=y +# CONFIG_RCU_CPU_STALL_INFO is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_KPROBES_SANITY_TEST 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_DEBUG_PER_CPU_MAPS is not set +# CONFIG_LKDTM is not set +# CONFIG_CPU_NOTIFIER_ERROR_INJECT is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_NOP_TRACER=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +CONFIG_KPROBE_EVENT=y +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +CONFIG_DYNAMIC_DEBUG=y +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_ARM_KPROBES_TEST is not set +# CONFIG_PID_IN_CONTEXTIDR is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY=y +# CONFIG_SECURITYFS is not set +CONFIG_SECURITY_NETWORK=y +# CONFIG_SECURITY_NETWORK_XFRM is not set +# CONFIG_SECURITY_PATH is not set +CONFIG_LSM_MMAP_MIN_ADDR=32768 +CONFIG_SECURITY_SELINUX=y +# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set +# CONFIG_SECURITY_SELINUX_DISABLE is not set +CONFIG_SECURITY_SELINUX_DEVELOP=y +CONFIG_SECURITY_SELINUX_FORCE_PERMISSIVE=y +CONFIG_SECURITY_SELINUX_AVC_STATS=y +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_SECURITY_YAMA is not set +# CONFIG_IMA is not set +CONFIG_DEFAULT_SECURITY_SELINUX=y +# CONFIG_DEFAULT_SECURITY_DAC is not set +CONFIG_DEFAULT_SECURITY="selinux" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=m +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_PCRYPT is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# 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=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# 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=y +# CONFIG_CRYPTO_SHA1_ARM is not set +CONFIG_CRYPTO_SHA256=y +# 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_AES_ARM is not set +# CONFIG_CRYPTO_AES_ARM_BS is not set +# 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=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=m +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_CRYPTO_DEV_QCRYPTO is not set +# CONFIG_CRYPTO_DEV_QCE is not set +# CONFIG_CRYPTO_DEV_QCEDEV is not set +# CONFIG_CRYPTO_DEV_OTA_CRYPTO is not set +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_CRC_CCITT=y +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=y +# CONFIG_CRC8 is not set +CONFIG_AUDIT_GENERIC=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_LZ4_COMPRESS=y +CONFIG_LZ4_DECOMPRESS=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_NLATTR=y +# CONFIG_AVERAGE is not set +# CONFIG_CORDIC is not set